summaryrefslogtreecommitdiff
path: root/Utilities
diff options
context:
space:
mode:
authorDongHun Kwak <dh0128.kwak@samsung.com>2021-10-08 09:14:03 +0900
committerDongHun Kwak <dh0128.kwak@samsung.com>2021-10-08 09:14:03 +0900
commitd140263a497b4a86818ab5e2017a66df43eb83fb (patch)
tree4acfe8c906ce669c5fc92689df2c3c83a32d881c /Utilities
parente1763ae434c946bd1c1e9a7cc66a905ebe027bbd (diff)
downloadcmake-d140263a497b4a86818ab5e2017a66df43eb83fb.tar.gz
cmake-d140263a497b4a86818ab5e2017a66df43eb83fb.tar.bz2
cmake-d140263a497b4a86818ab5e2017a66df43eb83fb.zip
Imported Upstream version 3.15.0upstream/3.15.0
Diffstat (limited to 'Utilities')
-rw-r--r--Utilities/Doxygen/CMakeLists.txt2
-rw-r--r--Utilities/IWYU/mapping.imp1
-rw-r--r--Utilities/Release/linux64_release.cmake1
-rw-r--r--Utilities/Release/osx_release.cmake1
-rwxr-xr-xUtilities/Release/push.bash70
-rwxr-xr-xUtilities/Release/release_cmake.sh.in2
-rw-r--r--Utilities/Release/upload_release.cmake39
-rw-r--r--Utilities/Release/win32_release.cmake15
-rw-r--r--Utilities/Release/win64_release.cmake15
-rwxr-xr-xUtilities/Scripts/update-curl.bash2
-rwxr-xr-xUtilities/Scripts/update-expat.bash2
-rw-r--r--Utilities/Scripts/update-third-party.bash24
-rwxr-xr-xUtilities/Scripts/update-zstd.bash36
-rw-r--r--Utilities/Sphinx/CMakeLists.txt2
-rw-r--r--Utilities/cmThirdParty.h.in1
-rw-r--r--Utilities/cm_zstd.h14
-rw-r--r--Utilities/cmcompress/CMakeLists.txt5
-rw-r--r--Utilities/cmcompress/Copyright.txt34
-rw-r--r--Utilities/cmcompress/cmcompress.c551
-rw-r--r--Utilities/cmcompress/cmcompress.h195
-rw-r--r--Utilities/cmcompress/compress.c.original1308
-rw-r--r--Utilities/cmcurl/CMake/CurlTests.c52
-rw-r--r--Utilities/cmcurl/CMake/FindGSS.cmake4
-rw-r--r--Utilities/cmcurl/CMake/OtherTests.cmake67
-rw-r--r--Utilities/cmcurl/CMakeLists.txt75
-rw-r--r--Utilities/cmcurl/COPYING2
-rw-r--r--Utilities/cmcurl/include/curl/curl.h66
-rw-r--r--Utilities/cmcurl/include/curl/curlver.h12
-rw-r--r--Utilities/cmcurl/include/curl/typecheck-gcc.h14
-rw-r--r--Utilities/cmcurl/include/curl/urlapi.h7
-rw-r--r--Utilities/cmcurl/lib/Makefile.inc19
-rw-r--r--Utilities/cmcurl/lib/altsvc.c569
-rw-r--r--Utilities/cmcurl/lib/altsvc.h77
-rw-r--r--Utilities/cmcurl/lib/amigaos.c32
-rw-r--r--Utilities/cmcurl/lib/amigaos.h10
-rw-r--r--Utilities/cmcurl/lib/asyn-ares.c157
-rw-r--r--Utilities/cmcurl/lib/asyn-thread.c70
-rw-r--r--Utilities/cmcurl/lib/asyn.h36
-rw-r--r--Utilities/cmcurl/lib/base64.c33
-rw-r--r--Utilities/cmcurl/lib/conncache.c38
-rw-r--r--Utilities/cmcurl/lib/conncache.h8
-rw-r--r--Utilities/cmcurl/lib/connect.c114
-rw-r--r--Utilities/cmcurl/lib/connect.h9
-rw-r--r--Utilities/cmcurl/lib/cookie.c202
-rw-r--r--Utilities/cmcurl/lib/cookie.h13
-rw-r--r--Utilities/cmcurl/lib/curl_addrinfo.c32
-rw-r--r--Utilities/cmcurl/lib/curl_addrinfo.h13
-rw-r--r--Utilities/cmcurl/lib/curl_config.h.cmake10
-rw-r--r--Utilities/cmcurl/lib/curl_endian.c10
-rw-r--r--Utilities/cmcurl/lib/curl_fnmatch.c15
-rw-r--r--Utilities/cmcurl/lib/curl_get_line.c55
-rw-r--r--Utilities/cmcurl/lib/curl_get_line.h (renamed from Utilities/cmcurl/lib/vtls/axtls.h)18
-rw-r--r--Utilities/cmcurl/lib/curl_gssapi.c6
-rw-r--r--Utilities/cmcurl/lib/curl_gssapi.h16
-rw-r--r--Utilities/cmcurl/lib/curl_md4.h4
-rw-r--r--Utilities/cmcurl/lib/curl_md5.h10
-rw-r--r--Utilities/cmcurl/lib/curl_memory.h4
-rw-r--r--Utilities/cmcurl/lib/curl_multibyte.c12
-rw-r--r--Utilities/cmcurl/lib/curl_ntlm_core.c69
-rw-r--r--Utilities/cmcurl/lib/curl_ntlm_core.h4
-rw-r--r--Utilities/cmcurl/lib/curl_ntlm_wb.c86
-rw-r--r--Utilities/cmcurl/lib/curl_ntlm_wb.h11
-rw-r--r--Utilities/cmcurl/lib/curl_path.c6
-rw-r--r--Utilities/cmcurl/lib/curl_printf.h16
-rw-r--r--Utilities/cmcurl/lib/curl_rtmp.c9
-rw-r--r--Utilities/cmcurl/lib/curl_sasl.c40
-rw-r--r--Utilities/cmcurl/lib/curl_setup.h29
-rw-r--r--Utilities/cmcurl/lib/curlx.h11
-rw-r--r--Utilities/cmcurl/lib/dict.c11
-rw-r--r--Utilities/cmcurl/lib/doh.c127
-rw-r--r--Utilities/cmcurl/lib/doh.h10
-rw-r--r--Utilities/cmcurl/lib/easy.c111
-rw-r--r--Utilities/cmcurl/lib/escape.c2
-rw-r--r--Utilities/cmcurl/lib/file.c29
-rw-r--r--Utilities/cmcurl/lib/fileinfo.c5
-rw-r--r--Utilities/cmcurl/lib/formdata.c14
-rw-r--r--Utilities/cmcurl/lib/formdata.h11
-rw-r--r--Utilities/cmcurl/lib/ftp.c103
-rw-r--r--Utilities/cmcurl/lib/ftp.h3
-rw-r--r--Utilities/cmcurl/lib/ftplistparser.c7
-rw-r--r--Utilities/cmcurl/lib/getinfo.c8
-rw-r--r--Utilities/cmcurl/lib/gopher.c28
-rw-r--r--Utilities/cmcurl/lib/hostasyn.c8
-rw-r--r--Utilities/cmcurl/lib/hostcheck.c11
-rw-r--r--Utilities/cmcurl/lib/hostip.c153
-rw-r--r--Utilities/cmcurl/lib/hostip.h19
-rw-r--r--Utilities/cmcurl/lib/hostip4.c4
-rw-r--r--Utilities/cmcurl/lib/hostip6.c14
-rw-r--r--Utilities/cmcurl/lib/http.c492
-rw-r--r--Utilities/cmcurl/lib/http.h9
-rw-r--r--Utilities/cmcurl/lib/http2.c76
-rw-r--r--Utilities/cmcurl/lib/http2.h7
-rw-r--r--Utilities/cmcurl/lib/http_digest.c5
-rw-r--r--Utilities/cmcurl/lib/http_digest.h12
-rw-r--r--Utilities/cmcurl/lib/http_negotiate.c132
-rw-r--r--Utilities/cmcurl/lib/http_negotiate.h8
-rw-r--r--Utilities/cmcurl/lib/http_ntlm.c40
-rw-r--r--Utilities/cmcurl/lib/http_ntlm.h10
-rw-r--r--Utilities/cmcurl/lib/http_proxy.c4
-rw-r--r--Utilities/cmcurl/lib/if2ip.c59
-rw-r--r--Utilities/cmcurl/lib/if2ip.h6
-rw-r--r--Utilities/cmcurl/lib/imap.c41
-rw-r--r--Utilities/cmcurl/lib/inet_ntop.c12
-rw-r--r--Utilities/cmcurl/lib/inet_pton.c3
-rw-r--r--Utilities/cmcurl/lib/ldap.c22
-rw-r--r--Utilities/cmcurl/lib/libcurl.rc12
-rw-r--r--Utilities/cmcurl/lib/md4.c114
-rw-r--r--Utilities/cmcurl/lib/md5.c174
-rw-r--r--Utilities/cmcurl/lib/memdebug.c154
-rw-r--r--Utilities/cmcurl/lib/memdebug.h123
-rw-r--r--Utilities/cmcurl/lib/mime.c94
-rw-r--r--Utilities/cmcurl/lib/mime.h27
-rw-r--r--Utilities/cmcurl/lib/mprintf.c2
-rw-r--r--Utilities/cmcurl/lib/multi.c916
-rw-r--r--Utilities/cmcurl/lib/multihandle.h48
-rw-r--r--Utilities/cmcurl/lib/multiif.h33
-rw-r--r--Utilities/cmcurl/lib/netrc.c16
-rw-r--r--Utilities/cmcurl/lib/netrc.h11
-rw-r--r--Utilities/cmcurl/lib/non-ascii.c4
-rw-r--r--Utilities/cmcurl/lib/openldap.c13
-rw-r--r--Utilities/cmcurl/lib/parsedate.c41
-rw-r--r--Utilities/cmcurl/lib/pingpong.c9
-rw-r--r--Utilities/cmcurl/lib/pingpong.h7
-rw-r--r--Utilities/cmcurl/lib/pipeline.c404
-rw-r--r--Utilities/cmcurl/lib/pipeline.h56
-rw-r--r--Utilities/cmcurl/lib/pop3.c32
-rw-r--r--Utilities/cmcurl/lib/progress.c150
-rw-r--r--Utilities/cmcurl/lib/rand.h7
-rw-r--r--Utilities/cmcurl/lib/rtsp.c35
-rw-r--r--Utilities/cmcurl/lib/security.c19
-rw-r--r--Utilities/cmcurl/lib/sendf.c63
-rw-r--r--Utilities/cmcurl/lib/setopt.c358
-rw-r--r--Utilities/cmcurl/lib/sigpipe.h5
-rw-r--r--Utilities/cmcurl/lib/smb.c9
-rw-r--r--Utilities/cmcurl/lib/smtp.c33
-rw-r--r--Utilities/cmcurl/lib/socks.c26
-rw-r--r--Utilities/cmcurl/lib/socks_gssapi.c6
-rw-r--r--Utilities/cmcurl/lib/socks_sspi.c11
-rw-r--r--Utilities/cmcurl/lib/splay.c4
-rw-r--r--Utilities/cmcurl/lib/ssh-libssh.c90
-rw-r--r--Utilities/cmcurl/lib/ssh.c92
-rw-r--r--Utilities/cmcurl/lib/strerror.c44
-rw-r--r--Utilities/cmcurl/lib/strerror.h11
-rw-r--r--Utilities/cmcurl/lib/system_win32.c82
-rw-r--r--Utilities/cmcurl/lib/system_win32.h6
-rw-r--r--Utilities/cmcurl/lib/telnet.c34
-rw-r--r--Utilities/cmcurl/lib/tftp.c46
-rw-r--r--Utilities/cmcurl/lib/timeval.c46
-rw-r--r--Utilities/cmcurl/lib/timeval.h4
-rw-r--r--Utilities/cmcurl/lib/transfer.c361
-rw-r--r--Utilities/cmcurl/lib/transfer.h20
-rw-r--r--Utilities/cmcurl/lib/url.c969
-rw-r--r--Utilities/cmcurl/lib/url.h14
-rw-r--r--Utilities/cmcurl/lib/urlapi-int.h12
-rw-r--r--Utilities/cmcurl/lib/urlapi.c230
-rw-r--r--Utilities/cmcurl/lib/urldata.h634
-rw-r--r--Utilities/cmcurl/lib/vauth/cleartext.c40
-rw-r--r--Utilities/cmcurl/lib/vauth/digest.c15
-rw-r--r--Utilities/cmcurl/lib/vauth/digest_sspi.c4
-rw-r--r--Utilities/cmcurl/lib/vauth/krb5_gssapi.c6
-rw-r--r--Utilities/cmcurl/lib/vauth/krb5_sspi.c6
-rw-r--r--Utilities/cmcurl/lib/vauth/ntlm.c262
-rw-r--r--Utilities/cmcurl/lib/vauth/ntlm_sspi.c51
-rw-r--r--Utilities/cmcurl/lib/vauth/oauth2.c56
-rw-r--r--Utilities/cmcurl/lib/vauth/spnego_gssapi.c14
-rw-r--r--Utilities/cmcurl/lib/vauth/spnego_sspi.c56
-rw-r--r--Utilities/cmcurl/lib/vauth/vauth.c42
-rw-r--r--Utilities/cmcurl/lib/vauth/vauth.h20
-rw-r--r--Utilities/cmcurl/lib/version.c55
-rw-r--r--Utilities/cmcurl/lib/vtls/axtls.c741
-rw-r--r--Utilities/cmcurl/lib/vtls/cyassl.c42
-rw-r--r--Utilities/cmcurl/lib/vtls/gskit.c20
-rw-r--r--Utilities/cmcurl/lib/vtls/gtls.c82
-rw-r--r--Utilities/cmcurl/lib/vtls/mbedtls.c45
-rw-r--r--Utilities/cmcurl/lib/vtls/mesalink.c8
-rw-r--r--Utilities/cmcurl/lib/vtls/nss.c90
-rw-r--r--Utilities/cmcurl/lib/vtls/openssl.c309
-rw-r--r--Utilities/cmcurl/lib/vtls/polarssl.c17
-rw-r--r--Utilities/cmcurl/lib/vtls/polarssl_threadlock.c59
-rw-r--r--Utilities/cmcurl/lib/vtls/polarssl_threadlock.h9
-rw-r--r--Utilities/cmcurl/lib/vtls/schannel.c345
-rw-r--r--Utilities/cmcurl/lib/vtls/schannel.h4
-rw-r--r--Utilities/cmcurl/lib/vtls/schannel_verify.c55
-rw-r--r--Utilities/cmcurl/lib/vtls/sectransp.c (renamed from Utilities/cmcurl/lib/vtls/darwinssl.c)194
-rw-r--r--Utilities/cmcurl/lib/vtls/sectransp.h (renamed from Utilities/cmcurl/lib/vtls/darwinssl.h)14
-rw-r--r--Utilities/cmcurl/lib/vtls/vtls.c67
-rw-r--r--Utilities/cmcurl/lib/vtls/vtls.h9
-rw-r--r--Utilities/cmcurl/lib/warnless.c40
-rw-r--r--Utilities/cmcurl/lib/warnless.h6
-rw-r--r--Utilities/cmcurl/lib/wildcard.c6
-rw-r--r--Utilities/cmcurl/lib/wildcard.h10
-rw-r--r--Utilities/cmcurl/lib/x509asn1.c269
-rw-r--r--Utilities/cmexpat/CMakeLists.txt2
-rw-r--r--Utilities/cmexpat/ConfigureChecks.cmake35
-rw-r--r--Utilities/cmexpat/README.md55
-rw-r--r--Utilities/cmexpat/expat_config.h.cmake30
-rw-r--r--Utilities/cmexpat/lib/ascii.h32
-rw-r--r--Utilities/cmexpat/lib/asciitab.h32
-rw-r--r--Utilities/cmexpat/lib/expat.h36
-rw-r--r--Utilities/cmexpat/lib/expat_external.h92
-rw-r--r--Utilities/cmexpat/lib/iasciitab.h32
-rw-r--r--Utilities/cmexpat/lib/internal.h36
-rw-r--r--Utilities/cmexpat/lib/latin1tab.h32
-rw-r--r--Utilities/cmexpat/lib/loadlibrary.c4
-rw-r--r--Utilities/cmexpat/lib/nametab.h32
-rw-r--r--Utilities/cmexpat/lib/siphash.h17
-rw-r--r--Utilities/cmexpat/lib/utf8tab.h33
-rw-r--r--Utilities/cmexpat/lib/winconfig.h41
-rw-r--r--Utilities/cmexpat/lib/xmlparse.c2715
-rw-r--r--Utilities/cmexpat/lib/xmlrole.c32
-rw-r--r--Utilities/cmexpat/lib/xmlrole.h32
-rw-r--r--Utilities/cmexpat/lib/xmltok.c90
-rw-r--r--Utilities/cmexpat/lib/xmltok.h37
-rw-r--r--Utilities/cmexpat/lib/xmltok_impl.c115
-rw-r--r--Utilities/cmexpat/lib/xmltok_impl.h31
-rw-r--r--Utilities/cmexpat/lib/xmltok_ns.c33
-rw-r--r--Utilities/cmjsoncpp/CMakeLists.txt2
-rw-r--r--Utilities/cmjsoncpp/include/json/config.h17
-rw-r--r--Utilities/cmlibarchive/CMakeLists.txt8
-rw-r--r--Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c2
-rw-r--r--Utilities/cmlibarchive/libarchive/archive_version_details.c2
-rw-r--r--Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c2
-rw-r--r--Utilities/cmliblzma/CMakeLists.txt2
-rw-r--r--Utilities/cmlibrhash/CMakeLists.txt2
-rw-r--r--Utilities/cmlibuv/CMakeLists.txt2
-rw-r--r--Utilities/cmlibuv/src/unix/atomic-ops.h4
-rw-r--r--Utilities/cmzlib/CMakeLists.txt2
-rw-r--r--Utilities/cmzstd/CMakeLists.txt47
-rw-r--r--Utilities/cmzstd/LICENSE30
-rw-r--r--Utilities/cmzstd/README.md167
-rw-r--r--Utilities/cmzstd/lib/common/bitstream.h455
-rw-r--r--Utilities/cmzstd/lib/common/compiler.h140
-rw-r--r--Utilities/cmzstd/lib/common/cpu.h215
-rw-r--r--Utilities/cmzstd/lib/common/debug.c44
-rw-r--r--Utilities/cmzstd/lib/common/debug.h134
-rw-r--r--Utilities/cmzstd/lib/common/entropy_common.c236
-rw-r--r--Utilities/cmzstd/lib/common/error_private.c54
-rw-r--r--Utilities/cmzstd/lib/common/error_private.h76
-rw-r--r--Utilities/cmzstd/lib/common/fse.h708
-rw-r--r--Utilities/cmzstd/lib/common/fse_decompress.c309
-rw-r--r--Utilities/cmzstd/lib/common/huf.h358
-rw-r--r--Utilities/cmzstd/lib/common/mem.h380
-rw-r--r--Utilities/cmzstd/lib/common/pool.c340
-rw-r--r--Utilities/cmzstd/lib/common/pool.h84
-rw-r--r--Utilities/cmzstd/lib/common/threading.c75
-rw-r--r--Utilities/cmzstd/lib/common/threading.h123
-rw-r--r--Utilities/cmzstd/lib/common/xxhash.c876
-rw-r--r--Utilities/cmzstd/lib/common/xxhash.h305
-rw-r--r--Utilities/cmzstd/lib/common/zstd_common.c83
-rw-r--r--Utilities/cmzstd/lib/common/zstd_errors.h93
-rw-r--r--Utilities/cmzstd/lib/common/zstd_internal.h266
-rw-r--r--Utilities/cmzstd/lib/compress/fse_compress.c721
-rw-r--r--Utilities/cmzstd/lib/compress/hist.c203
-rw-r--r--Utilities/cmzstd/lib/compress/hist.h95
-rw-r--r--Utilities/cmzstd/lib/compress/huf_compress.c798
-rw-r--r--Utilities/cmzstd/lib/compress/zstd_compress.c4290
-rw-r--r--Utilities/cmzstd/lib/compress/zstd_compress_internal.h860
-rw-r--r--Utilities/cmzstd/lib/compress/zstd_double_fast.c499
-rw-r--r--Utilities/cmzstd/lib/compress/zstd_double_fast.h38
-rw-r--r--Utilities/cmzstd/lib/compress/zstd_fast.c391
-rw-r--r--Utilities/cmzstd/lib/compress/zstd_fast.h37
-rw-r--r--Utilities/cmzstd/lib/compress/zstd_lazy.c1106
-rw-r--r--Utilities/cmzstd/lib/compress/zstd_lazy.h67
-rw-r--r--Utilities/cmzstd/lib/compress/zstd_ldm.c597
-rw-r--r--Utilities/cmzstd/lib/compress/zstd_ldm.h105
-rw-r--r--Utilities/cmzstd/lib/compress/zstd_opt.c1217
-rw-r--r--Utilities/cmzstd/lib/compress/zstd_opt.h56
-rw-r--r--Utilities/cmzstd/lib/compress/zstdmt_compress.c2107
-rw-r--r--Utilities/cmzstd/lib/compress/zstdmt_compress.h174
-rw-r--r--Utilities/cmzstd/lib/decompress/huf_decompress.c1232
-rw-r--r--Utilities/cmzstd/lib/decompress/zstd_ddict.c240
-rw-r--r--Utilities/cmzstd/lib/decompress/zstd_ddict.h44
-rw-r--r--Utilities/cmzstd/lib/decompress/zstd_decompress.c1672
-rw-r--r--Utilities/cmzstd/lib/decompress/zstd_decompress_block.c1307
-rw-r--r--Utilities/cmzstd/lib/decompress/zstd_decompress_block.h59
-rw-r--r--Utilities/cmzstd/lib/decompress/zstd_decompress_internal.h168
-rw-r--r--Utilities/cmzstd/lib/deprecated/zbuff.h213
-rw-r--r--Utilities/cmzstd/lib/deprecated/zbuff_common.c26
-rw-r--r--Utilities/cmzstd/lib/deprecated/zbuff_compress.c147
-rw-r--r--Utilities/cmzstd/lib/deprecated/zbuff_decompress.c75
-rw-r--r--Utilities/cmzstd/lib/dictBuilder/cover.c1081
-rw-r--r--Utilities/cmzstd/lib/dictBuilder/cover.h83
-rw-r--r--Utilities/cmzstd/lib/dictBuilder/divsufsort.c1913
-rw-r--r--Utilities/cmzstd/lib/dictBuilder/divsufsort.h67
-rw-r--r--Utilities/cmzstd/lib/dictBuilder/fastcover.c728
-rw-r--r--Utilities/cmzstd/lib/dictBuilder/zdict.c1111
-rw-r--r--Utilities/cmzstd/lib/dictBuilder/zdict.h267
-rw-r--r--Utilities/cmzstd/lib/zstd.h1766
288 files changed, 39979 insertions, 9737 deletions
diff --git a/Utilities/Doxygen/CMakeLists.txt b/Utilities/Doxygen/CMakeLists.txt
index 2cdd7672d..ce4cfaf4b 100644
--- a/Utilities/Doxygen/CMakeLists.txt
+++ b/Utilities/Doxygen/CMakeLists.txt
@@ -3,7 +3,7 @@
if(NOT CMake_SOURCE_DIR)
set(CMakeDeveloperReference_STANDALONE 1)
- cmake_minimum_required(VERSION 3.1...3.12 FATAL_ERROR)
+ cmake_minimum_required(VERSION 3.1...3.14 FATAL_ERROR)
get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH)
get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH)
include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake)
diff --git a/Utilities/IWYU/mapping.imp b/Utilities/IWYU/mapping.imp
index 482a08dd8..0393ff199 100644
--- a/Utilities/IWYU/mapping.imp
+++ b/Utilities/IWYU/mapping.imp
@@ -68,6 +68,7 @@
{ symbol: [ "std::__decay_and_strip<cmGeneratorTarget *&>::__type", private, "\"cmConfigure.h\"", public ] },
{ symbol: [ "std::__decay_and_strip<cmFindCommon::PathLabel &>::__type", private, "\"cmConfigure.h\"", public ] },
{ symbol: [ "std::__decay_and_strip<cmSearchPath>::__type", private, "\"cmConfigure.h\"", public ] },
+ { symbol: [ "std::__decay_and_strip<cm::string_view>::__type", private, "\"cmConfigure.h\"", public ] },
{ symbol: [ "std::__decay_and_strip<std::basic_string<char> &>::__type", private, "\"cmConfigure.h\"", public ] },
{ symbol: [ "std::__decay_and_strip<const std::basic_string<char> &>::__type", private, "\"cmConfigure.h\"", public ] },
{ symbol: [ "std::__decay_and_strip<cmFindPackageCommand::PathLabel &>::__type", private, "\"cmConfigure.h\"", public ] },
diff --git a/Utilities/Release/linux64_release.cmake b/Utilities/Release/linux64_release.cmake
index fa585d74b..958ed2544 100644
--- a/Utilities/Release/linux64_release.cmake
+++ b/Utilities/Release/linux64_release.cmake
@@ -44,6 +44,7 @@ CMake_QT_STATIC_QXcbIntegrationPlugin_LIBRARIES:STRING=${qt_xcb_libs}
set(ENV [[
export CMAKE_PREFIX_PATH=/opt/binutils-2.31
]])
+set(SIGN "")
# Exclude Qt5 tests because our Qt5 is static.
set(EXTRA_CTEST_ARGS "-E Qt5")
diff --git a/Utilities/Release/osx_release.cmake b/Utilities/Release/osx_release.cmake
index be11d470b..ac35872ea 100644
--- a/Utilities/Release/osx_release.cmake
+++ b/Utilities/Release/osx_release.cmake
@@ -29,5 +29,6 @@ CMake_TEST_NO_FindPackageModeMakefileTest:BOOL=TRUE
set(ENV [[
export CMAKE_PREFIX_PATH='/Users/kitware/SDKs/qt-5.6.2-clang-x64'
]])
+set(SIGN "")
get_filename_component(path "${CMAKE_CURRENT_LIST_FILE}" PATH)
include(${path}/release_cmake.cmake)
diff --git a/Utilities/Release/push.bash b/Utilities/Release/push.bash
new file mode 100755
index 000000000..1c8efe900
--- /dev/null
+++ b/Utilities/Release/push.bash
@@ -0,0 +1,70 @@
+#!/usr/bin/env bash
+
+usage='usage: push.bash [<options>] [--] <dest>
+
+Options:
+
+ --dir <dir> Specify subdirectory under destination.
+ Defaults to "v<version>".
+ --version <ver> CMake <major>.<minor> version number to push.
+ Defaults to version of source tree.
+'
+
+die() {
+ echo "$@" 1>&2; exit 1
+}
+
+cmake_source_dir="${BASH_SOURCE%/*}/../.."
+
+cmake_version_component()
+{
+ sed -n "
+/^set(CMake_VERSION_${1}/ {s/set(CMake_VERSION_${1} *\([0-9]*\))/\1/;p;}
+" "${cmake_source_dir}/Source/CMakeVersion.cmake"
+}
+
+
+version=''
+dir=''
+while test "$#" != 0; do
+ case "$1" in
+ --dir) shift; dir="$1" ;;
+ --version) shift; version="$1" ;;
+ --) shift ; break ;;
+ -*) die "$usage" ;;
+ *) break ;;
+ esac
+ shift
+done
+test "$#" = 1 || die "$usage"
+readonly dest="$1"
+
+if test -z "$version"; then
+ cmake_version_major="$(cmake_version_component MAJOR)"
+ cmake_version_minor="$(cmake_version_component MINOR)"
+ version="${cmake_version_major}.${cmake_version_minor}"
+fi
+readonly version
+
+if test -z "$dir"; then
+ dir="v${version}"
+fi
+readonly dir
+
+for f in cmake-${version}*; do
+ if ! test -f "${f}"; then
+ continue
+ fi
+
+ echo "pushing '${f}'"
+
+ # Make a copy with a new timestamp and atomically rename into place.
+ tf="${dest}/${dir}/.tmp.${f}"
+ df="${dest}/${dir}/${f}"
+ cp "${f}" "${tf}"
+ mv "${tf}" "${df}"
+
+ # Pause to give each file a distinct time stamp even with 1s resolution
+ # so that sorting by time also sorts alphabetically.
+ sleep 1.1
+done
diff --git a/Utilities/Release/release_cmake.sh.in b/Utilities/Release/release_cmake.sh.in
index f363b3d8a..696a3f445 100755
--- a/Utilities/Release/release_cmake.sh.in
+++ b/Utilities/Release/release_cmake.sh.in
@@ -150,7 +150,7 @@ for GEN in $generators; do
check_exit_value $? "Create $GEN package" || exit 1
done
-
+@SIGN@
echo "End release"
date
diff --git a/Utilities/Release/upload_release.cmake b/Utilities/Release/upload_release.cmake
deleted file mode 100644
index 3613ae7a7..000000000
--- a/Utilities/Release/upload_release.cmake
+++ /dev/null
@@ -1,39 +0,0 @@
-set(CTEST_RUN_CURRENT_SCRIPT 0)
-if(NOT VERSION)
- set(VERSION 3.14)
-endif()
-if(NOT DEFINED PROJECT_PREFIX)
- set(PROJECT_PREFIX cmake-${VERSION})
-endif()
-if(NOT DEFINED DIR)
- set(DIR "v${VERSION}")
-endif()
-file(GLOB FILES ${CMAKE_CURRENT_SOURCE_DIR} "${PROJECT_PREFIX}*")
-list(SORT FILES)
-list(REVERSE FILES)
-message("${FILES}")
-set(UPLOAD_LOC
- "kitware@www.cmake.org:/projects/FTP/pub/cmake/${DIR}")
-set(count 0)
-foreach(file ${FILES})
- if(NOT IS_DIRECTORY ${file})
- message("upload ${file} ${UPLOAD_LOC}")
- execute_process(COMMAND
- scp ${file} ${UPLOAD_LOC}
- RESULT_VARIABLE result)
- if("${result}" GREATER 0)
- message(FATAL_ERROR "failed to upload file to ${UPLOAD_LOC}")
- endif()
-
- # Pause to give each upload a distinct (to the nearest second)
- # time stamp
- if(COMMAND ctest_sleep)
- ctest_sleep(2)
- endif()
-
- math(EXPR count "${count} + 1")
- endif()
-endforeach()
-if(${count} EQUAL 0)
- message(FATAL_ERROR "Error no files uploaded.")
-endif()
diff --git a/Utilities/Release/win32_release.cmake b/Utilities/Release/win32_release.cmake
index c03c66550..468e5f437 100644
--- a/Utilities/Release/win32_release.cmake
+++ b/Utilities/Release/win32_release.cmake
@@ -8,12 +8,17 @@ set(CPACK_BINARY_GENERATORS "WIX ZIP")
set(CPACK_SOURCE_GENERATORS "ZIP")
set(MAKE_PROGRAM "ninja")
set(MAKE "${MAKE_PROGRAM} -j16")
-set(qt_prefix "c:/Qt/5.6.3/msvc2017-32-xp-mt")
+set(qt_prefix "c:/Qt/5.12.1/msvc2017-32-w7-mt")
set(qt_win_libs
${qt_prefix}/plugins/platforms/qwindows.lib
- ${qt_prefix}/lib/Qt5PlatformSupport.lib
+ ${qt_prefix}/plugins/styles/qwindowsvistastyle.lib
+ ${qt_prefix}/lib/Qt5EventDispatcherSupport.lib
+ ${qt_prefix}/lib/Qt5FontDatabaseSupport.lib
+ ${qt_prefix}/lib/Qt5ThemeSupport.lib
${qt_prefix}/lib/qtfreetype.lib
+ ${qt_prefix}/lib/qtlibpng.lib
imm32.lib
+ wtsapi32.lib
)
set(INITIAL_CACHE "CMAKE_BUILD_TYPE:STRING=Release
CMAKE_DOC_DIR:STRING=doc/cmake
@@ -31,7 +36,7 @@ CMAKE_PREFIX_PATH:STRING=${qt_prefix}
CMake_TEST_Qt4:BOOL=OFF
CMake_TEST_Qt5:BOOL=OFF
")
-set(ppflags "-D_WIN32_WINNT=0x601 -DNTDDI_VERSION=0x06010000 -D_USING_V110_SDK71_")
+set(ppflags "-D_WIN32_WINNT=0x601 -DNTDDI_VERSION=0x06010000")
set(CFLAGS "${ppflags}")
set(CXXFLAGS "${ppflags}")
set(ENV ". ~/rel/env32")
@@ -40,5 +45,9 @@ set(GIT_EXTRA "git config core.autocrlf true")
if(CMAKE_CREATE_VERSION STREQUAL "nightly")
# Some tests fail spuriously too often.
set(EXTRA_CTEST_ARGS "-E 'ConsoleBuf|Module.ExternalData'")
+ set(SIGN "")
+else()
+ string(APPEND INITIAL_CACHE "CMake_INSTALL_SIGNTOOL:STRING=signtool\n")
+ set(SIGN [[signtool sign -v -a -tr http://timestamp.digicert.com -fd sha256 -td sha256 -d "CMake Windows Installer" cmake-*.msi]])
endif()
include(${path}/release_cmake.cmake)
diff --git a/Utilities/Release/win64_release.cmake b/Utilities/Release/win64_release.cmake
index 84172067d..5a93ce6b3 100644
--- a/Utilities/Release/win64_release.cmake
+++ b/Utilities/Release/win64_release.cmake
@@ -8,12 +8,17 @@ set(CPACK_BINARY_GENERATORS "WIX ZIP")
set(CPACK_SOURCE_GENERATORS "")
set(MAKE_PROGRAM "ninja")
set(MAKE "${MAKE_PROGRAM} -j16")
-set(qt_prefix "c:/Qt/5.6.3/msvc2017-64-xp-mt")
+set(qt_prefix "c:/Qt/5.12.1/msvc2017-64-w7-mt")
set(qt_win_libs
${qt_prefix}/plugins/platforms/qwindows.lib
- ${qt_prefix}/lib/Qt5PlatformSupport.lib
+ ${qt_prefix}/plugins/styles/qwindowsvistastyle.lib
+ ${qt_prefix}/lib/Qt5EventDispatcherSupport.lib
+ ${qt_prefix}/lib/Qt5FontDatabaseSupport.lib
+ ${qt_prefix}/lib/Qt5ThemeSupport.lib
${qt_prefix}/lib/qtfreetype.lib
+ ${qt_prefix}/lib/qtlibpng.lib
imm32.lib
+ wtsapi32.lib
)
set(INITIAL_CACHE "CMAKE_BUILD_TYPE:STRING=Release
CMAKE_DOC_DIR:STRING=doc/cmake
@@ -31,7 +36,7 @@ CMAKE_PREFIX_PATH:STRING=${qt_prefix}
CMake_TEST_Qt4:BOOL=OFF
CMake_TEST_Qt5:BOOL=OFF
")
-set(ppflags "-D_WIN32_WINNT=0x601 -DNTDDI_VERSION=0x06010000 -D_USING_V110_SDK71_")
+set(ppflags "-D_WIN32_WINNT=0x601 -DNTDDI_VERSION=0x06010000")
set(CFLAGS "${ppflags}")
set(CXXFLAGS "${ppflags}")
set(ENV ". ~/rel/env64")
@@ -40,5 +45,9 @@ set(GIT_EXTRA "git config core.autocrlf true")
if(CMAKE_CREATE_VERSION STREQUAL "nightly")
# Some tests fail spuriously too often.
set(EXTRA_CTEST_ARGS "-E 'ConsoleBuf|Module.ExternalData'")
+ set(SIGN "")
+else()
+ string(APPEND INITIAL_CACHE "CMake_INSTALL_SIGNTOOL:STRING=signtool\n")
+ set(SIGN [[signtool sign -v -a -tr http://timestamp.digicert.com -fd sha256 -td sha256 -d "CMake Windows Installer" cmake-*.msi]])
endif()
include(${path}/release_cmake.cmake)
diff --git a/Utilities/Scripts/update-curl.bash b/Utilities/Scripts/update-curl.bash
index fb46052e8..5c2a331c0 100755
--- a/Utilities/Scripts/update-curl.bash
+++ b/Utilities/Scripts/update-curl.bash
@@ -8,7 +8,7 @@ readonly name="curl"
readonly ownership="Curl Upstream <curl-library@cool.haxx.se>"
readonly subtree="Utilities/cmcurl"
readonly repo="https://github.com/curl/curl.git"
-readonly tag="curl-7_62_0"
+readonly tag="curl-7_65_0"
readonly shortlog=false
readonly paths="
CMake/*
diff --git a/Utilities/Scripts/update-expat.bash b/Utilities/Scripts/update-expat.bash
index 4ac5ef32c..0b52ddc67 100755
--- a/Utilities/Scripts/update-expat.bash
+++ b/Utilities/Scripts/update-expat.bash
@@ -8,7 +8,7 @@ readonly name="expat"
readonly ownership="Expat Upstream <kwrobot@kitware.com>"
readonly subtree="Utilities/cmexpat"
readonly repo="https://github.com/libexpat/libexpat.git"
-readonly tag="R_2_2_3"
+readonly tag="R_2_2_7"
readonly shortlog=false
readonly paths="
expat/lib/asciitab.h
diff --git a/Utilities/Scripts/update-third-party.bash b/Utilities/Scripts/update-third-party.bash
index 670946ef4..fcab871bd 100644
--- a/Utilities/Scripts/update-third-party.bash
+++ b/Utilities/Scripts/update-third-party.bash
@@ -71,8 +71,6 @@ warn () {
readonly regex_date='20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
readonly basehash_regex="$name $regex_date ([0-9a-f]*)"
-readonly basehash="$( git rev-list --author="$ownership" --grep="$basehash_regex" -n 1 HEAD )"
-readonly upstream_old_short="$( git cat-file commit "$basehash" | sed -n '/'"$basehash_regex"'/ {s/.*(//;s/)//;p}' | egrep '^[0-9a-f]+$' )"
########################################################################
# Sanity checking
@@ -87,6 +85,18 @@ readonly upstream_old_short="$( git cat-file commit "$basehash" | sed -n '/'"$ba
die "'repo' is empty"
[ -n "$tag" ] || \
die "'tag' is empty"
+
+# Check for an empty destination directory on disk. By checking on disk and
+# not in the repo it allows a library to be freshly re-inialized in a single
+# commit rather than first deleting the old copy in one commit and adding the
+# new copy in a seperate commit.
+if [ ! -d "$(git rev-parse --show-toplevel)/$subtree" ]; then
+ readonly basehash=""
+else
+ readonly basehash="$( git rev-list --author="$ownership" --grep="$basehash_regex" -n 1 HEAD )"
+fi
+readonly upstream_old_short="$( git cat-file commit "$basehash" | sed -n '/'"$basehash_regex"'/ {s/.*(//;s/)//;p}' | egrep '^[0-9a-f]+$' )"
+
[ -n "$basehash" ] || \
warn "'basehash' is empty; performing initial import"
readonly do_shortlog="${shortlog-false}"
@@ -104,6 +114,8 @@ trap "rm -rf '$workdir'" EXIT
git clone "$repo" "$upstreamdir"
if [ -n "$basehash" ]; then
+ # Remove old worktrees
+ git worktree prune
# Use the existing package's history
git worktree add "$extractdir" "$basehash"
# Clear out the working tree
@@ -163,13 +175,17 @@ popd
if [ -n "$basehash" ]; then
git merge --log -s recursive "-Xsubtree=$subtree/" --no-commit "upstream-$name"
else
+ # Note: on Windows 'git merge --help' will open a browser, and the check
+ # will fail, so use the flag by default.
unrelated_histories_flag=""
- if git merge --help | grep -q -e allow-unrelated-histories; then
+ if git --version | grep -q windows; then
+ unrelated_histories_flag="--allow-unrelated-histories "
+ elif git merge --help | grep -q -e allow-unrelated-histories; then
unrelated_histories_flag="--allow-unrelated-histories "
fi
readonly unrelated_histories_flag
- git fetch "$extractdir" "upstream-$name:upstream-$name"
+ git fetch "$extractdir" "+upstream-$name:upstream-$name"
git merge --log -s ours --no-commit $unrelated_histories_flag "upstream-$name"
git read-tree -u --prefix="$subtree/" "upstream-$name"
fi
diff --git a/Utilities/Scripts/update-zstd.bash b/Utilities/Scripts/update-zstd.bash
new file mode 100755
index 000000000..ce2c66b64
--- /dev/null
+++ b/Utilities/Scripts/update-zstd.bash
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+
+set -e
+set -x
+shopt -s dotglob
+
+readonly name="zstd"
+readonly ownership="zstd upstream <kwrobot@kitware.com>"
+readonly subtree="Utilities/cmzstd"
+readonly repo="https://github.com/facebook/zstd.git"
+readonly tag="v1.3.8"
+readonly shortlog=false
+readonly paths="
+ LICENSE
+ README.md
+ lib/common/*.c
+ lib/common/*.h
+ lib/compress/*.c
+ lib/compress/*.h
+ lib/decompress/*.c
+ lib/decompress/*.h
+ lib/deprecated/*.c
+ lib/deprecated/*.h
+ lib/dictBuilder/*.c
+ lib/dictBuilder/*.h
+ lib/zstd.h
+"
+
+extract_source () {
+ git_archive
+ pushd "${extractdir}/${name}-reduced"
+ echo "* -whitespace" > .gitattributes
+ popd
+}
+
+. "${BASH_SOURCE%/*}/update-third-party.bash"
diff --git a/Utilities/Sphinx/CMakeLists.txt b/Utilities/Sphinx/CMakeLists.txt
index 15204d63f..c5b2bfe2e 100644
--- a/Utilities/Sphinx/CMakeLists.txt
+++ b/Utilities/Sphinx/CMakeLists.txt
@@ -3,7 +3,7 @@
if(NOT CMake_SOURCE_DIR)
set(CMakeHelp_STANDALONE 1)
- cmake_minimum_required(VERSION 3.1...3.12 FATAL_ERROR)
+ cmake_minimum_required(VERSION 3.1...3.14 FATAL_ERROR)
get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH)
get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH)
include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake)
diff --git a/Utilities/cmThirdParty.h.in b/Utilities/cmThirdParty.h.in
index 46e0490c0..1456e34c7 100644
--- a/Utilities/cmThirdParty.h.in
+++ b/Utilities/cmThirdParty.h.in
@@ -15,5 +15,6 @@
#cmakedefine CMAKE_USE_SYSTEM_JSONCPP
#cmakedefine CMAKE_USE_SYSTEM_LIBRHASH
#cmakedefine CMAKE_USE_SYSTEM_LIBUV
+#cmakedefine CMAKE_USE_SYSTEM_ZSTD
#endif
diff --git a/Utilities/cm_zstd.h b/Utilities/cm_zstd.h
new file mode 100644
index 000000000..4bda996aa
--- /dev/null
+++ b/Utilities/cm_zstd.h
@@ -0,0 +1,14 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cm_zstd_h
+#define cm_zstd_h
+
+/* Use the libzstd configured for CMake. */
+#include "cmThirdParty.h"
+#ifdef CMAKE_USE_SYSTEM_ZSTD
+# include <zstd.h>
+#else
+# include <cmzstd/lib/zstd.h>
+#endif
+
+#endif
diff --git a/Utilities/cmcompress/CMakeLists.txt b/Utilities/cmcompress/CMakeLists.txt
deleted file mode 100644
index 806357327..000000000
--- a/Utilities/cmcompress/CMakeLists.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-PROJECT(CMCompress)
-
-ADD_LIBRARY(cmcompress cmcompress.c)
-
-INSTALL(FILES Copyright.txt DESTINATION ${CMAKE_DOC_DIR}/cmcompress)
diff --git a/Utilities/cmcompress/Copyright.txt b/Utilities/cmcompress/Copyright.txt
deleted file mode 100644
index 162332f69..000000000
--- a/Utilities/cmcompress/Copyright.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-Copyright (c) 1985, 1986 The Regents of the University of California.
-All rights reserved.
-
-This code is derived from software contributed to Berkeley by
-James A. Woods, derived from original work by Spencer Thomas
-and Joseph Orost.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. 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.
-3. All advertising materials mentioning features or use of this software
- must display the following acknowledgement:
- This product includes software developed by the University of
- California, Berkeley and its contributors.
-4. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
diff --git a/Utilities/cmcompress/cmcompress.c b/Utilities/cmcompress/cmcompress.c
deleted file mode 100644
index ea845ed5f..000000000
--- a/Utilities/cmcompress/cmcompress.c
+++ /dev/null
@@ -1,551 +0,0 @@
-/*
- * Copyright (c) 1985, 1986 The Regents of the University of California.
- * All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * James A. Woods, derived from original work by Spencer Thomas
- * and Joseph Orost.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
- */
-
-#include "cmcompress.h"
-
-#include <errno.h>
-#include <string.h>
-
-static const char_type magic_header[] = { "\037\235" }; /* 1F 9D */
-
-/* Defines for third byte of header */
-#define BIT_MASK 0x1f
-#define BLOCK_MASK 0x80
-#define CHECK_GAP 10000 /* ratio check interval */
-/* Masks 0x40 and 0x20 are free. I think 0x20 should mean that there is
- a fourth header byte (for expansion).
- */
-#define INIT_BITS 9 /* initial number of bits/code */
-
-#ifdef COMPATIBLE /* But wrong! */
-# define MAXCODE(n_bits) (1 << (n_bits) - 1)
-#else
-# define MAXCODE(n_bits) ((1 << (n_bits)) - 1)
-#endif /* COMPATIBLE */
-
-#define htabof(i) cdata->htab[i]
-#define codetabof(i) cdata->codetab[i]
-
-/*
- * the next two codes should not be changed lightly, as they must not
- * lie within the contiguous general code space.
- */
-#define FIRST 257 /* first free entry */
-#define CLEAR 256 /* table clear output code */
-
-#ifdef DEBUG
-static void prratio( FILE *stream, long int num, long int den);
-#endif
-
-int cmcompress_compress_initialize(struct cmcompress_stream* cdata)
-{
- cdata->maxbits = BITS; /* user settable max # bits/code */
- cdata->maxmaxcode = 1 << BITS; /* should NEVER generate this code */
- cdata->hsize = HSIZE; /* for dynamic table sizing */
- cdata->free_ent = 0; /* first unused entry */
- cdata->nomagic = 0; /* Use a 3-byte magic number header, unless old file */
- cdata->block_compress = BLOCK_MASK;
- cdata->clear_flg = 0;
- cdata->ratio = 0;
- cdata->checkpoint = CHECK_GAP;
-
- cdata->input_stream = 0;
- cdata->output_stream = 0;
- cdata->client_data = 0;
- return 1;
-}
-
-static void cl_hash(struct cmcompress_stream* cdata, count_int hsize) /* reset code table */
-{
- register count_int *htab_p = cdata->htab+hsize;
- register long i;
- register long m1 = -1;
-
- i = hsize - 16;
- do
- { /* might use Sys V memset(3) here */
- *(htab_p-16) = m1;
- *(htab_p-15) = m1;
- *(htab_p-14) = m1;
- *(htab_p-13) = m1;
- *(htab_p-12) = m1;
- *(htab_p-11) = m1;
- *(htab_p-10) = m1;
- *(htab_p-9) = m1;
- *(htab_p-8) = m1;
- *(htab_p-7) = m1;
- *(htab_p-6) = m1;
- *(htab_p-5) = m1;
- *(htab_p-4) = m1;
- *(htab_p-3) = m1;
- *(htab_p-2) = m1;
- *(htab_p-1) = m1;
- htab_p -= 16;
- }
- while ((i -= 16) >= 0);
- for ( i += 16; i > 0; i-- )
- {
- *--htab_p = m1;
- }
-}
-
-/*-
- * Output the given code.
- * Inputs:
- * code: A n_bits-bit integer. If == -1, then EOF. This assumes
- * that n_bits =< (long)wordsize - 1.
- * Outputs:
- * Outputs code to the file.
- * Assumptions:
- * Chars are 8 bits long.
- * Algorithm:
- * Maintain a BITS character long buffer (so that 8 codes will
- * fit in it exactly). Use the VAX insv instruction to insert each
- * code in turn. When the buffer fills up empty it and start over.
- */
-
-static char buf[BITS];
-
-#ifndef vax
-char_type lmask[9] = {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00};
-char_type rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
-#endif /* vax */
-
-static int output(struct cmcompress_stream* cdata, code_int code)
-{
-#ifdef DEBUG
- static int col = 0;
-#endif /* DEBUG */
-
- /*
- * On the VAX, it is important to have the register declarations
- * in exactly the order given, or the asm will break.
- */
- register int r_off = cdata->offset, bits= cdata->n_bits;
- register char * bp = buf;
-
-#ifdef DEBUG
- if ( verbose )
- {
- fprintf( stderr, "%5d%c", code,
- (col+=6) >= 74 ? (col = 0, '\n') : ' ' );
- }
-#endif /* DEBUG */
- if ( code >= 0 )
- {
-#if defined(vax) && !defined(__GNUC__)
- /*
- * VAX and PCC DEPENDENT!! Implementation on other machines is
- * below.
- *
- * Translation: Insert BITS bits from the argument starting at
- * cdata->offset bits from the beginning of buf.
- */
- 0; /* Work around for pcc -O bug with asm and if stmt */
- asm( "insv 4(ap),r11,r10,(r9)" );
-#else
- /*
- * byte/bit numbering on the VAX is simulated by the following code
- */
- /*
- * Get to the first byte.
- */
- bp += (r_off >> 3);
- r_off &= 7;
- /*
- * Since code is always >= 8 bits, only need to mask the first
- * hunk on the left.
- */
- *bp = (char)((*bp & rmask[r_off]) | ((code << r_off) & lmask[r_off]));
- bp++;
- bits -= (8 - r_off);
- code >>= 8 - r_off;
- /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
- if ( bits >= 8 )
- {
- *bp++ = (char)(code);
- code >>= 8;
- bits -= 8;
- }
- /* Last bits. */
- if(bits)
- {
- *bp = (char)(code);
- }
-#endif /* vax */
- cdata->offset += cdata->n_bits;
- if ( cdata->offset == (cdata->n_bits << 3) )
- {
- bp = buf;
- bits = cdata->n_bits;
- cdata->bytes_out += bits;
- do
- {
- if ( cdata->output_stream(cdata, bp, 1) != 1 )
- {
- return 0;
- }
- bp++;
- }
- while(--bits);
- cdata->offset = 0;
- }
-
- /*
- * If the next entry is going to be too big for the code size,
- * then increase it, if possible.
- */
- if ( cdata->free_ent > cdata->maxcode || (cdata->clear_flg > 0))
- {
- /*
- * Write the whole buffer, because the input side won't
- * discover the size increase until after it has read it.
- */
- if ( cdata->offset > 0 )
- {
- if ( cdata->output_stream(cdata, buf, cdata->n_bits) != cdata->n_bits )
- {
- return 0;
- }
- cdata->bytes_out += cdata->n_bits;
- }
- cdata->offset = 0;
-
- if ( cdata->clear_flg )
- {
- cdata->maxcode = MAXCODE (cdata->n_bits = INIT_BITS);
- cdata->clear_flg = 0;
- }
- else
- {
- cdata->n_bits++;
- if ( cdata->n_bits == cdata->maxbits )
- {
- cdata->maxcode = cdata->maxmaxcode;
- }
- else
- {
- cdata->maxcode = MAXCODE(cdata->n_bits);
- }
- }
-#ifdef DEBUG
- if ( debug )
- {
- fprintf( stderr, "\nChange to %d bits\n", cdata->n_bits );
- col = 0;
- }
-#endif /* DEBUG */
- }
- }
- else
- {
- /*
- * At EOF, write the rest of the buffer.
- */
- if ( cdata->offset > 0 )
- {
- cdata->offset = (cdata->offset + 7) / 8;
- if ( cdata->output_stream(cdata, buf, cdata->offset ) != cdata->offset )
- {
- return 0;
- }
- cdata->bytes_out += cdata->offset;
- }
- cdata->offset = 0;
- (void)fflush( stdout );
- if( ferror( stdout ) )
- {
- return 0;
- }
-#ifdef DEBUG
- if ( verbose )
- {
- fprintf( stderr, "\n" );
- }
-#endif
- }
- return 1;
-}
-
-/*
- * compress stdin to stdout
- *
- * Algorithm: use open addressing double hashing (no chaining) on the
- * prefix code / next character combination. We do a variant of Knuth's
- * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
- * secondary probe. Here, the modular division first probe is gives way
- * to a faster exclusive-or manipulation. Also do block compression with
- * an adaptive reset, whereby the code table is cleared when the compression
- * ratio decreases, but after the table fills. The variable-length output
- * codes are re-sized at this point, and a special CLEAR code is generated
- * for the decompressor. Late addition: construct the table according to
- * file size for noticeable speed improvement on small files. Please direct
- * questions about this implementation to ames!jaw.
- */
-
-int cmcompress_compress_start(struct cmcompress_stream* cdata)
-{
-#ifndef COMPATIBLE
- if (cdata->nomagic == 0)
- {
- char headLast = (char)(cdata->maxbits | cdata->block_compress);
- cdata->output_stream(cdata, (const char*)magic_header, 2);
- cdata->output_stream(cdata, &headLast, 1);
- if(ferror(stdout))
- {
- printf("Error...\n");
- }
- }
-#endif /* COMPATIBLE */
-
- cdata->offset = 0;
- cdata->bytes_out = 3; /* includes 3-byte header mojo */
- cdata->out_count = 0;
- cdata->clear_flg = 0;
- cdata->ratio = 0;
- cdata->in_count = 1;
- cdata->checkpoint = CHECK_GAP;
- cdata->maxcode = MAXCODE(cdata->n_bits = INIT_BITS);
- cdata->free_ent = ((cdata->block_compress) ? FIRST : 256 );
-
- cdata->first_pass = 1;
-
- cdata->hshift = 0;
- for ( cdata->fcode = (long) cdata->hsize; cdata->fcode < 65536L; cdata->fcode *= 2L )
- {
- cdata->hshift++;
- }
- cdata->hshift = 8 - cdata->hshift; /* set hash code range bound */
-
- cdata->hsize_reg = cdata->hsize;
- cl_hash(cdata, (count_int) cdata->hsize_reg); /* clear hash table */
-
- return 1;
-}
-
-static int cl_block (struct cmcompress_stream* cdata) /* table clear for block compress */
-{
- register long int rat;
-
- cdata->checkpoint = cdata->in_count + CHECK_GAP;
-#ifdef DEBUG
- if ( cdata->debug )
- {
- fprintf ( stderr, "count: %ld, ratio: ", cdata->in_count );
- prratio ( stderr, cdata->in_count, cdata->bytes_out );
- fprintf ( stderr, "\n");
- }
-#endif /* DEBUG */
-
- if(cdata->in_count > 0x007fffff)
- { /* shift will overflow */
- rat = cdata->bytes_out >> 8;
- if(rat == 0)
- { /* Don't divide by zero */
- rat = 0x7fffffff;
- }
- else
- {
- rat = cdata->in_count / rat;
- }
- }
- else
- {
- rat = (cdata->in_count << 8) / cdata->bytes_out; /* 8 fractional bits */
- }
- if ( rat > cdata->ratio )
- {
- cdata->ratio = rat;
- }
- else
- {
- cdata->ratio = 0;
-#ifdef DEBUG
- if(cdata->verbose)
- {
- dump_tab(); /* dump string table */
- }
-#endif
- cl_hash (cdata, (count_int) cdata->hsize );
- cdata->free_ent = FIRST;
- cdata->clear_flg = 1;
- if ( !output (cdata, (code_int) CLEAR ) )
- {
- return 0;
- }
-#ifdef DEBUG
- if(cdata->debug)
- {
- fprintf ( stderr, "clear\n" );
- }
-#endif /* DEBUG */
- }
- return 1;
-}
-
-
-int cmcompress_compress(struct cmcompress_stream* cdata, void* buff, size_t n)
-{
- register code_int i;
- register int c;
- register int disp;
-
- unsigned char* input_buffer = (unsigned char*)buff;
-
- size_t cc;
-
- /*printf("cmcompress_compress(%p, %p, %d)\n", cdata, buff, n);*/
-
- if ( cdata->first_pass )
- {
- cdata->ent = input_buffer[0];
- ++ input_buffer;
- -- n;
- cdata->first_pass = 0;
- }
-
- for ( cc = 0; cc < n; ++ cc )
- {
- c = input_buffer[cc];
- cdata->in_count++;
- cdata->fcode = (long) (((long) c << cdata->maxbits) + cdata->ent);
- i = ((c << cdata->hshift) ^ cdata->ent); /* xor hashing */
-
- if ( htabof (i) == cdata->fcode )
- {
- cdata->ent = codetabof (i);
- continue;
- }
- else if ( (long)htabof (i) < 0 ) /* empty slot */
- {
- goto nomatch;
- }
- disp = (int)(cdata->hsize_reg - i); /* secondary hash (after G. Knott) */
- if ( i == 0 )
- {
- disp = 1;
- }
-probe:
- if ( (i -= disp) < 0 )
- {
- i += cdata->hsize_reg;
- }
-
- if ( htabof (i) == cdata->fcode )
- {
- cdata->ent = codetabof (i);
- continue;
- }
- if ( (long)htabof (i) > 0 )
- {
- goto probe;
- }
-nomatch:
- if ( !output(cdata, (code_int) cdata->ent ) )
- {
- return 0;
- }
- cdata->out_count++;
- cdata->ent = c;
- if (
-#ifdef SIGNED_COMPARE_SLOW
- (unsigned) cdata->free_ent < (unsigned) cdata->maxmaxcode
-#else
- cdata->free_ent < cdata->maxmaxcode
-#endif
- )
- {
- codetabof (i) = (unsigned short)(cdata->free_ent++); /* code -> hashtable */
- htabof (i) = cdata->fcode;
- }
- else if ( (count_int)cdata->in_count >= cdata->checkpoint && cdata->block_compress )
- {
- if ( !cl_block (cdata) )
- {
- return 0;
- }
- }
- }
-
- return 1;
-}
-
-int cmcompress_compress_finalize(struct cmcompress_stream* cdata)
-{
- /*
- * Put out the final code.
- */
- if ( !output(cdata, (code_int)cdata->ent ) )
- {
- return 0;
- }
- cdata->out_count++;
- if ( !output(cdata, (code_int)-1 ) )
- {
- return 0;
- }
-
- if(cdata->bytes_out > cdata->in_count) /* exit(2) if no savings */
- {
- return 0;
- }
- return 1;
-}
-
-
-#if defined(DEBUG)
-static void prratio(FILE *stream, long int num, long int den)
-{
- register int q; /* Doesn't need to be long */
-
- if(num > 214748L)
- { /* 2147483647/10000 */
- q = num / (den / 10000L);
- }
- else
- {
- q = 10000L * num / den; /* Long calculations, though */
- }
- if (q < 0)
- {
- putc('-', stream);
- q = -q;
- }
- fprintf(stream, "%d.%02d%%", q / 100, q % 100);
-}
-#endif
-
diff --git a/Utilities/cmcompress/cmcompress.h b/Utilities/cmcompress/cmcompress.h
deleted file mode 100644
index 4cd3a1c3e..000000000
--- a/Utilities/cmcompress/cmcompress.h
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (c) 1985, 1986 The Regents of the University of California.
- * All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * James A. Woods, derived from original work by Spencer Thomas
- * and Joseph Orost.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
- */
-
-#ifndef cmcompress__h_
-#define cmcompress__h_
-
-#include <stdio.h>
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
- /*
- * Set USERMEM to the maximum amount of physical user memory available
- * in bytes. USERMEM is used to determine the maximum BITS that can be used
- * for compression.
- *
- * SACREDMEM is the amount of physical memory saved for others; compress
- * will hog the rest.
- */
-#ifndef SACREDMEM
-#define SACREDMEM 0
-#endif
-
-#ifndef USERMEM
-# define USERMEM 450000 /* default user memory */
-#endif
-
-#ifdef pdp11
-# define BITS 12 /* max bits/code for 16-bit machine */
-# define NO_UCHAR /* also if "unsigned char" functions as signed char */
-# undef USERMEM
-#endif /* pdp11 */ /* don't forget to compile with -i */
-
-#ifdef USERMEM
-# if USERMEM >= (433484+SACREDMEM)
-# define PBITS 16
-# else
-# if USERMEM >= (229600+SACREDMEM)
-# define PBITS 15
-# else
-# if USERMEM >= (127536+SACREDMEM)
-# define PBITS 14
-# else
-# if USERMEM >= (73464+SACREDMEM)
-# define PBITS 13
-# else
-# define PBITS 12
-# endif
-# endif
-# endif
-# endif
-# undef USERMEM
-#endif /* USERMEM */
-
-#ifdef PBITS /* Preferred BITS for this memory size */
-# ifndef BITS
-# define BITS PBITS
-# endif /* BITS */
-#endif /* PBITS */
-
-#if BITS == 16
-# define HSIZE 69001 /* 95% occupancy */
-#endif
-#if BITS == 15
-# define HSIZE 35023 /* 94% occupancy */
-#endif
-#if BITS == 14
-# define HSIZE 18013 /* 91% occupancy */
-#endif
-#if BITS == 13
-# define HSIZE 9001 /* 91% occupancy */
-#endif
-#if BITS <= 12
-# define HSIZE 5003 /* 80% occupancy */
-#endif
-
- /*
- * a code_int must be able to hold 2**BITS values of type int, and also -1
- */
-#if BITS > 15
- typedef long int code_int;
-#else
- typedef int code_int;
-#endif
-
-#ifdef SIGNED_COMPARE_SLOW
- typedef unsigned long int count_int;
- typedef unsigned short int count_short;
-#else
- typedef long int count_int;
-#endif
-
-#ifdef NO_UCHAR
- typedef char char_type;
-#else
- typedef unsigned char char_type;
-#endif /* UCHAR */
-
-
-
- struct cmcompress_stream
- {
- int n_bits; /* number of bits/code */
- int maxbits; /* user settable max # bits/code */
- code_int maxcode; /* maximum code, given n_bits */
- code_int maxmaxcode; /* should NEVER generate this code */
-
- count_int htab [HSIZE];
- unsigned short codetab [HSIZE];
-
- code_int hsize; /* for dynamic table sizing */
- code_int free_ent; /* first unused entry */
- int nomagic; /* Use a 3-byte magic number header, unless old file */
-
- /*
- * block compression parameters -- after all codes are used up,
- * and compression rate changes, start over.
- */
- int block_compress;
- int clear_flg;
- long int ratio;
- count_int checkpoint;
-
-#ifdef DEBUG
- int debug;
- int verbose;
-#endif
-
- /* compress internals */
- int offset;
- long int in_count; /* length of input */
- long int bytes_out; /* length of compressed output */
- long int out_count; /* # of codes output (for debugging) */
-
- /* internals */
- code_int ent;
- code_int hsize_reg;
- int hshift;
-
- long fcode;
- int first_pass;
-
- /* For input and output */
- int (*input_stream)(void*);
- int (*output_stream)(void*, const char*,int);
- void* client_data;
- };
-
- int cmcompress_compress_initialize(struct cmcompress_stream* cdata);
- int cmcompress_compress_start(struct cmcompress_stream* cdata);
- int cmcompress_compress(struct cmcompress_stream* cdata, void* buff, size_t n);
- int cmcompress_compress_finalize(struct cmcompress_stream* cdata);
-
-#ifdef __cplusplus
-}
-#endif
-
-
-#endif /* cmcompress__h_ */
diff --git a/Utilities/cmcompress/compress.c.original b/Utilities/cmcompress/compress.c.original
deleted file mode 100644
index 5062bda98..000000000
--- a/Utilities/cmcompress/compress.c.original
+++ /dev/null
@@ -1,1308 +0,0 @@
-/*
- * Copyright (c) 1985, 1986 The Regents of the University of California.
- * All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * James A. Woods, derived from original work by Spencer Thomas
- * and Joseph Orost.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
- */
-
-#ifndef lint
-char copyright[] =
-"@(#) Copyright (c) 1985, 1986 The Regents of the University of California.\n\
- All rights reserved.\n";
-#endif /* not lint */
-
-#ifndef lint
-static char sccsid[] = "@(#)compress.c 5.19 (Berkeley) 3/18/91";
-#endif /* not lint */
-
-/*
- * compress.c - File compression ala IEEE Computer, June 1984.
- *
- * Authors: Spencer W. Thomas (decvax!utah-cs!thomas)
- * Jim McKie (decvax!mcvax!jim)
- * Steve Davies (decvax!vax135!petsd!peora!srd)
- * Ken Turkowski (decvax!decwrl!turtlevax!ken)
- * James A. Woods (decvax!ihnp4!ames!jaw)
- * Joe Orost (decvax!vax135!petsd!joe)
- */
-
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <signal.h>
-#include <utime.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include <string.h>
-
-/*
- * Set USERMEM to the maximum amount of physical user memory available
- * in bytes. USERMEM is used to determine the maximum BITS that can be used
- * for compression.
- *
- * SACREDMEM is the amount of physical memory saved for others; compress
- * will hog the rest.
- */
-#ifndef SACREDMEM
-#define SACREDMEM 0
-#endif
-
-#ifndef USERMEM
-# define USERMEM 450000 /* default user memory */
-#endif
-
-#ifdef pdp11
-# define BITS 12 /* max bits/code for 16-bit machine */
-# define NO_UCHAR /* also if "unsigned char" functions as signed char */
-# undef USERMEM
-#endif /* pdp11 */ /* don't forget to compile with -i */
-
-#ifdef USERMEM
-# if USERMEM >= (433484+SACREDMEM)
-# define PBITS 16
-# else
-# if USERMEM >= (229600+SACREDMEM)
-# define PBITS 15
-# else
-# if USERMEM >= (127536+SACREDMEM)
-# define PBITS 14
-# else
-# if USERMEM >= (73464+SACREDMEM)
-# define PBITS 13
-# else
-# define PBITS 12
-# endif
-# endif
-# endif
-# endif
-# undef USERMEM
-#endif /* USERMEM */
-
-#ifdef PBITS /* Preferred BITS for this memory size */
-# ifndef BITS
-# define BITS PBITS
-# endif BITS
-#endif /* PBITS */
-
-#if BITS == 16
-# define HSIZE 69001 /* 95% occupancy */
-#endif
-#if BITS == 15
-# define HSIZE 35023 /* 94% occupancy */
-#endif
-#if BITS == 14
-# define HSIZE 18013 /* 91% occupancy */
-#endif
-#if BITS == 13
-# define HSIZE 9001 /* 91% occupancy */
-#endif
-#if BITS <= 12
-# define HSIZE 5003 /* 80% occupancy */
-#endif
-
-/*
- * a code_int must be able to hold 2**BITS values of type int, and also -1
- */
-#if BITS > 15
-typedef long int code_int;
-#else
-typedef int code_int;
-#endif
-
-#ifdef SIGNED_COMPARE_SLOW
-typedef unsigned long int count_int;
-typedef unsigned short int count_short;
-#else
-typedef long int count_int;
-#endif
-
-#ifdef NO_UCHAR
- typedef char char_type;
-#else
- typedef unsigned char char_type;
-#endif /* UCHAR */
-char_type magic_header[] = { "\037\235" }; /* 1F 9D */
-
-/* Defines for third byte of header */
-#define BIT_MASK 0x1f
-#define BLOCK_MASK 0x80
-/* Masks 0x40 and 0x20 are free. I think 0x20 should mean that there is
- a fourth header byte (for expansion).
-*/
-#define INIT_BITS 9 /* initial number of bits/code */
-
-int n_bits; /* number of bits/code */
-int maxbits = BITS; /* user settable max # bits/code */
-code_int maxcode; /* maximum code, given n_bits */
-code_int maxmaxcode = 1 << BITS; /* should NEVER generate this code */
-#ifdef COMPATIBLE /* But wrong! */
-# define MAXCODE(n_bits) (1 << (n_bits) - 1)
-#else
-# define MAXCODE(n_bits) ((1 << (n_bits)) - 1)
-#endif /* COMPATIBLE */
-
-count_int htab [HSIZE];
-unsigned short codetab [HSIZE];
-
-#define htabof(i) htab[i]
-#define codetabof(i) codetab[i]
-code_int hsize = HSIZE; /* for dynamic table sizing */
-count_int fsize;
-
-/*
- * To save much memory, we overlay the table used by compress() with those
- * used by decompress(). The tab_prefix table is the same size and type
- * as the codetab. The tab_suffix table needs 2**BITS characters. We
- * get this from the beginning of htab. The output stack uses the rest
- * of htab, and contains characters. There is plenty of room for any
- * possible stack (stack used to be 8000 characters).
- */
-
-#define tab_prefixof(i) codetabof(i)
-# define tab_suffixof(i) ((char_type *)(htab))[i]
-# define de_stack ((char_type *)&tab_suffixof(1<<BITS))
-
-code_int free_ent = 0; /* first unused entry */
-int exit_stat = 0; /* per-file status */
-int perm_stat = 0; /* permanent status */
-
-code_int getcode();
-
-int nomagic = 0; /* Use a 3-byte magic number header, unless old file */
-int zcat_flg = 0; /* Write output on stdout, suppress messages */
-int precious = 1; /* Don't unlink output file on interrupt */
-int quiet = 1; /* don't tell me about compression */
-
-/*
- * block compression parameters -- after all codes are used up,
- * and compression rate changes, start over.
- */
-int block_compress = BLOCK_MASK;
-int clear_flg = 0;
-long int ratio = 0;
-#define CHECK_GAP 10000 /* ratio check interval */
-count_int checkpoint = CHECK_GAP;
-/*
- * the next two codes should not be changed lightly, as they must not
- * lie within the contiguous general code space.
- */
-#define FIRST 257 /* first free entry */
-#define CLEAR 256 /* table clear output code */
-
-int force = 0;
-char ofname [100];
-#ifdef DEBUG
-int debug, verbose;
-#endif
-sig_t oldint;
-int bgnd_flag;
-
-int do_decomp = 0;
-
-/*-
- * Algorithm from "A Technique for High Performance Data Compression",
- * Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19.
- *
- * Usage: compress [-dfvc] [-b bits] [file ...]
- * Inputs:
- * -d: If given, decompression is done instead.
- *
- * -c: Write output on stdout, don't remove original.
- *
- * -b: Parameter limits the max number of bits/code.
- *
- * -f: Forces output file to be generated, even if one already
- * exists, and even if no space is saved by compressing.
- * If -f is not used, the user will be prompted if stdin is
- * a tty, otherwise, the output file will not be overwritten.
- *
- * -v: Write compression statistics
- *
- * file ...: Files to be compressed. If none specified, stdin
- * is used.
- * Outputs:
- * file.Z: Compressed form of file with same mode, owner, and utimes
- * or stdout (if stdin used as input)
- *
- * Assumptions:
- * When filenames are given, replaces with the compressed version
- * (.Z suffix) only if the file decreases in size.
- * Algorithm:
- * Modified Lempel-Ziv method (LZW). Basically finds common
- * substrings and replaces them with a variable size code. This is
- * deterministic, and can be done on the fly. Thus, the decompression
- * procedure needs no input table, but tracks the way the table was built.
- */
-
-main(argc, argv)
- int argc;
- char **argv;
-{
- extern int optind;
- extern char *optarg;
- struct stat statbuf;
- int ch, overwrite;
- char **filelist, **fileptr, *cp, tempname[MAXPATHLEN];
- void onintr(), oops();
-
- /* This bg check only works for sh. */
- if ((oldint = signal(SIGINT, SIG_IGN)) != SIG_IGN) {
- (void)signal(SIGINT, onintr);
- (void)signal(SIGSEGV, oops); /* XXX */
- }
- bgnd_flag = oldint != SIG_DFL;
-
-#ifdef COMPATIBLE
- nomagic = 1; /* Original didn't have a magic number */
-#endif
-
- if (cp = rindex(argv[0], '/'))
- ++cp;
- else
- cp = argv[0];
- if (strcmp(cp, "uncompress") == 0)
- do_decomp = 1;
- else if(strcmp(cp, "zcat") == 0) {
- do_decomp = 1;
- zcat_flg = 1;
- }
-
- /*
- * -b maxbits => maxbits.
- * -C => generate output compatible with compress 2.0.
- * -c => cat all output to stdout
- * -D => debug
- * -d => do_decomp
- * -f => force overwrite of output file
- * -n => no header: useful to uncompress old files
- * -V => print Version; debug verbose
- * -v => unquiet
- */
-
- overwrite = 0;
-#ifdef DEBUG
- while ((ch = getopt(argc, argv, "b:CcDdfnVv")) != EOF)
-#else
- while ((ch = getopt(argc, argv, "b:Ccdfnv")) != EOF)
-#endif
- switch(ch) {
- case 'b':
- maxbits = atoi(optarg);
- break;
- case 'C':
- block_compress = 0;
- break;
- case 'c':
- zcat_flg = 1;
- break;
-#ifdef DEBUG
- case 'D':
- debug = 1;
- break;
-#endif
- case 'd':
- do_decomp = 1;
- break;
- case 'f':
- overwrite = 1;
- force = 1;
- break;
- case 'n':
- nomagic = 1;
- break;
- case 'q':
- quiet = 1;
- break;
-#ifdef DEBUG
- case 'V':
- verbose = 1;
- break;
-#endif
- case 'v':
- quiet = 0;
- break;
- case '?':
- default:
- usage();
- }
- argc -= optind;
- argv += optind;
-
- if (maxbits < INIT_BITS)
- maxbits = INIT_BITS;
- if (maxbits > BITS)
- maxbits = BITS;
- maxmaxcode = 1 << maxbits;
-
- /* Build useless input file list. */
- filelist = fileptr = (char **)(malloc(argc * sizeof(*argv)));
- while (*argv)
- *fileptr++ = *argv++;
- *fileptr = NULL;
-
- if (*filelist != NULL) {
- for (fileptr = filelist; *fileptr; fileptr++) {
- exit_stat = 0;
- if (do_decomp) { /* DECOMPRESSION */
- /* Check for .Z suffix */
- if (strcmp(*fileptr + strlen(*fileptr) - 2, ".Z") != 0) {
- /* No .Z: tack one on */
- strcpy(tempname, *fileptr);
- strcat(tempname, ".Z");
- *fileptr = tempname;
- }
- /* Open input file */
- if ((freopen(*fileptr, "r", stdin)) == NULL) {
- perror(*fileptr);
- perm_stat = 1;
- continue;
- }
- /* Check the magic number */
- if (nomagic == 0) {
- if ((getchar() != (magic_header[0] & 0xFF))
- || (getchar() != (magic_header[1] & 0xFF))) {
- fprintf(stderr, "%s: not in compressed format\n",
- *fileptr);
- continue;
- }
- maxbits = getchar(); /* set -b from file */
- block_compress = maxbits & BLOCK_MASK;
- maxbits &= BIT_MASK;
- maxmaxcode = 1 << maxbits;
- if(maxbits > BITS) {
- fprintf(stderr,
- "%s: compressed with %d bits, can only handle %d bits\n",
- *fileptr, maxbits, BITS);
- continue;
- }
- }
- /* Generate output filename */
- strcpy(ofname, *fileptr);
- ofname[strlen(*fileptr) - 2] = '\0'; /* Strip off .Z */
- } else { /* COMPRESSION */
- if (strcmp(*fileptr + strlen(*fileptr) - 2, ".Z") == 0) {
- fprintf(stderr, "%s: already has .Z suffix -- no change\n",
- *fileptr);
- continue;
- }
- /* Open input file */
- if ((freopen(*fileptr, "r", stdin)) == NULL) {
- perror(*fileptr);
- perm_stat = 1;
- continue;
- }
- stat ( *fileptr, &statbuf );
- fsize = (long) statbuf.st_size;
- /*
- * tune hash table size for small files -- ad hoc,
- * but the sizes match earlier #defines, which
- * serve as upper bounds on the number of output codes.
- */
- hsize = HSIZE;
- if ( fsize < (1 << 12) )
- hsize = MIN ( 5003, HSIZE );
- else if ( fsize < (1 << 13) )
- hsize = MIN ( 9001, HSIZE );
- else if ( fsize < (1 << 14) )
- hsize = MIN ( 18013, HSIZE );
- else if ( fsize < (1 << 15) )
- hsize = MIN ( 35023, HSIZE );
- else if ( fsize < 47000 )
- hsize = MIN ( 50021, HSIZE );
-
- /* Generate output filename */
- strcpy(ofname, *fileptr);
- strcat(ofname, ".Z");
- }
- /* Check for overwrite of existing file */
- if (overwrite == 0 && zcat_flg == 0) {
- if (stat(ofname, &statbuf) == 0) {
- char response[2];
- response[0] = 'n';
- fprintf(stderr, "%s already exists;", ofname);
- if (bgnd_flag == 0 && isatty(2)) {
- fprintf(stderr, " do you wish to overwrite %s (y or n)? ",
- ofname);
- fflush(stderr);
- read(2, response, 2);
- while (response[1] != '\n') {
- if (read(2, response+1, 1) < 0) { /* Ack! */
- perror("stderr"); break;
- }
- }
- }
- if (response[0] != 'y') {
- fprintf(stderr, "\tnot overwritten\n");
- continue;
- }
- }
- }
- if(zcat_flg == 0) { /* Open output file */
- if (freopen(ofname, "w", stdout) == NULL) {
- perror(ofname);
- perm_stat = 1;
- continue;
- }
- precious = 0;
- if(!quiet)
- fprintf(stderr, "%s: ", *fileptr);
- }
-
- /* Actually do the compression/decompression */
- if (do_decomp == 0) compress();
-#ifndef DEBUG
- else decompress();
-#else
- else if (debug == 0) decompress();
- else printcodes();
- if (verbose) dump_tab();
-#endif /* DEBUG */
- if(zcat_flg == 0) {
- copystat(*fileptr, ofname); /* Copy stats */
- precious = 1;
- if((exit_stat == 1) || (!quiet))
- putc('\n', stderr);
- }
- }
- } else { /* Standard input */
- if (do_decomp == 0) {
- compress();
-#ifdef DEBUG
- if(verbose) dump_tab();
-#endif /* DEBUG */
- if(!quiet)
- putc('\n', stderr);
- } else {
- /* Check the magic number */
- if (nomagic == 0) {
- if ((getchar()!=(magic_header[0] & 0xFF))
- || (getchar()!=(magic_header[1] & 0xFF))) {
- fprintf(stderr, "stdin: not in compressed format\n");
- exit(1);
- }
- maxbits = getchar(); /* set -b from file */
- block_compress = maxbits & BLOCK_MASK;
- maxbits &= BIT_MASK;
- maxmaxcode = 1 << maxbits;
- fsize = 100000; /* assume stdin large for USERMEM */
- if(maxbits > BITS) {
- fprintf(stderr,
- "stdin: compressed with %d bits, can only handle %d bits\n",
- maxbits, BITS);
- exit(1);
- }
- }
-#ifndef DEBUG
- decompress();
-#else
- if (debug == 0) decompress();
- else printcodes();
- if (verbose) dump_tab();
-#endif /* DEBUG */
- }
- }
- exit(perm_stat ? perm_stat : exit_stat);
-}
-
-static int offset;
-long int in_count = 1; /* length of input */
-long int bytes_out; /* length of compressed output */
-long int out_count = 0; /* # of codes output (for debugging) */
-
-/*
- * compress stdin to stdout
- *
- * Algorithm: use open addressing double hashing (no chaining) on the
- * prefix code / next character combination. We do a variant of Knuth's
- * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
- * secondary probe. Here, the modular division first probe is gives way
- * to a faster exclusive-or manipulation. Also do block compression with
- * an adaptive reset, whereby the code table is cleared when the compression
- * ratio decreases, but after the table fills. The variable-length output
- * codes are re-sized at this point, and a special CLEAR code is generated
- * for the decompressor. Late addition: construct the table according to
- * file size for noticeable speed improvement on small files. Please direct
- * questions about this implementation to ames!jaw.
- */
-
-compress()
-{
- register long fcode;
- register code_int i = 0;
- register int c;
- register code_int ent;
- register int disp;
- register code_int hsize_reg;
- register int hshift;
-
-#ifndef COMPATIBLE
- if (nomagic == 0) {
- putchar(magic_header[0]);
- putchar(magic_header[1]);
- putchar((char)(maxbits | block_compress));
- if(ferror(stdout))
- writeerr();
- }
-#endif /* COMPATIBLE */
-
- offset = 0;
- bytes_out = 3; /* includes 3-byte header mojo */
- out_count = 0;
- clear_flg = 0;
- ratio = 0;
- in_count = 1;
- checkpoint = CHECK_GAP;
- maxcode = MAXCODE(n_bits = INIT_BITS);
- free_ent = ((block_compress) ? FIRST : 256 );
-
- ent = getchar ();
-
- hshift = 0;
- for ( fcode = (long) hsize; fcode < 65536L; fcode *= 2L )
- hshift++;
- hshift = 8 - hshift; /* set hash code range bound */
-
- hsize_reg = hsize;
- cl_hash( (count_int) hsize_reg); /* clear hash table */
-
-#ifdef SIGNED_COMPARE_SLOW
- while ( (c = getchar()) != (unsigned) EOF ) {
-#else
- while ( (c = getchar()) != EOF ) {
-#endif
- in_count++;
- fcode = (long) (((long) c << maxbits) + ent);
- i = ((c << hshift) ^ ent); /* xor hashing */
-
- if ( htabof (i) == fcode ) {
- ent = codetabof (i);
- continue;
- } else if ( (long)htabof (i) < 0 ) /* empty slot */
- goto nomatch;
- disp = hsize_reg - i; /* secondary hash (after G. Knott) */
- if ( i == 0 )
- disp = 1;
-probe:
- if ( (i -= disp) < 0 )
- i += hsize_reg;
-
- if ( htabof (i) == fcode ) {
- ent = codetabof (i);
- continue;
- }
- if ( (long)htabof (i) > 0 )
- goto probe;
-nomatch:
- output ( (code_int) ent );
- out_count++;
- ent = c;
-#ifdef SIGNED_COMPARE_SLOW
- if ( (unsigned) free_ent < (unsigned) maxmaxcode) {
-#else
- if ( free_ent < maxmaxcode ) {
-#endif
- codetabof (i) = free_ent++; /* code -> hashtable */
- htabof (i) = fcode;
- }
- else if ( (count_int)in_count >= checkpoint && block_compress )
- cl_block ();
- }
- /*
- * Put out the final code.
- */
- output( (code_int)ent );
- out_count++;
- output( (code_int)-1 );
-
- /*
- * Print out stats on stderr
- */
- if(zcat_flg == 0 && !quiet) {
-#ifdef DEBUG
- fprintf( stderr,
- "%ld chars in, %ld codes (%ld bytes) out, compression factor: ",
- in_count, out_count, bytes_out );
- prratio( stderr, in_count, bytes_out );
- fprintf( stderr, "\n");
- fprintf( stderr, "\tCompression as in compact: " );
- prratio( stderr, in_count-bytes_out, in_count );
- fprintf( stderr, "\n");
- fprintf( stderr, "\tLargest code (of last block) was %d (%d bits)\n",
- free_ent - 1, n_bits );
-#else /* !DEBUG */
- fprintf( stderr, "Compression: " );
- prratio( stderr, in_count-bytes_out, in_count );
-#endif /* DEBUG */
- }
- if(bytes_out > in_count) /* exit(2) if no savings */
- exit_stat = 2;
- return;
-}
-
-/*-
- * Output the given code.
- * Inputs:
- * code: A n_bits-bit integer. If == -1, then EOF. This assumes
- * that n_bits =< (long)wordsize - 1.
- * Outputs:
- * Outputs code to the file.
- * Assumptions:
- * Chars are 8 bits long.
- * Algorithm:
- * Maintain a BITS character long buffer (so that 8 codes will
- * fit in it exactly). Use the VAX insv instruction to insert each
- * code in turn. When the buffer fills up empty it and start over.
- */
-
-static char buf[BITS];
-
-#ifndef vax
-char_type lmask[9] = {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00};
-char_type rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
-#endif /* vax */
-
-output( code )
-code_int code;
-{
-#ifdef DEBUG
- static int col = 0;
-#endif /* DEBUG */
-
- /*
- * On the VAX, it is important to have the register declarations
- * in exactly the order given, or the asm will break.
- */
- register int r_off = offset, bits= n_bits;
- register char * bp = buf;
-
-#ifdef DEBUG
- if ( verbose )
- fprintf( stderr, "%5d%c", code,
- (col+=6) >= 74 ? (col = 0, '\n') : ' ' );
-#endif /* DEBUG */
- if ( code >= 0 ) {
-#if defined(vax) && !defined(__GNUC__)
- /*
- * VAX and PCC DEPENDENT!! Implementation on other machines is
- * below.
- *
- * Translation: Insert BITS bits from the argument starting at
- * offset bits from the beginning of buf.
- */
- 0; /* Work around for pcc -O bug with asm and if stmt */
- asm( "insv 4(ap),r11,r10,(r9)" );
-#else
-/*
- * byte/bit numbering on the VAX is simulated by the following code
- */
- /*
- * Get to the first byte.
- */
- bp += (r_off >> 3);
- r_off &= 7;
- /*
- * Since code is always >= 8 bits, only need to mask the first
- * hunk on the left.
- */
- *bp = (*bp & rmask[r_off]) | (code << r_off) & lmask[r_off];
- bp++;
- bits -= (8 - r_off);
- code >>= 8 - r_off;
- /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
- if ( bits >= 8 ) {
- *bp++ = code;
- code >>= 8;
- bits -= 8;
- }
- /* Last bits. */
- if(bits)
- *bp = code;
-#endif /* vax */
- offset += n_bits;
- if ( offset == (n_bits << 3) ) {
- bp = buf;
- bits = n_bits;
- bytes_out += bits;
- do {
- putchar(*bp++);
- if (ferror(stdout))
- writeerr();
- } while(--bits);
- offset = 0;
- }
-
- /*
- * If the next entry is going to be too big for the code size,
- * then increase it, if possible.
- */
- if ( free_ent > maxcode || (clear_flg > 0))
- {
- /*
- * Write the whole buffer, because the input side won't
- * discover the size increase until after it has read it.
- */
- if ( offset > 0 ) {
- if( fwrite( buf, 1, n_bits, stdout ) != n_bits)
- writeerr();
- bytes_out += n_bits;
- }
- offset = 0;
-
- if ( clear_flg ) {
- maxcode = MAXCODE (n_bits = INIT_BITS);
- clear_flg = 0;
- }
- else {
- n_bits++;
- if ( n_bits == maxbits )
- maxcode = maxmaxcode;
- else
- maxcode = MAXCODE(n_bits);
- }
-#ifdef DEBUG
- if ( debug ) {
- fprintf( stderr, "\nChange to %d bits\n", n_bits );
- col = 0;
- }
-#endif /* DEBUG */
- }
- } else {
- /*
- * At EOF, write the rest of the buffer.
- */
- if ( offset > 0 ) {
- offset = (offset + 7) / 8;
- if( fwrite( buf, 1, offset, stdout ) != offset )
- writeerr();
- bytes_out += offset;
- }
- offset = 0;
- (void)fflush( stdout );
- if( ferror( stdout ) )
- writeerr();
-#ifdef DEBUG
- if ( verbose )
- fprintf( stderr, "\n" );
-#endif
- }
-}
-
-/*
- * Decompress stdin to stdout. This routine adapts to the codes in the
- * file building the "string" table on-the-fly; requiring no table to
- * be stored in the compressed file. The tables used herein are shared
- * with those of the compress() routine. See the definitions above.
- */
-
-decompress() {
- register char_type *stackp;
- register int finchar;
- register code_int code, oldcode, incode;
- int n, nwritten, offset; /* Variables for buffered write */
- char buff[BUFSIZ]; /* Buffer for buffered write */
-
-
- /*
- * As above, initialize the first 256 entries in the table.
- */
- maxcode = MAXCODE(n_bits = INIT_BITS);
- for ( code = 255; code >= 0; code-- ) {
- tab_prefixof(code) = 0;
- tab_suffixof(code) = (char_type)code;
- }
- free_ent = ((block_compress) ? FIRST : 256 );
-
- finchar = oldcode = getcode();
- if(oldcode == -1) /* EOF already? */
- return; /* Get out of here */
-
- /* first code must be 8 bits = char */
- n=0;
- buff[n++] = (char)finchar;
-
- stackp = de_stack;
-
- while ( (code = getcode()) > -1 ) {
-
- if ( (code == CLEAR) && block_compress ) {
- for ( code = 255; code >= 0; code-- )
- tab_prefixof(code) = 0;
- clear_flg = 1;
- free_ent = FIRST - 1;
- if ( (code = getcode ()) == -1 ) /* O, untimely death! */
- break;
- }
- incode = code;
- /*
- * Special case for KwKwK string.
- */
- if ( code >= free_ent ) {
- *stackp++ = finchar;
- code = oldcode;
- }
-
- /*
- * Generate output characters in reverse order
- */
-#ifdef SIGNED_COMPARE_SLOW
- while ( ((unsigned long)code) >= ((unsigned long)256) ) {
-#else
- while ( code >= 256 ) {
-#endif
- *stackp++ = tab_suffixof(code);
- code = tab_prefixof(code);
- }
- *stackp++ = finchar = tab_suffixof(code);
-
- /*
- * And put them out in forward order
- */
- do {
- /*
- * About 60% of the time is spent in the putchar() call
- * that appeared here. It was originally
- * putchar ( *--stackp );
- * If we buffer the writes ourselves, we can go faster (about
- * 30%).
- *
- * At this point, the next line is the next *big* time
- * sink in the code. It takes up about 10% of the time.
- */
- buff[n++] = *--stackp;
- if (n == BUFSIZ) {
- offset = 0;
- do {
- nwritten = write(fileno(stdout), &buff[offset], n);
- if (nwritten < 0)
- writeerr();
- offset += nwritten;
- } while ((n -= nwritten) > 0);
- }
- } while ( stackp > de_stack );
-
- /*
- * Generate the new entry.
- */
- if ( (code=free_ent) < maxmaxcode ) {
- tab_prefixof(code) = (unsigned short)oldcode;
- tab_suffixof(code) = finchar;
- free_ent = code+1;
- }
- /*
- * Remember previous code.
- */
- oldcode = incode;
- }
- /*
- * Flush the stuff remaining in our buffer...
- */
- offset = 0;
- while (n > 0) {
- nwritten = write(fileno(stdout), &buff[offset], n);
- if (nwritten < 0)
- writeerr();
- offset += nwritten;
- n -= nwritten;
- }
-}
-
-/*-
- * Read one code from the standard input. If EOF, return -1.
- * Inputs:
- * stdin
- * Outputs:
- * code or -1 is returned.
- */
-code_int
-getcode() {
- /*
- * On the VAX, it is important to have the register declarations
- * in exactly the order given, or the asm will break.
- */
- register code_int code;
- static int offset = 0, size = 0;
- static char_type buf[BITS];
- register int r_off, bits;
- register char_type *bp = buf;
-
- if ( clear_flg > 0 || offset >= size || free_ent > maxcode ) {
- /*
- * If the next entry will be too big for the current code
- * size, then we must increase the size. This implies reading
- * a new buffer full, too.
- */
- if ( free_ent > maxcode ) {
- n_bits++;
- if ( n_bits == maxbits )
- maxcode = maxmaxcode; /* won't get any bigger now */
- else
- maxcode = MAXCODE(n_bits);
- }
- if ( clear_flg > 0) {
- maxcode = MAXCODE (n_bits = INIT_BITS);
- clear_flg = 0;
- }
- size = fread( buf, 1, n_bits, stdin );
- if ( size <= 0 )
- return -1; /* end of file */
- offset = 0;
- /* Round size down to integral number of codes */
- size = (size << 3) - (n_bits - 1);
- }
- r_off = offset;
- bits = n_bits;
-#ifdef vax
- asm( "extzv r10,r9,(r8),r11" );
-#else /* not a vax */
- /*
- * Get to the first byte.
- */
- bp += (r_off >> 3);
- r_off &= 7;
- /* Get first part (low order bits) */
-#ifdef NO_UCHAR
- code = ((*bp++ >> r_off) & rmask[8 - r_off]) & 0xff;
-#else
- code = (*bp++ >> r_off);
-#endif /* NO_UCHAR */
- bits -= (8 - r_off);
- r_off = 8 - r_off; /* now, offset into code word */
- /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
- if ( bits >= 8 ) {
-#ifdef NO_UCHAR
- code |= (*bp++ & 0xff) << r_off;
-#else
- code |= *bp++ << r_off;
-#endif /* NO_UCHAR */
- r_off += 8;
- bits -= 8;
- }
- /* high order bits. */
- code |= (*bp & rmask[bits]) << r_off;
-#endif /* vax */
- offset += n_bits;
-
- return code;
-}
-
-#ifdef DEBUG
-printcodes()
-{
- /*
- * Just print out codes from input file. For debugging.
- */
- code_int code;
- int col = 0, bits;
-
- bits = n_bits = INIT_BITS;
- maxcode = MAXCODE(n_bits);
- free_ent = ((block_compress) ? FIRST : 256 );
- while ( ( code = getcode() ) >= 0 ) {
- if ( (code == CLEAR) && block_compress ) {
- free_ent = FIRST - 1;
- clear_flg = 1;
- }
- else if ( free_ent < maxmaxcode )
- free_ent++;
- if ( bits != n_bits ) {
- fprintf(stderr, "\nChange to %d bits\n", n_bits );
- bits = n_bits;
- col = 0;
- }
- fprintf(stderr, "%5d%c", code, (col+=6) >= 74 ? (col = 0, '\n') : ' ' );
- }
- putc( '\n', stderr );
- exit( 0 );
-}
-
-code_int sorttab[1<<BITS]; /* sorted pointers into htab */
-
-dump_tab() /* dump string table */
-{
- register int i, first;
- register ent;
-#define STACK_SIZE 15000
- int stack_top = STACK_SIZE;
- register c;
-
- if(do_decomp == 0) { /* compressing */
- register int flag = 1;
-
- for(i=0; i<hsize; i++) { /* build sort pointers */
- if((long)htabof(i) >= 0) {
- sorttab[codetabof(i)] = i;
- }
- }
- first = block_compress ? FIRST : 256;
- for(i = first; i < free_ent; i++) {
- fprintf(stderr, "%5d: \"", i);
- de_stack[--stack_top] = '\n';
- de_stack[--stack_top] = '"';
- stack_top = in_stack((htabof(sorttab[i])>>maxbits)&0xff,
- stack_top);
- for(ent=htabof(sorttab[i]) & ((1<<maxbits)-1);
- ent > 256;
- ent=htabof(sorttab[ent]) & ((1<<maxbits)-1)) {
- stack_top = in_stack(htabof(sorttab[ent]) >> maxbits,
- stack_top);
- }
- stack_top = in_stack(ent, stack_top);
- fwrite( &de_stack[stack_top], 1, STACK_SIZE-stack_top, stderr);
- stack_top = STACK_SIZE;
- }
- } else if(!debug) { /* decompressing */
-
- for ( i = 0; i < free_ent; i++ ) {
- ent = i;
- c = tab_suffixof(ent);
- if ( isascii(c) && isprint(c) )
- fprintf( stderr, "%5d: %5d/'%c' \"",
- ent, tab_prefixof(ent), c );
- else
- fprintf( stderr, "%5d: %5d/\\%03o \"",
- ent, tab_prefixof(ent), c );
- de_stack[--stack_top] = '\n';
- de_stack[--stack_top] = '"';
- for ( ; ent != NULL;
- ent = (ent >= FIRST ? tab_prefixof(ent) : NULL) ) {
- stack_top = in_stack(tab_suffixof(ent), stack_top);
- }
- fwrite( &de_stack[stack_top], 1, STACK_SIZE - stack_top, stderr );
- stack_top = STACK_SIZE;
- }
- }
-}
-
-int
-in_stack(c, stack_top)
- register c, stack_top;
-{
- if ( (isascii(c) && isprint(c) && c != '\\') || c == ' ' ) {
- de_stack[--stack_top] = c;
- } else {
- switch( c ) {
- case '\n': de_stack[--stack_top] = 'n'; break;
- case '\t': de_stack[--stack_top] = 't'; break;
- case '\b': de_stack[--stack_top] = 'b'; break;
- case '\f': de_stack[--stack_top] = 'f'; break;
- case '\r': de_stack[--stack_top] = 'r'; break;
- case '\\': de_stack[--stack_top] = '\\'; break;
- default:
- de_stack[--stack_top] = '0' + c % 8;
- de_stack[--stack_top] = '0' + (c / 8) % 8;
- de_stack[--stack_top] = '0' + c / 64;
- break;
- }
- de_stack[--stack_top] = '\\';
- }
- return stack_top;
-}
-#endif /* DEBUG */
-
-writeerr()
-{
- (void)fprintf(stderr, "compress: %s: %s\n",
- ofname[0] ? ofname : "stdout", strerror(errno));
- (void)unlink(ofname);
- exit(1);
-}
-
-copystat(ifname, ofname)
-char *ifname, *ofname;
-{
- struct stat statbuf;
- int mode;
- struct utimbuf tp;
-
- fclose(stdout);
- if (stat(ifname, &statbuf)) { /* Get stat on input file */
- perror(ifname);
- return;
- }
- if ((statbuf.st_mode & S_IFMT/*0170000*/) != S_IFREG/*0100000*/) {
- if(quiet)
- fprintf(stderr, "%s: ", ifname);
- fprintf(stderr, " -- not a regular file: unchanged");
- exit_stat = 1;
- perm_stat = 1;
- } else if (statbuf.st_nlink > 1) {
- if(quiet)
- fprintf(stderr, "%s: ", ifname);
- fprintf(stderr, " -- has %d other links: unchanged",
- statbuf.st_nlink - 1);
- exit_stat = 1;
- perm_stat = 1;
- } else if (exit_stat == 2 && (!force)) { /* No compression: remove file.Z */
- if(!quiet)
- fprintf(stderr, " -- file unchanged");
- } else { /* ***** Successful Compression ***** */
- exit_stat = 0;
- mode = statbuf.st_mode & 07777;
- if (chmod(ofname, mode)) /* Copy modes */
- perror(ofname);
- chown(ofname, statbuf.st_uid, statbuf.st_gid); /* Copy ownership */
- tp.actime = statbuf.st_atime;
- tp.modtime = statbuf.st_mtime;
- utime(ofname, &tp); /* Update last accessed and modified times */
- if (unlink(ifname)) /* Remove input file */
- perror(ifname);
- if(!quiet)
- fprintf(stderr, " -- replaced with %s", ofname);
- return; /* Successful return */
- }
-
- /* Unsuccessful return -- one of the tests failed */
- if (unlink(ofname))
- perror(ofname);
-}
-
-void
-onintr ( )
-{
- if (!precious)
- unlink ( ofname );
- exit ( 1 );
-}
-
-void
-oops ( ) /* wild pointer -- assume bad input */
-{
- if ( do_decomp )
- fprintf ( stderr, "uncompress: corrupt input\n" );
- unlink ( ofname );
- exit ( 1 );
-}
-
-cl_block () /* table clear for block compress */
-{
- register long int rat;
-
- checkpoint = in_count + CHECK_GAP;
-#ifdef DEBUG
- if ( debug ) {
- fprintf ( stderr, "count: %ld, ratio: ", in_count );
- prratio ( stderr, in_count, bytes_out );
- fprintf ( stderr, "\n");
- }
-#endif /* DEBUG */
-
- if(in_count > 0x007fffff) { /* shift will overflow */
- rat = bytes_out >> 8;
- if(rat == 0) { /* Don't divide by zero */
- rat = 0x7fffffff;
- } else {
- rat = in_count / rat;
- }
- } else {
- rat = (in_count << 8) / bytes_out; /* 8 fractional bits */
- }
- if ( rat > ratio ) {
- ratio = rat;
- } else {
- ratio = 0;
-#ifdef DEBUG
- if(verbose)
- dump_tab(); /* dump string table */
-#endif
- cl_hash ( (count_int) hsize );
- free_ent = FIRST;
- clear_flg = 1;
- output ( (code_int) CLEAR );
-#ifdef DEBUG
- if(debug)
- fprintf ( stderr, "clear\n" );
-#endif /* DEBUG */
- }
-}
-
-cl_hash(hsize) /* reset code table */
- register count_int hsize;
-{
- register count_int *htab_p = htab+hsize;
- register long i;
- register long m1 = -1;
-
- i = hsize - 16;
- do { /* might use Sys V memset(3) here */
- *(htab_p-16) = m1;
- *(htab_p-15) = m1;
- *(htab_p-14) = m1;
- *(htab_p-13) = m1;
- *(htab_p-12) = m1;
- *(htab_p-11) = m1;
- *(htab_p-10) = m1;
- *(htab_p-9) = m1;
- *(htab_p-8) = m1;
- *(htab_p-7) = m1;
- *(htab_p-6) = m1;
- *(htab_p-5) = m1;
- *(htab_p-4) = m1;
- *(htab_p-3) = m1;
- *(htab_p-2) = m1;
- *(htab_p-1) = m1;
- htab_p -= 16;
- } while ((i -= 16) >= 0);
- for ( i += 16; i > 0; i-- )
- *--htab_p = m1;
-}
-
-prratio(stream, num, den)
-FILE *stream;
-long int num, den;
-{
- register int q; /* Doesn't need to be long */
-
- if(num > 214748L) { /* 2147483647/10000 */
- q = num / (den / 10000L);
- } else {
- q = 10000L * num / den; /* Long calculations, though */
- }
- if (q < 0) {
- putc('-', stream);
- q = -q;
- }
- fprintf(stream, "%d.%02d%%", q / 100, q % 100);
-}
-
-usage()
-{
- (void)fprintf(stderr,
-#ifdef DEBUG
- "compress [-CDVcdfnv] [-b maxbits] [file ...]\n");
-#else
- "compress [-Ccdfnv] [-b maxbits] [file ...]\n");
-#endif
- exit(1);
-}
diff --git a/Utilities/cmcurl/CMake/CurlTests.c b/Utilities/cmcurl/CMake/CurlTests.c
index 9388c835b..07b516b4d 100644
--- a/Utilities/cmcurl/CMake/CurlTests.c
+++ b/Utilities/cmcurl/CMake/CurlTests.c
@@ -240,6 +240,7 @@ int main()
#ifndef inet_ntoa_r
func_type func;
func = (func_type)inet_ntoa_r;
+ (void)func;
#endif
return 0;
}
@@ -255,6 +256,7 @@ int main()
#ifndef inet_ntoa_r
func_type func;
func = (func_type)&inet_ntoa_r;
+ (void)func;
#endif
return 0;
}
@@ -553,8 +555,8 @@ main() {
#include <time.h>
int
main() {
- struct timespec ts = {0, 0};
- clock_gettime(CLOCK_MONOTONIC, &ts);
+ struct timespec ts = {0, 0};
+ clock_gettime(CLOCK_MONOTONIC, &ts);
return 0;
}
#endif
@@ -565,3 +567,49 @@ main() {
return 0;
}
#endif
+#ifdef HAVE_VARIADIC_MACROS_C99
+#define c99_vmacro3(first, ...) fun3(first, __VA_ARGS__)
+#define c99_vmacro2(first, ...) fun2(first, __VA_ARGS__)
+
+int fun3(int arg1, int arg2, int arg3);
+int fun2(int arg1, int arg2);
+
+int fun3(int arg1, int arg2, int arg3) {
+ return arg1 + arg2 + arg3;
+}
+int fun2(int arg1, int arg2) {
+ return arg1 + arg2;
+}
+
+int
+main() {
+ int res3 = c99_vmacro3(1, 2, 3);
+ int res2 = c99_vmacro2(1, 2);
+ (void)res3;
+ (void)res2;
+ return 0;
+}
+#endif
+#ifdef HAVE_VARIADIC_MACROS_GCC
+#define gcc_vmacro3(first, args...) fun3(first, args)
+#define gcc_vmacro2(first, args...) fun2(first, args)
+
+int fun3(int arg1, int arg2, int arg3);
+int fun2(int arg1, int arg2);
+
+int fun3(int arg1, int arg2, int arg3) {
+ return arg1 + arg2 + arg3;
+}
+int fun2(int arg1, int arg2) {
+ return arg1 + arg2;
+}
+
+int
+main() {
+ int res3 = gcc_vmacro3(1, 2, 3);
+ int res2 = gcc_vmacro2(1, 2);
+ (void)res3;
+ (void)res2;
+ return 0;
+}
+#endif
diff --git a/Utilities/cmcurl/CMake/FindGSS.cmake b/Utilities/cmcurl/CMake/FindGSS.cmake
index 7a637fc29..8a28f2fb6 100644
--- a/Utilities/cmcurl/CMake/FindGSS.cmake
+++ b/Utilities/cmcurl/CMake/FindGSS.cmake
@@ -68,7 +68,7 @@ if(NOT _GSS_FOUND) #not found by pkg-config. Let's take more traditional approac
# should also work in an odd case when multiple directories are given
string(STRIP "${_GSS_CFLAGS}" _GSS_CFLAGS)
string(REGEX REPLACE " +-I" ";" _GSS_CFLAGS "${_GSS_CFLAGS}")
- string(REGEX REPLACE " +-([^I][^ \\t;]*)" ";-\\1"_GSS_CFLAGS "${_GSS_CFLAGS}")
+ string(REGEX REPLACE " +-([^I][^ \\t;]*)" ";-\\1" _GSS_CFLAGS "${_GSS_CFLAGS}")
foreach(_flag ${_GSS_CFLAGS})
if(_flag MATCHES "^-I.*")
@@ -91,7 +91,7 @@ if(NOT _GSS_FOUND) #not found by pkg-config. Let's take more traditional approac
# this script gives us libraries and link directories. Blah. We have to deal with it.
string(STRIP "${_GSS_LIB_FLAGS}" _GSS_LIB_FLAGS)
string(REGEX REPLACE " +-(L|l)" ";-\\1" _GSS_LIB_FLAGS "${_GSS_LIB_FLAGS}")
- string(REGEX REPLACE " +-([^Ll][^ \\t;]*)" ";-\\1"_GSS_LIB_FLAGS "${_GSS_LIB_FLAGS}")
+ string(REGEX REPLACE " +-([^Ll][^ \\t;]*)" ";-\\1" _GSS_LIB_FLAGS "${_GSS_LIB_FLAGS}")
foreach(_flag ${_GSS_LIB_FLAGS})
if(_flag MATCHES "^-l.*")
diff --git a/Utilities/cmcurl/CMake/OtherTests.cmake b/Utilities/cmcurl/CMake/OtherTests.cmake
index ce6d3e13a..c1c9aa32a 100644
--- a/Utilities/cmcurl/CMake/OtherTests.cmake
+++ b/Utilities/cmcurl/CMake/OtherTests.cmake
@@ -24,6 +24,8 @@ else()
add_header_include(HAVE_SYS_SOCKET_H "sys/socket.h")
endif()
+set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
+
check_c_source_compiles("${_source_epilogue}
int main(void) {
recv(0, 0, 0, 0);
@@ -177,23 +179,6 @@ int main(void) {
return 0;
}" HAVE_STRUCT_TIMEVAL)
-
-include(CheckCSourceRuns)
-# See HAVE_POLL in CMakeLists.txt for why poll is disabled on macOS
-if(NOT APPLE)
- set(CMAKE_REQUIRED_FLAGS)
- if(HAVE_SYS_POLL_H)
- set(CMAKE_REQUIRED_FLAGS "-DHAVE_SYS_POLL_H")
- endif()
- check_c_source_runs("
- #ifdef HAVE_SYS_POLL_H
- # include <sys/poll.h>
- #endif
- int main(void) {
- return poll((void *)0, 0, 10 /*ms*/);
- }" HAVE_POLL_FINE)
-endif()
-
set(HAVE_SIG_ATOMIC_T 1)
set(CMAKE_REQUIRED_FLAGS)
if(HAVE_SIGNAL_H)
@@ -229,3 +214,51 @@ check_type_size("struct sockaddr_storage" SIZEOF_STRUCT_SOCKADDR_STORAGE)
if(HAVE_SIZEOF_STRUCT_SOCKADDR_STORAGE)
set(HAVE_STRUCT_SOCKADDR_STORAGE 1)
endif()
+
+unset(CMAKE_TRY_COMPILE_TARGET_TYPE)
+
+if(NOT DEFINED CMAKE_TOOLCHAIN_FILE)
+ # if not cross-compilation...
+ include(CheckCSourceRuns)
+ set(CMAKE_REQUIRED_FLAGS "")
+ if(HAVE_SYS_POLL_H)
+ set(CMAKE_REQUIRED_FLAGS "-DHAVE_SYS_POLL_H")
+ elseif(HAVE_POLL_H)
+ set(CMAKE_REQUIRED_FLAGS "-DHAVE_POLL_H")
+ endif()
+ check_c_source_runs("
+ #include <stdlib.h>
+ #include <sys/time.h>
+
+ #ifdef HAVE_SYS_POLL_H
+ # include <sys/poll.h>
+ #elif HAVE_POLL_H
+ # include <poll.h>
+ #endif
+
+ int main(void)
+ {
+ if(0 != poll(0, 0, 10)) {
+ return 1; /* fail */
+ }
+ else {
+ /* detect the 10.12 poll() breakage */
+ struct timeval before, after;
+ int rc;
+ size_t us;
+
+ gettimeofday(&before, NULL);
+ rc = poll(NULL, 0, 500);
+ gettimeofday(&after, NULL);
+
+ us = (after.tv_sec - before.tv_sec) * 1000000 +
+ (after.tv_usec - before.tv_usec);
+
+ if(us < 400000) {
+ return 1;
+ }
+ }
+ return 0;
+ }" HAVE_POLL_FINE)
+endif()
+
diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt
index 1c96497cd..37522fc10 100644
--- a/Utilities/cmcurl/CMakeLists.txt
+++ b/Utilities/cmcurl/CMakeLists.txt
@@ -53,10 +53,11 @@ elseif(APPLE)
endif()
if(NOT OSX_VERSION VERSION_LESS 10.6 AND
CMAKE_C_COMPILER_ID MATCHES "GNU|Clang|AppleClang")
- set(CMAKE_USE_DARWINSSL ON CACHE INTERNAL "enable Apple OS native SSL/TLS")
+ set(CMAKE_USE_SECTRANSP ON CACHE INTERNAL "enable Apple OS native SSL/TLS")
else()
- set(CMAKE_USE_DARWINSSL OFF CACHE INTERNAL "enable Apple OS native SSL/TLS")
+ set(CMAKE_USE_SECTRANSP OFF CACHE INTERNAL "enable Apple OS native SSL/TLS")
endif()
+ unset(CMAKE_USE_DARWINSSL CACHE)
endif()
set(CMAKE_USE_MBEDTLS OFF CACHE INTERNAL "Enable mbedTLS for SSL/TLS")
@@ -77,7 +78,7 @@ endif(APPLE)
# Disable warnings to avoid changing 3rd party code.
if(CMAKE_C_COMPILER_ID MATCHES
- "^(GNU|Clang|AppleClang|XL|VisualAge|SunPro|MIPSpro|HP|Intel)$")
+ "^(GNU|Clang|AppleClang|XLClang|XL|VisualAge|SunPro|HP|Intel)$")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w")
elseif(CMAKE_C_COMPILER_ID STREQUAL "PathScale")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -woffall")
@@ -90,7 +91,7 @@ endif()
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
@@ -143,7 +144,6 @@ string(REGEX MATCH "#define LIBCURL_VERSION_NUM 0x[0-9a-fA-F]+"
CURL_VERSION_NUM ${CURL_VERSION_H_CONTENTS})
string(REGEX REPLACE "[^0]+0x" "" CURL_VERSION_NUM ${CURL_VERSION_NUM})
-include_regular_expression("^.*$") # Sukender: Is it necessary?
# Setup package meta-data
# SET(PACKAGE "curl")
@@ -158,7 +158,6 @@ endif()
set(OPERATING_SYSTEM "${CMAKE_SYSTEM_NAME}")
set(OS "\"${CMAKE_SYSTEM_NAME}\"")
-include_directories(${PROJECT_BINARY_DIR}/include/curl)
include_directories(${CURL_SOURCE_DIR}/include)
option(CURL_WERROR "Turn compiler warnings into errors" OFF)
@@ -171,9 +170,11 @@ if(WIN32)
option(ENABLE_INET_PTON "Set to OFF to prevent usage of inet_pton when building against modern SDKs while still requiring compatibility with older Windows versions, such as Windows XP, Windows Server 2003 etc." ON)
endif()
+if(0) # This code not needed for building within CMake.
cmake_dependent_option(ENABLE_THREADED_RESOLVER "Set to ON to enable threaded DNS lookup"
ON "NOT ENABLE_ARES"
OFF)
+endif()
option(ENABLE_DEBUG "Set to ON to enable curl debug features" OFF)
option(ENABLE_CURLDEBUG "Set to ON to build with TrackMemory feature enabled" OFF)
@@ -193,11 +194,7 @@ endif()
if(ENABLE_DEBUG)
# DEBUGBUILD will be defined only for Debug builds
- if(NOT CMAKE_VERSION VERSION_LESS 3.0)
- set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS $<$<CONFIG:Debug>:DEBUGBUILD>)
- else()
- set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_DEBUG DEBUGBUILD)
- endif()
+ set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS $<$<CONFIG:Debug>:DEBUGBUILD>)
set(ENABLE_CURLDEBUG ON)
endif()
@@ -393,10 +390,10 @@ if(WIN32)
endif()
# check SSL libraries
-# TODO support GNUTLS, NSS, POLARSSL, AXTLS, CYASSL
+# TODO support GNUTLS, NSS, POLARSSL, CYASSL
if(APPLE)
- option(CMAKE_USE_DARWINSSL "enable Apple OS native SSL/TLS" OFF)
+ option(CMAKE_USE_SECTRANSP "enable Apple OS native SSL/TLS" OFF)
endif()
if(WIN32)
option(CMAKE_USE_WINSSL "enable Windows native SSL/TLS" OFF)
@@ -406,13 +403,13 @@ endif()
option(CMAKE_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF)
set(openssl_default ON)
-if(WIN32 OR CMAKE_USE_DARWINSSL OR CMAKE_USE_WINSSL OR CMAKE_USE_MBEDTLS)
+if(WIN32 OR CMAKE_USE_SECTRANSP OR CMAKE_USE_WINSSL OR CMAKE_USE_MBEDTLS)
set(openssl_default OFF)
endif()
count_true(enabled_ssl_options_count
CMAKE_USE_WINSSL
- CMAKE_USE_DARWINSSL
+ CMAKE_USE_SECTRANSP
CMAKE_USE_OPENSSL
CMAKE_USE_MBEDTLS
)
@@ -432,6 +429,10 @@ if(CURL_WINDOWS_SSPI)
endif()
if(CMAKE_USE_DARWINSSL)
+ message(FATAL_ERROR "The cmake option CMAKE_USE_DARWINSSL was renamed to CMAKE_USE_SECTRANSP.")
+endif()
+
+if(CMAKE_USE_SECTRANSP)
find_library(COREFOUNDATION_FRAMEWORK "CoreFoundation")
if(NOT COREFOUNDATION_FRAMEWORK)
message(FATAL_ERROR "CoreFoundation framework not found")
@@ -443,7 +444,7 @@ if(CMAKE_USE_DARWINSSL)
endif()
set(SSL_ENABLED ON)
- set(USE_DARWINSSL ON)
+ set(USE_SECTRANSP ON)
list(APPEND CURL_LIBS "${COREFOUNDATION_FRAMEWORK}" "${SECURITY_FRAMEWORK}")
endif()
@@ -578,6 +579,7 @@ if(NOT CURL_DISABLE_LDAP)
list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LBER_LIB})
endif()
check_c_source_compiles("${_SRC_STRING}" NOT_NEED_LBER_H)
+ unset(CMAKE_REQUIRED_LIBRARIES)
if(NOT_NEED_LBER_H)
set(NEED_LBER_H OFF)
@@ -685,6 +687,7 @@ if(CMAKE_USE_LIBSSH2)
check_function_exists(libssh2_scp_send64 HAVE_LIBSSH2_SCP_SEND64)
check_function_exists(libssh2_session_handshake HAVE_LIBSSH2_SESSION_HANDSHAKE)
set(CMAKE_EXTRA_INCLUDE_FILES "")
+ unset(CMAKE_REQUIRED_LIBRARIES)
endif()
endif()
@@ -699,7 +702,7 @@ if(CMAKE_USE_GSSAPI)
message(STATUS "Found ${GSS_FLAVOUR} GSSAPI version: \"${GSS_VERSION}\"")
- list(APPEND CMAKE_REQUIRED_INCLUDES ${GSS_INCLUDE_DIRECTORIES})
+ list(APPEND CMAKE_REQUIRED_INCLUDES ${GSS_INCLUDE_DIR})
check_include_file_concat("gssapi/gssapi.h" HAVE_GSSAPI_GSSAPI_H)
check_include_file_concat("gssapi/gssapi_generic.h" HAVE_GSSAPI_GSSAPI_GENERIC_H)
check_include_file_concat("gssapi/gssapi_krb5.h" HAVE_GSSAPI_GSSAPI_KRB5_H)
@@ -732,10 +735,11 @@ if(CMAKE_USE_GSSAPI)
if(NOT HAVE_GSS_C_NT_HOSTBASED_SERVICE)
set(HAVE_OLD_GSSMIT ON)
endif()
+ unset(CMAKE_REQUIRED_LIBRARIES)
endif()
- include_directories(${GSS_INCLUDE_DIRECTORIES})
+ include_directories(${GSS_INCLUDE_DIR})
link_directories(${GSS_LINK_DIRECTORIES})
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GSS_COMPILER_FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${GSS_LINKER_FLAGS}")
@@ -942,12 +946,8 @@ endif()
check_symbol_exists(basename "${CURL_INCLUDES}" HAVE_BASENAME)
check_symbol_exists(socket "${CURL_INCLUDES}" HAVE_SOCKET)
-# poll on macOS is unreliable, it first did not exist, then was broken until
-# fixed in 10.9 only to break again in 10.12.
-if(NOT APPLE)
- check_symbol_exists(poll "${CURL_INCLUDES}" HAVE_POLL)
-endif()
check_symbol_exists(select "${CURL_INCLUDES}" HAVE_SELECT)
+check_symbol_exists(poll "${CURL_INCLUDES}" HAVE_POLL)
check_symbol_exists(strdup "${CURL_INCLUDES}" HAVE_STRDUP)
check_symbol_exists(strstr "${CURL_INCLUDES}" HAVE_STRSTR)
check_symbol_exists(strtok_r "${CURL_INCLUDES}" HAVE_STRTOK_R)
@@ -1003,6 +1003,8 @@ check_symbol_exists(freeifaddrs "${CURL_INCLUDES}" HAVE_FREEIFADDRS)
check_symbol_exists(pipe "${CURL_INCLUDES}" HAVE_PIPE)
check_symbol_exists(ftruncate "${CURL_INCLUDES}" HAVE_FTRUNCATE)
check_symbol_exists(getprotobyname "${CURL_INCLUDES}" HAVE_GETPROTOBYNAME)
+check_symbol_exists(getpeername "${CURL_INCLUDES}" HAVE_GETPEERNAME)
+check_symbol_exists(getsockname "${CURL_INCLUDES}" HAVE_GETSOCKNAME)
check_symbol_exists(getrlimit "${CURL_INCLUDES}" HAVE_GETRLIMIT)
check_symbol_exists(setlocale "${CURL_INCLUDES}" HAVE_SETLOCALE)
check_symbol_exists(setmode "${CURL_INCLUDES}" HAVE_SETMODE)
@@ -1086,6 +1088,8 @@ foreach(CURL_TEST
HAVE_INET_NTOA_R_DECL_REENTRANT
HAVE_GETADDRINFO
HAVE_FILE_OFFSET_BITS
+ HAVE_VARIADIC_MACROS_C99
+ HAVE_VARIADIC_MACROS_GCC
)
curl_internal_test(${CURL_TEST})
endforeach()
@@ -1247,7 +1251,7 @@ if(CURL_WERROR)
endif()
# Ugly (but functional) way to include "Makefile.inc" by transforming it (= regenerate it).
-function(TRANSFORM_MAKEFILE_INC INPUT_FILE OUTPUT_FILE)
+function(transform_makefile_inc INPUT_FILE OUTPUT_FILE)
file(READ ${INPUT_FILE} MAKEFILE_INC_TEXT)
string(REPLACE "$(top_srcdir)" "\${CURL_SOURCE_DIR}" MAKEFILE_INC_TEXT ${MAKEFILE_INC_TEXT})
string(REPLACE "$(top_builddir)" "\${CURL_BINARY_DIR}" MAKEFILE_INC_TEXT ${MAKEFILE_INC_TEXT})
@@ -1311,10 +1315,7 @@ endfunction()
# Clear list and try to detect available features
set(_items)
-_add_if("WinSSL" SSL_ENABLED AND USE_WINDOWS_SSPI)
-_add_if("OpenSSL" SSL_ENABLED AND USE_OPENSSL)
-_add_if("DarwinSSL" SSL_ENABLED AND USE_DARWINSSL)
-_add_if("mbedTLS" SSL_ENABLED AND USE_MBEDTLS)
+_add_if("SSL" SSL_ENABLED)
_add_if("IPv6" ENABLE_IPV6)
_add_if("unix-sockets" USE_UNIX_SOCKETS)
_add_if("libz" HAVE_LIBZ)
@@ -1332,7 +1333,7 @@ _add_if("Kerberos" NOT CURL_DISABLE_CRYPTO_AUTH AND
(HAVE_GSSAPI OR USE_WINDOWS_SSPI))
# NTLM support requires crypto function adaptions from various SSL libs
# TODO alternative SSL libs tests for SSP1, GNUTLS, NSS
-if(NOT CURL_DISABLE_CRYPTO_AUTH AND (USE_OPENSSL OR USE_WINDOWS_SSPI OR USE_DARWINSSL OR USE_MBEDTLS))
+if(NOT CURL_DISABLE_CRYPTO_AUTH AND (USE_OPENSSL OR USE_WINDOWS_SSPI OR USE_SECTRANSP OR USE_MBEDTLS))
_add_if("NTLM" 1)
# TODO missing option (autoconf: --enable-ntlm-wb)
_add_if("NTLM_WB" NOT CURL_DISABLE_HTTP AND NTLM_WB_ENABLED)
@@ -1371,10 +1372,24 @@ _add_if("SCP" USE_LIBSSH2)
_add_if("SFTP" USE_LIBSSH2)
_add_if("RTSP" NOT CURL_DISABLE_RTSP)
_add_if("RTMP" USE_LIBRTMP)
-list(SORT _items)
+if(_items)
+ list(SORT _items)
+endif()
string(REPLACE ";" " " SUPPORT_PROTOCOLS "${_items}")
message(STATUS "Enabled protocols: ${SUPPORT_PROTOCOLS}")
+# Clear list and collect SSL backends
+set(_items)
+_add_if("WinSSL" SSL_ENABLED AND USE_WINDOWS_SSPI)
+_add_if("OpenSSL" SSL_ENABLED AND USE_OPENSSL)
+_add_if("Secure Transport" SSL_ENABLED AND USE_SECTRANSP)
+_add_if("mbedTLS" SSL_ENABLED AND USE_MBEDTLS)
+if(_items)
+ list(SORT _items)
+endif()
+string(REPLACE ";" " " SSL_BACKENDS "${_items}")
+message(STATUS "Enabled SSL backends: ${SSL_BACKENDS}")
+
# curl-config needs the following options to be set.
set(CC "${CMAKE_C_COMPILER}")
# TODO probably put a -D... options here?
diff --git a/Utilities/cmcurl/COPYING b/Utilities/cmcurl/COPYING
index 560a49dce..3528bd756 100644
--- a/Utilities/cmcurl/COPYING
+++ b/Utilities/cmcurl/COPYING
@@ -1,6 +1,6 @@
COPYRIGHT AND PERMISSION NOTICE
-Copyright (c) 1996 - 2018, Daniel Stenberg, <daniel@haxx.se>, and many
+Copyright (c) 1996 - 2019, Daniel Stenberg, <daniel@haxx.se>, and many
contributors, see the THANKS file.
All rights reserved.
diff --git a/Utilities/cmcurl/include/curl/curl.h b/Utilities/cmcurl/include/curl/curl.h
index 63bb291b3..089c427db 100644
--- a/Utilities/cmcurl/include/curl/curl.h
+++ b/Utilities/cmcurl/include/curl/curl.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -91,6 +91,11 @@
#include <support/SupportDefs.h>
#endif
+/* Compatibility for non-Clang compilers */
+#ifndef __has_declspec_attribute
+# define __has_declspec_attribute(x) 0
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -109,7 +114,9 @@ typedef void CURLSH;
#ifdef CURL_STATICLIB
# define CURL_EXTERN
-#elif defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__)
+#elif defined(WIN32) || defined(__SYMBIAN32__) || \
+ (__has_declspec_attribute(dllexport) && \
+ __has_declspec_attribute(dllimport))
# if defined(BUILDING_LIBCURL)
# define CURL_EXTERN __declspec(dllexport)
# else
@@ -144,8 +151,8 @@ typedef enum {
CURLSSLBACKEND_POLARSSL = 6,
CURLSSLBACKEND_WOLFSSL = 7,
CURLSSLBACKEND_SCHANNEL = 8,
- CURLSSLBACKEND_DARWINSSL = 9,
- CURLSSLBACKEND_AXTLS = 10,
+ CURLSSLBACKEND_SECURETRANSPORT = 9,
+ CURLSSLBACKEND_AXTLS = 10, /* never used since 7.63.0 */
CURLSSLBACKEND_MBEDTLS = 11,
CURLSSLBACKEND_MESALINK = 12
} curl_sslbackend;
@@ -153,7 +160,10 @@ typedef enum {
/* aliases for library clones and renames */
#define CURLSSLBACKEND_LIBRESSL CURLSSLBACKEND_OPENSSL
#define CURLSSLBACKEND_BORINGSSL CURLSSLBACKEND_OPENSSL
+
+/* deprecated names: */
#define CURLSSLBACKEND_CYASSL CURLSSLBACKEND_WOLFSSL
+#define CURLSSLBACKEND_DARWINSSL CURLSSLBACKEND_SECURETRANSPORT
struct curl_httppost {
struct curl_httppost *next; /* next entry in the list */
@@ -280,7 +290,7 @@ typedef enum {
struct curl_fileinfo {
char *filename;
curlfiletype filetype;
- time_t time;
+ time_t time; /* always zero! */
unsigned int perm;
int uid;
int gid;
@@ -355,11 +365,21 @@ typedef int (*curl_seek_callback)(void *instream,
signal libcurl to pause sending data on the current transfer. */
#define CURL_READFUNC_PAUSE 0x10000001
+/* Return code for when the trailing headers' callback has terminated
+ without any errors*/
+#define CURL_TRAILERFUNC_OK 0
+/* Return code for when was an error in the trailing header's list and we
+ want to abort the request */
+#define CURL_TRAILERFUNC_ABORT 1
+
typedef size_t (*curl_read_callback)(char *buffer,
size_t size,
size_t nitems,
void *instream);
+typedef int (*curl_trailer_callback)(struct curl_slist **list,
+ void *userdata);
+
typedef enum {
CURLSOCKTYPE_IPCXN, /* socket created for a specific IP connection */
CURLSOCKTYPE_ACCEPT, /* socket created by accept() call */
@@ -585,9 +605,6 @@ typedef enum {
CURL_LAST /* never use! */
} CURLcode;
-/* added in 7.62.0 */
-#define CURLE_SSL_CACERT CURLE_PEER_FAILED_VERIFICATION
-
#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
the obsolete stuff removed! */
@@ -602,6 +619,9 @@ typedef enum {
#define CURLOPT_ENCODING CURLOPT_ACCEPT_ENCODING
#define CURLE_FTP_WEIRD_SERVER_REPLY CURLE_WEIRD_SERVER_REPLY
+/* The following were added in 7.62.0 */
+#define CURLE_SSL_CACERT CURLE_PEER_FAILED_VERIFICATION
+
/* The following were added in 7.21.5, April 2011 */
#define CURLE_UNKNOWN_TELNET_OPTION CURLE_UNKNOWN_OPTION
@@ -861,6 +881,14 @@ typedef enum {
#define CURLHEADER_UNIFIED 0
#define CURLHEADER_SEPARATE (1<<0)
+/* CURLALTSVC_* are bits for the CURLOPT_ALTSVC_CTRL option */
+#define CURLALTSVC_IMMEDIATELY (1<<0)
+#define CURLALTSVC_ALTUSED (1<<1)
+#define CURLALTSVC_READONLYFILE (1<<2)
+#define CURLALTSVC_H1 (1<<3)
+#define CURLALTSVC_H2 (1<<4)
+#define CURLALTSVC_H3 (1<<5)
+
/* CURLPROTO_ defines are for the CURLOPT_*PROTOCOLS options */
#define CURLPROTO_HTTP (1<<0)
#define CURLPROTO_HTTPS (1<<1)
@@ -1872,6 +1900,27 @@ typedef enum {
/* Time in ms between connection upkeep calls for long-lived connections. */
CINIT(UPKEEP_INTERVAL_MS, LONG, 281),
+ /* Specify URL using CURL URL API. */
+ CINIT(CURLU, OBJECTPOINT, 282),
+
+ /* add trailing data just after no more data is available */
+ CINIT(TRAILERFUNCTION, FUNCTIONPOINT, 283),
+
+ /* pointer to be passed to HTTP_TRAILER_FUNCTION */
+ CINIT(TRAILERDATA, OBJECTPOINT, 284),
+
+ /* set this to 1L to allow HTTP/0.9 responses or 0L to disallow */
+ CINIT(HTTP09_ALLOWED, LONG, 285),
+
+ /* alt-svc control bitmask */
+ CINIT(ALTSVC_CTRL, LONG, 286),
+
+ /* alt-svc cache file name to possibly read from/write to */
+ CINIT(ALTSVC, STRINGPOINT, 287),
+
+ /* maximum age of a connection to consider it for reuse (in seconds) */
+ CINIT(MAXAGE_CONN, LONG, 288),
+
CURLOPT_LASTENTRY /* the last unused */
} CURLoption;
@@ -2734,6 +2783,7 @@ typedef struct {
#define CURL_VERSION_HTTPS_PROXY (1<<21) /* HTTPS-proxy support built-in */
#define CURL_VERSION_MULTI_SSL (1<<22) /* Multiple SSL backends available */
#define CURL_VERSION_BROTLI (1<<23) /* Brotli features are present. */
+#define CURL_VERSION_ALTSVC (1<<24) /* Alt-Svc handling built-in */
/*
* NAME curl_version_info()
diff --git a/Utilities/cmcurl/include/curl/curlver.h b/Utilities/cmcurl/include/curl/curlver.h
index 3d7b1fbfe..04efb93ae 100644
--- a/Utilities/cmcurl/include/curl/curlver.h
+++ b/Utilities/cmcurl/include/curl/curlver.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -26,16 +26,16 @@
a script at release-time. This was made its own header file in 7.11.2 */
/* This is the global package copyright */
-#define LIBCURL_COPYRIGHT "1996 - 2018 Daniel Stenberg, <daniel@haxx.se>."
+#define LIBCURL_COPYRIGHT "1996 - 2019 Daniel Stenberg, <daniel@haxx.se>."
/* This is the version number of the libcurl package from which this header
file origins: */
-#define LIBCURL_VERSION "7.62.0"
+#define LIBCURL_VERSION "7.65.0"
/* The numeric version number is also available "in parts" by using these
defines: */
#define LIBCURL_VERSION_MAJOR 7
-#define LIBCURL_VERSION_MINOR 62
+#define LIBCURL_VERSION_MINOR 65
#define LIBCURL_VERSION_PATCH 0
/* This is the numeric version of the libcurl version number, meant for easier
@@ -57,7 +57,7 @@
CURL_VERSION_BITS() macro since curl's own configure script greps for it
and needs it to contain the full number.
*/
-#define LIBCURL_VERSION_NUM 0x073E00
+#define LIBCURL_VERSION_NUM 0x074100
/*
* This is the date and time when the full source package was created. The
@@ -70,7 +70,7 @@
*/
#define LIBCURL_TIMESTAMP "[unreleased]"
-#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|z)
+#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z))
#define CURL_AT_LEAST_VERSION(x,y,z) \
(LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z))
diff --git a/Utilities/cmcurl/include/curl/typecheck-gcc.h b/Utilities/cmcurl/include/curl/typecheck-gcc.h
index 244336204..2d1de4d43 100644
--- a/Utilities/cmcurl/include/curl/typecheck-gcc.h
+++ b/Utilities/cmcurl/include/curl/typecheck-gcc.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -113,7 +113,6 @@ __extension__ ({ \
})
/* wraps curl_easy_getinfo() with typechecking */
-/* FIXME: don't allow const pointers */
#define curl_easy_getinfo(handle, info, arg) \
__extension__ ({ \
__typeof__(info) _curl_info = info; \
@@ -146,9 +145,8 @@ __extension__ ({ \
curl_easy_getinfo(handle, _curl_info, arg); \
})
-/* TODO: typechecking for curl_share_setopt() and curl_multi_setopt(),
- * for now just make sure that the functions are called with three
- * arguments
+/*
+ * For now, just make sure that the functions are called with three arguments
*/
#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param)
#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param)
@@ -256,6 +254,7 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t,
#define _curl_is_string_option(option) \
((option) == CURLOPT_ABSTRACT_UNIX_SOCKET || \
(option) == CURLOPT_ACCEPT_ENCODING || \
+ (option) == CURLOPT_ALTSVC || \
(option) == CURLOPT_CAINFO || \
(option) == CURLOPT_CAPATH || \
(option) == CURLOPT_COOKIE || \
@@ -363,6 +362,7 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t,
(option) == CURLOPT_SSL_CTX_DATA || \
(option) == CURLOPT_WRITEDATA || \
(option) == CURLOPT_RESOLVER_START_DATA || \
+ (option) == CURLOPT_CURLU || \
0)
/* evaluates to true if option takes a POST data argument (void* or char*) */
@@ -504,10 +504,6 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t,
_curl_is_arr((expr), char) || \
_curl_is_arr((expr), unsigned char))
-/* FIXME: the whole callback checking is messy...
- * The idea is to tolerate char vs. void and const vs. not const
- * pointers in arguments at least
- */
/* helper: __builtin_types_compatible_p distinguishes between functions and
* function pointers, hide it */
#define _curl_callback_compatible(func, type) \
diff --git a/Utilities/cmcurl/include/curl/urlapi.h b/Utilities/cmcurl/include/curl/urlapi.h
index 90dd56c00..58e89d85c 100644
--- a/Utilities/cmcurl/include/curl/urlapi.h
+++ b/Utilities/cmcurl/include/curl/urlapi.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2018 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -22,6 +22,8 @@
*
***************************************************************************/
+#include "curl.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -58,7 +60,8 @@ typedef enum {
CURLUPART_PORT,
CURLUPART_PATH,
CURLUPART_QUERY,
- CURLUPART_FRAGMENT
+ CURLUPART_FRAGMENT,
+ CURLUPART_ZONEID /* added in 7.65.0 */
} CURLUPart;
#define CURLU_DEFAULT_PORT (1<<0) /* return default port number */
diff --git a/Utilities/cmcurl/lib/Makefile.inc b/Utilities/cmcurl/lib/Makefile.inc
index 4aa042259..235b82b0e 100644
--- a/Utilities/cmcurl/lib/Makefile.inc
+++ b/Utilities/cmcurl/lib/Makefile.inc
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
@@ -28,13 +28,13 @@ LIB_VAUTH_CFILES = vauth/vauth.c vauth/cleartext.c vauth/cram.c \
LIB_VAUTH_HFILES = vauth/vauth.h vauth/digest.h vauth/ntlm.h
LIB_VTLS_CFILES = vtls/openssl.c vtls/gtls.c vtls/vtls.c vtls/nss.c \
- vtls/polarssl.c vtls/polarssl_threadlock.c vtls/axtls.c \
+ vtls/polarssl.c vtls/polarssl_threadlock.c \
vtls/cyassl.c vtls/schannel.c vtls/schannel_verify.c \
- vtls/darwinssl.c vtls/gskit.c vtls/mbedtls.c vtls/mesalink.c
+ vtls/sectransp.c vtls/gskit.c vtls/mbedtls.c vtls/mesalink.c
LIB_VTLS_HFILES = vtls/openssl.h vtls/vtls.h vtls/gtls.h \
- vtls/nssg.h vtls/polarssl.h vtls/polarssl_threadlock.h vtls/axtls.h \
- vtls/cyassl.h vtls/schannel.h vtls/darwinssl.h vtls/gskit.h \
+ vtls/nssg.h vtls/polarssl.h vtls/polarssl_threadlock.h \
+ vtls/cyassl.h vtls/schannel.h vtls/sectransp.h vtls/gskit.h \
vtls/mbedtls.h vtls/mesalink.h
LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
@@ -52,10 +52,10 @@ LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
openldap.c curl_gethostname.c gopher.c idn_win32.c \
http_proxy.c non-ascii.c asyn-ares.c asyn-thread.c curl_gssapi.c \
http_ntlm.c curl_ntlm_wb.c curl_ntlm_core.c curl_sasl.c rand.c \
- curl_multibyte.c hostcheck.c conncache.c pipeline.c dotdot.c \
+ curl_multibyte.c hostcheck.c conncache.c dotdot.c \
x509asn1.c http2.c smb.c curl_endian.c curl_des.c system_win32.c \
mime.c sha256.c setopt.c curl_path.c curl_ctype.c curl_range.c psl.c \
- doh.c urlapi.c
+ doh.c urlapi.c curl_get_line.c altsvc.c
LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \
formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h if2ip.h \
@@ -72,10 +72,11 @@ LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \
curl_gethostname.h gopher.h http_proxy.h non-ascii.h asyn.h \
http_ntlm.h curl_gssapi.h curl_ntlm_wb.h curl_ntlm_core.h \
curl_sasl.h curl_multibyte.h hostcheck.h conncache.h \
- curl_setup_once.h multihandle.h setup-vms.h pipeline.h dotdot.h \
+ curl_setup_once.h multihandle.h setup-vms.h dotdot.h \
x509asn1.h http2.h sigpipe.h smb.h curl_endian.h curl_des.h \
curl_printf.h system_win32.h rand.h mime.h curl_sha256.h setopt.h \
- curl_path.h curl_ctype.h curl_range.h psl.h doh.h urlapi-int.h
+ curl_path.h curl_ctype.h curl_range.h psl.h doh.h urlapi-int.h \
+ curl_get_line.h altsvc.h
LIB_RCFILES = libcurl.rc
diff --git a/Utilities/cmcurl/lib/altsvc.c b/Utilities/cmcurl/lib/altsvc.c
new file mode 100644
index 000000000..85a4e01b5
--- /dev/null
+++ b/Utilities/cmcurl/lib/altsvc.c
@@ -0,0 +1,569 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+/*
+ * The Alt-Svc: header is defined in RFC 7838:
+ * https://tools.ietf.org/html/rfc7838
+ */
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_HTTP) && defined(USE_ALTSVC)
+#include <curl/curl.h>
+#include "urldata.h"
+#include "altsvc.h"
+#include "curl_get_line.h"
+#include "strcase.h"
+#include "parsedate.h"
+#include "sendf.h"
+#include "warnless.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define MAX_ALTSVC_LINE 4095
+#define MAX_ALTSVC_DATELENSTR "64"
+#define MAX_ALTSVC_DATELEN 64
+#define MAX_ALTSVC_HOSTLENSTR "512"
+#define MAX_ALTSVC_HOSTLEN 512
+#define MAX_ALTSVC_ALPNLENSTR "10"
+#define MAX_ALTSVC_ALPNLEN 10
+
+static enum alpnid alpn2alpnid(char *name)
+{
+ if(strcasecompare(name, "h1"))
+ return ALPN_h1;
+ if(strcasecompare(name, "h2"))
+ return ALPN_h2;
+ if(strcasecompare(name, "h2c"))
+ return ALPN_h2c;
+ if(strcasecompare(name, "h3"))
+ return ALPN_h3;
+ return ALPN_none; /* unknown, probably rubbish input */
+}
+
+/* Given the ALPN ID, return the name */
+const char *Curl_alpnid2str(enum alpnid id)
+{
+ switch(id) {
+ case ALPN_h1:
+ return "h1";
+ case ALPN_h2:
+ return "h2";
+ case ALPN_h2c:
+ return "h2c";
+ case ALPN_h3:
+ return "h3";
+ default:
+ return ""; /* bad */
+ }
+}
+
+
+static void altsvc_free(struct altsvc *as)
+{
+ free(as->srchost);
+ free(as->dsthost);
+ free(as);
+}
+
+static struct altsvc *altsvc_createid(const char *srchost,
+ const char *dsthost,
+ enum alpnid srcalpnid,
+ enum alpnid dstalpnid,
+ unsigned int srcport,
+ unsigned int dstport)
+{
+ struct altsvc *as = calloc(sizeof(struct altsvc), 1);
+ if(!as)
+ return NULL;
+
+ as->srchost = strdup(srchost);
+ if(!as->srchost)
+ goto error;
+ as->dsthost = strdup(dsthost);
+ if(!as->dsthost)
+ goto error;
+
+ as->srcalpnid = srcalpnid;
+ as->dstalpnid = dstalpnid;
+ as->srcport = curlx_ultous(srcport);
+ as->dstport = curlx_ultous(dstport);
+
+ return as;
+ error:
+ altsvc_free(as);
+ return NULL;
+}
+
+static struct altsvc *altsvc_create(char *srchost,
+ char *dsthost,
+ char *srcalpn,
+ char *dstalpn,
+ unsigned int srcport,
+ unsigned int dstport)
+{
+ enum alpnid dstalpnid = alpn2alpnid(dstalpn);
+ enum alpnid srcalpnid = alpn2alpnid(srcalpn);
+ if(!srcalpnid || !dstalpnid)
+ return NULL;
+ return altsvc_createid(srchost, dsthost, srcalpnid, dstalpnid,
+ srcport, dstport);
+}
+
+/* only returns SERIOUS errors */
+static CURLcode altsvc_add(struct altsvcinfo *asi, char *line)
+{
+ /* Example line:
+ h2 example.com 443 h3 shiny.example.com 8443 "20191231 10:00:00" 1
+ */
+ char srchost[MAX_ALTSVC_HOSTLEN + 1];
+ char dsthost[MAX_ALTSVC_HOSTLEN + 1];
+ char srcalpn[MAX_ALTSVC_ALPNLEN + 1];
+ char dstalpn[MAX_ALTSVC_ALPNLEN + 1];
+ char date[MAX_ALTSVC_DATELEN + 1];
+ unsigned int srcport;
+ unsigned int dstport;
+ unsigned int prio;
+ unsigned int persist;
+ int rc;
+
+ rc = sscanf(line,
+ "%" MAX_ALTSVC_ALPNLENSTR "s %" MAX_ALTSVC_HOSTLENSTR "s %u "
+ "%" MAX_ALTSVC_ALPNLENSTR "s %" MAX_ALTSVC_HOSTLENSTR "s %u "
+ "\"%" MAX_ALTSVC_DATELENSTR "[^\"]\" %u %u",
+ srcalpn, srchost, &srcport,
+ dstalpn, dsthost, &dstport,
+ date, &persist, &prio);
+ if(9 == rc) {
+ struct altsvc *as;
+ time_t expires = curl_getdate(date, NULL);
+ as = altsvc_create(srchost, dsthost, srcalpn, dstalpn, srcport, dstport);
+ if(as) {
+ as->expires = expires;
+ as->prio = prio;
+ as->persist = persist ? 1 : 0;
+ Curl_llist_insert_next(&asi->list, asi->list.tail, as, &as->node);
+ asi->num++; /* one more entry */
+ }
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Load alt-svc entries from the given file. The text based line-oriented file
+ * format is documented here:
+ * https://github.com/curl/curl/wiki/QUIC-implementation
+ *
+ * This function only returns error on major problems that prevents alt-svc
+ * handling to work completely. It will ignore individual syntactical errors
+ * etc.
+ */
+static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file)
+{
+ CURLcode result = CURLE_OK;
+ char *line = NULL;
+ FILE *fp = fopen(file, FOPEN_READTEXT);
+ if(fp) {
+ line = malloc(MAX_ALTSVC_LINE);
+ if(!line)
+ goto fail;
+ while(Curl_get_line(line, MAX_ALTSVC_LINE, fp)) {
+ char *lineptr = line;
+ while(*lineptr && ISBLANK(*lineptr))
+ lineptr++;
+ if(*lineptr == '#')
+ /* skip commented lines */
+ continue;
+
+ altsvc_add(asi, lineptr);
+ }
+ free(line); /* free the line buffer */
+ fclose(fp);
+ }
+ return result;
+
+ fail:
+ free(line);
+ fclose(fp);
+ return CURLE_OUT_OF_MEMORY;
+}
+
+/*
+ * Write this single altsvc entry to a single output line
+ */
+
+static CURLcode altsvc_out(struct altsvc *as, FILE *fp)
+{
+ struct tm stamp;
+ CURLcode result = Curl_gmtime(as->expires, &stamp);
+ if(result)
+ return result;
+
+ fprintf(fp,
+ "%s %s %u "
+ "%s %s %u "
+ "\"%d%02d%02d "
+ "%02d:%02d:%02d\" "
+ "%u %d\n",
+ Curl_alpnid2str(as->srcalpnid), as->srchost, as->srcport,
+ Curl_alpnid2str(as->dstalpnid), as->dsthost, as->dstport,
+ stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
+ stamp.tm_hour, stamp.tm_min, stamp.tm_sec,
+ as->persist, as->prio);
+ return CURLE_OK;
+}
+
+/* ---- library-wide functions below ---- */
+
+/*
+ * Curl_altsvc_init() creates a new altsvc cache.
+ * It returns the new instance or NULL if something goes wrong.
+ */
+struct altsvcinfo *Curl_altsvc_init(void)
+{
+ struct altsvcinfo *asi = calloc(sizeof(struct altsvcinfo), 1);
+ if(!asi)
+ return NULL;
+ Curl_llist_init(&asi->list, NULL);
+
+ /* set default behavior */
+ asi->flags = CURLALTSVC_H1
+#ifdef USE_NGHTTP2
+ | CURLALTSVC_H2
+#endif
+#ifdef USE_HTTP3
+ | CURLALTSVC_H3
+#endif
+ ;
+ return asi;
+}
+
+/*
+ * Curl_altsvc_load() loads alt-svc from file.
+ */
+CURLcode Curl_altsvc_load(struct altsvcinfo *asi, const char *file)
+{
+ CURLcode result;
+ DEBUGASSERT(asi);
+ result = altsvc_load(asi, file);
+ return result;
+}
+
+/*
+ * Curl_altsvc_ctrl() passes on the external bitmask.
+ */
+CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl)
+{
+ DEBUGASSERT(asi);
+ if(!ctrl)
+ /* unexpected */
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ asi->flags = ctrl;
+ return CURLE_OK;
+}
+
+/*
+ * Curl_altsvc_cleanup() frees an altsvc cache instance and all associated
+ * resources.
+ */
+void Curl_altsvc_cleanup(struct altsvcinfo *altsvc)
+{
+ struct curl_llist_element *e;
+ struct curl_llist_element *n;
+ if(altsvc) {
+ for(e = altsvc->list.head; e; e = n) {
+ struct altsvc *as = e->ptr;
+ n = e->next;
+ altsvc_free(as);
+ }
+ free(altsvc);
+ }
+}
+
+/*
+ * Curl_altsvc_save() writes the altsvc cache to a file.
+ */
+CURLcode Curl_altsvc_save(struct altsvcinfo *altsvc, const char *file)
+{
+ struct curl_llist_element *e;
+ struct curl_llist_element *n;
+ CURLcode result = CURLE_OK;
+ FILE *out;
+
+ if(!altsvc)
+ /* no cache activated */
+ return CURLE_OK;
+
+ if((altsvc->flags & CURLALTSVC_READONLYFILE) || !file[0])
+ /* marked as read-only or zero length file name */
+ return CURLE_OK;
+ out = fopen(file, FOPEN_WRITETEXT);
+ if(!out)
+ return CURLE_WRITE_ERROR;
+ fputs("# Your alt-svc cache. https://curl.haxx.se/docs/alt-svc.html\n"
+ "# This file was generated by libcurl! Edit at your own risk.\n",
+ out);
+ for(e = altsvc->list.head; e; e = n) {
+ struct altsvc *as = e->ptr;
+ n = e->next;
+ result = altsvc_out(as, out);
+ if(result)
+ break;
+ }
+ fclose(out);
+ return result;
+}
+
+static CURLcode getalnum(const char **ptr, char *alpnbuf, size_t buflen)
+{
+ size_t len;
+ const char *protop;
+ const char *p = *ptr;
+ while(*p && ISBLANK(*p))
+ p++;
+ protop = p;
+ while(*p && ISALNUM(*p))
+ p++;
+ len = p - protop;
+
+ if(!len || (len >= buflen))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ memcpy(alpnbuf, protop, len);
+ alpnbuf[len] = 0;
+ *ptr = p;
+ return CURLE_OK;
+}
+
+/* altsvc_flush() removes all alternatives for this source origin from the
+ list */
+static void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid,
+ const char *srchost, unsigned short srcport)
+{
+ struct curl_llist_element *e;
+ struct curl_llist_element *n;
+ for(e = asi->list.head; e; e = n) {
+ struct altsvc *as = e->ptr;
+ n = e->next;
+ if((srcalpnid == as->srcalpnid) &&
+ (srcport == as->srcport) &&
+ strcasecompare(srchost, as->srchost)) {
+ Curl_llist_remove(&asi->list, e, NULL);
+ altsvc_free(as);
+ asi->num--;
+ }
+ }
+}
+
+#ifdef DEBUGBUILD
+/* to play well with debug builds, we can *set* a fixed time this will
+ return */
+static time_t debugtime(void *unused)
+{
+ char *timestr = getenv("CURL_TIME");
+ (void)unused;
+ if(timestr) {
+ unsigned long val = strtol(timestr, NULL, 10);
+ return (time_t)val;
+ }
+ return time(NULL);
+}
+#define time(x) debugtime(x)
+#endif
+
+/*
+ * Curl_altsvc_parse() takes an incoming alt-svc response header and stores
+ * the data correctly in the cache.
+ *
+ * 'value' points to the header *value*. That's contents to the right of the
+ * header name.
+ */
+CURLcode Curl_altsvc_parse(struct Curl_easy *data,
+ struct altsvcinfo *asi, const char *value,
+ enum alpnid srcalpnid, const char *srchost,
+ unsigned short srcport)
+{
+ const char *p = value;
+ size_t len;
+ enum alpnid dstalpnid = srcalpnid; /* the same by default */
+ char namebuf[MAX_ALTSVC_HOSTLEN] = "";
+ char alpnbuf[MAX_ALTSVC_ALPNLEN] = "";
+ struct altsvc *as;
+ unsigned short dstport = srcport; /* the same by default */
+ const char *semip;
+ time_t maxage = 24 * 3600; /* default is 24 hours */
+ bool persist = FALSE;
+ CURLcode result = getalnum(&p, alpnbuf, sizeof(alpnbuf));
+ if(result)
+ return result;
+
+ DEBUGASSERT(asi);
+
+ /* Flush all cached alternatives for this source origin, if any */
+ altsvc_flush(asi, srcalpnid, srchost, srcport);
+
+ /* "clear" is a magic keyword */
+ if(strcasecompare(alpnbuf, "clear")) {
+ return CURLE_OK;
+ }
+
+ /* The 'ma' and 'persist' flags are annoyingly meant for all alternatives
+ but are set after the list on the line. Scan for the semicolons and get
+ those fields first! */
+ semip = p;
+ do {
+ semip = strchr(semip, ';');
+ if(semip) {
+ char option[32];
+ unsigned long num;
+ char *end_ptr;
+ semip++; /* pass the semicolon */
+ result = getalnum(&semip, option, sizeof(option));
+ if(result)
+ break;
+ while(*semip && ISBLANK(*semip))
+ semip++;
+ if(*semip != '=')
+ continue;
+ semip++;
+ num = strtoul(semip, &end_ptr, 10);
+ if(num < ULONG_MAX) {
+ if(strcasecompare("ma", option))
+ maxage = num;
+ else if(strcasecompare("persist", option) && (num == 1))
+ persist = TRUE;
+ }
+ semip = end_ptr;
+ }
+ } while(semip);
+
+ do {
+ if(*p == '=') {
+ /* [protocol]="[host][:port]" */
+ dstalpnid = alpn2alpnid(alpnbuf);
+ if(!dstalpnid) {
+ infof(data, "Unknown alt-svc protocol \"%s\", ignoring...\n", alpnbuf);
+ return CURLE_OK;
+ }
+ p++;
+ if(*p == '\"') {
+ const char *dsthost;
+ p++;
+ if(*p != ':') {
+ /* host name starts here */
+ const char *hostp = p;
+ while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-')))
+ p++;
+ len = p - hostp;
+ if(!len || (len >= MAX_ALTSVC_HOSTLEN))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ memcpy(namebuf, hostp, len);
+ namebuf[len] = 0;
+ dsthost = namebuf;
+ }
+ else {
+ /* no destination name, use source host */
+ dsthost = srchost;
+ }
+ if(*p == ':') {
+ /* a port number */
+ char *end_ptr;
+ unsigned long port = strtoul(++p, &end_ptr, 10);
+ if(port > USHRT_MAX || end_ptr == p || *end_ptr != '\"') {
+ infof(data, "Unknown alt-svc port number, ignoring...\n");
+ return CURLE_OK;
+ }
+ p = end_ptr;
+ dstport = curlx_ultous(port);
+ }
+ if(*p++ != '\"')
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ as = altsvc_createid(srchost, dsthost,
+ srcalpnid, dstalpnid,
+ srcport, dstport);
+ if(as) {
+ /* The expires time also needs to take the Age: value (if any) into
+ account. [See RFC 7838 section 3.1] */
+ as->expires = maxage + time(NULL);
+ as->persist = persist;
+ Curl_llist_insert_next(&asi->list, asi->list.tail, as, &as->node);
+ asi->num++; /* one more entry */
+ infof(data, "Added alt-svc: %s:%d over %s\n", dsthost, dstport,
+ Curl_alpnid2str(dstalpnid));
+ }
+ }
+ /* after the double quote there can be a comma if there's another
+ string or a semicolon if no more */
+ if(*p == ',') {
+ /* comma means another alternative is presented */
+ p++;
+ result = getalnum(&p, alpnbuf, sizeof(alpnbuf));
+ if(result)
+ /* failed to parse, but since we already did at least one host we
+ return OK */
+ return CURLE_OK;
+ }
+ }
+ } while(*p && (*p != ';') && (*p != '\n') && (*p != '\r'));
+
+ return CURLE_OK;
+}
+
+/*
+ * Return TRUE on a match
+ */
+bool Curl_altsvc_lookup(struct altsvcinfo *asi,
+ enum alpnid srcalpnid, const char *srchost,
+ int srcport,
+ enum alpnid *dstalpnid, const char **dsthost,
+ int *dstport)
+{
+ struct curl_llist_element *e;
+ struct curl_llist_element *n;
+ time_t now = time(NULL);
+ DEBUGASSERT(asi);
+ DEBUGASSERT(srchost);
+ DEBUGASSERT(dsthost);
+
+ for(e = asi->list.head; e; e = n) {
+ struct altsvc *as = e->ptr;
+ n = e->next;
+ if(as->expires < now) {
+ /* an expired entry, remove */
+ altsvc_free(as);
+ continue;
+ }
+ if((as->srcalpnid == srcalpnid) &&
+ strcasecompare(as->srchost, srchost) &&
+ as->srcport == srcport) {
+ /* match */
+ *dstalpnid = as->dstalpnid;
+ *dsthost = as->dsthost;
+ *dstport = as->dstport;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+#endif /* CURL_DISABLE_HTTP || USE_ALTSVC */
diff --git a/Utilities/cmcurl/lib/altsvc.h b/Utilities/cmcurl/lib/altsvc.h
new file mode 100644
index 000000000..eefb45bf6
--- /dev/null
+++ b/Utilities/cmcurl/lib/altsvc.h
@@ -0,0 +1,77 @@
+#ifndef HEADER_CURL_ALTSVC_H
+#define HEADER_CURL_ALTSVC_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_HTTP) && defined(USE_ALTSVC)
+#include <curl/curl.h>
+#include "llist.h"
+
+enum alpnid {
+ ALPN_none,
+ ALPN_h1,
+ ALPN_h2,
+ ALPN_h2c,
+ ALPN_h3
+};
+
+struct altsvc {
+ char *srchost;
+ char *dsthost;
+ unsigned short srcport;
+ unsigned short dstport;
+ enum alpnid srcalpnid;
+ enum alpnid dstalpnid;
+ time_t expires;
+ bool persist;
+ int prio;
+ struct curl_llist_element node;
+};
+
+struct altsvcinfo {
+ char *filename;
+ struct curl_llist list; /* list of entries */
+ size_t num; /* number of alt-svc entries */
+ long flags; /* the publicly set bitmask */
+};
+
+const char *Curl_alpnid2str(enum alpnid id);
+struct altsvcinfo *Curl_altsvc_init(void);
+CURLcode Curl_altsvc_load(struct altsvcinfo *asi, const char *file);
+CURLcode Curl_altsvc_save(struct altsvcinfo *asi, const char *file);
+CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl);
+void Curl_altsvc_cleanup(struct altsvcinfo *altsvc);
+CURLcode Curl_altsvc_parse(struct Curl_easy *data,
+ struct altsvcinfo *altsvc, const char *value,
+ enum alpnid srcalpn, const char *srchost,
+ unsigned short srcport);
+bool Curl_altsvc_lookup(struct altsvcinfo *asi,
+ enum alpnid srcalpnid, const char *srchost,
+ int srcport,
+ enum alpnid *dstalpnid, const char **dsthost,
+ int *dstport);
+#else
+/* disabled */
+#define Curl_altsvc_save(a,b)
+#endif /* CURL_DISABLE_HTTP || USE_ALTSVC */
+#endif /* HEADER_CURL_ALTSVC_H */
diff --git a/Utilities/cmcurl/lib/amigaos.c b/Utilities/cmcurl/lib/amigaos.c
index 4f55b30e7..cf44bdc8d 100644
--- a/Utilities/cmcurl/lib/amigaos.c
+++ b/Utilities/cmcurl/lib/amigaos.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -22,17 +22,26 @@
#include "curl_setup.h"
-#if defined(__AMIGA__) && !defined(__ixemul__)
-
-#include <amitcp/socketbasetags.h>
+#ifdef __AMIGA__
+# include "amigaos.h"
+# if defined(HAVE_PROTO_BSDSOCKET_H) && !defined(USE_AMISSL)
+# include <amitcp/socketbasetags.h>
+# endif
+# ifdef __libnix__
+# include <stabs.h>
+# endif
+#endif
-#include "amigaos.h"
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+#ifdef __AMIGA__
+#if defined(HAVE_PROTO_BSDSOCKET_H) && !defined(USE_AMISSL)
struct Library *SocketBase = NULL;
extern int errno, h_errno;
#ifdef __libnix__
-#include <stabs.h>
void __request(const char *msg);
#else
# define __request(msg) Printf(msg "\n\a")
@@ -74,4 +83,13 @@ bool Curl_amiga_init()
ADD2EXIT(Curl_amiga_cleanup, -50);
#endif
-#endif /* __AMIGA__ && ! __ixemul__ */
+#endif /* HAVE_PROTO_BSDSOCKET_H */
+
+#ifdef USE_AMISSL
+void Curl_amiga_X509_free(X509 *a)
+{
+ X509_free(a);
+}
+#endif /* USE_AMISSL */
+#endif /* __AMIGA__ */
+
diff --git a/Utilities/cmcurl/lib/amigaos.h b/Utilities/cmcurl/lib/amigaos.h
index 7c0926cc3..c776c9c9b 100644
--- a/Utilities/cmcurl/lib/amigaos.h
+++ b/Utilities/cmcurl/lib/amigaos.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -23,7 +23,7 @@
***************************************************************************/
#include "curl_setup.h"
-#if defined(__AMIGA__) && !defined(__ixemul__)
+#if defined(__AMIGA__) && defined(HAVE_BSDSOCKET_H) && !defined(USE_AMISSL)
bool Curl_amiga_init();
void Curl_amiga_cleanup();
@@ -35,4 +35,10 @@ void Curl_amiga_cleanup();
#endif
+#ifdef USE_AMISSL
+#include <openssl/x509v3.h>
+void Curl_amiga_X509_free(X509 *a);
+#endif /* USE_AMISSL */
+
#endif /* HEADER_CURL_AMIGAOS_H */
+
diff --git a/Utilities/cmcurl/lib/asyn-ares.c b/Utilities/cmcurl/lib/asyn-ares.c
index 5cfb2602d..8561a4724 100644
--- a/Utilities/cmcurl/lib/asyn-ares.c
+++ b/Utilities/cmcurl/lib/asyn-ares.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -68,7 +68,7 @@
#include "progress.h"
# if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
- (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__))
+ (defined(WIN32) || defined(__SYMBIAN32__))
# define CARES_STATICLIB
# endif
# include <ares.h>
@@ -89,8 +89,20 @@ struct ResolverResults {
int num_pending; /* number of ares_gethostbyname() requests */
Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */
int last_status;
+ struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */
};
+/* How long we are willing to wait for additional parallel responses after
+ obtaining a "definitive" one.
+
+ This is intended to equal the c-ares default timeout. cURL always uses that
+ default value. Unfortunately, c-ares doesn't expose its default timeout in
+ its API, but it is officially documented as 5 seconds.
+
+ See query_completed_cb() for an explanation of how this is used.
+ */
+#define HAPPY_EYEBALLS_DNS_TIMEOUT 5000
+
/*
* Curl_resolver_global_init() - the generic low-level asynchronous name
* resolve API. Called from curl_global_init() to initialize global resolver
@@ -119,6 +131,17 @@ void Curl_resolver_global_cleanup(void)
#endif
}
+
+static void Curl_ares_sock_state_cb(void *data, ares_socket_t socket_fd,
+ int readable, int writable)
+{
+ struct Curl_easy *easy = data;
+ if(!readable && !writable) {
+ DEBUGASSERT(easy);
+ Curl_multi_closed(easy, socket_fd);
+ }
+}
+
/*
* Curl_resolver_init()
*
@@ -126,9 +149,14 @@ void Curl_resolver_global_cleanup(void)
* URL-state specific environment ('resolver' member of the UrlState
* structure). Fills the passed pointer by the initialized ares_channel.
*/
-CURLcode Curl_resolver_init(void **resolver)
+CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
{
- int status = ares_init((ares_channel*)resolver);
+ int status;
+ struct ares_options options;
+ int optmask = ARES_OPT_SOCK_STATE_CB;
+ options.sock_state_cb = Curl_ares_sock_state_cb;
+ options.sock_state_cb_data = easy;
+ status = ares_init_options((ares_channel*)resolver, &options, optmask);
if(status != ARES_SUCCESS) {
if(status == ARES_ENOMEM)
return CURLE_OUT_OF_MEMORY;
@@ -159,12 +187,15 @@ void Curl_resolver_cleanup(void *resolver)
* environment ('resolver' member of the UrlState structure). Duplicates the
* 'from' ares channel and passes the resulting channel to the 'to' pointer.
*/
-int Curl_resolver_duphandle(void **to, void *from)
+CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
{
- /* Clone the ares channel for the new handle */
- if(ARES_SUCCESS != ares_dup((ares_channel*)to, (ares_channel)from))
- return CURLE_FAILED_INIT;
- return CURLE_OK;
+ (void)from;
+ /*
+ * it would be better to call ares_dup instead, but right now
+ * it is not possible to set 'sock_state_cb_data' outside of
+ * ares_init_options
+ */
+ return Curl_resolver_init(easy, to);
}
static void destroy_async_data(struct Curl_async *async);
@@ -180,6 +211,17 @@ void Curl_resolver_cancel(struct connectdata *conn)
}
/*
+ * We're equivalent to Curl_resolver_cancel() for the c-ares resolver. We
+ * never block.
+ */
+void Curl_resolver_kill(struct connectdata *conn)
+{
+ /* We don't need to check the resolver state because we can be called safely
+ at any time and we always do the same thing. */
+ Curl_resolver_cancel(conn);
+}
+
+/*
* destroy_async_data() cleans up async resolver data.
*/
static void destroy_async_data(struct Curl_async *async)
@@ -289,9 +331,9 @@ static int waitperform(struct connectdata *conn, int timeout_ms)
/* move through the descriptors and ask for processing on them */
for(i = 0; i < num; i++)
ares_process_fd((ares_channel)data->state.resolver,
- pfd[i].revents & (POLLRDNORM|POLLIN)?
+ (pfd[i].revents & (POLLRDNORM|POLLIN))?
pfd[i].fd:ARES_SOCKET_BAD,
- pfd[i].revents & (POLLWRNORM|POLLOUT)?
+ (pfd[i].revents & (POLLWRNORM|POLLOUT))?
pfd[i].fd:ARES_SOCKET_BAD);
}
return nfds;
@@ -317,6 +359,29 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
waitperform(conn, 0);
+ /* Now that we've checked for any last minute results above, see if there are
+ any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer
+ expires. */
+ if(res
+ && res->num_pending
+ /* This is only set to non-zero if the timer was started. */
+ && (res->happy_eyeballs_dns_time.tv_sec
+ || res->happy_eyeballs_dns_time.tv_usec)
+ && (Curl_timediff(Curl_now(), res->happy_eyeballs_dns_time)
+ >= HAPPY_EYEBALLS_DNS_TIMEOUT)) {
+ /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer
+ running. */
+ memset(
+ &res->happy_eyeballs_dns_time, 0, sizeof(res->happy_eyeballs_dns_time));
+
+ /* Cancel the raw c-ares request, which will fire query_completed_cb() with
+ ARES_ECANCELLED synchronously for all pending responses. This will
+ leave us with res->num_pending == 0, which is perfect for the next
+ block. */
+ ares_cancel((ares_channel)data->state.resolver);
+ DEBUGASSERT(res->num_pending == 0);
+ }
+
if(res && !res->num_pending) {
if(dns) {
(void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai);
@@ -342,13 +407,13 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
/*
* Curl_resolver_wait_resolv()
*
- * waits for a resolve to finish. This function should be avoided since using
+ * Waits for a resolve to finish. This function should be avoided since using
* this risk getting the multi interface to "hang".
*
* If 'entry' is non-NULL, make it point to the resolved dns entry
*
- * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
- * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
+ * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
+ * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
*/
CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
struct Curl_dns_entry **entry)
@@ -425,9 +490,7 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
if(result)
/* close the connection, since we can't return failure here without
- cleaning up this connection properly.
- TODO: remove this action from here, it is not a name resolver decision.
- */
+ cleaning up this connection properly. */
connclose(conn, "c-ares resolve failed");
return result;
@@ -487,6 +550,66 @@ static void query_completed_cb(void *arg, /* (struct connectdata *) */
/* A successful result overwrites any previous error */
if(res->last_status != ARES_SUCCESS)
res->last_status = status;
+
+ /* If there are responses still pending, we presume they must be the
+ complementary IPv4 or IPv6 lookups that we started in parallel in
+ Curl_resolver_getaddrinfo() (for Happy Eyeballs). If we've got a
+ "definitive" response from one of a set of parallel queries, we need to
+ think about how long we're willing to wait for more responses. */
+ if(res->num_pending
+ /* Only these c-ares status values count as "definitive" for these
+ purposes. For example, ARES_ENODATA is what we expect when there is
+ no IPv6 entry for a domain name, and that's not a reason to get more
+ aggressive in our timeouts for the other response. Other errors are
+ either a result of bad input (which should affect all parallel
+ requests), local or network conditions, non-definitive server
+ responses, or us cancelling the request. */
+ && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) {
+ /* Right now, there can only be up to two parallel queries, so don't
+ bother handling any other cases. */
+ DEBUGASSERT(res->num_pending == 1);
+
+ /* It's possible that one of these parallel queries could succeed
+ quickly, but the other could always fail or timeout (when we're
+ talking to a pool of DNS servers that can only successfully resolve
+ IPv4 address, for example).
+
+ It's also possible that the other request could always just take
+ longer because it needs more time or only the second DNS server can
+ fulfill it successfully. But, to align with the philosophy of Happy
+ Eyeballs, we don't want to wait _too_ long or users will think
+ requests are slow when IPv6 lookups don't actually work (but IPv4 ones
+ do).
+
+ So, now that we have a usable answer (some IPv4 addresses, some IPv6
+ addresses, or "no such domain"), we start a timeout for the remaining
+ pending responses. Even though it is typical that this resolved
+ request came back quickly, that needn't be the case. It might be that
+ this completing request didn't get a result from the first DNS server
+ or even the first round of the whole DNS server pool. So it could
+ already be quite some time after we issued the DNS queries in the
+ first place. Without modifying c-ares, we can't know exactly where in
+ its retry cycle we are. We could guess based on how much time has
+ gone by, but it doesn't really matter. Happy Eyeballs tells us that,
+ given usable information in hand, we simply don't want to wait "too
+ much longer" after we get a result.
+
+ We simply wait an additional amount of time equal to the default
+ c-ares query timeout. That is enough time for a typical parallel
+ response to arrive without being "too long". Even on a network
+ where one of the two types of queries is failing or timing out
+ constantly, this will usually mean we wait a total of the default
+ c-ares timeout (5 seconds) plus the round trip time for the successful
+ request, which seems bearable. The downside is that c-ares might race
+ with us to issue one more retry just before we give up, but it seems
+ better to "waste" that request instead of trying to guess the perfect
+ timeout to prevent it. After all, we don't even know where in the
+ c-ares retry cycle each request is.
+ */
+ res->happy_eyeballs_dns_time = Curl_now();
+ Curl_expire(
+ conn->data, HAPPY_EYEBALLS_DNS_TIMEOUT, EXPIRE_HAPPY_EYEBALLS_DNS);
+ }
}
}
diff --git a/Utilities/cmcurl/lib/asyn-thread.c b/Utilities/cmcurl/lib/asyn-thread.c
index 2a59294e5..55e0811c5 100644
--- a/Utilities/cmcurl/lib/asyn-thread.c
+++ b/Utilities/cmcurl/lib/asyn-thread.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -108,8 +108,9 @@ void Curl_resolver_global_cleanup(void)
* URL-state specific environment ('resolver' member of the UrlState
* structure).
*/
-CURLcode Curl_resolver_init(void **resolver)
+CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
{
+ (void)easy;
*resolver = calloc(1, sizeof(struct resdata));
if(!*resolver)
return CURLE_OUT_OF_MEMORY;
@@ -132,10 +133,10 @@ void Curl_resolver_cleanup(void *resolver)
* Called from curl_easy_duphandle() to duplicate resolver URL state-specific
* environment ('resolver' member of the UrlState structure).
*/
-int Curl_resolver_duphandle(void **to, void *from)
+CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
{
(void)from;
- return Curl_resolver_init(to);
+ return Curl_resolver_init(easy, to);
}
static void destroy_async_data(struct Curl_async *);
@@ -276,7 +277,7 @@ static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg)
char service[12];
int rc;
- snprintf(service, sizeof(service), "%d", tsd->port);
+ msnprintf(service, sizeof(service), "%d", tsd->port);
rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
@@ -460,23 +461,15 @@ static CURLcode resolver_error(struct connectdata *conn)
return result;
}
-/*
- * Curl_resolver_wait_resolv()
- *
- * waits for a resolve to finish. This function should be avoided since using
- * this risk getting the multi interface to "hang".
- *
- * If 'entry' is non-NULL, make it point to the resolved dns entry
- *
- * This is the version for resolves-in-a-thread.
- */
-CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
- struct Curl_dns_entry **entry)
+static CURLcode thread_wait_resolv(struct connectdata *conn,
+ struct Curl_dns_entry **entry,
+ bool report)
{
struct thread_data *td = (struct thread_data*) conn->async.os_specific;
CURLcode result = CURLE_OK;
DEBUGASSERT(conn && td);
+ DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
/* wait for the thread to resolve the name */
if(Curl_thread_join(&td->thread_hnd)) {
@@ -491,18 +484,55 @@ CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
if(entry)
*entry = conn->async.dns;
- if(!conn->async.dns)
+ if(!conn->async.dns && report)
/* a name was not resolved, report error */
result = resolver_error(conn);
destroy_async_data(&conn->async);
- if(!conn->async.dns)
+ if(!conn->async.dns && report)
connclose(conn, "asynch resolve failed");
return result;
}
+
+/*
+ * Until we gain a way to signal the resolver threads to stop early, we must
+ * simply wait for them and ignore their results.
+ */
+void Curl_resolver_kill(struct connectdata *conn)
+{
+ struct thread_data *td = (struct thread_data*) conn->async.os_specific;
+
+ /* If we're still resolving, we must wait for the threads to fully clean up,
+ unfortunately. Otherwise, we can simply cancel to clean up any resolver
+ data. */
+ if(td && td->thread_hnd != curl_thread_t_null)
+ (void)thread_wait_resolv(conn, NULL, FALSE);
+ else
+ Curl_resolver_cancel(conn);
+}
+
+/*
+ * Curl_resolver_wait_resolv()
+ *
+ * Waits for a resolve to finish. This function should be avoided since using
+ * this risk getting the multi interface to "hang".
+ *
+ * If 'entry' is non-NULL, make it point to the resolved dns entry
+ *
+ * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
+ * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
+ *
+ * This is the version for resolves-in-a-thread.
+ */
+CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
+ struct Curl_dns_entry **entry)
+{
+ return thread_wait_resolv(conn, entry, TRUE);
+}
+
/*
* Curl_resolver_is_resolved() is called repeatedly to check if a previous
* name resolve request has completed. It should also make sure to time-out if
@@ -678,7 +708,7 @@ Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
hints.ai_family = pf;
hints.ai_socktype = conn->socktype;
- snprintf(sbuf, sizeof(sbuf), "%d", port);
+ msnprintf(sbuf, sizeof(sbuf), "%d", port);
reslv->start = Curl_now();
/* fire up a new resolver thread! */
diff --git a/Utilities/cmcurl/lib/asyn.h b/Utilities/cmcurl/lib/asyn.h
index 3adc3664a..ccd4b1f7e 100644
--- a/Utilities/cmcurl/lib/asyn.h
+++ b/Utilities/cmcurl/lib/asyn.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -60,7 +60,7 @@ void Curl_resolver_global_cleanup(void);
* Returning anything else than CURLE_OK fails curl_easy_init() with the
* correspondent code.
*/
-CURLcode Curl_resolver_init(void **resolver);
+CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver);
/*
* Curl_resolver_cleanup()
@@ -79,17 +79,33 @@ void Curl_resolver_cleanup(void *resolver);
* pointer. Returning anything else than CURLE_OK causes failed
* curl_easy_duphandle() call.
*/
-int Curl_resolver_duphandle(void **to, void *from);
+CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to,
+ void *from);
/*
* Curl_resolver_cancel().
*
* It is called from inside other functions to cancel currently performing
* resolver request. Should also free any temporary resources allocated to
- * perform a request.
+ * perform a request. This never waits for resolver threads to complete.
+ *
+ * It is safe to call this when conn is in any state.
*/
void Curl_resolver_cancel(struct connectdata *conn);
+/*
+ * Curl_resolver_kill().
+ *
+ * This acts like Curl_resolver_cancel() except it will block until any threads
+ * associated with the resolver are complete. This never blocks for resolvers
+ * that do not use threads. This is intended to be the "last chance" function
+ * that cleans up an in-progress resolver completely (before its owner is about
+ * to die).
+ *
+ * It is safe to call this when conn is in any state.
+ */
+void Curl_resolver_kill(struct connectdata *conn);
+
/* Curl_resolver_getsock()
*
* This function is called from the multi_getsock() function. 'sock' is a
@@ -116,14 +132,13 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
/*
* Curl_resolver_wait_resolv()
*
- * waits for a resolve to finish. This function should be avoided since using
+ * Waits for a resolve to finish. This function should be avoided since using
* this risk getting the multi interface to "hang".
*
* If 'entry' is non-NULL, make it point to the resolved dns entry
*
- * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
- * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
-
+ * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
+ * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
*/
CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
struct Curl_dns_entry **dnsentry);
@@ -147,11 +162,12 @@ Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
#ifndef CURLRES_ASYNCH
/* convert these functions if an asynch resolver isn't used */
#define Curl_resolver_cancel(x) Curl_nop_stmt
+#define Curl_resolver_kill(x) Curl_nop_stmt
#define Curl_resolver_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST
#define Curl_resolver_wait_resolv(x,y) CURLE_COULDNT_RESOLVE_HOST
#define Curl_resolver_getsock(x,y,z) 0
-#define Curl_resolver_duphandle(x,y) CURLE_OK
-#define Curl_resolver_init(x) CURLE_OK
+#define Curl_resolver_duphandle(x,y,z) CURLE_OK
+#define Curl_resolver_init(x,y) CURLE_OK
#define Curl_resolver_global_init() CURLE_OK
#define Curl_resolver_global_cleanup() Curl_nop_stmt
#define Curl_resolver_cleanup(x) Curl_nop_stmt
diff --git a/Utilities/cmcurl/lib/base64.c b/Utilities/cmcurl/lib/base64.c
index 6370e4cdf..fb081a6bb 100644
--- a/Utilities/cmcurl/lib/base64.c
+++ b/Utilities/cmcurl/lib/base64.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -23,6 +23,11 @@
/* Base64 encoding/decoding */
#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_HTTP_AUTH) || defined(USE_LIBSSH2) || \
+ defined(USE_LIBSSH) || !defined(CURL_DISABLE_LDAP) || \
+ !defined(CURL_DISABLE_DOH) || defined(USE_SSL)
+
#include "urldata.h" /* for the Curl_easy definition */
#include "warnless.h"
#include "curl_base64.h"
@@ -233,24 +238,24 @@ static CURLcode base64_encode(const char *table64,
switch(inputparts) {
case 1: /* only one byte read */
- snprintf(output, 5, "%c%c==",
- table64[obuf[0]],
- table64[obuf[1]]);
+ msnprintf(output, 5, "%c%c==",
+ table64[obuf[0]],
+ table64[obuf[1]]);
break;
case 2: /* two bytes read */
- snprintf(output, 5, "%c%c%c=",
- table64[obuf[0]],
- table64[obuf[1]],
- table64[obuf[2]]);
+ msnprintf(output, 5, "%c%c%c=",
+ table64[obuf[0]],
+ table64[obuf[1]],
+ table64[obuf[2]]);
break;
default:
- snprintf(output, 5, "%c%c%c%c",
- table64[obuf[0]],
- table64[obuf[1]],
- table64[obuf[2]],
- table64[obuf[3]]);
+ msnprintf(output, 5, "%c%c%c%c",
+ table64[obuf[0]],
+ table64[obuf[1]],
+ table64[obuf[2]],
+ table64[obuf[3]]);
break;
}
output += 4;
@@ -317,3 +322,5 @@ CURLcode Curl_base64url_encode(struct Curl_easy *data,
{
return base64_encode(base64url, data, inputbuff, insize, outptr, outlen);
}
+
+#endif /* no users so disabled */
diff --git a/Utilities/cmcurl/lib/conncache.c b/Utilities/cmcurl/lib/conncache.c
index 6fbf3b1d2..535091996 100644
--- a/Utilities/cmcurl/lib/conncache.c
+++ b/Utilities/cmcurl/lib/conncache.c
@@ -6,7 +6,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2012 - 2016, Linus Nielsen Feltzing, <linus@haxx.se>
- * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -175,12 +175,12 @@ static void hashkey(struct connectdata *conn, char *buf,
DEBUGASSERT(len > 32);
/* put the number first so that the hostname gets cut off if too long */
- snprintf(buf, len, "%ld%s", conn->port, hostname);
+ msnprintf(buf, len, "%ld%s", conn->port, hostname);
}
-void Curl_conncache_unlock(struct connectdata *conn)
+void Curl_conncache_unlock(struct Curl_easy *data)
{
- CONN_UNLOCK(conn->data);
+ CONN_UNLOCK(data);
}
/* Returns number of connections currently held in the connection cache.
@@ -302,9 +302,14 @@ CURLcode Curl_conncache_add_conn(struct conncache *connc,
return result;
}
-void Curl_conncache_remove_conn(struct connectdata *conn, bool lock)
+/*
+ * Removes the connectdata object from the connection cache *and* clears the
+ * ->data pointer association. Pass TRUE/FALSE in the 'lock' argument
+ * depending on if the parent function already holds the lock or not.
+ */
+void Curl_conncache_remove_conn(struct Curl_easy *data,
+ struct connectdata *conn, bool lock)
{
- struct Curl_easy *data = conn->data;
struct connectbundle *bundle = conn->bundle;
struct conncache *connc = data->state.conn_cache;
@@ -323,6 +328,7 @@ void Curl_conncache_remove_conn(struct connectdata *conn, bool lock)
DEBUGF(infof(data, "The cache now contains %zu members\n",
connc->num_conn));
}
+ conn->data = NULL; /* clear the association */
if(lock) {
CONN_UNLOCK(data);
}
@@ -386,8 +392,8 @@ bool Curl_conncache_foreach(struct Curl_easy *data,
NOTE: no locking is done here as this is presumably only done when cleaning
up a cache!
*/
-struct connectdata *
-Curl_conncache_find_first_connection(struct conncache *connc)
+static struct connectdata *
+conncache_find_first_connection(struct conncache *connc)
{
struct curl_hash_iterator iter;
struct curl_hash_element *he;
@@ -427,6 +433,8 @@ bool Curl_conncache_return_conn(struct connectdata *conn)
data->multi->maxconnects;
struct connectdata *conn_candidate = NULL;
+ conn->data = NULL; /* no owner anymore */
+ conn->lastused = Curl_now(); /* it was used up until now */
if(maxconnects > 0 &&
Curl_conncache_size(data) > maxconnects) {
infof(data, "Connection cache is full, closing the oldest one.\n");
@@ -470,9 +478,9 @@ Curl_conncache_extract_bundle(struct Curl_easy *data,
while(curr) {
conn = curr->ptr;
- if(!CONN_INUSE(conn)) {
+ if(!CONN_INUSE(conn) && !conn->data) {
/* Set higher score for the age passed since the connection was used */
- score = Curl_timediff(now, conn->now);
+ score = Curl_timediff(now, conn->lastused);
if(score > highscore) {
highscore = score;
@@ -528,9 +536,9 @@ Curl_conncache_extract_oldest(struct Curl_easy *data)
while(curr) {
conn = curr->ptr;
- if(!CONN_INUSE(conn)) {
+ if(!CONN_INUSE(conn) && !conn->data) {
/* Set higher score for the age passed since the connection was used */
- score = Curl_timediff(now, conn->now);
+ score = Curl_timediff(now, conn->lastused);
if(score > highscore) {
highscore = score;
@@ -560,20 +568,18 @@ void Curl_conncache_close_all_connections(struct conncache *connc)
{
struct connectdata *conn;
- conn = Curl_conncache_find_first_connection(connc);
+ conn = conncache_find_first_connection(connc);
while(conn) {
SIGPIPE_VARIABLE(pipe_st);
conn->data = connc->closure_handle;
sigpipe_ignore(conn->data, &pipe_st);
- conn->data->easy_conn = NULL; /* clear the easy handle's connection
- pointer */
/* This will remove the connection from the cache */
connclose(conn, "kill all");
(void)Curl_disconnect(connc->closure_handle, conn, FALSE);
sigpipe_restore(&pipe_st);
- conn = Curl_conncache_find_first_connection(connc);
+ conn = conncache_find_first_connection(connc);
}
if(connc->closure_handle) {
diff --git a/Utilities/cmcurl/lib/conncache.h b/Utilities/cmcurl/lib/conncache.h
index eedd7a800..35be9e0aa 100644
--- a/Utilities/cmcurl/lib/conncache.h
+++ b/Utilities/cmcurl/lib/conncache.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2015 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2015 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 2012 - 2014, Linus Nielsen Feltzing, <linus@haxx.se>
*
* This software is licensed as described in the file COPYING, which
@@ -40,7 +40,6 @@ struct conncache {
#define BUNDLE_NO_MULTIUSE -1
#define BUNDLE_UNKNOWN 0 /* initial value */
-#define BUNDLE_PIPELINING 1
#define BUNDLE_MULTIPLEX 2
struct connectbundle {
@@ -56,7 +55,7 @@ void Curl_conncache_destroy(struct conncache *connc);
/* return the correct bundle, to a host or a proxy */
struct connectbundle *Curl_conncache_find_bundle(struct connectdata *conn,
struct conncache *connc);
-void Curl_conncache_unlock(struct connectdata *conn);
+void Curl_conncache_unlock(struct Curl_easy *data);
/* returns number of connections currently held in the connection cache */
size_t Curl_conncache_size(struct Curl_easy *data);
size_t Curl_conncache_bundle_size(struct connectdata *conn);
@@ -64,7 +63,8 @@ size_t Curl_conncache_bundle_size(struct connectdata *conn);
bool Curl_conncache_return_conn(struct connectdata *conn);
CURLcode Curl_conncache_add_conn(struct conncache *connc,
struct connectdata *conn) WARN_UNUSED_RESULT;
-void Curl_conncache_remove_conn(struct connectdata *conn,
+void Curl_conncache_remove_conn(struct Curl_easy *data,
+ struct connectdata *conn,
bool lock);
bool Curl_conncache_foreach(struct Curl_easy *data,
struct conncache *connc,
diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c
index 41f220268..002535b42 100644
--- a/Utilities/cmcurl/lib/connect.c
+++ b/Utilities/cmcurl/lib/connect.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -357,7 +357,7 @@ static CURLcode bindlocal(struct connectdata *conn,
conn->ip_version = CURL_IPRESOLVE_V6;
#endif
- rc = Curl_resolv(conn, dev, 0, &h);
+ rc = Curl_resolv(conn, dev, 0, FALSE, &h);
if(rc == CURLRESOLV_PENDING)
(void)Curl_resolver_wait_resolv(conn, &h);
conn->ip_version = ipver;
@@ -446,9 +446,10 @@ static CURLcode bindlocal(struct connectdata *conn,
curl_socklen_t size = sizeof(add);
memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
+ char buffer[STRERROR_LEN];
data->state.os_errno = error = SOCKERRNO;
failf(data, "getsockname() failed with errno %d: %s",
- error, Curl_strerror(conn, error));
+ error, Curl_strerror(error, buffer, sizeof(buffer)));
return CURLE_INTERFACE_FAILED;
}
infof(data, "Local port: %hu\n", port);
@@ -470,10 +471,12 @@ static CURLcode bindlocal(struct connectdata *conn,
else
break;
}
-
- data->state.os_errno = error = SOCKERRNO;
- failf(data, "bind failed with errno %d: %s",
- error, Curl_strerror(conn, error));
+ {
+ char buffer[STRERROR_LEN];
+ data->state.os_errno = error = SOCKERRNO;
+ failf(data, "bind failed with errno %d: %s",
+ error, Curl_strerror(error, buffer, sizeof(buffer)));
+ }
return CURLE_INTERFACE_FAILED;
}
@@ -522,7 +525,7 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
err = 0;
}
#endif
-#ifdef __minix
+#if defined(EBADIOCTL) && defined(__minix)
/* Minix 3.1.x doesn't support getsockopt on UDP sockets */
if(EBADIOCTL == err) {
SET_SOCKERRNO(0);
@@ -617,12 +620,14 @@ void Curl_persistconninfo(struct connectdata *conn)
conn->data->info.conn_local_port = conn->local_port;
}
+UNITTEST bool getaddressinfo(struct sockaddr *sa, char *addr,
+ long *port);
+
/* retrieves ip address and port from a sockaddr structure.
note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */
-bool Curl_getaddressinfo(struct sockaddr *sa, char *addr,
- long *port)
+UNITTEST bool getaddressinfo(struct sockaddr *sa, char *addr,
+ long *port)
{
- unsigned short us_port;
struct sockaddr_in *si = NULL;
#ifdef ENABLE_IPV6
struct sockaddr_in6 *si6 = NULL;
@@ -636,7 +641,7 @@ bool Curl_getaddressinfo(struct sockaddr *sa, char *addr,
si = (struct sockaddr_in *)(void *) sa;
if(Curl_inet_ntop(sa->sa_family, &si->sin_addr,
addr, MAX_IPADR_LEN)) {
- us_port = ntohs(si->sin_port);
+ unsigned short us_port = ntohs(si->sin_port);
*port = us_port;
return TRUE;
}
@@ -646,7 +651,7 @@ bool Curl_getaddressinfo(struct sockaddr *sa, char *addr,
si6 = (struct sockaddr_in6 *)(void *) sa;
if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr,
addr, MAX_IPADR_LEN)) {
- us_port = ntohs(si6->sin6_port);
+ unsigned short us_port = ntohs(si6->sin6_port);
*port = us_port;
return TRUE;
}
@@ -655,7 +660,7 @@ bool Curl_getaddressinfo(struct sockaddr *sa, char *addr,
#if defined(HAVE_SYS_UN_H) && defined(AF_UNIX)
case AF_UNIX:
su = (struct sockaddr_un*)sa;
- snprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
+ msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
*port = 0;
return TRUE;
#endif
@@ -673,49 +678,57 @@ bool Curl_getaddressinfo(struct sockaddr *sa, char *addr,
connection */
void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd)
{
- curl_socklen_t len;
- struct Curl_sockaddr_storage ssrem;
- struct Curl_sockaddr_storage ssloc;
- struct Curl_easy *data = conn->data;
-
if(conn->socktype == SOCK_DGRAM)
/* there's no connection! */
return;
+#if defined(HAVE_GETPEERNAME) || defined(HAVE_GETSOCKNAME)
if(!conn->bits.reuse && !conn->bits.tcp_fastopen) {
+ struct Curl_easy *data = conn->data;
+ char buffer[STRERROR_LEN];
+ struct Curl_sockaddr_storage ssrem;
+ struct Curl_sockaddr_storage ssloc;
+ curl_socklen_t len;
+#ifdef HAVE_GETPEERNAME
len = sizeof(struct Curl_sockaddr_storage);
if(getpeername(sockfd, (struct sockaddr*) &ssrem, &len)) {
int error = SOCKERRNO;
failf(data, "getpeername() failed with errno %d: %s",
- error, Curl_strerror(conn, error));
+ error, Curl_strerror(error, buffer, sizeof(buffer)));
return;
}
-
+#endif
+#ifdef HAVE_GETSOCKNAME
len = sizeof(struct Curl_sockaddr_storage);
memset(&ssloc, 0, sizeof(ssloc));
if(getsockname(sockfd, (struct sockaddr*) &ssloc, &len)) {
int error = SOCKERRNO;
failf(data, "getsockname() failed with errno %d: %s",
- error, Curl_strerror(conn, error));
+ error, Curl_strerror(error, buffer, sizeof(buffer)));
return;
}
-
- if(!Curl_getaddressinfo((struct sockaddr*)&ssrem,
- conn->primary_ip, &conn->primary_port)) {
+#endif
+#ifdef HAVE_GETPEERNAME
+ if(!getaddressinfo((struct sockaddr*)&ssrem,
+ conn->primary_ip, &conn->primary_port)) {
failf(data, "ssrem inet_ntop() failed with errno %d: %s",
- errno, Curl_strerror(conn, errno));
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
return;
}
memcpy(conn->ip_addr_str, conn->primary_ip, MAX_IPADR_LEN);
-
- if(!Curl_getaddressinfo((struct sockaddr*)&ssloc,
- conn->local_ip, &conn->local_port)) {
+#endif
+#ifdef HAVE_GETSOCKNAME
+ if(!getaddressinfo((struct sockaddr*)&ssloc,
+ conn->local_ip, &conn->local_port)) {
failf(data, "ssloc inet_ntop() failed with errno %d: %s",
- errno, Curl_strerror(conn, errno));
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
return;
}
-
+#endif
}
+#else /* !HAVE_GETSOCKNAME && !HAVE_GETPEERNAME */
+ (void)sockfd; /* unused */
+#endif
/* persist connection info in session handle */
Curl_persistconninfo(conn);
@@ -836,9 +849,11 @@ CURLcode Curl_is_connected(struct connectdata *conn,
if(conn->tempaddr[i]) {
CURLcode status;
char ipaddress[MAX_IPADR_LEN];
+ char buffer[STRERROR_LEN];
Curl_printable_address(conn->tempaddr[i], ipaddress, MAX_IPADR_LEN);
infof(data, "connect to %s port %ld failed: %s\n",
- ipaddress, conn->port, Curl_strerror(conn, error));
+ ipaddress, conn->port,
+ Curl_strerror(error, buffer, sizeof(buffer)));
conn->timeoutms_per_addr = conn->tempaddr[i]->ai_next == NULL ?
allow : allow / 2;
@@ -854,8 +869,8 @@ CURLcode Curl_is_connected(struct connectdata *conn,
if(result) {
/* no more addresses to try */
-
const char *hostname;
+ char buffer[STRERROR_LEN];
/* if the first address family runs out of addresses to try before
the happy eyeball timeout, go ahead and try the next family now */
@@ -875,13 +890,14 @@ CURLcode Curl_is_connected(struct connectdata *conn,
hostname = conn->host.name;
failf(data, "Failed to connect to %s port %ld: %s",
- hostname, conn->port, Curl_strerror(conn, error));
+ hostname, conn->port,
+ Curl_strerror(error, buffer, sizeof(buffer)));
}
return result;
}
-void Curl_tcpnodelay(struct connectdata *conn, curl_socket_t sockfd)
+static void tcpnodelay(struct connectdata *conn, curl_socket_t sockfd)
{
#if defined(TCP_NODELAY)
#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
@@ -889,6 +905,7 @@ void Curl_tcpnodelay(struct connectdata *conn, curl_socket_t sockfd)
#endif
curl_socklen_t onoff = (curl_socklen_t) 1;
int level = IPPROTO_TCP;
+ char buffer[STRERROR_LEN];
#if defined(CURL_DISABLE_VERBOSE_STRINGS)
(void) conn;
@@ -897,7 +914,7 @@ void Curl_tcpnodelay(struct connectdata *conn, curl_socket_t sockfd)
if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
sizeof(onoff)) < 0)
infof(data, "Could not set TCP_NODELAY: %s\n",
- Curl_strerror(conn, SOCKERRNO));
+ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
else
infof(data, "TCP_NODELAY set\n");
#else
@@ -917,9 +934,11 @@ static void nosigpipe(struct connectdata *conn,
struct Curl_easy *data = conn->data;
int onoff = 1;
if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
- sizeof(onoff)) < 0)
+ sizeof(onoff)) < 0) {
+ char buffer[STRERROR_LEN];
infof(data, "Could not set SO_NOSIGPIPE: %s\n",
- Curl_strerror(conn, SOCKERRNO));
+ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+ }
}
#else
#define nosigpipe(x,y) Curl_nop_stmt
@@ -995,6 +1014,7 @@ static CURLcode singleipconnect(struct connectdata *conn,
#ifdef TCP_FASTOPEN_CONNECT
int optval = 1;
#endif
+ char buffer[STRERROR_LEN];
*sockp = CURL_SOCKET_BAD;
@@ -1006,15 +1026,15 @@ static CURLcode singleipconnect(struct connectdata *conn,
return CURLE_OK;
/* store remote address and port used in this connection attempt */
- if(!Curl_getaddressinfo((struct sockaddr*)&addr.sa_addr,
- ipaddress, &port)) {
+ if(!getaddressinfo((struct sockaddr*)&addr.sa_addr,
+ ipaddress, &port)) {
/* malformed address or bug in inet_ntop, try next address */
failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
- errno, Curl_strerror(conn, errno));
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
Curl_closesocket(conn, sockfd);
return CURLE_OK;
}
- infof(data, " Trying %s...\n", ipaddress);
+ infof(data, " Trying %s:%ld...\n", ipaddress, port);
#ifdef ENABLE_IPV6
is_tcp = (addr.family == AF_INET || addr.family == AF_INET6) &&
@@ -1023,7 +1043,7 @@ static CURLcode singleipconnect(struct connectdata *conn,
is_tcp = (addr.family == AF_INET) && addr.socktype == SOCK_STREAM;
#endif
if(is_tcp && data->set.tcp_nodelay)
- Curl_tcpnodelay(conn, sockfd);
+ tcpnodelay(conn, sockfd);
nosigpipe(conn, sockfd);
@@ -1146,7 +1166,7 @@ static CURLcode singleipconnect(struct connectdata *conn,
default:
/* unknown error, fallthrough and try another address! */
infof(data, "Immediate connect fail for %s: %s\n",
- ipaddress, Curl_strerror(conn, error));
+ ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
data->state.os_errno = error;
/* connect failed */
@@ -1314,7 +1334,7 @@ int Curl_closesocket(struct connectdata *conn,
conn->sock_accepted[SECONDARYSOCKET] = FALSE;
else {
int rc;
- Curl_multi_closed(conn, sock);
+ Curl_multi_closed(conn->data, sock);
Curl_set_in_callback(conn->data, true);
rc = conn->fclosesocket(conn->closesocket_client, sock);
Curl_set_in_callback(conn->data, false);
@@ -1324,7 +1344,7 @@ int Curl_closesocket(struct connectdata *conn,
if(conn)
/* tell the multi-socket code about this */
- Curl_multi_closed(conn, sock);
+ Curl_multi_closed(conn->data, sock);
sclose(sock);
@@ -1420,7 +1440,7 @@ void Curl_conncontrol(struct connectdata *conn,
if((ctrl == CONNCTRL_STREAM) &&
(conn->handler->flags & PROTOPT_STREAM))
DEBUGF(infof(conn->data, "Kill stream: %s\n", reason));
- else if(closeit != conn->bits.close) {
+ else if((bit)closeit != conn->bits.close) {
DEBUGF(infof(conn->data, "Marked for [%s]: %s\n",
closeit?"closure":"keep alive", reason));
conn->bits.close = closeit; /* the only place in the source code that
diff --git a/Utilities/cmcurl/lib/connect.h b/Utilities/cmcurl/lib/connect.h
index 193dc6397..6a5c755cc 100644
--- a/Utilities/cmcurl/lib/connect.h
+++ b/Utilities/cmcurl/lib/connect.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -76,11 +76,6 @@ void Curl_persistconninfo(struct connectdata *conn);
int Curl_closesocket(struct connectdata *conn, curl_socket_t sock);
/*
- * Get presentation format IP address and port from a sockaddr.
- */
-bool Curl_getaddressinfo(struct sockaddr *sa, char *addr, long *port);
-
-/*
* The Curl_sockaddr_ex structure is basically libcurl's external API
* curl_sockaddr structure with enough space available to directly hold any
* protocol-specific address structures. The variable declared here will be
@@ -111,8 +106,6 @@ CURLcode Curl_socket(struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
curl_socket_t *sockfd);
-void Curl_tcpnodelay(struct connectdata *conn, curl_socket_t sockfd);
-
/*
* Curl_conncontrol() marks the end of a connection/stream. The 'closeit'
* argument specifies if it is the end of a connection or a stream.
diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c
index a342c61de..9a9e14d01 100644
--- a/Utilities/cmcurl/lib/cookie.c
+++ b/Utilities/cmcurl/lib/cookie.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -93,6 +93,7 @@ Example set of cookies:
#include "share.h"
#include "strtoofft.h"
#include "strcase.h"
+#include "curl_get_line.h"
#include "curl_memrchr.h"
#include "inet_pton.h"
@@ -223,7 +224,7 @@ static bool pathmatch(const char *cookie_path, const char *request_uri)
goto pathmatched;
}
- /* here, cookie_path_len < url_path_len */
+ /* here, cookie_path_len < uri_path_len */
if(uri_path[cookie_path_len] == '/') {
ret = TRUE;
goto pathmatched;
@@ -433,9 +434,10 @@ Curl_cookie_add(struct Curl_easy *data,
bool noexpire, /* if TRUE, skip remove_expired() */
char *lineptr, /* first character of the line */
const char *domain, /* default domain */
- const char *path) /* full path used when this cookie is set,
+ const char *path, /* full path used when this cookie is set,
used to get default path for the cookie
unless set */
+ bool secure) /* TRUE if connection is over secure origin */
{
struct Cookie *clist;
struct Cookie *co;
@@ -527,6 +529,19 @@ Curl_cookie_add(struct Curl_easy *data,
while(*whatptr && ISBLANK(*whatptr))
whatptr++;
+ /*
+ * Check if we have a reserved prefix set before anything else, as we
+ * otherwise have to test for the prefix in both the cookie name and
+ * "the rest". Prefixes must start with '__' and end with a '-', so
+ * only test for names where that can possibly be true.
+ */
+ if(nlen > 3 && name[0] == '_' && name[1] == '_') {
+ if(strncasecompare("__Secure-", name, 9))
+ co->prefix |= COOKIE_PREFIX__SECURE;
+ else if(strncasecompare("__Host-", name, 7))
+ co->prefix |= COOKIE_PREFIX__HOST;
+ }
+
if(!co->name) {
/* The very first name/value pair is the actual cookie name */
if(!sep) {
@@ -546,8 +561,20 @@ Curl_cookie_add(struct Curl_easy *data,
/* this was a "<name>=" with no content, and we must allow
'secure' and 'httponly' specified this weirdly */
done = TRUE;
- if(strcasecompare("secure", name))
- co->secure = TRUE;
+ /*
+ * secure cookies are only allowed to be set when the connection is
+ * using a secure protocol, or when the cookie is being set by
+ * reading from file
+ */
+ if(strcasecompare("secure", name)) {
+ if(secure || !c->running) {
+ co->secure = TRUE;
+ }
+ else {
+ badcookie = TRUE;
+ break;
+ }
+ }
else if(strcasecompare("httponly", name))
co->httponly = TRUE;
else if(sep)
@@ -675,7 +702,10 @@ Curl_cookie_add(struct Curl_easy *data,
/* overflow, used max value */
co->expires = CURL_OFF_T_MAX;
else if(!offt) {
- if(CURL_OFF_T_MAX - now < co->expires)
+ if(!co->expires)
+ /* already expired */
+ co->expires = 1;
+ else if(CURL_OFF_T_MAX - now < co->expires)
/* would overflow */
co->expires = CURL_OFF_T_MAX;
else
@@ -828,7 +858,13 @@ Curl_cookie_add(struct Curl_easy *data,
fields++; /* add a field and fall down to secure */
/* FALLTHROUGH */
case 3:
- co->secure = strcasecompare(ptr, "TRUE")?TRUE:FALSE;
+ co->secure = FALSE;
+ if(strcasecompare(ptr, "TRUE")) {
+ if(secure || c->running)
+ co->secure = TRUE;
+ else
+ badcookie = TRUE;
+ }
break;
case 4:
if(curlx_strtoofft(ptr, NULL, 10, &co->expires))
@@ -838,6 +874,13 @@ Curl_cookie_add(struct Curl_easy *data,
co->name = strdup(ptr);
if(!co->name)
badcookie = TRUE;
+ else {
+ /* For Netscape file format cookies we check prefix on the name */
+ if(strncasecompare("__Secure-", co->name, 9))
+ co->prefix |= COOKIE_PREFIX__SECURE;
+ else if(strncasecompare("__Host-", co->name, 7))
+ co->prefix |= COOKIE_PREFIX__HOST;
+ }
break;
case 6:
co->value = strdup(ptr);
@@ -866,6 +909,26 @@ Curl_cookie_add(struct Curl_easy *data,
}
+ if(co->prefix & COOKIE_PREFIX__SECURE) {
+ /* The __Secure- prefix only requires that the cookie be set secure */
+ if(!co->secure) {
+ freecookie(co);
+ return NULL;
+ }
+ }
+ if(co->prefix & COOKIE_PREFIX__HOST) {
+ /*
+ * The __Host- prefix requires the cookie to be secure, have a "/" path
+ * and not have a domain set.
+ */
+ if(co->secure && co->path && strcmp(co->path, "/") == 0 && !co->tailmatch)
+ ;
+ else {
+ freecookie(co);
+ return NULL;
+ }
+ }
+
if(!c->running && /* read from a file */
c->newsession && /* clean session cookies */
!co->expires) { /* this is a session cookie since it doesn't expire! */
@@ -926,9 +989,31 @@ Curl_cookie_add(struct Curl_easy *data,
/* the domains were identical */
if(clist->spath && co->spath) {
- if(strcasecompare(clist->spath, co->spath)) {
- replace_old = TRUE;
+ if(clist->secure && !co->secure && !secure) {
+ size_t cllen;
+ const char *sep;
+
+ /*
+ * A non-secure cookie may not overlay an existing secure cookie.
+ * For an existing cookie "a" with path "/login", refuse a new
+ * cookie "a" with for example path "/login/en", while the path
+ * "/loginhelper" is ok.
+ */
+
+ sep = strchr(clist->spath + 1, '/');
+
+ if(sep)
+ cllen = sep - clist->spath;
+ else
+ cllen = strlen(clist->spath);
+
+ if(strncasecompare(clist->spath, co->spath, cllen)) {
+ freecookie(co);
+ return NULL;
+ }
}
+ else if(strcasecompare(clist->spath, co->spath))
+ replace_old = TRUE;
else
replace_old = FALSE;
}
@@ -1003,33 +1088,6 @@ Curl_cookie_add(struct Curl_easy *data,
return co;
}
-/*
- * get_line() makes sure to only return complete whole lines that fit in 'len'
- * bytes and end with a newline.
- */
-static char *get_line(char *buf, int len, FILE *input)
-{
- bool partial = FALSE;
- while(1) {
- char *b = fgets(buf, len, input);
- if(b) {
- size_t rlen = strlen(b);
- if(rlen && (b[rlen-1] == '\n')) {
- if(partial) {
- partial = FALSE;
- continue;
- }
- return b;
- }
- /* read a partial, discard the next piece that ends with newline */
- partial = TRUE;
- }
- else
- break;
- }
- return NULL;
-}
-
/*****************************************************************************
*
@@ -1087,7 +1145,7 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
line = malloc(MAX_COOKIE_LINE);
if(!line)
goto fail;
- while(get_line(line, MAX_COOKIE_LINE, fp)) {
+ while(Curl_get_line(line, MAX_COOKIE_LINE, fp)) {
if(checkprefix("Set-Cookie:", line)) {
/* This is a cookie line, get it! */
lineptr = &line[11];
@@ -1100,7 +1158,7 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
while(*lineptr && ISBLANK(*lineptr))
lineptr++;
- Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL);
+ Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE);
}
free(line); /* free the line buffer */
remove_expired(c); /* run this once, not on every cookie */
@@ -1451,27 +1509,14 @@ static int cookie_output(struct CookieInfo *c, const char *dumphere)
struct Cookie *co;
FILE *out;
bool use_stdout = FALSE;
- char *format_ptr;
- unsigned int i;
- unsigned int j;
- struct Cookie **array;
- if((NULL == c) || (0 == c->numcookies))
- /* If there are no known cookies, we don't write or even create any
- destination file */
+ if(!c)
+ /* no cookie engine alive */
return 0;
/* at first, remove expired cookies */
remove_expired(c);
- /* make sure we still have cookies after expiration */
- if(0 == c->numcookies)
- return 0;
-
- array = malloc(sizeof(struct Cookie *) * c->numcookies);
- if(!array)
- return 1;
-
if(!strcmp("-", dumphere)) {
/* use stdout */
out = stdout;
@@ -1480,7 +1525,6 @@ static int cookie_output(struct CookieInfo *c, const char *dumphere)
else {
out = fopen(dumphere, FOPEN_WRITETEXT);
if(!out) {
- free(array);
return 1; /* failure */
}
}
@@ -1490,32 +1534,44 @@ static int cookie_output(struct CookieInfo *c, const char *dumphere)
"# This file was generated by libcurl! Edit at your own risk.\n\n",
out);
- j = 0;
- for(i = 0; i < COOKIE_HASH_SIZE; i++) {
- for(co = c->cookies[i]; co; co = co->next) {
- if(!co->domain)
- continue;
- array[j++] = co;
- }
- }
-
- qsort(array, c->numcookies, sizeof(struct Cookie *), cookie_sort_ct);
+ if(c->numcookies) {
+ unsigned int i;
+ unsigned int j;
+ struct Cookie **array;
- for(i = 0; i < j; i++) {
- format_ptr = get_netscape_format(array[i]);
- if(format_ptr == NULL) {
- fprintf(out, "#\n# Fatal libcurl error\n");
- free(array);
+ array = malloc(sizeof(struct Cookie *) * c->numcookies);
+ if(!array) {
if(!use_stdout)
fclose(out);
return 1;
}
- fprintf(out, "%s\n", format_ptr);
- free(format_ptr);
- }
- free(array);
+ j = 0;
+ for(i = 0; i < COOKIE_HASH_SIZE; i++) {
+ for(co = c->cookies[i]; co; co = co->next) {
+ if(!co->domain)
+ continue;
+ array[j++] = co;
+ }
+ }
+
+ qsort(array, c->numcookies, sizeof(struct Cookie *), cookie_sort_ct);
+ for(i = 0; i < j; i++) {
+ char *format_ptr = get_netscape_format(array[i]);
+ if(format_ptr == NULL) {
+ fprintf(out, "#\n# Fatal libcurl error\n");
+ free(array);
+ if(!use_stdout)
+ fclose(out);
+ return 1;
+ }
+ fprintf(out, "%s\n", format_ptr);
+ free(format_ptr);
+ }
+
+ free(array);
+ }
if(!use_stdout)
fclose(out);
diff --git a/Utilities/cmcurl/lib/cookie.h b/Utilities/cmcurl/lib/cookie.h
index a9f90ca71..b2730cfb9 100644
--- a/Utilities/cmcurl/lib/cookie.h
+++ b/Utilities/cmcurl/lib/cookie.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -44,8 +44,16 @@ struct Cookie {
bool livecookie; /* updated from a server, not a stored file */
bool httponly; /* true if the httponly directive is present */
int creationtime; /* time when the cookie was written */
+ unsigned char prefix; /* bitmap fields indicating which prefix are set */
};
+/*
+ * Available cookie prefixes, as defined in
+ * draft-ietf-httpbis-rfc6265bis-02
+ */
+#define COOKIE_PREFIX__SECURE (1<<0)
+#define COOKIE_PREFIX__HOST (1<<1)
+
#define COOKIE_HASH_SIZE 256
struct CookieInfo {
@@ -85,7 +93,8 @@ struct Curl_easy;
struct Cookie *Curl_cookie_add(struct Curl_easy *data,
struct CookieInfo *, bool header, bool noexpiry,
char *lineptr,
- const char *domain, const char *path);
+ const char *domain, const char *path,
+ bool secure);
struct Cookie *Curl_cookie_getlist(struct CookieInfo *, const char *,
const char *, bool);
diff --git a/Utilities/cmcurl/lib/curl_addrinfo.c b/Utilities/cmcurl/lib/curl_addrinfo.c
index fd49679c9..16c4779c1 100644
--- a/Utilities/cmcurl/lib/curl_addrinfo.c
+++ b/Utilities/cmcurl/lib/curl_addrinfo.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -539,7 +539,7 @@ Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, bool abstract)
#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) && \
defined(HAVE_FREEADDRINFO)
/*
- * curl_dofreeaddrinfo()
+ * curl_dbg_freeaddrinfo()
*
* This is strictly for memory tracing and are using the same style as the
* family otherwise present in memdebug.c. I put these ones here since they
@@ -547,23 +547,23 @@ Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, bool abstract)
*/
void
-curl_dofreeaddrinfo(struct addrinfo *freethis,
- int line, const char *source)
+curl_dbg_freeaddrinfo(struct addrinfo *freethis,
+ int line, const char *source)
{
+ curl_dbg_log("ADDR %s:%d freeaddrinfo(%p)\n",
+ source, line, (void *)freethis);
#ifdef USE_LWIPSOCK
lwip_freeaddrinfo(freethis);
#else
(freeaddrinfo)(freethis);
#endif
- curl_memlog("ADDR %s:%d freeaddrinfo(%p)\n",
- source, line, (void *)freethis);
}
#endif /* defined(CURLDEBUG) && defined(HAVE_FREEADDRINFO) */
#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO)
/*
- * curl_dogetaddrinfo()
+ * curl_dbg_getaddrinfo()
*
* This is strictly for memory tracing and are using the same style as the
* family otherwise present in memdebug.c. I put these ones here since they
@@ -571,11 +571,11 @@ curl_dofreeaddrinfo(struct addrinfo *freethis,
*/
int
-curl_dogetaddrinfo(const char *hostname,
- const char *service,
- const struct addrinfo *hints,
- struct addrinfo **result,
- int line, const char *source)
+curl_dbg_getaddrinfo(const char *hostname,
+ const char *service,
+ const struct addrinfo *hints,
+ struct addrinfo **result,
+ int line, const char *source)
{
#ifdef USE_LWIPSOCK
int res = lwip_getaddrinfo(hostname, service, hints, result);
@@ -584,11 +584,11 @@ curl_dogetaddrinfo(const char *hostname,
#endif
if(0 == res)
/* success */
- curl_memlog("ADDR %s:%d getaddrinfo() = %p\n",
- source, line, (void *)*result);
+ curl_dbg_log("ADDR %s:%d getaddrinfo() = %p\n",
+ source, line, (void *)*result);
else
- curl_memlog("ADDR %s:%d getaddrinfo() failed\n",
- source, line);
+ curl_dbg_log("ADDR %s:%d getaddrinfo() failed\n",
+ source, line);
return res;
}
#endif /* defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) */
diff --git a/Utilities/cmcurl/lib/curl_addrinfo.h b/Utilities/cmcurl/lib/curl_addrinfo.h
index 8f6f3d106..205e121ea 100644
--- a/Utilities/cmcurl/lib/curl_addrinfo.h
+++ b/Utilities/cmcurl/lib/curl_addrinfo.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -86,17 +86,14 @@ Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, bool abstract);
#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) && \
defined(HAVE_FREEADDRINFO)
void
-curl_dofreeaddrinfo(struct addrinfo *freethis,
- int line, const char *source);
+curl_dbg_freeaddrinfo(struct addrinfo *freethis, int line, const char *source);
#endif
#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO)
int
-curl_dogetaddrinfo(const char *hostname,
- const char *service,
- const struct addrinfo *hints,
- struct addrinfo **result,
- int line, const char *source);
+curl_dbg_getaddrinfo(const char *hostname, const char *service,
+ const struct addrinfo *hints, struct addrinfo **result,
+ int line, const char *source);
#endif
#ifdef HAVE_GETADDRINFO
diff --git a/Utilities/cmcurl/lib/curl_config.h.cmake b/Utilities/cmcurl/lib/curl_config.h.cmake
index d5e3a90cf..b285c3fdd 100644
--- a/Utilities/cmcurl/lib/curl_config.h.cmake
+++ b/Utilities/cmcurl/lib/curl_config.h.cmake
@@ -226,6 +226,12 @@
/* Define to 1 if you have the `getprotobyname' function. */
#cmakedefine HAVE_GETPROTOBYNAME 1
+/* Define to 1 if you have the `getpeername' function. */
+#cmakedefine HAVE_GETPEERNAME 1
+
+/* Define to 1 if you have the `getsockname' function. */
+#cmakedefine HAVE_GETSOCKNAME 1
+
/* Define to 1 if you have the `getpwuid' function. */
#cmakedefine HAVE_GETPWUID 1
@@ -930,8 +936,8 @@
/* if PolarSSL is enabled */
#cmakedefine USE_POLARSSL 1
-/* if DarwinSSL is enabled */
-#cmakedefine USE_DARWINSSL 1
+/* if Secure Transport is enabled */
+#cmakedefine USE_SECTRANSP 1
/* if mbedTLS is enabled */
#cmakedefine USE_MBEDTLS 1
diff --git a/Utilities/cmcurl/lib/curl_endian.c b/Utilities/cmcurl/lib/curl_endian.c
index c25db4956..b7563b3de 100644
--- a/Utilities/cmcurl/lib/curl_endian.c
+++ b/Utilities/cmcurl/lib/curl_endian.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -82,7 +82,7 @@ unsigned short Curl_read16_be(const unsigned char *buf)
}
/*
- * Curl_write32_le()
+ * write32_le()
*
* This function converts a 32-bit integer from the native endian format,
* to little endian format ready for sending down the wire.
@@ -92,7 +92,7 @@ unsigned short Curl_read16_be(const unsigned char *buf)
* value [in] - The 32-bit integer value.
* buffer [in] - A pointer to the output buffer.
*/
-void Curl_write32_le(const int value, unsigned char *buffer)
+static void write32_le(const int value, unsigned char *buffer)
{
buffer[0] = (char)(value & 0x000000FF);
buffer[1] = (char)((value & 0x0000FF00) >> 8);
@@ -118,7 +118,7 @@ void Curl_write64_le(const long long value, unsigned char *buffer)
void Curl_write64_le(const __int64 value, unsigned char *buffer)
#endif
{
- Curl_write32_le((int)value, buffer);
- Curl_write32_le((int)(value >> 32), buffer + 4);
+ write32_le((int)value, buffer);
+ write32_le((int)(value >> 32), buffer + 4);
}
#endif /* CURL_SIZEOF_CURL_OFF_T > 4 */
diff --git a/Utilities/cmcurl/lib/curl_fnmatch.c b/Utilities/cmcurl/lib/curl_fnmatch.c
index fbfd85c43..ab3e7428d 100644
--- a/Utilities/cmcurl/lib/curl_fnmatch.c
+++ b/Utilities/cmcurl/lib/curl_fnmatch.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -21,7 +21,7 @@
***************************************************************************/
#include "curl_setup.h"
-
+#ifndef CURL_DISABLE_FTP
#include <curl/curl.h>
#include "curl_fnmatch.h"
@@ -32,15 +32,6 @@
#ifndef HAVE_FNMATCH
-/*
- * TODO:
- *
- * Make this function match POSIX. Test 1307 includes a set of test patterns
- * that returns different results with a POSIX fnmatch() than with this
- * implementation and this is considered a bug where POSIX is the guiding
- * light.
- */
-
#define CURLFNM_CHARSET_LEN (sizeof(char) * 256)
#define CURLFNM_CHSET_SIZE (CURLFNM_CHARSET_LEN + 15)
@@ -394,3 +385,5 @@ int Curl_fnmatch(void *ptr, const char *pattern, const char *string)
}
#endif
+
+#endif /* if FTP is disabled */
diff --git a/Utilities/cmcurl/lib/curl_get_line.c b/Utilities/cmcurl/lib/curl_get_line.c
new file mode 100644
index 000000000..c4194851a
--- /dev/null
+++ b/Utilities/cmcurl/lib/curl_get_line.c
@@ -0,0 +1,55 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include "curl_get_line.h"
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/*
+ * get_line() makes sure to only return complete whole lines that fit in 'len'
+ * bytes and end with a newline.
+ */
+char *Curl_get_line(char *buf, int len, FILE *input)
+{
+ bool partial = FALSE;
+ while(1) {
+ char *b = fgets(buf, len, input);
+ if(b) {
+ size_t rlen = strlen(b);
+ if(rlen && (b[rlen-1] == '\n')) {
+ if(partial) {
+ partial = FALSE;
+ continue;
+ }
+ return b;
+ }
+ /* read a partial, discard the next piece that ends with newline */
+ partial = TRUE;
+ }
+ else
+ break;
+ }
+ return NULL;
+}
diff --git a/Utilities/cmcurl/lib/vtls/axtls.h b/Utilities/cmcurl/lib/curl_get_line.h
index cb8187272..532ab080a 100644
--- a/Utilities/cmcurl/lib/vtls/axtls.h
+++ b/Utilities/cmcurl/lib/curl_get_line.h
@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_AXTLS_H
-#define HEADER_CURL_AXTLS_H
+#ifndef HEADER_CURL_GET_LINE_H
+#define HEADER_CURL_GET_LINE_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -7,8 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2010, DirecTV, Contact: Eric Hu <ehu@directv.com>
- * Copyright (C) 2010 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -23,11 +22,8 @@
*
***************************************************************************/
-#ifdef USE_AXTLS
-#include "curl/curl.h"
-#include "urldata.h"
+/* get_line() makes sure to only return complete whole lines that fit in 'len'
+ * bytes and end with a newline. */
+char *Curl_get_line(char *buf, int len, FILE *input);
-extern const struct Curl_ssl Curl_ssl_axtls;
-
-#endif /* USE_AXTLS */
-#endif /* HEADER_CURL_AXTLS_H */
+#endif /* HEADER_CURL_GET_LINE_H */
diff --git a/Utilities/cmcurl/lib/curl_gssapi.c b/Utilities/cmcurl/lib/curl_gssapi.c
index f007986c0..d854ab0c7 100644
--- a/Utilities/cmcurl/lib/curl_gssapi.c
+++ b/Utilities/cmcurl/lib/curl_gssapi.c
@@ -97,9 +97,9 @@ static size_t display_gss_error(OM_uint32 status, int type,
&msg_ctx,
&status_string);
if(GSS_LOG_BUFFER_LEN > len + status_string.length + 3) {
- len += snprintf(buf + len, GSS_LOG_BUFFER_LEN - len,
- "%.*s. ", (int)status_string.length,
- (char *)status_string.value);
+ len += msnprintf(buf + len, GSS_LOG_BUFFER_LEN - len,
+ "%.*s. ", (int)status_string.length,
+ (char *)status_string.value);
}
gss_release_buffer(&min_stat, &status_string);
} while(!GSS_ERROR(maj_stat) && msg_ctx != 0);
diff --git a/Utilities/cmcurl/lib/curl_gssapi.h b/Utilities/cmcurl/lib/curl_gssapi.h
index 9700a2817..88f68dbbb 100644
--- a/Utilities/cmcurl/lib/curl_gssapi.h
+++ b/Utilities/cmcurl/lib/curl_gssapi.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2011 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2011 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -26,19 +26,6 @@
#include "urldata.h"
#ifdef HAVE_GSSAPI
-
-#ifdef HAVE_GSSGNU
-# include <gss.h>
-#elif defined HAVE_GSSMIT
- /* MIT style */
-# include <gssapi/gssapi.h>
-# include <gssapi/gssapi_generic.h>
-# include <gssapi/gssapi_krb5.h>
-#else
- /* Heimdal-style */
-# include <gssapi.h>
-#endif
-
extern gss_OID_desc Curl_spnego_mech_oid;
extern gss_OID_desc Curl_krb5_mech_oid;
@@ -71,5 +58,4 @@ void Curl_gss_log_error(struct Curl_easy *data, const char *prefix,
#define GSSAUTH_P_PRIVACY 4
#endif /* HAVE_GSSAPI */
-
#endif /* HEADER_CURL_GSSAPI_H */
diff --git a/Utilities/cmcurl/lib/curl_md4.h b/Utilities/cmcurl/lib/curl_md4.h
index e0690416d..392203f9e 100644
--- a/Utilities/cmcurl/lib/curl_md4.h
+++ b/Utilities/cmcurl/lib/curl_md4.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -25,11 +25,13 @@
#include "curl_setup.h"
#if defined(USE_NSS) || defined(USE_OS400CRYPTO) || \
+ (defined(USE_OPENSSL) && defined(OPENSSL_NO_MD4)) || \
(defined(USE_MBEDTLS) && !defined(MBEDTLS_MD4_C))
void Curl_md4it(unsigned char *output, const unsigned char *input, size_t len);
#endif /* defined(USE_NSS) || defined(USE_OS400CRYPTO) ||
+ (defined(USE_OPENSSL) && defined(OPENSSL_NO_MD4)) ||
(defined(USE_MBEDTLS) && !defined(MBEDTLS_MD4_C)) */
#endif /* HEADER_CURL_MD4_H */
diff --git a/Utilities/cmcurl/lib/curl_md5.h b/Utilities/cmcurl/lib/curl_md5.h
index 5f70c9634..aaf25f61b 100644
--- a/Utilities/cmcurl/lib/curl_md5.h
+++ b/Utilities/cmcurl/lib/curl_md5.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -53,10 +53,10 @@ void Curl_md5it(unsigned char *output,
const unsigned char *input);
MD5_context * Curl_MD5_init(const MD5_params *md5params);
-int Curl_MD5_update(MD5_context *context,
- const unsigned char *data,
- unsigned int len);
-int Curl_MD5_final(MD5_context *context, unsigned char *result);
+CURLcode Curl_MD5_update(MD5_context *context,
+ const unsigned char *data,
+ unsigned int len);
+CURLcode Curl_MD5_final(MD5_context *context, unsigned char *result);
#endif
diff --git a/Utilities/cmcurl/lib/curl_memory.h b/Utilities/cmcurl/lib/curl_memory.h
index fccf46879..ce38a08cd 100644
--- a/Utilities/cmcurl/lib/curl_memory.h
+++ b/Utilities/cmcurl/lib/curl_memory.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -39,7 +39,7 @@
*
* File lib/strdup.c is an exception, given that it provides a strdup
* clone implementation while using malloc. Extra care needed inside
- * this one. TODO: revisit this paragraph and related code.
+ * this one.
*
* The need for curl_memory.h inclusion is due to libcurl's feature
* of allowing library user to provide memory replacement functions,
diff --git a/Utilities/cmcurl/lib/curl_multibyte.c b/Utilities/cmcurl/lib/curl_multibyte.c
index e78bb5002..e48334faf 100644
--- a/Utilities/cmcurl/lib/curl_multibyte.c
+++ b/Utilities/cmcurl/lib/curl_multibyte.c
@@ -64,13 +64,13 @@ char *Curl_convert_wchar_to_UTF8(const wchar_t *str_w)
char *str_utf8 = NULL;
if(str_w) {
- int str_utf8_len = WideCharToMultiByte(CP_UTF8, 0, str_w, -1, NULL,
- 0, NULL, NULL);
- if(str_utf8_len > 0) {
- str_utf8 = malloc(str_utf8_len * sizeof(wchar_t));
+ int bytes = WideCharToMultiByte(CP_UTF8, 0, str_w, -1,
+ NULL, 0, NULL, NULL);
+ if(bytes > 0) {
+ str_utf8 = malloc(bytes);
if(str_utf8) {
- if(WideCharToMultiByte(CP_UTF8, 0, str_w, -1, str_utf8, str_utf8_len,
- NULL, FALSE) == 0) {
+ if(WideCharToMultiByte(CP_UTF8, 0, str_w, -1, str_utf8, bytes,
+ NULL, NULL) == 0) {
free(str_utf8);
return NULL;
}
diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.c b/Utilities/cmcurl/lib/curl_ntlm_core.c
index 922e85a92..b6df38f71 100644
--- a/Utilities/cmcurl/lib/curl_ntlm_core.c
+++ b/Utilities/cmcurl/lib/curl_ntlm_core.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -38,7 +38,7 @@
3. USE_GNUTLS
4. USE_NSS
5. USE_MBEDTLS
- 6. USE_DARWINSSL
+ 6. USE_SECTRANSP
7. USE_OS400CRYPTO
8. USE_WIN32_CRYPTO
@@ -54,23 +54,15 @@
#ifdef USE_OPENSSL
-# ifdef USE_OPENSSL
-# include <openssl/des.h>
-# ifndef OPENSSL_NO_MD4
-# include <openssl/md4.h>
-# endif
-# include <openssl/md5.h>
-# include <openssl/ssl.h>
-# include <openssl/rand.h>
+# include <openssl/des.h>
+# ifndef OPENSSL_NO_MD4
+# include <openssl/md4.h>
# else
-# include <des.h>
-# ifndef OPENSSL_NO_MD4
-# include <md4.h>
-# endif
-# include <md5.h>
-# include <ssl.h>
-# include <rand.h>
+# include "curl_md4.h"
# endif
+# include <openssl/md5.h>
+# include <openssl/ssl.h>
+# include <openssl/rand.h>
# if (OPENSSL_VERSION_NUMBER < 0x00907001L)
# define DES_key_schedule des_key_schedule
# define DES_cblock des_cblock
@@ -111,7 +103,7 @@
# include "curl_md4.h"
# endif
-#elif defined(USE_DARWINSSL)
+#elif defined(USE_SECTRANSP)
# include <CommonCrypto/CommonCryptor.h>
# include <CommonCrypto/CommonDigest.h>
@@ -300,7 +292,7 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out,
return mbedtls_des_crypt_ecb(&ctx, in, out) == 0;
}
-#elif defined(USE_DARWINSSL)
+#elif defined(USE_SECTRANSP)
static bool encrypt_des(const unsigned char *in, unsigned char *out,
const unsigned char *key_56)
@@ -447,7 +439,7 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys,
setup_des_key(keys + 14, &des);
gcry_cipher_encrypt(des, results + 16, 8, plaintext, 8);
gcry_cipher_close(des);
-#elif defined(USE_NSS) || defined(USE_MBEDTLS) || defined(USE_DARWINSSL) \
+#elif defined(USE_NSS) || defined(USE_MBEDTLS) || defined(USE_SECTRANSP) \
|| defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO)
encrypt_des(plaintext, results, keys);
encrypt_des(plaintext, results + 8, keys + 7);
@@ -511,7 +503,7 @@ CURLcode Curl_ntlm_core_mk_lm_hash(struct Curl_easy *data,
setup_des_key(pw + 7, &des);
gcry_cipher_encrypt(des, lmbuffer + 8, 8, magic, 8);
gcry_cipher_close(des);
-#elif defined(USE_NSS) || defined(USE_MBEDTLS) || defined(USE_DARWINSSL) \
+#elif defined(USE_NSS) || defined(USE_MBEDTLS) || defined(USE_SECTRANSP) \
|| defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO)
encrypt_des(magic, lmbuffer, pw);
encrypt_des(magic, lmbuffer + 8, pw + 7);
@@ -578,10 +570,14 @@ CURLcode Curl_ntlm_core_mk_nt_hash(struct Curl_easy *data,
{
/* Create NT hashed password. */
#ifdef USE_OPENSSL
+#if !defined(OPENSSL_NO_MD4)
MD4_CTX MD4pw;
MD4_Init(&MD4pw);
MD4_Update(&MD4pw, pw, 2 * len);
MD4_Final(ntbuffer, &MD4pw);
+#else
+ Curl_md4it(ntbuffer, pw, 2 * len);
+#endif
#elif defined(USE_GNUTLS_NETTLE)
struct md4_ctx MD4pw;
md4_init(&MD4pw);
@@ -601,7 +597,7 @@ CURLcode Curl_ntlm_core_mk_nt_hash(struct Curl_easy *data,
#else
Curl_md4it(ntbuffer, pw, 2 * len);
#endif
-#elif defined(USE_DARWINSSL)
+#elif defined(USE_SECTRANSP)
(void)CC_MD4(pw, (CC_LONG)(2 * len), ntbuffer);
#elif defined(USE_OS400CRYPTO)
Curl_md4it(ntbuffer, pw, 2 * len);
@@ -631,9 +627,9 @@ CURLcode Curl_ntlm_core_mk_nt_hash(struct Curl_easy *data,
#if defined(USE_NTLM_V2) && !defined(USE_WINDOWS_SSPI)
/* This returns the HMAC MD5 digest */
-CURLcode Curl_hmac_md5(const unsigned char *key, unsigned int keylen,
- const unsigned char *data, unsigned int datalen,
- unsigned char *output)
+static CURLcode hmac_md5(const unsigned char *key, unsigned int keylen,
+ const unsigned char *data, unsigned int datalen,
+ unsigned char *output)
{
HMAC_context *ctxt = Curl_HMAC_init(Curl_HMAC_MD5, key, keylen);
@@ -678,9 +674,8 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_hash(const char *user, size_t userlen,
ascii_uppercase_to_unicode_le(identity, user, userlen);
ascii_to_unicode_le(identity + (userlen << 1), domain, domlen);
- result = Curl_hmac_md5(ntlmhash, 16, identity, curlx_uztoui(identity_len),
- ntlmv2hash);
-
+ result = hmac_md5(ntlmhash, 16, identity, curlx_uztoui(identity_len),
+ ntlmv2hash);
free(identity);
return result;
@@ -753,12 +748,12 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash,
return CURLE_OUT_OF_MEMORY;
/* Create the BLOB structure */
- snprintf((char *)ptr + NTLM_HMAC_MD5_LEN, NTLMv2_BLOB_LEN,
- "%c%c%c%c" /* NTLMv2_BLOB_SIGNATURE */
- "%c%c%c%c", /* Reserved = 0 */
- NTLMv2_BLOB_SIGNATURE[0], NTLMv2_BLOB_SIGNATURE[1],
- NTLMv2_BLOB_SIGNATURE[2], NTLMv2_BLOB_SIGNATURE[3],
- 0, 0, 0, 0);
+ msnprintf((char *)ptr + NTLM_HMAC_MD5_LEN, NTLMv2_BLOB_LEN,
+ "%c%c%c%c" /* NTLMv2_BLOB_SIGNATURE */
+ "%c%c%c%c", /* Reserved = 0 */
+ NTLMv2_BLOB_SIGNATURE[0], NTLMv2_BLOB_SIGNATURE[1],
+ NTLMv2_BLOB_SIGNATURE[2], NTLMv2_BLOB_SIGNATURE[3],
+ 0, 0, 0, 0);
Curl_write64_le(tw, ptr + 24);
memcpy(ptr + 32, challenge_client, 8);
@@ -766,8 +761,8 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash,
/* Concatenate the Type 2 challenge with the BLOB and do HMAC MD5 */
memcpy(ptr + 8, &ntlm->nonce[0], 8);
- result = Curl_hmac_md5(ntlmv2hash, NTLM_HMAC_MD5_LEN, ptr + 8,
- NTLMv2_BLOB_LEN + 8, hmac_output);
+ result = hmac_md5(ntlmv2hash, NTLM_HMAC_MD5_LEN, ptr + 8,
+ NTLMv2_BLOB_LEN + 8, hmac_output);
if(result) {
free(ptr);
return result;
@@ -809,7 +804,7 @@ CURLcode Curl_ntlm_core_mk_lmv2_resp(unsigned char *ntlmv2hash,
memcpy(&data[0], challenge_server, 8);
memcpy(&data[8], challenge_client, 8);
- result = Curl_hmac_md5(ntlmv2hash, 16, &data[0], 16, hmac_output);
+ result = hmac_md5(ntlmv2hash, 16, &data[0], 16, hmac_output);
if(result)
return result;
diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.h b/Utilities/cmcurl/lib/curl_ntlm_core.h
index 07ef5deae..3b4b8053c 100644
--- a/Utilities/cmcurl/lib/curl_ntlm_core.h
+++ b/Utilities/cmcurl/lib/curl_ntlm_core.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -43,9 +43,7 @@
/* Define USE_NTRESPONSES in order to make the type-3 message include
* the NT response message. */
-#if !defined(USE_OPENSSL) || !defined(OPENSSL_NO_MD4)
#define USE_NTRESPONSES
-#endif
/* Define USE_NTLM2SESSION in order to make the type-3 message include the
NTLM2Session response message, requires USE_NTRESPONSES defined to 1 and a
diff --git a/Utilities/cmcurl/lib/curl_ntlm_wb.c b/Utilities/cmcurl/lib/curl_ntlm_wb.c
index a4791eb41..80266e2a4 100644
--- a/Utilities/cmcurl/lib/curl_ntlm_wb.c
+++ b/Utilities/cmcurl/lib/curl_ntlm_wb.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -53,6 +53,8 @@
#include "url.h"
#include "strerror.h"
#include "strdup.h"
+#include "strcase.h"
+
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@@ -74,7 +76,7 @@
# define sclose_nolog(x) close((x))
#endif
-void Curl_ntlm_wb_cleanup(struct connectdata *conn)
+void Curl_http_auth_cleanup_ntlm_wb(struct connectdata *conn)
{
if(conn->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD) {
sclose(conn->ntlm_auth_hlpr_socket);
@@ -124,6 +126,7 @@ static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp)
struct passwd pw, *pw_res;
char pwbuf[1024];
#endif
+ char buffer[STRERROR_LEN];
/* Return if communication with ntlm_auth already set up */
if(conn->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD ||
@@ -179,13 +182,13 @@ static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp)
if(access(ntlm_auth, X_OK) != 0) {
failf(conn->data, "Could not access ntlm_auth: %s errno %d: %s",
- ntlm_auth, errno, Curl_strerror(conn, errno));
+ ntlm_auth, errno, Curl_strerror(errno, buffer, sizeof(buffer)));
goto done;
}
if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds)) {
failf(conn->data, "Could not open socket pair. errno %d: %s",
- errno, Curl_strerror(conn, errno));
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
goto done;
}
@@ -194,7 +197,7 @@ static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp)
sclose(sockfds[0]);
sclose(sockfds[1]);
failf(conn->data, "Could not fork. errno %d: %s",
- errno, Curl_strerror(conn, errno));
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
goto done;
}
else if(!child_pid) {
@@ -206,13 +209,13 @@ static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp)
sclose_nolog(sockfds[0]);
if(dup2(sockfds[1], STDIN_FILENO) == -1) {
failf(conn->data, "Could not redirect child stdin. errno %d: %s",
- errno, Curl_strerror(conn, errno));
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
exit(1);
}
if(dup2(sockfds[1], STDOUT_FILENO) == -1) {
failf(conn->data, "Could not redirect child stdout. errno %d: %s",
- errno, Curl_strerror(conn, errno));
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
exit(1);
}
@@ -232,7 +235,7 @@ static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp)
sclose_nolog(sockfds[1]);
failf(conn->data, "Could not execl(). errno %d: %s",
- errno, Curl_strerror(conn, errno));
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
exit(1);
}
@@ -332,6 +335,48 @@ done:
return CURLE_REMOTE_ACCESS_DENIED;
}
+CURLcode Curl_input_ntlm_wb(struct connectdata *conn,
+ bool proxy,
+ const char *header)
+{
+ curlntlm *state = proxy ? &conn->proxy_ntlm_state : &conn->http_ntlm_state;
+
+ if(!checkprefix("NTLM", header))
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ header += strlen("NTLM");
+ while(*header && ISSPACE(*header))
+ header++;
+
+ if(*header) {
+ conn->challenge_header = strdup(header);
+ if(!conn->challenge_header)
+ return CURLE_OUT_OF_MEMORY;
+
+ *state = NTLMSTATE_TYPE2; /* We got a type-2 message */
+ }
+ else {
+ if(*state == NTLMSTATE_LAST) {
+ infof(conn->data, "NTLM auth restarted\n");
+ Curl_http_auth_cleanup_ntlm_wb(conn);
+ }
+ else if(*state == NTLMSTATE_TYPE3) {
+ infof(conn->data, "NTLM handshake rejected\n");
+ Curl_http_auth_cleanup_ntlm_wb(conn);
+ *state = NTLMSTATE_NONE;
+ return CURLE_REMOTE_ACCESS_DENIED;
+ }
+ else if(*state >= NTLMSTATE_TYPE1) {
+ infof(conn->data, "NTLM handshake failure (internal error)\n");
+ return CURLE_REMOTE_ACCESS_DENIED;
+ }
+
+ *state = NTLMSTATE_TYPE1; /* We should send away a type-1 */
+ }
+
+ return CURLE_OK;
+}
+
/*
* This is for creating ntlm header output by delegating challenge/response
* to Samba's winbind daemon helper ntlm_auth.
@@ -344,8 +389,7 @@ CURLcode Curl_output_ntlm_wb(struct connectdata *conn,
char **allocuserpwd;
/* point to the name and password for this */
const char *userp;
- /* point to the correct struct with this */
- struct ntlmdata *ntlm;
+ curlntlm *state;
struct auth *authp;
CURLcode res = CURLE_OK;
@@ -357,13 +401,13 @@ CURLcode Curl_output_ntlm_wb(struct connectdata *conn,
if(proxy) {
allocuserpwd = &conn->allocptr.proxyuserpwd;
userp = conn->http_proxy.user;
- ntlm = &conn->proxyntlm;
+ state = &conn->proxy_ntlm_state;
authp = &conn->data->state.authproxy;
}
else {
allocuserpwd = &conn->allocptr.userpwd;
userp = conn->user;
- ntlm = &conn->ntlm;
+ state = &conn->http_ntlm_state;
authp = &conn->data->state.authhost;
}
authp->done = FALSE;
@@ -372,7 +416,7 @@ CURLcode Curl_output_ntlm_wb(struct connectdata *conn,
if(!userp)
userp = "";
- switch(ntlm->state) {
+ switch(*state) {
case NTLMSTATE_TYPE1:
default:
/* Use Samba's 'winbind' daemon to support NTLM authentication,
@@ -391,7 +435,7 @@ CURLcode Curl_output_ntlm_wb(struct connectdata *conn,
res = ntlm_wb_init(conn, userp);
if(res)
return res;
- res = ntlm_wb_response(conn, "YR\n", ntlm->state);
+ res = ntlm_wb_response(conn, "YR\n", *state);
if(res)
return res;
@@ -405,11 +449,12 @@ CURLcode Curl_output_ntlm_wb(struct connectdata *conn,
return CURLE_OUT_OF_MEMORY;
conn->response_header = NULL;
break;
+
case NTLMSTATE_TYPE2:
input = aprintf("TT %s\n", conn->challenge_header);
if(!input)
return CURLE_OUT_OF_MEMORY;
- res = ntlm_wb_response(conn, input, ntlm->state);
+ res = ntlm_wb_response(conn, input, *state);
free(input);
input = NULL;
if(res)
@@ -420,17 +465,20 @@ CURLcode Curl_output_ntlm_wb(struct connectdata *conn,
proxy ? "Proxy-" : "",
conn->response_header);
DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd));
- ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */
+ *state = NTLMSTATE_TYPE3; /* we sent a type-3 */
authp->done = TRUE;
- Curl_ntlm_wb_cleanup(conn);
+ Curl_http_auth_cleanup_ntlm_wb(conn);
if(!*allocuserpwd)
return CURLE_OUT_OF_MEMORY;
break;
+
case NTLMSTATE_TYPE3:
/* connection is already authenticated,
* don't send a header in future requests */
- free(*allocuserpwd);
- *allocuserpwd = NULL;
+ *state = NTLMSTATE_LAST;
+ /* FALLTHROUGH */
+ case NTLMSTATE_LAST:
+ Curl_safefree(*allocuserpwd);
authp->done = TRUE;
break;
}
diff --git a/Utilities/cmcurl/lib/curl_ntlm_wb.h b/Utilities/cmcurl/lib/curl_ntlm_wb.h
index aba3d469c..3cf841cf2 100644
--- a/Utilities/cmcurl/lib/curl_ntlm_wb.h
+++ b/Utilities/cmcurl/lib/curl_ntlm_wb.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -27,11 +27,14 @@
#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \
defined(NTLM_WB_ENABLED)
-/* this is for creating ntlm header output by delegating challenge/response
- to Samba's winbind daemon helper ntlm_auth */
+/* this is for ntlm header input */
+CURLcode Curl_input_ntlm_wb(struct connectdata *conn, bool proxy,
+ const char *header);
+
+/* this is for creating ntlm header output */
CURLcode Curl_output_ntlm_wb(struct connectdata *conn, bool proxy);
-void Curl_ntlm_wb_cleanup(struct connectdata *conn);
+void Curl_http_auth_cleanup_ntlm_wb(struct connectdata *conn);
#endif /* !CURL_DISABLE_HTTP && USE_NTLM && NTLM_WB_ENABLED */
diff --git a/Utilities/cmcurl/lib/curl_path.c b/Utilities/cmcurl/lib/curl_path.c
index 68f3e44ba..85dddcef1 100644
--- a/Utilities/cmcurl/lib/curl_path.c
+++ b/Utilities/cmcurl/lib/curl_path.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -22,6 +22,8 @@
#include "curl_setup.h"
+#if defined(USE_SSH)
+
#include <curl/curl.h>
#include "curl_memory.h"
#include "curl_path.h"
@@ -193,3 +195,5 @@ CURLcode Curl_get_pathname(const char **cpp, char **path, char *homedir)
Curl_safefree(*path);
return CURLE_QUOTE_ERROR;
}
+
+#endif /* if SSH is used */
diff --git a/Utilities/cmcurl/lib/curl_printf.h b/Utilities/cmcurl/lib/curl_printf.h
index 49857cdb0..0d37b8e57 100644
--- a/Utilities/cmcurl/lib/curl_printf.h
+++ b/Utilities/cmcurl/lib/curl_printf.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -31,7 +31,7 @@
# undef printf
# undef fprintf
-# undef snprintf
+# undef msnprintf
# undef vprintf
# undef vfprintf
# undef vsnprintf
@@ -39,18 +39,10 @@
# undef vaprintf
# define printf curl_mprintf
# define fprintf curl_mfprintf
-# define snprintf curl_msnprintf
+# define msnprintf curl_msnprintf
# define vprintf curl_mvprintf
# define vfprintf curl_mvfprintf
-# define vsnprintf curl_mvsnprintf
+# define mvsnprintf curl_mvsnprintf
# define aprintf curl_maprintf
# define vaprintf curl_mvaprintf
-
-/* We define away the sprintf functions unconditonally since we don't want
- internal code to be using them, intentionally or by mistake!*/
-# undef sprintf
-# undef vsprintf
-# define sprintf sprintf_was_used
-# define vsprintf vsprintf_was_used
-
#endif /* HEADER_CURL_PRINTF_H */
diff --git a/Utilities/cmcurl/lib/curl_rtmp.c b/Utilities/cmcurl/lib/curl_rtmp.c
index f09f2f332..16b1de1ae 100644
--- a/Utilities/cmcurl/lib/curl_rtmp.c
+++ b/Utilities/cmcurl/lib/curl_rtmp.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 2010, Howard Chu, <hyc@highlandsun.com>
*
* This software is licensed as described in the file COPYING, which
@@ -239,17 +239,18 @@ static CURLcode rtmp_connect(struct connectdata *conn, bool *done)
static CURLcode rtmp_do(struct connectdata *conn, bool *done)
{
+ struct Curl_easy *data = conn->data;
RTMP *r = conn->proto.generic;
if(!RTMP_ConnectStream(r, 0))
return CURLE_FAILED_INIT;
if(conn->data->set.upload) {
- Curl_pgrsSetUploadSize(conn->data, conn->data->state.infilesize);
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+ Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
}
else
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
*done = TRUE;
return CURLE_OK;
}
diff --git a/Utilities/cmcurl/lib/curl_sasl.c b/Utilities/cmcurl/lib/curl_sasl.c
index 354bc5448..018e4228b 100644
--- a/Utilities/cmcurl/lib/curl_sasl.c
+++ b/Utilities/cmcurl/lib/curl_sasl.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -31,6 +31,9 @@
#include "curl_setup.h"
+#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \
+ !defined(CURL_DISABLE_POP3)
+
#include <curl/curl.h>
#include "urldata.h"
@@ -83,14 +86,14 @@ void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused)
#if defined(USE_KERBEROS5)
/* Cleanup the gssapi structure */
if(authused == SASL_MECH_GSSAPI) {
- Curl_auth_gssapi_cleanup(&conn->krb5);
+ Curl_auth_cleanup_gssapi(&conn->krb5);
}
#endif
#if defined(USE_NTLM)
/* Cleanup the NTLM structure */
if(authused == SASL_MECH_NTLM) {
- Curl_auth_ntlm_cleanup(&conn->ntlm);
+ Curl_auth_cleanup_ntlm(&conn->ntlm);
}
#endif
@@ -290,7 +293,7 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn,
#if defined(USE_KERBEROS5)
if((enabledmechs & SASL_MECH_GSSAPI) && Curl_auth_is_gssapi_supported() &&
Curl_auth_user_contains_domain(conn->user)) {
- sasl->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */
+ sasl->mutual_auth = FALSE;
mech = SASL_MECH_STRING_GSSAPI;
state1 = SASL_GSSAPI;
state2 = SASL_GSSAPI_TOKEN;
@@ -300,8 +303,7 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn,
result = Curl_auth_create_gssapi_user_message(data, conn->user,
conn->passwd,
service,
- data->easy_conn->
- host.name,
+ data->conn->host.name,
sasl->mutual_auth,
NULL, &conn->krb5,
&resp, &len);
@@ -358,10 +360,9 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn,
sasl->authused = SASL_MECH_XOAUTH2;
if(force_ir || data->set.sasl_ir)
- result = Curl_auth_create_oauth_bearer_message(data, conn->user,
- NULL, 0,
- conn->oauth_bearer,
- &resp, &len);
+ result = Curl_auth_create_xoauth_bearer_message(data, conn->user,
+ conn->oauth_bearer,
+ &resp, &len);
}
else if(enabledmechs & SASL_MECH_PLAIN) {
mech = SASL_MECH_STRING_PLAIN;
@@ -369,8 +370,8 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn,
sasl->authused = SASL_MECH_PLAIN;
if(force_ir || data->set.sasl_ir)
- result = Curl_auth_create_plain_message(data, conn->user, conn->passwd,
- &resp, &len);
+ result = Curl_auth_create_plain_message(data, NULL, conn->user,
+ conn->passwd, &resp, &len);
}
else if(enabledmechs & SASL_MECH_LOGIN) {
mech = SASL_MECH_STRING_LOGIN;
@@ -452,9 +453,8 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn,
*progress = SASL_DONE;
return result;
case SASL_PLAIN:
- result = Curl_auth_create_plain_message(data, conn->user, conn->passwd,
- &resp,
- &len);
+ result = Curl_auth_create_plain_message(data, NULL, conn->user,
+ conn->passwd, &resp, &len);
break;
case SASL_LOGIN:
result = Curl_auth_create_login_message(data, conn->user, &resp, &len);
@@ -517,7 +517,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn,
result = Curl_auth_create_gssapi_user_message(data, conn->user,
conn->passwd,
service,
- data->easy_conn->host.name,
+ data->conn->host.name,
sasl->mutual_auth, NULL,
&conn->krb5,
&resp, &len);
@@ -563,10 +563,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn,
newstate = SASL_OAUTH2_RESP;
}
else
- result = Curl_auth_create_oauth_bearer_message(data, conn->user,
- NULL, 0,
- conn->oauth_bearer,
- &resp, &len);
+ result = Curl_auth_create_xoauth_bearer_message(data, conn->user,
+ conn->oauth_bearer,
+ &resp, &len);
break;
case SASL_OAUTH2_RESP:
@@ -627,3 +626,4 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn,
return result;
}
+#endif /* protocols are enabled that use SASL */
diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h
index 5cdbc5923..efba5ddec 100644
--- a/Utilities/cmcurl/lib/curl_setup.h
+++ b/Utilities/cmcurl/lib/curl_setup.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -314,11 +314,12 @@
#endif
#ifdef __AMIGA__
-# ifndef __ixemul__
-# include <exec/types.h>
-# include <exec/execbase.h>
-# include <proto/exec.h>
-# include <proto/dos.h>
+# include <exec/types.h>
+# include <exec/execbase.h>
+# include <proto/exec.h>
+# include <proto/dos.h>
+# ifdef HAVE_PROTO_BSDSOCKET_H
+# include <proto/bsdsocket.h> /* ensure bsdsocket.library use */
# define select(a,b,c,d,e) WaitSelect(a,b,c,d,e,0)
# endif
#endif
@@ -661,9 +662,9 @@ int netware_init(void);
#define LIBIDN_REQUIRED_VERSION "0.4.1"
#if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_NSS) || \
- defined(USE_POLARSSL) || defined(USE_AXTLS) || defined(USE_MBEDTLS) || \
+ defined(USE_POLARSSL) || defined(USE_MBEDTLS) || \
defined(USE_CYASSL) || defined(USE_SCHANNEL) || \
- defined(USE_DARWINSSL) || defined(USE_GSKIT) || defined(USE_MESALINK)
+ defined(USE_SECTRANSP) || defined(USE_GSKIT) || defined(USE_MESALINK)
#define USE_SSL /* SSL support has been enabled */
#endif
@@ -682,7 +683,7 @@ int netware_init(void);
/* Single point where USE_NTLM definition might be defined */
#if !defined(CURL_DISABLE_NTLM) && !defined(CURL_DISABLE_CRYPTO_AUTH)
#if defined(USE_OPENSSL) || defined(USE_WINDOWS_SSPI) || \
- defined(USE_GNUTLS) || defined(USE_NSS) || defined(USE_DARWINSSL) || \
+ defined(USE_GNUTLS) || defined(USE_NSS) || defined(USE_SECTRANSP) || \
defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) || \
defined(USE_MBEDTLS)
@@ -700,6 +701,10 @@ int netware_init(void);
#error "No longer supported. Set CURLOPT_CAINFO at runtime instead."
#endif
+#if defined(USE_LIBSSH2) || defined(USE_LIBSSH) || defined(USE_WOLFSSH)
+#define USE_SSH
+#endif
+
/*
* Provide a mechanism to silence picky compilers, such as gcc 4.6+.
* Parameters should of course normally not be unused, but for example when
@@ -831,4 +836,10 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
size_t buflen, struct passwd **result);
#endif
+#ifdef DEBUGBUILD
+#define UNITTEST
+#else
+#define UNITTEST static
+#endif
+
#endif /* HEADER_CURL_SETUP_H */
diff --git a/Utilities/cmcurl/lib/curlx.h b/Utilities/cmcurl/lib/curlx.h
index 4c77d4f2e..3e9b516f8 100644
--- a/Utilities/cmcurl/lib/curlx.h
+++ b/Utilities/cmcurl/lib/curlx.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -81,22 +81,21 @@
# undef printf
# undef fprintf
# undef sprintf
-# undef snprintf
+# undef msnprintf
# undef vprintf
# undef vfprintf
# undef vsprintf
-# undef vsnprintf
+# undef mvsnprintf
# undef aprintf
# undef vaprintf
# define printf curlx_mprintf
# define fprintf curlx_mfprintf
# define sprintf curlx_msprintf
-# define snprintf curlx_msnprintf
+# define msnprintf curlx_msnprintf
# define vprintf curlx_mvprintf
# define vfprintf curlx_mvfprintf
-# define vsprintf curlx_mvsprintf
-# define vsnprintf curlx_mvsnprintf
+# define mvsnprintf curlx_mvsnprintf
# define aprintf curlx_maprintf
# define vaprintf curlx_mvaprintf
#endif /* ENABLE_CURLX_PRINTF */
diff --git a/Utilities/cmcurl/lib/dict.c b/Utilities/cmcurl/lib/dict.c
index 78ef046d4..208a2336f 100644
--- a/Utilities/cmcurl/lib/dict.c
+++ b/Utilities/cmcurl/lib/dict.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -137,7 +137,6 @@ static CURLcode dict_do(struct connectdata *conn, bool *done)
curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
char *path = data->state.up.path;
- curl_off_t *bytecount = &data->req.bytecount;
*done = TRUE; /* unconditionally */
@@ -200,8 +199,7 @@ static CURLcode dict_do(struct connectdata *conn, bool *done)
failf(data, "Failed sending DICT request");
return result;
}
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount,
- -1, NULL); /* no upload */
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); /* no upload */
}
else if(strncasecompare(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) ||
strncasecompare(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-1) ||
@@ -247,8 +245,7 @@ static CURLcode dict_do(struct connectdata *conn, bool *done)
failf(data, "Failed sending DICT request");
return result;
}
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount,
- -1, NULL); /* no upload */
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
}
else {
@@ -270,7 +267,7 @@ static CURLcode dict_do(struct connectdata *conn, bool *done)
return result;
}
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount, -1, NULL);
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
}
}
diff --git a/Utilities/cmcurl/lib/doh.c b/Utilities/cmcurl/lib/doh.c
index ef6013db9..6d1f3303b 100644
--- a/Utilities/cmcurl/lib/doh.c
+++ b/Utilities/cmcurl/lib/doh.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2018 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -22,17 +22,19 @@
#include "curl_setup.h"
+#ifndef CURL_DISABLE_DOH
+
#include "urldata.h"
#include "curl_addrinfo.h"
#include "doh.h"
-#ifdef USE_NGHTTP2
#include "sendf.h"
#include "multiif.h"
#include "url.h"
#include "share.h"
#include "curl_base64.h"
#include "connect.h"
+#include "strdup.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@@ -143,8 +145,8 @@ doh_write_cb(void *contents, size_t size, size_t nmemb, void *userp)
/* suspiciously much for us */
return 0;
- mem->memory = realloc(mem->memory, mem->size + realsize);
- if(mem->memory == NULL)
+ mem->memory = Curl_saferealloc(mem->memory, mem->size + realsize);
+ if(!mem->memory)
/* out of memory! */
return 0;
@@ -160,7 +162,7 @@ static int Curl_doh_done(struct Curl_easy *doh, CURLcode result)
struct Curl_easy *data = doh->set.dohfor;
/* so one of the DOH request done for the 'data' transfer is now complete! */
data->req.doh.pending--;
- infof(data, "a DOH request is completed, %d to go\n", data->req.doh.pending);
+ infof(data, "a DOH request is completed, %u to go\n", data->req.doh.pending);
if(result)
infof(data, "DOH request %s\n", curl_easy_strerror(result));
@@ -173,8 +175,12 @@ static int Curl_doh_done(struct Curl_easy *doh, CURLcode result)
return 0;
}
-#define ERROR_CHECK_SETOPT(x,y) result = curl_easy_setopt(doh, x, y); \
- if(result) goto error
+#define ERROR_CHECK_SETOPT(x,y) \
+do { \
+ result = curl_easy_setopt(doh, x, y); \
+ if(result) \
+ goto error; \
+} WHILE_FALSE
static CURLcode dohprobe(struct Curl_easy *data,
struct dnsprobe *p, DNStype dnstype,
@@ -234,13 +240,76 @@ static CURLcode dohprobe(struct Curl_easy *data,
ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen);
}
ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
+#ifdef USE_NGHTTP2
ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
+#endif
#ifndef CURLDEBUG
/* enforce HTTPS if not debug */
ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
#endif
ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
- ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
+ if(data->set.verbose)
+ ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
+ if(data->set.no_signal)
+ ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
+
+ /* Inherit *some* SSL options from the user's transfer. This is a
+ best-guess as to which options are needed for compatibility. #3661 */
+ if(data->set.ssl.falsestart)
+ ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L);
+ if(data->set.ssl.primary.verifyhost)
+ ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST, 2L);
+ if(data->set.proxy_ssl.primary.verifyhost)
+ ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_VERIFYHOST, 2L);
+ if(data->set.ssl.primary.verifypeer)
+ ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER, 1L);
+ if(data->set.proxy_ssl.primary.verifypeer)
+ ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_VERIFYPEER, 1L);
+ if(data->set.ssl.primary.verifystatus)
+ ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS, 1L);
+ if(data->set.str[STRING_SSL_CAFILE_ORIG]) {
+ ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
+ data->set.str[STRING_SSL_CAFILE_ORIG]);
+ }
+ if(data->set.str[STRING_SSL_CAFILE_PROXY]) {
+ ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAINFO,
+ data->set.str[STRING_SSL_CAFILE_PROXY]);
+ }
+ if(data->set.str[STRING_SSL_CAPATH_ORIG]) {
+ ERROR_CHECK_SETOPT(CURLOPT_CAPATH,
+ data->set.str[STRING_SSL_CAPATH_ORIG]);
+ }
+ if(data->set.str[STRING_SSL_CAPATH_PROXY]) {
+ ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAPATH,
+ data->set.str[STRING_SSL_CAPATH_PROXY]);
+ }
+ if(data->set.str[STRING_SSL_CRLFILE_ORIG]) {
+ ERROR_CHECK_SETOPT(CURLOPT_CRLFILE,
+ data->set.str[STRING_SSL_CRLFILE_ORIG]);
+ }
+ if(data->set.str[STRING_SSL_CRLFILE_PROXY]) {
+ ERROR_CHECK_SETOPT(CURLOPT_PROXY_CRLFILE,
+ data->set.str[STRING_SSL_CRLFILE_PROXY]);
+ }
+ if(data->set.ssl.certinfo)
+ ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L);
+ if(data->set.str[STRING_SSL_RANDOM_FILE]) {
+ ERROR_CHECK_SETOPT(CURLOPT_RANDOM_FILE,
+ data->set.str[STRING_SSL_RANDOM_FILE]);
+ }
+ if(data->set.str[STRING_SSL_EGDSOCKET]) {
+ ERROR_CHECK_SETOPT(CURLOPT_EGDSOCKET,
+ data->set.str[STRING_SSL_EGDSOCKET]);
+ }
+ if(data->set.ssl.no_revoke)
+ ERROR_CHECK_SETOPT(CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
+ if(data->set.proxy_ssl.no_revoke)
+ ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
+ if(data->set.ssl.fsslctx)
+ ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx);
+ if(data->set.ssl.fsslctxp)
+ ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp);
+
doh->set.fmultidone = Curl_doh_done;
doh->set.dohfor = data; /* identify for which transfer this is done */
p->easy = doh;
@@ -515,7 +584,6 @@ UNITTEST DOHcode doh_decode(unsigned char *doh,
unsigned short qdcount;
unsigned short ancount;
unsigned short type = 0;
- unsigned short class;
unsigned short rdlength;
unsigned short nscount;
unsigned short arcount;
@@ -524,7 +592,7 @@ UNITTEST DOHcode doh_decode(unsigned char *doh,
if(dohlen < 12)
return DOH_TOO_SMALL_BUFFER; /* too small */
- if(doh[0] || doh[1])
+ if(!doh || doh[0] || doh[1])
return DOH_DNS_BAD_ID; /* bad ID */
rcode = doh[3] & 0x0f;
if(rcode)
@@ -543,6 +611,7 @@ UNITTEST DOHcode doh_decode(unsigned char *doh,
ancount = get16bit(doh, 6);
while(ancount) {
+ unsigned short class;
unsigned int ttl;
rc = skipqname(doh, dohlen, &index);
@@ -660,13 +729,13 @@ static void showdoh(struct Curl_easy *data,
char buffer[128];
char *ptr;
size_t len;
- snprintf(buffer, 128, "DOH AAAA: ");
+ msnprintf(buffer, 128, "DOH AAAA: ");
ptr = &buffer[10];
len = 118;
for(j = 0; j < 16; j += 2) {
size_t l;
- snprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j],
- d->addr[i].ip.v6[j + 1]);
+ msnprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j],
+ d->addr[i].ip.v6[j + 1]);
l = strlen(ptr);
len -= l;
ptr += l;
@@ -827,8 +896,6 @@ CURLcode Curl_doh_is_resolved(struct connectdata *conn,
DOHcode rc;
DOHcode rc2;
struct dohentry de;
- struct Curl_dns_entry *dns;
- struct Curl_addrinfo *ai;
/* remove DOH handles from multi handle and close them */
curl_multi_remove_handle(data->multi, data->req.doh.probe[0].easy);
Curl_close(data->req.doh.probe[0].easy);
@@ -853,11 +920,14 @@ CURLcode Curl_doh_is_resolved(struct connectdata *conn,
&de);
free(data->req.doh.probe[1].serverdoh.memory);
if(rc2) {
- infof(data, "DOG: %s type %s for %s\n", doh_strerror(rc2),
+ infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc2),
type2name(data->req.doh.probe[1].dnstype),
data->req.doh.host);
}
if(!rc || !rc2) {
+ struct Curl_dns_entry *dns;
+ struct Curl_addrinfo *ai;
+
infof(data, "DOH Host name: %s\n", data->req.doh.host);
showdoh(data, &de);
@@ -894,27 +964,4 @@ CURLcode Curl_doh_is_resolved(struct connectdata *conn,
return CURLE_OK;
}
-#else /* !USE_NGHTTP2 */
-/*
- */
-Curl_addrinfo *Curl_doh(struct connectdata *conn,
- const char *hostname,
- int port,
- int *waitp)
-{
- (void)conn;
- (void)hostname;
- (void)port;
- (void)waitp;
- return NULL;
-}
-
-CURLcode Curl_doh_is_resolved(struct connectdata *conn,
- struct Curl_dns_entry **dnsp)
-{
- (void)conn;
- (void)dnsp;
- return CURLE_NOT_BUILT_IN;
-}
-
-#endif /* USE_NGHTTP2 */
+#endif /* CURL_DISABLE_DOH */
diff --git a/Utilities/cmcurl/lib/doh.h b/Utilities/cmcurl/lib/doh.h
index 83c79bc5d..34bfa6f2b 100644
--- a/Utilities/cmcurl/lib/doh.h
+++ b/Utilities/cmcurl/lib/doh.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2018 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -25,6 +25,8 @@
#include "urldata.h"
#include "curl_addrinfo.h"
+#ifndef CURL_DISABLE_DOH
+
/*
* Curl_doh() resolve a name using DoH (DNS-over-HTTPS). It resolves a name
* and returns a 'Curl_addrinfo *' with the address information.
@@ -102,4 +104,10 @@ DOHcode doh_decode(unsigned char *doh,
struct dohentry *d);
void de_cleanup(struct dohentry *d);
#endif
+
+#else /* if DOH is disabled */
+#define Curl_doh(a,b,c,d) NULL
+#define Curl_doh_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST
+#endif
+
#endif /* HEADER_CURL_DOH_H */
diff --git a/Utilities/cmcurl/lib/easy.c b/Utilities/cmcurl/lib/easy.c
index 4de4e6522..4a6f96567 100644
--- a/Utilities/cmcurl/lib/easy.c
+++ b/Utilities/cmcurl/lib/easy.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -75,6 +75,7 @@
#include "ssh.h"
#include "setopt.h"
#include "http_digest.h"
+#include "system_win32.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -83,70 +84,6 @@
void Curl_version_init(void);
-/* win32_cleanup() is for win32 socket cleanup functionality, the opposite
- of win32_init() */
-static void win32_cleanup(void)
-{
-#ifdef USE_WINSOCK
- WSACleanup();
-#endif
-#ifdef USE_WINDOWS_SSPI
- Curl_sspi_global_cleanup();
-#endif
-}
-
-/* win32_init() performs win32 socket initialization to properly setup the
- stack to allow networking */
-static CURLcode win32_init(void)
-{
-#ifdef USE_WINSOCK
- WORD wVersionRequested;
- WSADATA wsaData;
- int res;
-
-#if defined(ENABLE_IPV6) && (USE_WINSOCK < 2)
- Error IPV6_requires_winsock2
-#endif
-
- wVersionRequested = MAKEWORD(USE_WINSOCK, USE_WINSOCK);
-
- res = WSAStartup(wVersionRequested, &wsaData);
-
- if(res != 0)
- /* Tell the user that we couldn't find a usable */
- /* winsock.dll. */
- return CURLE_FAILED_INIT;
-
- /* Confirm that the Windows Sockets DLL supports what we need.*/
- /* Note that if the DLL supports versions greater */
- /* than wVersionRequested, it will still return */
- /* wVersionRequested in wVersion. wHighVersion contains the */
- /* highest supported version. */
-
- if(LOBYTE(wsaData.wVersion) != LOBYTE(wVersionRequested) ||
- HIBYTE(wsaData.wVersion) != HIBYTE(wVersionRequested) ) {
- /* Tell the user that we couldn't find a usable */
-
- /* winsock.dll. */
- WSACleanup();
- return CURLE_FAILED_INIT;
- }
- /* The Windows Sockets DLL is acceptable. Proceed. */
-#elif defined(USE_LWIPSOCK)
- lwip_init();
-#endif
-
-#ifdef USE_WINDOWS_SSPI
- {
- CURLcode result = Curl_sspi_global_init();
- if(result)
- return result;
- }
-#endif
-
- return CURLE_OK;
-}
-
/* true globals -- for curl_global_init() and curl_global_cleanup() */
static unsigned int initialized;
static long init_flags;
@@ -223,11 +160,12 @@ static CURLcode global_init(long flags, bool memoryfuncs)
return CURLE_FAILED_INIT;
}
- if(flags & CURL_GLOBAL_WIN32)
- if(win32_init()) {
- DEBUGF(fprintf(stderr, "Error: win32_init failed\n"));
- return CURLE_FAILED_INIT;
- }
+#ifdef WIN32
+ if(Curl_win32_init(flags)) {
+ DEBUGF(fprintf(stderr, "Error: win32_init failed\n"));
+ return CURLE_FAILED_INIT;
+ }
+#endif
#ifdef __AMIGA__
if(!Curl_amiga_init()) {
@@ -327,12 +265,12 @@ void curl_global_cleanup(void)
if(--initialized)
return;
- Curl_global_host_cache_dtor();
Curl_ssl_cleanup();
Curl_resolver_global_cleanup();
- if(init_flags & CURL_GLOBAL_WIN32)
- win32_cleanup();
+#ifdef WIN32
+ Curl_win32_cleanup(init_flags);
+#endif
Curl_amiga_cleanup();
@@ -489,8 +427,8 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */
mask. Convert from libcurl bitmask to the poll one. */
m->socket.events = socketcb2poll(what);
infof(easy, "socket cb: socket %d UPDATED as %s%s\n", s,
- what&CURL_POLL_IN?"IN":"",
- what&CURL_POLL_OUT?"OUT":"");
+ (what&CURL_POLL_IN)?"IN":"",
+ (what&CURL_POLL_OUT)?"OUT":"");
}
break;
}
@@ -513,8 +451,8 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */
m->socket.revents = 0;
ev->list = m;
infof(easy, "socket cb: socket %d ADDED as %s%s\n", s,
- what&CURL_POLL_IN?"IN":"",
- what&CURL_POLL_OUT?"OUT":"");
+ (what&CURL_POLL_IN)?"IN":"",
+ (what&CURL_POLL_OUT)?"OUT":"");
}
else
return CURLE_OUT_OF_MEMORY;
@@ -621,7 +559,7 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
return CURLE_RECV_ERROR;
if(mcode)
- return CURLE_URL_MALFORMAT; /* TODO: return a proper error! */
+ return CURLE_URL_MALFORMAT;
/* we don't really care about the "msgs_in_queue" value returned in the
second argument */
@@ -664,12 +602,12 @@ static CURLcode easy_transfer(struct Curl_multi *multi)
while(!done && !mcode) {
int still_running = 0;
- int rc;
+ bool gotsocket = FALSE;
- mcode = curl_multi_wait(multi, NULL, 0, 1000, &rc);
+ mcode = Curl_multi_wait(multi, NULL, 0, 1000, NULL, &gotsocket);
if(!mcode) {
- if(!rc) {
+ if(!gotsocket) {
long sleep_ms;
/* If it returns without any filedescriptor instantly, we need to
@@ -688,6 +626,7 @@ static CURLcode easy_transfer(struct Curl_multi *multi)
/* only read 'still_running' if curl_multi_perform() return OK */
if(!mcode && !still_running) {
+ int rc;
CURLMsg *msg = curl_multi_info_read(multi, &rc);
if(msg) {
result = msg->data.result;
@@ -966,7 +905,8 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
}
/* Clone the resolver handle, if present, for the new handle */
- if(Curl_resolver_duphandle(&outcurl->state.resolver,
+ if(Curl_resolver_duphandle(outcurl,
+ &outcurl->state.resolver,
data->state.resolver))
goto fail;
@@ -1021,7 +961,10 @@ void curl_easy_reset(struct Curl_easy *data)
/* zero out authentication data: */
memset(&data->state.authhost, 0, sizeof(struct auth));
memset(&data->state.authproxy, 0, sizeof(struct auth));
- Curl_digest_cleanup(data);
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+ Curl_http_auth_cleanup_digest(data);
+#endif
}
/*
@@ -1058,7 +1001,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
unsigned int i;
unsigned int count = data->state.tempcount;
struct tempbuf writebuf[3]; /* there can only be three */
- struct connectdata *conn = data->easy_conn;
+ struct connectdata *conn = data->conn;
struct Curl_easy *saved_data = NULL;
/* copy the structs to allow for immediate re-pausing */
diff --git a/Utilities/cmcurl/lib/escape.c b/Utilities/cmcurl/lib/escape.c
index afd3899f9..7121db31c 100644
--- a/Utilities/cmcurl/lib/escape.c
+++ b/Utilities/cmcurl/lib/escape.c
@@ -122,7 +122,7 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string,
return NULL;
}
- snprintf(&ns[strindex], 4, "%%%02X", in);
+ msnprintf(&ns[strindex], 4, "%%%02X", in);
strindex += 3;
}
diff --git a/Utilities/cmcurl/lib/file.c b/Utilities/cmcurl/lib/file.c
index 722b55e9d..d349cd924 100644
--- a/Utilities/cmcurl/lib/file.c
+++ b/Utilities/cmcurl/lib/file.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -311,7 +311,7 @@ static CURLcode file_upload(struct connectdata *conn)
if(result)
break;
- if(readcount <= 0) /* fix questionable compare error. curlvms */
+ if(!readcount)
break;
nread = readcount;
@@ -417,8 +417,9 @@ static CURLcode file_do(struct connectdata *conn, bool *done)
struct tm buffer;
const struct tm *tm = &buffer;
char header[80];
- snprintf(header, sizeof(header),
- "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", expected_size);
+ msnprintf(header, sizeof(header),
+ "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n",
+ expected_size);
result = Curl_client_write(conn, CLIENTWRITE_HEADER, header, 0);
if(result)
return result;
@@ -434,16 +435,16 @@ static CURLcode file_do(struct connectdata *conn, bool *done)
return result;
/* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
- snprintf(header, sizeof(header),
- "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n%s",
- Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
- tm->tm_mday,
- Curl_month[tm->tm_mon],
- tm->tm_year + 1900,
- tm->tm_hour,
- tm->tm_min,
- tm->tm_sec,
- data->set.opt_no_body ? "": "\r\n");
+ msnprintf(header, sizeof(header),
+ "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n%s",
+ Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
+ tm->tm_mday,
+ Curl_month[tm->tm_mon],
+ tm->tm_year + 1900,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec,
+ data->set.opt_no_body ? "": "\r\n");
result = Curl_client_write(conn, CLIENTWRITE_HEADER, header, 0);
if(result)
return result;
diff --git a/Utilities/cmcurl/lib/fileinfo.c b/Utilities/cmcurl/lib/fileinfo.c
index 4e72e1eba..2630c9e46 100644
--- a/Utilities/cmcurl/lib/fileinfo.c
+++ b/Utilities/cmcurl/lib/fileinfo.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2010 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2010 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -21,7 +21,7 @@
***************************************************************************/
#include "curl_setup.h"
-
+#ifndef CURL_DISABLE_FTP
#include "strdup.h"
#include "fileinfo.h"
#include "curl_memory.h"
@@ -41,3 +41,4 @@ void Curl_fileinfo_cleanup(struct fileinfo *finfo)
Curl_safefree(finfo->info.b_data);
free(finfo);
}
+#endif
diff --git a/Utilities/cmcurl/lib/formdata.c b/Utilities/cmcurl/lib/formdata.c
index 202d930c7..429d479da 100644
--- a/Utilities/cmcurl/lib/formdata.c
+++ b/Utilities/cmcurl/lib/formdata.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -24,14 +24,14 @@
#include <curl/curl.h>
-#ifndef CURL_DISABLE_HTTP
+#include "formdata.h"
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_MIME)
#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
#include <libgen.h>
#endif
#include "urldata.h" /* for struct Curl_easy */
-#include "formdata.h"
#include "mime.h"
#include "non-ascii.h"
#include "vtls/vtls.h"
@@ -569,7 +569,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
if(((form->flags & HTTPPOST_FILENAME) ||
(form->flags & HTTPPOST_BUFFER)) &&
!form->contenttype) {
- char *f = form->flags & HTTPPOST_BUFFER?
+ char *f = (form->flags & HTTPPOST_BUFFER)?
form->showfilename : form->value;
char const *type;
type = Curl_mime_contenttype(f);
@@ -921,7 +921,8 @@ CURLcode Curl_getformdata(struct Curl_easy *data,
return result;
}
-#else /* CURL_DISABLE_HTTP */
+#else
+/* if disabled */
CURLFORMcode curl_formadd(struct curl_httppost **httppost,
struct curl_httppost **last_post,
...)
@@ -946,5 +947,4 @@ void curl_formfree(struct curl_httppost *form)
/* does nothing HTTP is disabled */
}
-
-#endif /* !defined(CURL_DISABLE_HTTP) */
+#endif /* if disabled */
diff --git a/Utilities/cmcurl/lib/formdata.h b/Utilities/cmcurl/lib/formdata.h
index 1246c2bc8..cb20805f5 100644
--- a/Utilities/cmcurl/lib/formdata.h
+++ b/Utilities/cmcurl/lib/formdata.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -22,6 +22,10 @@
*
***************************************************************************/
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_MIME
+
/* used by FormAdd for temporary storage */
typedef struct FormInfo {
char *name;
@@ -47,5 +51,10 @@ CURLcode Curl_getformdata(struct Curl_easy *data,
curl_mimepart *,
struct curl_httppost *post,
curl_read_callback fread_func);
+#else
+/* disabled */
+#define Curl_getformdata(a,b,c,d) CURLE_NOT_BUILT_IN
+#endif
+
#endif /* HEADER_CURL_FORMDATA_H */
diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c
index ce889ab29..53510f821 100644
--- a/Utilities/cmcurl/lib/ftp.c
+++ b/Utilities/cmcurl/lib/ftp.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -448,7 +448,6 @@ static CURLcode ReceivedServerConnect(struct connectdata *conn, bool *received)
static CURLcode InitiateTransfer(struct connectdata *conn)
{
struct Curl_easy *data = conn->data;
- struct FTP *ftp = data->req.protop;
CURLcode result = CURLE_OK;
if(conn->bits.ftp_use_data_ssl) {
@@ -461,24 +460,19 @@ static CURLcode InitiateTransfer(struct connectdata *conn)
}
if(conn->proto.ftpc.state_saved == FTP_STOR) {
- *(ftp->bytecountp) = 0;
-
/* When we know we're uploading a specified file, we can get the file
size prior to the actual upload. */
-
Curl_pgrsSetUploadSize(data, data->state.infilesize);
/* set the SO_SNDBUF for the secondary socket for those who need it */
Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
- SECONDARYSOCKET, ftp->bytecountp);
+ Curl_setup_transfer(data, -1, -1, FALSE, SECONDARYSOCKET);
}
else {
/* FTP download: */
- Curl_setup_transfer(conn, SECONDARYSOCKET,
- conn->proto.ftpc.retr_size_saved, FALSE,
- ftp->bytecountp, -1, NULL); /* no upload here */
+ Curl_setup_transfer(data, SECONDARYSOCKET,
+ conn->proto.ftpc.retr_size_saved, FALSE, -1);
}
conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
@@ -578,7 +572,6 @@ static CURLcode ftp_readresp(curl_socket_t sockfd,
#if defined(HAVE_GSSAPI)
/* handle the security-oriented responses 6xx ***/
- /* FIXME: some errorchecking perhaps... ***/
switch(code) {
case 631:
code = Curl_sec_read_msg(conn, buf, PROT_SAFE);
@@ -655,7 +648,7 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
while(!*ftpcode && !result) {
/* check and reset timeout value every lap */
- time_t timeout = Curl_pp_state_timeout(pp); /* timeout in milliseconds */
+ time_t timeout = Curl_pp_state_timeout(pp, FALSE);
time_t interval_ms;
if(timeout <= 0) {
@@ -955,7 +948,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
unsigned short port_max = 0;
unsigned short port;
bool possibly_non_local = TRUE;
-
+ char buffer[STRERROR_LEN];
char *addr = NULL;
/* Step 1, figure out what is requested,
@@ -1064,11 +1057,10 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
if(!host) {
/* not an interface and not a host name, get default by extracting
the IP from the control connection */
-
sslen = sizeof(ss);
if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
failf(data, "getsockname() failed: %s",
- Curl_strerror(conn, SOCKERRNO) );
+ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
free(addr);
return CURLE_FTP_PORT_FAILED;
}
@@ -1087,7 +1079,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
}
/* resolv ip/host to ip */
- rc = Curl_resolv(conn, host, 0, &h);
+ rc = Curl_resolv(conn, host, 0, FALSE, &h);
if(rc == CURLRESOLV_PENDING)
(void)Curl_resolver_wait_resolv(conn, &h);
if(h) {
@@ -1121,7 +1113,8 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
break;
}
if(!ai) {
- failf(data, "socket failure: %s", Curl_strerror(conn, error));
+ failf(data, "socket failure: %s",
+ Curl_strerror(error, buffer, sizeof(buffer)));
return CURLE_FTP_PORT_FAILED;
}
@@ -1145,14 +1138,13 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
/* The requested bind address is not local. Use the address used for
* the control connection instead and restart the port loop
*/
-
infof(data, "bind(port=%hu) on non-local address failed: %s\n", port,
- Curl_strerror(conn, error) );
+ Curl_strerror(error, buffer, sizeof(buffer)));
sslen = sizeof(ss);
if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
failf(data, "getsockname() failed: %s",
- Curl_strerror(conn, SOCKERRNO) );
+ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
Curl_closesocket(conn, portsock);
return CURLE_FTP_PORT_FAILED;
}
@@ -1162,7 +1154,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
}
if(error != EADDRINUSE && error != EACCES) {
failf(data, "bind(port=%hu) failed: %s", port,
- Curl_strerror(conn, error) );
+ Curl_strerror(error, buffer, sizeof(buffer)));
Curl_closesocket(conn, portsock);
return CURLE_FTP_PORT_FAILED;
}
@@ -1185,7 +1177,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
sslen = sizeof(ss);
if(getsockname(portsock, (struct sockaddr *)sa, &sslen)) {
failf(data, "getsockname() failed: %s",
- Curl_strerror(conn, SOCKERRNO) );
+ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
Curl_closesocket(conn, portsock);
return CURLE_FTP_PORT_FAILED;
}
@@ -1193,7 +1185,8 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
/* step 4, listen on the socket */
if(listen(portsock, 1)) {
- failf(data, "socket failure: %s", Curl_strerror(conn, SOCKERRNO));
+ failf(data, "socket failure: %s",
+ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
Curl_closesocket(conn, portsock);
return CURLE_FTP_PORT_FAILED;
}
@@ -1272,7 +1265,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
source++;
}
*dest = 0;
- snprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff));
+ msnprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff));
result = Curl_pp_sendf(&ftpc->pp, "%s %s", mode[fcmd], tmp);
if(result) {
@@ -1658,7 +1651,7 @@ static CURLcode ftp_state_ul_setup(struct connectdata *conn,
infof(data, "File already completely uploaded\n");
/* no data to transfer */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
/* Set ->transfer so that we won't get any error in
* ftp_done() because we didn't transfer anything! */
@@ -1940,7 +1933,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
*/
const char * const host_name = conn->bits.socksproxy ?
conn->socks_proxy.host.name : conn->http_proxy.host.name;
- rc = Curl_resolv(conn, host_name, (int)conn->port, &addr);
+ rc = Curl_resolv(conn, host_name, (int)conn->port, FALSE, &addr);
if(rc == CURLRESOLV_PENDING)
/* BLOCKING, ignores the return code but 'addr' will be NULL in
case of failure */
@@ -1956,7 +1949,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
}
else {
/* normal, direct, ftp connection */
- rc = Curl_resolv(conn, ftpc->newhost, ftpc->newport, &addr);
+ rc = Curl_resolv(conn, ftpc->newhost, ftpc->newport, FALSE, &addr);
if(rc == CURLRESOLV_PENDING)
/* BLOCKING */
(void)Curl_resolver_wait_resolv(conn, &addr);
@@ -2061,9 +2054,9 @@ static CURLcode ftp_state_mdtm_resp(struct connectdata *conn,
char timebuf[24];
time_t secs = time(NULL);
- snprintf(timebuf, sizeof(timebuf),
- "%04d%02d%02d %02d:%02d:%02d GMT",
- year, month, day, hour, minute, second);
+ msnprintf(timebuf, sizeof(timebuf),
+ "%04d%02d%02d %02d:%02d:%02d GMT",
+ year, month, day, hour, minute, second);
/* now, convert this into a time() value: */
data->info.filetime = curl_getdate(timebuf, &secs);
}
@@ -2086,15 +2079,15 @@ static CURLcode ftp_state_mdtm_resp(struct connectdata *conn,
return result;
/* format: "Tue, 15 Nov 1994 12:45:26" */
- snprintf(headerbuf, sizeof(headerbuf),
- "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
- Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
- tm->tm_mday,
- Curl_month[tm->tm_mon],
- tm->tm_year + 1900,
- tm->tm_hour,
- tm->tm_min,
- tm->tm_sec);
+ msnprintf(headerbuf, sizeof(headerbuf),
+ "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
+ Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
+ tm->tm_mday,
+ Curl_month[tm->tm_mon],
+ tm->tm_year + 1900,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec);
result = Curl_client_write(conn, CLIENTWRITE_BOTH, headerbuf, 0);
if(result)
return result;
@@ -2230,7 +2223,7 @@ static CURLcode ftp_state_retr(struct connectdata *conn,
if(ftp->downloadsize == 0) {
/* no data to transfer */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
infof(data, "File already completely downloaded\n");
/* Set ->transfer so that we won't get any error in ftp_done()
@@ -2276,8 +2269,8 @@ static CURLcode ftp_state_size_resp(struct connectdata *conn,
#ifdef CURL_FTP_HTTPSTYLE_HEAD
if(-1 != filesize) {
char clbuf[128];
- snprintf(clbuf, sizeof(clbuf),
- "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", filesize);
+ msnprintf(clbuf, sizeof(clbuf),
+ "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", filesize);
result = Curl_client_write(conn, CLIENTWRITE_BOTH, clbuf, 0);
if(result)
return result;
@@ -3054,7 +3047,7 @@ static CURLcode ftp_multi_statemach(struct connectdata *conn,
bool *done)
{
struct ftp_conn *ftpc = &conn->proto.ftpc;
- CURLcode result = Curl_pp_statemach(&ftpc->pp, FALSE);
+ CURLcode result = Curl_pp_statemach(&ftpc->pp, FALSE, FALSE);
/* Check for the state outside of the Curl_socket_check() return code checks
since at times we are in fact already in this state when this function
@@ -3071,7 +3064,7 @@ static CURLcode ftp_block_statemach(struct connectdata *conn)
CURLcode result = CURLE_OK;
while(ftpc->state != FTP_STOP) {
- result = Curl_pp_statemach(pp, TRUE);
+ result = Curl_pp_statemach(pp, TRUE, TRUE /* disconnecting */);
if(result)
break;
}
@@ -3308,33 +3301,33 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
;
else if(data->set.upload) {
if((-1 != data->state.infilesize) &&
- (data->state.infilesize != *ftp->bytecountp) &&
+ (data->state.infilesize != data->req.writebytecount) &&
!data->set.crlf &&
(ftp->transfer == FTPTRANSFER_BODY)) {
failf(data, "Uploaded unaligned file size (%" CURL_FORMAT_CURL_OFF_T
" out of %" CURL_FORMAT_CURL_OFF_T " bytes)",
- *ftp->bytecountp, data->state.infilesize);
+ data->req.bytecount, data->state.infilesize);
result = CURLE_PARTIAL_FILE;
}
}
else {
if((-1 != data->req.size) &&
- (data->req.size != *ftp->bytecountp) &&
+ (data->req.size != data->req.bytecount) &&
#ifdef CURL_DO_LINEEND_CONV
/* Most FTP servers don't adjust their file SIZE response for CRLFs, so
* we'll check to see if the discrepancy can be explained by the number
* of CRLFs we've changed to LFs.
*/
((data->req.size + data->state.crlf_conversions) !=
- *ftp->bytecountp) &&
+ data->req.bytecount) &&
#endif /* CURL_DO_LINEEND_CONV */
- (data->req.maxdownload != *ftp->bytecountp)) {
+ (data->req.maxdownload != data->req.bytecount)) {
failf(data, "Received only partial file: %" CURL_FORMAT_CURL_OFF_T
- " bytes", *ftp->bytecountp);
+ " bytes", data->req.bytecount);
result = CURLE_PARTIAL_FILE;
}
else if(!ftpc->dont_check &&
- !*ftp->bytecountp &&
+ !data->req.bytecount &&
(data->req.size>0)) {
failf(data, "No data was received!");
result = CURLE_FTP_COULDNT_RETR_FILE;
@@ -3496,7 +3489,7 @@ static CURLcode ftp_do_more(struct connectdata *conn, int *completep)
if(!conn->bits.tcpconnect[SECONDARYSOCKET]) {
if(Curl_connect_ongoing(conn)) {
/* As we're in TUNNEL_CONNECT state now, we know the proxy name and port
- aren't used so we blank their arguments. TODO: make this nicer */
+ aren't used so we blank their arguments. */
result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, NULL, 0);
return result;
@@ -3629,7 +3622,7 @@ static CURLcode ftp_do_more(struct connectdata *conn, int *completep)
if(!result && (ftp->transfer != FTPTRANSFER_BODY))
/* no data to transfer. FIX: it feels like a kludge to have this here
too! */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
if(!ftpc->wait_data_conn) {
/* no waiting for the data connection so this is now complete */
@@ -4309,7 +4302,7 @@ static CURLcode ftp_dophase_done(struct connectdata *conn,
if(ftp->transfer != FTPTRANSFER_BODY)
/* no data to transfer */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(conn->data, -1, -1, FALSE, -1);
else if(!connected)
/* since we didn't connect now, we want do_more to get called */
conn->bits.do_more = TRUE;
@@ -4396,7 +4389,6 @@ static CURLcode ftp_setup_connection(struct connectdata *conn)
return CURLE_OUT_OF_MEMORY;
ftp->path = &data->state.up.path[1]; /* don't include the initial slash */
- data->state.slash_removed = TRUE; /* we've skipped the slash */
/* FTP URLs support an extension like ";type=<typecode>" that
* we'll try to get now! */
@@ -4429,7 +4421,6 @@ static CURLcode ftp_setup_connection(struct connectdata *conn)
}
/* get some initial data into the ftp struct */
- ftp->bytecountp = &conn->data->req.bytecount;
ftp->transfer = FTPTRANSFER_BODY;
ftp->downloadsize = 0;
diff --git a/Utilities/cmcurl/lib/ftp.h b/Utilities/cmcurl/lib/ftp.h
index 38d03223c..828d69a21 100644
--- a/Utilities/cmcurl/lib/ftp.h
+++ b/Utilities/cmcurl/lib/ftp.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -102,7 +102,6 @@ typedef enum {
perhaps the Curl_easy is changed between the times the connection is
used. */
struct FTP {
- curl_off_t *bytecountp;
char *user; /* user name string */
char *passwd; /* password string */
char *path; /* points to the urlpieces struct field */
diff --git a/Utilities/cmcurl/lib/ftplistparser.c b/Utilities/cmcurl/lib/ftplistparser.c
index 249fe09c8..c4eb43732 100644
--- a/Utilities/cmcurl/lib/ftplistparser.c
+++ b/Utilities/cmcurl/lib/ftplistparser.c
@@ -405,7 +405,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
parser->state.UNIX.main = PL_UNIX_FILETYPE;
/* start FSM again not considering size of directory */
finfo->b_used = 0;
- i--;
+ continue;
}
break;
case PL_UNIX_TOTALSIZE_READING:
@@ -914,10 +914,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
case PL_WINNT_DIRORSIZE:
switch(parser->state.NT.sub.dirorsize) {
case PL_WINNT_DIRORSIZE_PRESPACE:
- if(c == ' ') {
-
- }
- else {
+ if(c != ' ') {
parser->item_offset = finfo->b_used - 1;
parser->item_length = 1;
parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT;
diff --git a/Utilities/cmcurl/lib/getinfo.c b/Utilities/cmcurl/lib/getinfo.c
index 54c2c2f1c..e118da80d 100644
--- a/Utilities/cmcurl/lib/getinfo.c
+++ b/Utilities/cmcurl/lib/getinfo.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -163,10 +163,10 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
*param_longp = (long)data->info.filetime;
break;
case CURLINFO_HEADER_SIZE:
- *param_longp = data->info.header_size;
+ *param_longp = (long)data->info.header_size;
break;
case CURLINFO_REQUEST_SIZE:
- *param_longp = data->info.request_size;
+ *param_longp = (long)data->info.request_size;
break;
case CURLINFO_SSL_VERIFYRESULT:
*param_longp = data->set.ssl.certverifyresult;
@@ -390,7 +390,7 @@ static CURLcode getinfo_slist(struct Curl_easy *data, CURLINFO info,
param_slistp;
struct curl_tlssessioninfo *tsi = &data->tsi;
#ifdef USE_SSL
- struct connectdata *conn = data->easy_conn;
+ struct connectdata *conn = data->conn;
#endif
*tsip = tsi;
diff --git a/Utilities/cmcurl/lib/gopher.c b/Utilities/cmcurl/lib/gopher.c
index b441a641d..b296c62d1 100644
--- a/Utilities/cmcurl/lib/gopher.c
+++ b/Utilities/cmcurl/lib/gopher.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -31,9 +31,11 @@
#include "progress.h"
#include "gopher.h"
#include "select.h"
+#include "strdup.h"
#include "url.h"
#include "escape.h"
#include "warnless.h"
+#include "curl_printf.h"
#include "curl_memory.h"
/* The last #include file should be: */
#include "memdebug.h"
@@ -76,9 +78,9 @@ static CURLcode gopher_do(struct connectdata *conn, bool *done)
CURLcode result = CURLE_OK;
struct Curl_easy *data = conn->data;
curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
-
- curl_off_t *bytecount = &data->req.bytecount;
+ char *gopherpath;
char *path = data->state.up.path;
+ char *query = data->state.up.query;
char *sel = NULL;
char *sel_org = NULL;
ssize_t amount, k;
@@ -86,20 +88,33 @@ static CURLcode gopher_do(struct connectdata *conn, bool *done)
*done = TRUE; /* unconditionally */
+ /* path is guaranteed non-NULL */
+ DEBUGASSERT(path);
+
+ if(query)
+ gopherpath = aprintf("%s?%s", path, query);
+ else
+ gopherpath = strdup(path);
+
+ if(!gopherpath)
+ return CURLE_OUT_OF_MEMORY;
+
/* Create selector. Degenerate cases: / and /1 => convert to "" */
- if(strlen(path) <= 2) {
+ if(strlen(gopherpath) <= 2) {
sel = (char *)"";
len = strlen(sel);
+ free(gopherpath);
}
else {
char *newp;
/* Otherwise, drop / and the first character (i.e., item type) ... */
- newp = path;
+ newp = gopherpath;
newp += 2;
/* ... and finally unescape */
result = Curl_urldecode(data, newp, 0, &sel, &len, FALSE);
+ free(gopherpath);
if(result)
return result;
sel_org = sel;
@@ -153,8 +168,7 @@ static CURLcode gopher_do(struct connectdata *conn, bool *done)
if(result)
return result;
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount,
- -1, NULL); /* no upload */
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
return CURLE_OK;
}
#endif /*CURL_DISABLE_GOPHER*/
diff --git a/Utilities/cmcurl/lib/hostasyn.c b/Utilities/cmcurl/lib/hostasyn.c
index 6ff60ba61..99d872b35 100644
--- a/Utilities/cmcurl/lib/hostasyn.c
+++ b/Utilities/cmcurl/lib/hostasyn.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -85,14 +85,14 @@ CURLcode Curl_addrinfo_callback(struct connectdata *conn,
dns = Curl_cache_addr(data, ai,
conn->async.hostname,
conn->async.port);
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+
if(!dns) {
/* failed to store, cleanup and return error */
Curl_freeaddrinfo(ai);
result = CURLE_OUT_OF_MEMORY;
}
-
- if(data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
}
else {
result = CURLE_OUT_OF_MEMORY;
diff --git a/Utilities/cmcurl/lib/hostcheck.c b/Utilities/cmcurl/lib/hostcheck.c
index c9d8112d8..115d24b2e 100644
--- a/Utilities/cmcurl/lib/hostcheck.c
+++ b/Utilities/cmcurl/lib/hostcheck.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -23,7 +23,6 @@
#include "curl_setup.h"
#if defined(USE_OPENSSL) \
- || defined(USE_AXTLS) \
|| defined(USE_GSKIT) \
|| defined(USE_SCHANNEL)
/* these backends use functions from this file */
@@ -128,16 +127,14 @@ static int hostmatch(char *hostname, char *pattern)
int Curl_cert_hostcheck(const char *match_pattern, const char *hostname)
{
- char *matchp;
- char *hostp;
int res = 0;
if(!match_pattern || !*match_pattern ||
!hostname || !*hostname) /* sanity check */
;
else {
- matchp = strdup(match_pattern);
+ char *matchp = strdup(match_pattern);
if(matchp) {
- hostp = strdup(hostname);
+ char *hostp = strdup(hostname);
if(hostp) {
if(hostmatch(hostp, matchp) == CURL_HOST_MATCH)
res = 1;
@@ -150,4 +147,4 @@ int Curl_cert_hostcheck(const char *match_pattern, const char *hostname)
return res;
}
-#endif /* OPENSSL, AXTLS, GSKIT or schannel+wince */
+#endif /* OPENSSL, GSKIT or schannel+wince */
diff --git a/Utilities/cmcurl/lib/hostip.c b/Utilities/cmcurl/lib/hostip.c
index f589a0b2c..cf33ed8f4 100644
--- a/Utilities/cmcurl/lib/hostip.c
+++ b/Utilities/cmcurl/lib/hostip.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -73,6 +73,8 @@
#define USE_ALARM_TIMEOUT
#endif
+#define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */
+
/*
* hostip.c explained
* ==================
@@ -112,43 +114,9 @@
* CURLRES_* defines based on the config*.h and curl_setup.h defines.
*/
-/* These two symbols are for the global DNS cache */
-static struct curl_hash hostname_cache;
-static int host_cache_initialized;
-
static void freednsentry(void *freethis);
/*
- * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
- * Global DNS cache is general badness. Do not use. This will be removed in
- * a future version. Use the share interface instead!
- *
- * Returns a struct curl_hash pointer on success, NULL on failure.
- */
-struct curl_hash *Curl_global_host_cache_init(void)
-{
- int rc = 0;
- if(!host_cache_initialized) {
- rc = Curl_hash_init(&hostname_cache, 7, Curl_hash_str,
- Curl_str_key_compare, freednsentry);
- if(!rc)
- host_cache_initialized = 1;
- }
- return rc?NULL:&hostname_cache;
-}
-
-/*
- * Destroy and cleanup the global DNS cache
- */
-void Curl_global_host_cache_dtor(void)
-{
- if(host_cache_initialized) {
- Curl_hash_destroy(&hostname_cache);
- host_cache_initialized = 0;
- }
-}
-
-/*
* Return # of addresses in a Curl_addrinfo struct
*/
int Curl_num_addresses(const Curl_addrinfo *addr)
@@ -198,23 +166,19 @@ Curl_printable_address(const Curl_addrinfo *ai, char *buf, size_t bufsize)
}
/*
- * Return a hostcache id string for the provided host + port, to be used by
- * the DNS caching.
+ * Create a hostcache id string for the provided host + port, to be used by
+ * the DNS caching. Without alloc.
*/
-static char *
-create_hostcache_id(const char *name, int port)
+static void
+create_hostcache_id(const char *name, int port, char *ptr, size_t buflen)
{
- /* create and return the new allocated entry */
- char *id = aprintf("%s:%d", name, port);
- char *ptr = id;
- if(ptr) {
- /* lower case the name part */
- while(*ptr && (*ptr != ':')) {
- *ptr = (char)TOLOWER(*ptr);
- ptr++;
- }
- }
- return id;
+ size_t len = strlen(name);
+ if(len > (buflen - 7))
+ len = buflen - 7;
+ /* store and lower case the name */
+ while(len--)
+ *ptr++ = (char)TOLOWER(*name++);
+ msnprintf(ptr, 7, ":%u", port);
}
struct hostcache_prune_data {
@@ -296,22 +260,27 @@ fetch_addr(struct connectdata *conn,
const char *hostname,
int port)
{
- char *entry_id = NULL;
struct Curl_dns_entry *dns = NULL;
size_t entry_len;
struct Curl_easy *data = conn->data;
+ char entry_id[MAX_HOSTCACHE_LEN];
/* Create an entry id, based upon the hostname and port */
- entry_id = create_hostcache_id(hostname, port);
- /* If we can't create the entry id, fail */
- if(!entry_id)
- return dns;
-
+ create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
entry_len = strlen(entry_id);
/* See if its already in our dns cache */
dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
+ /* No entry found in cache, check if we might have a wildcard entry */
+ if(!dns && data->change.wildcard_resolve) {
+ create_hostcache_id("*", port, entry_id, sizeof(entry_id));
+ entry_len = strlen(entry_id);
+
+ /* See if it's already in our dns cache */
+ dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
+ }
+
if(dns && (data->set.dns_cache_timeout != -1)) {
/* See whether the returned entry is stale. Done before we release lock */
struct hostcache_prune_data user;
@@ -326,9 +295,6 @@ fetch_addr(struct connectdata *conn,
}
}
- /* free the allocated entry_id again */
- free(entry_id);
-
return dns;
}
@@ -368,6 +334,9 @@ Curl_fetch_addr(struct connectdata *conn,
return dns;
}
+#ifndef CURL_DISABLE_SHUFFLE_DNS
+UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
+ Curl_addrinfo **addr);
/*
* Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo'
* struct by re-linking its linked list.
@@ -380,7 +349,8 @@ Curl_fetch_addr(struct connectdata *conn,
*
* @unittest: 1608
*/
-CURLcode Curl_shuffle_addr(struct Curl_easy *data, Curl_addrinfo **addr)
+UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
+ Curl_addrinfo **addr)
{
CURLcode result = CURLE_OK;
const int num_addrs = Curl_num_addresses(*addr);
@@ -431,6 +401,7 @@ CURLcode Curl_shuffle_addr(struct Curl_easy *data, Curl_addrinfo **addr)
}
return result;
}
+#endif
/*
* Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
@@ -447,32 +418,30 @@ Curl_cache_addr(struct Curl_easy *data,
const char *hostname,
int port)
{
- char *entry_id;
+ char entry_id[MAX_HOSTCACHE_LEN];
size_t entry_len;
struct Curl_dns_entry *dns;
struct Curl_dns_entry *dns2;
+#ifndef CURL_DISABLE_SHUFFLE_DNS
/* shuffle addresses if requested */
if(data->set.dns_shuffle_addresses) {
CURLcode result = Curl_shuffle_addr(data, &addr);
if(result)
return NULL;
}
-
- /* Create an entry id, based upon the hostname and port */
- entry_id = create_hostcache_id(hostname, port);
- /* If we can't create the entry id, fail */
- if(!entry_id)
- return NULL;
- entry_len = strlen(entry_id);
+#endif
/* Create a new cache entry */
dns = calloc(1, sizeof(struct Curl_dns_entry));
if(!dns) {
- free(entry_id);
return NULL;
}
+ /* Create an entry id, based upon the hostname and port */
+ create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
+ entry_len = strlen(entry_id);
+
dns->inuse = 1; /* the cache has the first reference */
dns->addr = addr; /* this is the address(es) */
time(&dns->timestamp);
@@ -484,16 +453,11 @@ Curl_cache_addr(struct Curl_easy *data,
(void *)dns);
if(!dns2) {
free(dns);
- free(entry_id);
return NULL;
}
dns = dns2;
dns->inuse++; /* mark entry as in-use */
-
- /* free the allocated entry_id */
- free(entry_id);
-
return dns;
}
@@ -521,6 +485,7 @@ Curl_cache_addr(struct Curl_easy *data,
int Curl_resolv(struct connectdata *conn,
const char *hostname,
int port,
+ bool allowDOH,
struct Curl_dns_entry **entry)
{
struct Curl_dns_entry *dns = NULL;
@@ -548,7 +513,7 @@ int Curl_resolv(struct connectdata *conn,
/* The entry was not in the cache. Resolve it to IP address */
Curl_addrinfo *addr;
- int respwait;
+ int respwait = 0;
/* Check what IP specifics the app has requested and if we can provide it.
* If not, bail out. */
@@ -566,7 +531,7 @@ int Curl_resolv(struct connectdata *conn,
return CURLRESOLV_ERROR;
}
- if(data->set.doh) {
+ if(allowDOH && data->set.doh) {
addr = Curl_doh(conn, hostname, port, &respwait);
}
else {
@@ -692,7 +657,7 @@ int Curl_resolv_timeout(struct connectdata *conn,
if(!timeout)
/* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
- return Curl_resolv(conn, hostname, port, entry);
+ return Curl_resolv(conn, hostname, port, TRUE, entry);
if(timeout < 1000) {
/* The alarm() function only provides integer second resolution, so if
@@ -754,7 +719,7 @@ int Curl_resolv_timeout(struct connectdata *conn,
/* Perform the actual name resolution. This might be interrupted by an
* alarm if it takes too long.
*/
- rc = Curl_resolv(conn, hostname, port, entry);
+ rc = Curl_resolv(conn, hostname, port, TRUE, entry);
#ifdef USE_ALARM_TIMEOUT
clean_up:
@@ -872,11 +837,14 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
char hostname[256];
int port = 0;
+ /* Default is no wildcard found */
+ data->change.wildcard_resolve = false;
+
for(hostp = data->change.resolve; hostp; hostp = hostp->next) {
+ char entry_id[MAX_HOSTCACHE_LEN];
if(!hostp->data)
continue;
if(hostp->data[0] == '-') {
- char *entry_id;
size_t entry_len;
if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) {
@@ -886,12 +854,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
}
/* Create an entry id, based upon the hostname and port */
- entry_id = create_hostcache_id(hostname, port);
- /* If we can't create the entry id, fail */
- if(!entry_id) {
- return CURLE_OUT_OF_MEMORY;
- }
-
+ create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
entry_len = strlen(entry_id);
if(data->share)
@@ -902,14 +865,10 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
-
- /* free the allocated entry_id again */
- free(entry_id);
}
else {
struct Curl_dns_entry *dns;
Curl_addrinfo *head = NULL, *tail = NULL;
- char *entry_id;
size_t entry_len;
char address[64];
#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
@@ -1005,12 +964,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
}
/* Create an entry id, based upon the hostname and port */
- entry_id = create_hostcache_id(hostname, port);
- /* If we can't create the entry id, fail */
- if(!entry_id) {
- Curl_freeaddrinfo(head);
- return CURLE_OUT_OF_MEMORY;
- }
+ create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
entry_len = strlen(entry_id);
if(data->share)
@@ -1031,8 +985,6 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
}
- /* free the allocated entry_id again */
- free(entry_id);
/* put this new host in the cache */
dns = Curl_cache_addr(data, head, hostname, port);
@@ -1052,6 +1004,13 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
}
infof(data, "Added %s:%d:%s to DNS cache\n",
hostname, port, addresses);
+
+ /* Wildcard hostname */
+ if(hostname[0] == '*' && hostname[1] == '\0') {
+ infof(data, "RESOLVE %s:%d is wildcard, enabling wildcard checks\n",
+ hostname, port);
+ data->change.wildcard_resolve = true;
+ }
}
}
data->change.resolve = NULL; /* dealt with now */
diff --git a/Utilities/cmcurl/lib/hostip.h b/Utilities/cmcurl/lib/hostip.h
index 29fd1ef7c..9dc0d5a17 100644
--- a/Utilities/cmcurl/lib/hostip.h
+++ b/Utilities/cmcurl/lib/hostip.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -83,8 +83,11 @@ struct Curl_dns_entry {
#define CURLRESOLV_ERROR -1
#define CURLRESOLV_RESOLVED 0
#define CURLRESOLV_PENDING 1
-int Curl_resolv(struct connectdata *conn, const char *hostname,
- int port, struct Curl_dns_entry **dnsentry);
+int Curl_resolv(struct connectdata *conn,
+ const char *hostname,
+ int port,
+ bool allowDOH,
+ struct Curl_dns_entry **dnsentry);
int Curl_resolv_timeout(struct connectdata *conn, const char *hostname,
int port, struct Curl_dns_entry **dnsentry,
time_t timeoutms);
@@ -179,16 +182,6 @@ Curl_fetch_addr(struct connectdata *conn,
int port);
/*
- * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo'
- * struct by re-linking its linked list.
- *
- * The addr argument should be the address of a pointer to the head node of a
- * `Curl_addrinfo` list and it will be modified to point to the new head after
- * shuffling.
- */
-CURLcode Curl_shuffle_addr(struct Curl_easy *data, Curl_addrinfo **addr);
-
-/*
* Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
*
* Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
diff --git a/Utilities/cmcurl/lib/hostip4.c b/Utilities/cmcurl/lib/hostip4.c
index 9d6f115ae..e6ba710d8 100644
--- a/Utilities/cmcurl/lib/hostip4.c
+++ b/Utilities/cmcurl/lib/hostip4.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -145,7 +145,7 @@ Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname,
hints.ai_family = PF_INET;
hints.ai_socktype = SOCK_STREAM;
if(port) {
- snprintf(sbuf, sizeof(sbuf), "%d", port);
+ msnprintf(sbuf, sizeof(sbuf), "%d", port);
sbufptr = sbuf;
}
diff --git a/Utilities/cmcurl/lib/hostip6.c b/Utilities/cmcurl/lib/hostip6.c
index 3bf47b467..5511f1aab 100644
--- a/Utilities/cmcurl/lib/hostip6.c
+++ b/Utilities/cmcurl/lib/hostip6.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -101,14 +101,16 @@ static void dump_addrinfo(struct connectdata *conn, const Curl_addrinfo *ai)
{
printf("dump_addrinfo:\n");
for(; ai; ai = ai->ai_next) {
- char buf[INET6_ADDRSTRLEN];
-
+ char buf[INET6_ADDRSTRLEN];
printf(" fam %2d, CNAME %s, ",
ai->ai_family, ai->ai_canonname ? ai->ai_canonname : "<none>");
if(Curl_printable_address(ai, buf, sizeof(buf)))
printf("%s\n", buf);
- else
- printf("failed; %s\n", Curl_strerror(conn, SOCKERRNO));
+ else {
+ char buffer[STRERROR_LEN];
+ printf("failed; %s\n",
+ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+ }
}
}
#else
@@ -178,7 +180,7 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
#endif
if(port) {
- snprintf(sbuf, sizeof(sbuf), "%d", port);
+ msnprintf(sbuf, sizeof(sbuf), "%d", port);
sbufptr = sbuf;
}
diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c
index 46ac15a6e..338c59a22 100644
--- a/Utilities/cmcurl/lib/http.c
+++ b/Utilities/cmcurl/lib/http.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -73,10 +73,10 @@
#include "http_proxy.h"
#include "warnless.h"
#include "non-ascii.h"
-#include "pipeline.h"
#include "http2.h"
#include "connect.h"
#include "strdup.h"
+#include "altsvc.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -92,7 +92,9 @@ static int http_getsock_do(struct connectdata *conn,
int numsocks);
static int http_should_fail(struct connectdata *conn);
+#ifndef CURL_DISABLE_PROXY
static CURLcode add_haproxy_protocol_header(struct connectdata *conn);
+#endif
#ifdef USE_SSL
static CURLcode https_connecting(struct connectdata *conn, bool *done);
@@ -102,13 +104,14 @@ static int https_getsock(struct connectdata *conn,
#else
#define https_connecting(x,y) CURLE_COULDNT_CONNECT
#endif
+static CURLcode http_setup_conn(struct connectdata *conn);
/*
* HTTP handler interface.
*/
const struct Curl_handler Curl_handler_http = {
"HTTP", /* scheme */
- Curl_http_setup_conn, /* setup_connection */
+ http_setup_conn, /* setup_connection */
Curl_http, /* do_it */
Curl_http_done, /* done */
ZERO_NULL, /* do_more */
@@ -133,7 +136,7 @@ const struct Curl_handler Curl_handler_http = {
*/
const struct Curl_handler Curl_handler_https = {
"HTTPS", /* scheme */
- Curl_http_setup_conn, /* setup_connection */
+ http_setup_conn, /* setup_connection */
Curl_http, /* do_it */
Curl_http_done, /* done */
ZERO_NULL, /* do_more */
@@ -153,7 +156,7 @@ const struct Curl_handler Curl_handler_https = {
};
#endif
-CURLcode Curl_http_setup_conn(struct connectdata *conn)
+static CURLcode http_setup_conn(struct connectdata *conn)
{
/* allocate the HTTP-specific struct for the Curl_easy, only to survive
during this request */
@@ -175,7 +178,7 @@ CURLcode Curl_http_setup_conn(struct connectdata *conn)
return CURLE_OK;
}
-
+#ifndef CURL_DISABLE_PROXY
/*
* checkProxyHeaders() checks the linked list of custom proxy headers
* if proxy headers are not available, then it will lookup into http header
@@ -202,6 +205,10 @@ char *Curl_checkProxyheaders(const struct connectdata *conn,
return NULL;
}
+#else
+/* disabled */
+#define Curl_checkProxyheaders(x,y) NULL
+#endif
/*
* Strip off leading and trailing whitespace from the value in the
@@ -256,6 +263,7 @@ char *Curl_copy_header_value(const char *header)
return value;
}
+#ifndef CURL_DISABLE_HTTP_AUTH
/*
* http_output_basic() sets up an Authorization: header (or the proxy version)
* for HTTP Basic authentication.
@@ -337,6 +345,8 @@ static CURLcode http_output_bearer(struct connectdata *conn)
return result;
}
+#endif
+
/* pickoneauth() selects the most favourable authentication method from the
* ones available and the ones we want.
*
@@ -415,7 +425,7 @@ static CURLcode http_perhapsrewind(struct connectdata *conn)
break;
}
- bytessent = http->writebytecount;
+ bytessent = data->req.writebytecount;
if(conn->bits.authneg) {
/* This is a state where we are known to be negotiating and we don't send
@@ -456,8 +466,8 @@ static CURLcode http_perhapsrewind(struct connectdata *conn)
(data->state.authproxy.picked == CURLAUTH_NTLM_WB) ||
(data->state.authhost.picked == CURLAUTH_NTLM_WB)) {
if(((expectsend - bytessent) < 2000) ||
- (conn->ntlm.state != NTLMSTATE_NONE) ||
- (conn->proxyntlm.state != NTLMSTATE_NONE)) {
+ (conn->http_ntlm_state != NTLMSTATE_NONE) ||
+ (conn->proxy_ntlm_state != NTLMSTATE_NONE)) {
/* The NTLM-negotiation has started *OR* there is just a little (<2K)
data left to send, keep on sending. */
@@ -479,8 +489,36 @@ static CURLcode http_perhapsrewind(struct connectdata *conn)
(curl_off_t)(expectsend - bytessent));
}
#endif
+#if defined(USE_SPNEGO)
+ /* There is still data left to send */
+ if((data->state.authproxy.picked == CURLAUTH_NEGOTIATE) ||
+ (data->state.authhost.picked == CURLAUTH_NEGOTIATE)) {
+ if(((expectsend - bytessent) < 2000) ||
+ (conn->http_negotiate_state != GSS_AUTHNONE) ||
+ (conn->proxy_negotiate_state != GSS_AUTHNONE)) {
+ /* The NEGOTIATE-negotiation has started *OR*
+ there is just a little (<2K) data left to send, keep on sending. */
+
+ /* rewind data when completely done sending! */
+ if(!conn->bits.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) {
+ conn->bits.rewindaftersend = TRUE;
+ infof(data, "Rewind stream after send\n");
+ }
+
+ return CURLE_OK;
+ }
- /* This is not NTLM or many bytes left to send: close */
+ if(conn->bits.close)
+ /* this is already marked to get closed */
+ return CURLE_OK;
+
+ infof(data, "NEGOTIATE send, close instead of sending %"
+ CURL_FORMAT_CURL_OFF_T " bytes\n",
+ (curl_off_t)(expectsend - bytessent));
+ }
+#endif
+
+ /* This is not NEGOTIATE/NTLM or many bytes left to send: close */
streamclose(conn, "Mid-auth HTTP and much data left to send");
data->req.size = 0; /* don't download any more than 0 bytes */
@@ -526,6 +564,12 @@ CURLcode Curl_http_auth_act(struct connectdata *conn)
pickhost = pickoneauth(&data->state.authhost, authmask);
if(!pickhost)
data->state.authproblem = TRUE;
+ if(data->state.authhost.picked == CURLAUTH_NTLM &&
+ conn->httpversion > 11) {
+ infof(data, "Forcing HTTP/1.1 for NTLM");
+ connclose(conn, "Force HTTP/1.1 connection");
+ conn->data->set.httpversion = CURL_HTTP_VERSION_1_1;
+ }
}
if(conn->bits.proxy_user_passwd &&
((data->req.httpcode == 407) ||
@@ -576,6 +620,7 @@ CURLcode Curl_http_auth_act(struct connectdata *conn)
return result;
}
+#ifndef CURL_DISABLE_HTTP_AUTH
/*
* Output the correct authentication header depending on the auth type
* and whether or not it is to a proxy.
@@ -592,10 +637,6 @@ output_auth_headers(struct connectdata *conn,
#if !defined(CURL_DISABLE_VERBOSE_STRINGS) || defined(USE_SPNEGO)
struct Curl_easy *data = conn->data;
#endif
-#ifdef USE_SPNEGO
- struct negotiatedata *negdata = proxy ?
- &data->state.proxyneg : &data->state.negotiate;
-#endif
#ifdef CURL_DISABLE_CRYPTO_AUTH
(void)request;
@@ -603,15 +644,11 @@ output_auth_headers(struct connectdata *conn,
#endif
#ifdef USE_SPNEGO
- negdata->state = GSS_AUTHNONE;
- if((authstatus->picked == CURLAUTH_NEGOTIATE) &&
- negdata->context && !GSS_ERROR(negdata->status)) {
+ if((authstatus->picked == CURLAUTH_NEGOTIATE)) {
auth = "Negotiate";
result = Curl_output_negotiate(conn, proxy);
if(result)
return result;
- authstatus->done = TRUE;
- negdata->state = GSS_AUTHSENT;
}
else
#endif
@@ -697,7 +734,7 @@ output_auth_headers(struct connectdata *conn,
*
* @param conn all information about the current connection
* @param request pointer to the request keyword
- * @param path pointer to the requested path
+ * @param path pointer to the requested path; should include query part
* @param proxytunnel boolean if this is the request setting up a "proxy
* tunnel"
*
@@ -744,7 +781,7 @@ Curl_http_output_auth(struct connectdata *conn,
#ifndef CURL_DISABLE_PROXY
/* Send proxy authentication header if needed */
if(conn->bits.httpproxy &&
- (conn->bits.tunnel_proxy == proxytunnel)) {
+ (conn->bits.tunnel_proxy == (bit)proxytunnel)) {
result = output_auth_headers(conn, authproxy, request, path, TRUE);
if(result)
return result;
@@ -772,6 +809,22 @@ Curl_http_output_auth(struct connectdata *conn,
return result;
}
+#else
+/* when disabled */
+CURLcode
+Curl_http_output_auth(struct connectdata *conn,
+ const char *request,
+ const char *path,
+ bool proxytunnel)
+{
+ (void)conn;
+ (void)request;
+ (void)path;
+ (void)proxytunnel;
+ return CURLE_OK;
+}
+#endif
+
/*
* Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate:
* headers. They are dealt with both in the transfer.c main loop and in the
@@ -787,8 +840,8 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, bool proxy,
struct Curl_easy *data = conn->data;
#ifdef USE_SPNEGO
- struct negotiatedata *negdata = proxy?
- &data->state.proxyneg:&data->state.negotiate;
+ curlnegotiate *negstate = proxy ? &conn->proxy_negotiate_state :
+ &conn->http_negotiate_state;
#endif
unsigned long *availp;
struct auth *authp;
@@ -827,21 +880,18 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, bool proxy,
authp->avail |= CURLAUTH_NEGOTIATE;
if(authp->picked == CURLAUTH_NEGOTIATE) {
- if(negdata->state == GSS_AUTHSENT ||
- negdata->state == GSS_AUTHNONE) {
- CURLcode result = Curl_input_negotiate(conn, proxy, auth);
- if(!result) {
- DEBUGASSERT(!data->req.newurl);
- data->req.newurl = strdup(data->change.url);
- if(!data->req.newurl)
- return CURLE_OUT_OF_MEMORY;
- data->state.authproblem = FALSE;
- /* we received a GSS auth token and we dealt with it fine */
- negdata->state = GSS_AUTHRECV;
- }
- else
- data->state.authproblem = TRUE;
+ CURLcode result = Curl_input_negotiate(conn, proxy, auth);
+ if(!result) {
+ DEBUGASSERT(!data->req.newurl);
+ data->req.newurl = strdup(data->change.url);
+ if(!data->req.newurl)
+ return CURLE_OUT_OF_MEMORY;
+ data->state.authproblem = FALSE;
+ /* we received a GSS auth token and we dealt with it fine */
+ *negstate = GSS_AUTHRECV;
}
+ else
+ data->state.authproblem = TRUE;
}
}
}
@@ -869,19 +919,10 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, bool proxy,
*availp |= CURLAUTH_NTLM_WB;
authp->avail |= CURLAUTH_NTLM_WB;
- /* Get the challenge-message which will be passed to
- * ntlm_auth for generating the type 3 message later */
- while(*auth && ISSPACE(*auth))
- auth++;
- if(checkprefix("NTLM", auth)) {
- auth += strlen("NTLM");
- while(*auth && ISSPACE(*auth))
- auth++;
- if(*auth) {
- conn->challenge_header = strdup(auth);
- if(!conn->challenge_header)
- return CURLE_OUT_OF_MEMORY;
- }
+ result = Curl_input_ntlm_wb(conn, proxy, auth);
+ if(result) {
+ infof(data, "Authentication problem. Ignoring this.\n");
+ data->state.authproblem = TRUE;
}
}
#endif
@@ -1111,14 +1152,13 @@ void Curl_add_buffer_free(Curl_send_buffer **inp)
CURLcode Curl_add_buffer_send(Curl_send_buffer **inp,
struct connectdata *conn,
- /* add the number of sent bytes to this
- counter */
- long *bytes_written,
+ /* add the number of sent bytes to this
+ counter */
+ curl_off_t *bytes_written,
- /* how much of the buffer contains body data */
+ /* how much of the buffer contains body data */
size_t included_body_bytes,
int socketindex)
-
{
ssize_t amount;
CURLcode result;
@@ -1214,7 +1254,8 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer **inp,
if(http) {
/* if we sent a piece of the body here, up the byte counter for it
accordingly */
- http->writebytecount += bodylen;
+ data->req.writebytecount += bodylen;
+ Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
if((size_t)amount != size) {
/* The whole request could not be sent in one system call. We must
@@ -1255,7 +1296,6 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer **inp,
This needs FIXing.
*/
return CURLE_SEND_ERROR;
- Curl_pipeline_leave_write(conn);
}
}
Curl_add_buffer_free(&in);
@@ -1432,12 +1472,14 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
/* nothing else to do except wait right now - we're not done here. */
return CURLE_OK;
+#ifndef CURL_DISABLE_PROXY
if(conn->data->set.haproxyprotocol) {
/* add HAProxy PROXY protocol header */
result = add_haproxy_protocol_header(conn);
if(result)
return result;
}
+#endif
if(conn->given->protocol & CURLPROTO_HTTPS) {
/* perform SSL initialization */
@@ -1464,6 +1506,7 @@ static int http_getsock_do(struct connectdata *conn,
return GETSOCK_WRITESOCK(0);
}
+#ifndef CURL_DISABLE_PROXY
static CURLcode add_haproxy_protocol_header(struct connectdata *conn)
{
char proxy_header[128];
@@ -1479,14 +1522,14 @@ static CURLcode add_haproxy_protocol_header(struct connectdata *conn)
strcpy(tcp_version, "TCP4");
}
- snprintf(proxy_header,
- sizeof(proxy_header),
- "PROXY %s %s %s %li %li\r\n",
- tcp_version,
- conn->data->info.conn_local_ip,
- conn->data->info.conn_primary_ip,
- conn->data->info.conn_local_port,
- conn->data->info.conn_primary_port);
+ msnprintf(proxy_header,
+ sizeof(proxy_header),
+ "PROXY %s %s %s %li %li\r\n",
+ tcp_version,
+ conn->data->info.conn_local_ip,
+ conn->data->info.conn_primary_ip,
+ conn->data->info.conn_local_port,
+ conn->data->info.conn_primary_port);
req_buffer = Curl_add_buffer_init();
if(!req_buffer)
@@ -1504,6 +1547,7 @@ static CURLcode add_haproxy_protocol_header(struct connectdata *conn)
return result;
}
+#endif
#ifdef USE_SSL
static CURLcode https_connecting(struct connectdata *conn, bool *done)
@@ -1547,20 +1591,6 @@ CURLcode Curl_http_done(struct connectdata *conn,
Curl_unencode_cleanup(conn);
-#ifdef USE_SPNEGO
- if(data->state.proxyneg.state == GSS_AUTHSENT ||
- data->state.negotiate.state == GSS_AUTHSENT) {
- /* add forbid re-use if http-code != 401/407 as a WA only needed for
- * 401/407 that signal auth failure (empty) otherwise state will be RECV
- * with current code.
- * Do not close CONNECT_ONLY connections. */
- if((data->req.httpcode != 401) && (data->req.httpcode != 407) &&
- !data->set.connect_only)
- streamclose(conn, "Negotiate transfer completed");
- Curl_cleanup_negotiate(data);
- }
-#endif
-
/* set the proper values (possibly modified on POST) */
conn->seek_func = data->set.seek_func; /* restore */
conn->seek_client = data->set.seek_client; /* restore */
@@ -1576,16 +1606,6 @@ CURLcode Curl_http_done(struct connectdata *conn,
Curl_mime_cleanpart(&http->form);
- switch(data->set.httpreq) {
- case HTTPREQ_PUT:
- case HTTPREQ_POST_FORM:
- case HTTPREQ_POST_MIME:
- data->req.bytecount = http->readbytecount + http->writebytecount;
- break;
- default:
- break;
- }
-
if(status)
return status;
@@ -1593,7 +1613,7 @@ CURLcode Curl_http_done(struct connectdata *conn,
entire operation is complete */
!conn->bits.retry &&
!data->set.connect_only &&
- (http->readbytecount +
+ (data->req.bytecount +
data->req.headerbytecount -
data->req.deductheadercount) <= 0) {
/* If this connection isn't simply closed to be retried, AND nothing was
@@ -1676,6 +1696,50 @@ enum proxy_use {
HEADER_CONNECT /* sending CONNECT to a proxy */
};
+/* used to compile the provided trailers into one buffer
+ will return an error code if one of the headers is
+ not formatted correctly */
+CURLcode Curl_http_compile_trailers(struct curl_slist *trailers,
+ Curl_send_buffer *buffer,
+ struct Curl_easy *handle)
+{
+ char *ptr = NULL;
+ CURLcode result = CURLE_OK;
+ const char *endofline_native = NULL;
+ const char *endofline_network = NULL;
+
+ if(
+#ifdef CURL_DO_LINEEND_CONV
+ (handle->set.prefer_ascii) ||
+#endif
+ (handle->set.crlf)) {
+ /* \n will become \r\n later on */
+ endofline_native = "\n";
+ endofline_network = "\x0a";
+ }
+ else {
+ endofline_native = "\r\n";
+ endofline_network = "\x0d\x0a";
+ }
+
+ while(trailers) {
+ /* only add correctly formatted trailers */
+ ptr = strchr(trailers->data, ':');
+ if(ptr && *(ptr + 1) == ' ') {
+ result = Curl_add_bufferf(&buffer, "%s%s", trailers->data,
+ endofline_native);
+ if(result)
+ return result;
+ }
+ else
+ infof(handle, "Malformatted trailing header ! Skipping trailer.");
+ trailers = trailers->next;
+ }
+ result = Curl_add_buffer(&buffer, endofline_network,
+ strlen(endofline_network));
+ return result;
+}
+
CURLcode Curl_add_custom_headers(struct connectdata *conn,
bool is_connect,
Curl_send_buffer *req_buffer)
@@ -1737,9 +1801,16 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
}
else {
if(*(--ptr) == ';') {
- /* send no-value custom header if terminated by semicolon */
- *ptr = ':';
- semicolonp = ptr;
+ /* copy the source */
+ semicolonp = strdup(headers->data);
+ if(!semicolonp) {
+ Curl_add_buffer_free(&req_buffer);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ /* put a colon where the semicolon is */
+ semicolonp[ptr - headers->data] = ':';
+ /* point at the colon */
+ optr = &semicolonp [ptr - headers->data];
}
}
ptr = optr;
@@ -1755,35 +1826,37 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
if(*ptr || semicolonp) {
/* only send this if the contents was non-blank or done special */
CURLcode result = CURLE_OK;
+ char *compare = semicolonp ? semicolonp : headers->data;
if(conn->allocptr.host &&
/* a Host: header was sent already, don't pass on any custom Host:
header as that will produce *two* in the same request! */
- checkprefix("Host:", headers->data))
+ checkprefix("Host:", compare))
;
else if(data->set.httpreq == HTTPREQ_POST_FORM &&
/* this header (extended by formdata.c) is sent later */
- checkprefix("Content-Type:", headers->data))
+ checkprefix("Content-Type:", compare))
;
else if(data->set.httpreq == HTTPREQ_POST_MIME &&
/* this header is sent later */
- checkprefix("Content-Type:", headers->data))
+ checkprefix("Content-Type:", compare))
;
else if(conn->bits.authneg &&
/* while doing auth neg, don't allow the custom length since
we will force length zero then */
- checkprefix("Content-Length:", headers->data))
+ checkprefix("Content-Length:", compare))
;
else if(conn->allocptr.te &&
/* when asking for Transfer-Encoding, don't pass on a custom
Connection: */
- checkprefix("Connection:", headers->data))
+ checkprefix("Connection:", compare))
;
else if((conn->httpversion == 20) &&
- checkprefix("Transfer-Encoding:", headers->data))
+ checkprefix("Transfer-Encoding:", compare))
/* HTTP/2 doesn't support chunked requests */
;
- else if(checkprefix("Authorization:", headers->data) &&
+ else if((checkprefix("Authorization:", compare) ||
+ checkprefix("Cookie:", compare)) &&
/* be careful of sending this potentially sensitive header to
other hosts */
(data->state.this_is_a_follow &&
@@ -1792,10 +1865,10 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
!strcasecompare(data->state.first_host, conn->host.name)))
;
else {
- result = Curl_add_bufferf(&req_buffer, "%s\r\n", headers->data);
+ result = Curl_add_bufferf(&req_buffer, "%s\r\n", compare);
}
if(semicolonp)
- *semicolonp = ';'; /* put back the semicolon */
+ free(semicolonp);
if(result)
return result;
}
@@ -1807,6 +1880,7 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
return CURLE_OK;
}
+#ifndef CURL_DISABLE_PARSEDATE
CURLcode Curl_add_timecondition(struct Curl_easy *data,
Curl_send_buffer *req_buffer)
{
@@ -1850,21 +1924,31 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data,
*/
/* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
- snprintf(datestr, sizeof(datestr),
- "%s: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
- condp,
- Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
- tm->tm_mday,
- Curl_month[tm->tm_mon],
- tm->tm_year + 1900,
- tm->tm_hour,
- tm->tm_min,
- tm->tm_sec);
+ msnprintf(datestr, sizeof(datestr),
+ "%s: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
+ condp,
+ Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
+ tm->tm_mday,
+ Curl_month[tm->tm_mon],
+ tm->tm_year + 1900,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec);
result = Curl_add_buffer(&req_buffer, datestr, strlen(datestr));
return result;
}
+#else
+/* disabled */
+CURLcode Curl_add_timecondition(struct Curl_easy *data,
+ Curl_send_buffer *req_buffer)
+{
+ (void)data;
+ (void)req_buffer;
+ return CURLE_OK;
+}
+#endif
/*
* Curl_http() gets called from the generic multi_do() function when a HTTP
@@ -1916,6 +2000,13 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
#ifdef USE_NGHTTP2
if(conn->data->set.httpversion ==
CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) {
+ if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
+ /* We don't support HTTP/2 proxies yet. Also it's debatable whether
+ or not this setting should apply to HTTP/2 proxies. */
+ infof(data, "Ignoring HTTP/2 prior knowledge due to proxy\n");
+ break;
+ }
+
DEBUGF(infof(data, "HTTP/2 over clean TCP\n"));
conn->httpversion = 20;
@@ -1947,7 +2038,6 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
data->state.first_remote_port = conn->remote_port;
}
- http->writebytecount = http->readbytecount = 0;
if((conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) &&
data->set.upload) {
@@ -1995,11 +2085,21 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
}
/* setup the authentication headers */
- result = Curl_http_output_auth(conn, request, path, FALSE);
- if(result)
- return result;
+ {
+ char *pq = NULL;
+ if(query && *query) {
+ pq = aprintf("%s?%s", path, query);
+ if(!pq)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ result = Curl_http_output_auth(conn, request, (pq ? pq : path), FALSE);
+ free(pq);
+ if(result)
+ return result;
+ }
- if((data->state.authhost.multipass || data->state.authproxy.multipass) &&
+ if(((data->state.authhost.multipass && !data->state.authhost.done)
+ || (data->state.authproxy.multipass && !data->state.authproxy.done)) &&
(httpreq != HTTPREQ_GET) &&
(httpreq != HTTPREQ_HEAD)) {
/* Auth is required and we are not authenticated yet. Make a PUT or POST
@@ -2084,6 +2184,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
http->sendit = NULL;
}
+#ifndef CURL_DISABLE_MIME
if(http->sendit) {
const char *cthdr = Curl_checkheaders(conn, "Content-Type");
@@ -2108,6 +2209,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
return result;
http->postsize = Curl_mime_size(http->sendit);
}
+#endif
ptr = Curl_checkheaders(conn, "Transfer-Encoding");
if(ptr) {
@@ -2293,8 +2395,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
if(!*data->state.up.path && path[strlen(path) - 1] != '/') {
*p++ = '/';
}
- snprintf(p, sizeof(ftp_typecode) - 1, ";type=%c",
- data->set.prefer_ascii ? 'a' : 'i');
+ msnprintf(p, sizeof(ftp_typecode) - 1, ";type=%c",
+ data->set.prefer_ascii ? 'a' : 'i');
}
}
if(conn->bits.user_passwd && !conn->bits.userpwd_in_url)
@@ -2635,9 +2737,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
failf(data, "Failed sending PUT request");
else
/* prepare for transfer */
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
- &http->readbytecount, postsize?FIRSTSOCKET:-1,
- postsize?&http->writebytecount:NULL);
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE,
+ postsize?FIRSTSOCKET:-1);
if(result)
return result;
break;
@@ -2657,12 +2758,11 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
failf(data, "Failed sending POST request");
else
/* setup variables for the upcoming transfer */
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
- -1, NULL);
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1);
break;
}
- postsize = http->postsize;
+ data->state.infilesize = postsize = http->postsize;
/* We only set Content-Length and allow a custom Content-Length if
we don't upload data chunked, as RFC2616 forbids us to set both
@@ -2678,6 +2778,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
return result;
}
+#ifndef CURL_DISABLE_MIME
/* Output mime-generated headers. */
{
struct curl_slist *hdr;
@@ -2688,6 +2789,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
return result;
}
}
+#endif
/* For really small posts we don't use Expect: headers at all, and for
the somewhat bigger ones we allow the app to disable it. Just make
@@ -2726,9 +2828,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
failf(data, "Failed sending POST request");
else
/* prepare for transfer */
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
- &http->readbytecount, postsize?FIRSTSOCKET:-1,
- postsize?&http->writebytecount:NULL);
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE,
+ postsize?FIRSTSOCKET:-1);
if(result)
return result;
@@ -2882,9 +2983,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
if(result)
failf(data, "Failed sending HTTP POST request");
else
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
- &http->readbytecount, http->postdata?FIRSTSOCKET:-1,
- http->postdata?&http->writebytecount:NULL);
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE,
+ http->postdata?FIRSTSOCKET:-1);
break;
default:
@@ -2900,33 +3000,30 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
failf(data, "Failed sending HTTP request");
else
/* HTTP GET/HEAD download: */
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
- http->postdata?FIRSTSOCKET:-1,
- http->postdata?&http->writebytecount:NULL);
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE,
+ http->postdata?FIRSTSOCKET:-1);
}
if(result)
return result;
- if(http->writebytecount) {
+ if(data->req.writebytecount) {
/* if a request-body has been sent off, we make sure this progress is noted
properly */
- Curl_pgrsSetUploadCounter(data, http->writebytecount);
+ Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
if(Curl_pgrsUpdate(conn))
result = CURLE_ABORTED_BY_CALLBACK;
- if(http->writebytecount >= postsize) {
+ if(data->req.writebytecount >= postsize) {
/* already sent the entire request body, mark the "upload" as
complete */
infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T
" out of %" CURL_FORMAT_CURL_OFF_T " bytes\n",
- http->writebytecount, postsize);
+ data->req.writebytecount, postsize);
data->req.upload_done = TRUE;
data->req.keepon &= ~KEEP_SEND; /* we're done writing */
data->req.exp100 = EXP100_SEND_DATA; /* already sent */
Curl_expire_done(data, EXPIRE_100_TIMEOUT);
}
- else
- data->req.writebytecount = http->writebytecount;
}
if((conn->httpversion == 20) && data->req.upload_chunky)
@@ -3161,6 +3258,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
k->header = FALSE;
k->badheader = HEADER_ALLBAD;
streamclose(conn, "bad HTTP: No end-of-message indicator");
+ if(!data->set.http09_allowed) {
+ failf(data, "Received HTTP/0.9 when not allowed\n");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
break;
}
}
@@ -3194,6 +3295,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
if(st == STATUS_BAD) {
streamclose(conn, "bad HTTP: No end-of-message indicator");
/* this is not the beginning of a protocol first header line */
+ if(!data->set.http09_allowed) {
+ failf(data, "Received HTTP/0.9 when not allowed\n");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
k->header = FALSE;
if(*nread)
/* since there's more, this is a partial bad header */
@@ -3306,14 +3411,31 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
#if defined(USE_NTLM)
if(conn->bits.close &&
(((data->req.httpcode == 401) &&
- (conn->ntlm.state == NTLMSTATE_TYPE2)) ||
+ (conn->http_ntlm_state == NTLMSTATE_TYPE2)) ||
((data->req.httpcode == 407) &&
- (conn->proxyntlm.state == NTLMSTATE_TYPE2)))) {
+ (conn->proxy_ntlm_state == NTLMSTATE_TYPE2)))) {
infof(data, "Connection closure while negotiating auth (HTTP 1.0?)\n");
data->state.authproblem = TRUE;
}
#endif
-
+#if defined(USE_SPNEGO)
+ if(conn->bits.close &&
+ (((data->req.httpcode == 401) &&
+ (conn->http_negotiate_state == GSS_AUTHRECV)) ||
+ ((data->req.httpcode == 407) &&
+ (conn->proxy_negotiate_state == GSS_AUTHRECV)))) {
+ infof(data, "Connection closure while negotiating auth (HTTP 1.0?)\n");
+ data->state.authproblem = TRUE;
+ }
+ if((conn->http_negotiate_state == GSS_AUTHDONE) &&
+ (data->req.httpcode != 401)) {
+ conn->http_negotiate_state = GSS_AUTHSUCC;
+ }
+ if((conn->proxy_negotiate_state == GSS_AUTHDONE) &&
+ (data->req.httpcode != 407)) {
+ conn->proxy_negotiate_state = GSS_AUTHSUCC;
+ }
+#endif
/*
* When all the headers have been parsed, see if we should give
* up and return an error.
@@ -3549,6 +3671,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
if(conn->httpversion != 20)
infof(data, "Lying server, not serving HTTP/2\n");
}
+ if(conn->httpversion < 20) {
+ conn->bundle->multiuse = BUNDLE_NO_MULTIUSE;
+ infof(data, "Mark bundle as not supporting multiuse\n");
+ }
}
else if(!nc) {
/* this is the real world, not a Nirvana
@@ -3586,7 +3712,6 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
conn->httpversion = 11; /* For us, RTSP acts like HTTP 1.1 */
}
else {
- /* TODO: do we care about the other cases here? */
nc = 0;
}
}
@@ -3639,26 +3764,12 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
}
else if(conn->httpversion >= 11 &&
!conn->bits.close) {
- /* If HTTP version is >= 1.1 and connection is persistent
- server supports pipelining. */
+ /* If HTTP version is >= 1.1 and connection is persistent */
DEBUGF(infof(data,
- "HTTP 1.1 or later with persistent connection, "
- "pipelining supported\n"));
- /* Activate pipelining if needed */
- if(conn->bundle) {
- if(!Curl_pipeline_site_blacklisted(data, conn))
- conn->bundle->multiuse = BUNDLE_PIPELINING;
- }
+ "HTTP 1.1 or later with persistent connection\n"));
}
switch(k->httpcode) {
- case 204:
- /* (quote from RFC2616, section 10.2.5): The server has
- * fulfilled the request but does not need to return an
- * entity-body ... The 204 response MUST NOT include a
- * message-body, and thus is always terminated by the first
- * empty line after the header fields. */
- /* FALLTHROUGH */
case 304:
/* (quote from RFC2616, section 10.3.5): The 304 response
* MUST NOT contain a message-body, and thus is always
@@ -3666,6 +3777,13 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
* fields. */
if(data->set.timecondition)
data->info.timecond = TRUE;
+ /* FALLTHROUGH */
+ case 204:
+ /* (quote from RFC2616, section 10.2.5): The server has
+ * fulfilled the request but does not need to return an
+ * entity-body ... The 204 response MUST NOT include a
+ * message-body, and thus is always terminated by the first
+ * empty line after the header fields. */
k->size = 0;
k->maxdownload = 0;
k->ignorecl = TRUE; /* ignore Content-Length headers */
@@ -3733,19 +3851,6 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
data->info.contenttype = contenttype;
}
}
- else if(checkprefix("Server:", k->p)) {
- if(conn->httpversion < 20) {
- /* only do this for non-h2 servers */
- char *server_name = Curl_copy_header_value(k->p);
-
- /* Turn off pipelining if the server version is blacklisted */
- if(conn->bundle && (conn->bundle->multiuse == BUNDLE_PIPELINING)) {
- if(Curl_pipeline_server_blacklisted(data, server_name))
- conn->bundle->multiuse = BUNDLE_NO_MULTIUSE;
- }
- free(server_name);
- }
- }
else if((conn->httpversion == 10) &&
conn->bits.httpproxy &&
Curl_compareheader(k->p,
@@ -3859,7 +3964,9 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
here, or else use real peer host name. */
conn->allocptr.cookiehost?
conn->allocptr.cookiehost:conn->host.name,
- data->state.up.path);
+ data->state.up.path,
+ (conn->handler->protocol&CURLPROTO_HTTPS)?
+ TRUE:FALSE);
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
}
#endif
@@ -3888,6 +3995,22 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
if(result)
return result;
}
+ #ifdef USE_SPNEGO
+ else if(checkprefix("Persistent-Auth", k->p)) {
+ struct negotiatedata *negdata = &conn->negotiate;
+ struct auth *authp = &data->state.authhost;
+ if(authp->picked == CURLAUTH_NEGOTIATE) {
+ char *persistentauth = Curl_copy_header_value(k->p);
+ if(!persistentauth)
+ return CURLE_OUT_OF_MEMORY;
+ negdata->noauthpersist = checkprefix("false", persistentauth);
+ negdata->havenoauthpersist = TRUE;
+ infof(data, "Negotiate: noauthpersist -> %d, header part: %s",
+ negdata->noauthpersist, persistentauth);
+ free(persistentauth);
+ }
+ }
+ #endif
else if((k->httpcode >= 300 && k->httpcode < 400) &&
checkprefix("Location:", k->p) &&
!data->req.location) {
@@ -3915,6 +4038,27 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
}
}
}
+#ifdef USE_ALTSVC
+ /* If enabled, the header is incoming and this is over HTTPS */
+ else if(data->asi && checkprefix("Alt-Svc:", k->p) &&
+ ((conn->handler->flags & PROTOPT_SSL) ||
+#ifdef CURLDEBUG
+ /* allow debug builds to circumvent the HTTPS restriction */
+ getenv("CURL_ALTSVC_HTTP")
+#else
+ 0
+#endif
+ )) {
+ /* the ALPN of the current request */
+ enum alpnid id = (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1;
+ result = Curl_altsvc_parse(data, data->asi,
+ &k->p[ strlen("Alt-Svc:") ],
+ id, conn->host.name,
+ curlx_uitous(conn->remote_port));
+ if(result)
+ return result;
+ }
+#endif
else if(conn->handler->protocol & CURLPROTO_RTSP) {
result = Curl_rtsp_parseheader(conn, k->p);
if(result)
diff --git a/Utilities/cmcurl/lib/http.h b/Utilities/cmcurl/lib/http.h
index 21fa701ab..a59fe7af0 100644
--- a/Utilities/cmcurl/lib/http.h
+++ b/Utilities/cmcurl/lib/http.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -65,7 +65,7 @@ CURLcode Curl_add_buffer(Curl_send_buffer **inp, const void *inptr,
size_t size) WARN_UNUSED_RESULT;
CURLcode Curl_add_buffer_send(Curl_send_buffer **inp,
struct connectdata *conn,
- long *bytes_written,
+ curl_off_t *bytes_written,
size_t included_body_bytes,
int socketindex);
@@ -74,6 +74,9 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data,
CURLcode Curl_add_custom_headers(struct connectdata *conn,
bool is_connect,
Curl_send_buffer *req_buffer);
+CURLcode Curl_http_compile_trailers(struct curl_slist *trailers,
+ Curl_send_buffer *buffer,
+ struct Curl_easy *handle);
/* protocol-specific functions set up to be called by the main engine */
CURLcode Curl_http(struct connectdata *conn, bool *done);
@@ -136,8 +139,6 @@ struct HTTP {
const char *p_pragma; /* Pragma: string */
const char *p_accept; /* Accept: string */
- curl_off_t readbytecount;
- curl_off_t writebytecount;
/* For FORM posting */
curl_mimepart form;
diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c
index 0c5f6db0b..8e7bc217e 100644
--- a/Utilities/cmcurl/lib/http2.c
+++ b/Utilities/cmcurl/lib/http2.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -111,8 +111,6 @@ static int http2_perform_getsock(const struct connectdata *conn,
int bitmap = GETSOCK_BLANK;
(void)numsocks;
- /* TODO We should check underlying socket state if it is SSL socket
- because of renegotiation. */
sock[0] = conn->sock[FIRSTSOCKET];
/* in a HTTP/2 connection we can basically always get a frame so we should
@@ -350,14 +348,14 @@ static const struct Curl_handler Curl_handler_http2_ssl = {
int Curl_http2_ver(char *p, size_t len)
{
nghttp2_info *h2 = nghttp2_version(0);
- return snprintf(p, len, " nghttp2/%s", h2->version_str);
+ return msnprintf(p, len, " nghttp2/%s", h2->version_str);
}
/* HTTP/2 error code to name based on the Error Code Registry.
https://tools.ietf.org/html/rfc7540#page-77
nghttp2_error_code enums are identical.
*/
-const char *Curl_http2_strerror(uint32_t err)
+static const char *http2_strerror(uint32_t err)
{
#ifndef NGHTTP2_HAS_HTTP2_STRERROR
const char *str[] = {
@@ -618,6 +616,18 @@ static int push_promise(struct Curl_easy *data,
return rv;
}
+/*
+ * multi_connchanged() is called to tell that there is a connection in
+ * this multi handle that has changed state (multiplexing become possible, the
+ * number of allowed streams changed or similar), and a subsequent use of this
+ * multi handle should move CONNECT_PEND handles back to CONNECT to have them
+ * retry.
+ */
+static void multi_connchanged(struct Curl_multi *multi)
+{
+ multi->recheckstate = TRUE;
+}
+
static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
void *userp)
{
@@ -650,7 +660,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
infof(conn->data,
"Connection state changed (MAX_CONCURRENT_STREAMS == %u)!\n",
httpc->settings.max_concurrent_streams);
- Curl_multi_connchanged(conn->data->multi);
+ multi_connchanged(conn->data->multi);
}
}
return 0;
@@ -800,7 +810,7 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
H2BUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
", stream %u\n",
len - nread, stream_id));
- data_s->easy_conn->proto.httpc.pause_stream_id = stream_id;
+ data_s->conn->proto.httpc.pause_stream_id = stream_id;
return NGHTTP2_ERR_PAUSE;
}
@@ -808,7 +818,7 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
/* pause execution of nghttp2 if we received data for another handle
in order to process them first. */
if(conn->data != data_s) {
- data_s->easy_conn->proto.httpc.pause_stream_id = stream_id;
+ data_s->conn->proto.httpc.pause_stream_id = stream_id;
return NGHTTP2_ERR_PAUSE;
}
@@ -837,7 +847,7 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
return 0;
}
H2BUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u\n",
- Curl_http2_strerror(error_code), error_code, stream_id));
+ http2_strerror(error_code), error_code, stream_id));
stream = data_s->req.protop;
if(!stream)
return NGHTTP2_ERR_CALLBACK_FAILURE;
@@ -854,6 +864,10 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
stream_id);
DEBUGASSERT(0);
}
+ if(stream_id == httpc->pause_stream_id) {
+ H2BUGF(infof(data_s, "Stopped the pause stream!\n"));
+ httpc->pause_stream_id = 0;
+ }
H2BUGF(infof(data_s, "Removed stream %u hash!\n", stream_id));
stream->stream_id = 0; /* cleared */
}
@@ -953,6 +967,28 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
char *h;
+ if(!strcmp(":authority", (const char *)name)) {
+ /* pseudo headers are lower case */
+ int rc = 0;
+ char *check = aprintf("%s:%d", conn->host.name, conn->remote_port);
+ if(!check)
+ /* no memory */
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ if(!Curl_strcasecompare(check, (const char *)value)) {
+ /* This is push is not for the same authority that was asked for in
+ * the URL. RFC 7540 section 8.2 says: "A client MUST treat a
+ * PUSH_PROMISE for which the server is not authoritative as a stream
+ * error of type PROTOCOL_ERROR."
+ */
+ (void)nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
+ stream_id, NGHTTP2_PROTOCOL_ERROR);
+ rc = NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
+ free(check);
+ if(rc)
+ return rc;
+ }
+
if(!stream->push_headers) {
stream->push_headers_alloc = 10;
stream->push_headers = malloc(stream->push_headers_alloc *
@@ -1193,7 +1229,7 @@ void Curl_http2_done(struct connectdata *conn, bool premature)
/*
* Initialize nghttp2 for a Curl connection
*/
-CURLcode Curl_http2_init(struct connectdata *conn)
+static CURLcode http2_init(struct connectdata *conn)
{
if(!conn->proto.httpc.h2) {
int rc;
@@ -1427,7 +1463,7 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn,
}
else if(httpc->error_code != NGHTTP2_NO_ERROR) {
failf(data, "HTTP/2 stream %d was not closed cleanly: %s (err %u)",
- stream->stream_id, Curl_http2_strerror(httpc->error_code),
+ stream->stream_id, http2_strerror(httpc->error_code),
httpc->error_code);
*err = CURLE_HTTP2_STREAM;
return -1;
@@ -1809,9 +1845,9 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
const void *mem, size_t len, CURLcode *err)
{
/*
- * BIG TODO: Currently, we send request in this function, but this
- * function is also used to send request body. It would be nice to
- * add dedicated function for request.
+ * Currently, we send request in this function, but this function is also
+ * used to send request body. It would be nice to add dedicated function for
+ * request.
*/
int rv;
struct http_conn *httpc = &conn->proto.httpc;
@@ -2137,7 +2173,7 @@ CURLcode Curl_http2_setup(struct connectdata *conn)
else
conn->handler = &Curl_handler_http2;
- result = Curl_http2_init(conn);
+ result = http2_init(conn);
if(result) {
Curl_add_buffer_free(&stream->header_recvbuf);
return result;
@@ -2159,7 +2195,7 @@ CURLcode Curl_http2_setup(struct connectdata *conn)
conn->bundle->multiuse = BUNDLE_MULTIPLEX;
infof(conn->data, "Connection state changed (HTTP/2 confirmed)\n");
- Curl_multi_connchanged(conn->data->multi);
+ multi_connchanged(conn->data->multi);
return CURLE_OK;
}
@@ -2367,6 +2403,14 @@ void Curl_http2_cleanup_dependencies(struct Curl_easy *data)
Curl_http2_remove_child(data->set.stream_depends_on, data);
}
+/* Only call this function for a transfer that already got a HTTP/2
+ CURLE_HTTP2_STREAM error! */
+bool Curl_h2_http_1_1_error(struct connectdata *conn)
+{
+ struct http_conn *httpc = &conn->proto.httpc;
+ return (httpc->error_code == NGHTTP2_HTTP_1_1_REQUIRED);
+}
+
#else /* !USE_NGHTTP2 */
/* Satisfy external references even if http2 is not compiled in. */
diff --git a/Utilities/cmcurl/lib/http2.h b/Utilities/cmcurl/lib/http2.h
index 4492ec211..db6217b11 100644
--- a/Utilities/cmcurl/lib/http2.h
+++ b/Utilities/cmcurl/lib/http2.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -59,8 +59,10 @@ CURLcode Curl_http2_add_child(struct Curl_easy *parent,
void Curl_http2_remove_child(struct Curl_easy *parent,
struct Curl_easy *child);
void Curl_http2_cleanup_dependencies(struct Curl_easy *data);
+
+/* returns true if the HTTP/2 stream error was HTTP_1_1_REQUIRED */
+bool Curl_h2_http_1_1_error(struct connectdata *conn);
#else /* USE_NGHTTP2 */
-#define Curl_http2_init(x) CURLE_UNSUPPORTED_PROTOCOL
#define Curl_http2_send_request(x) CURLE_UNSUPPORTED_PROTOCOL
#define Curl_http2_request_upgrade(x,y) CURLE_UNSUPPORTED_PROTOCOL
#define Curl_http2_setup(x) CURLE_UNSUPPORTED_PROTOCOL
@@ -74,6 +76,7 @@ void Curl_http2_cleanup_dependencies(struct Curl_easy *data);
#define Curl_http2_add_child(x, y, z)
#define Curl_http2_remove_child(x, y)
#define Curl_http2_cleanup_dependencies(x)
+#define Curl_h2_http_1_1_error(x) 0
#endif
#endif /* HEADER_CURL_HTTP2_H */
diff --git a/Utilities/cmcurl/lib/http_digest.c b/Utilities/cmcurl/lib/http_digest.c
index e2d865b0a..9616c30ed 100644
--- a/Utilities/cmcurl/lib/http_digest.c
+++ b/Utilities/cmcurl/lib/http_digest.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -28,6 +28,7 @@
#include "strcase.h"
#include "vauth/vauth.h"
#include "http_digest.h"
+
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@@ -171,7 +172,7 @@ CURLcode Curl_output_digest(struct connectdata *conn,
return CURLE_OK;
}
-void Curl_digest_cleanup(struct Curl_easy *data)
+void Curl_http_auth_cleanup_digest(struct Curl_easy *data)
{
Curl_auth_digest_cleanup(&data->state.digest);
Curl_auth_digest_cleanup(&data->state.proxydigest);
diff --git a/Utilities/cmcurl/lib/http_digest.h b/Utilities/cmcurl/lib/http_digest.h
index fd225c7c1..73410ae88 100644
--- a/Utilities/cmcurl/lib/http_digest.h
+++ b/Utilities/cmcurl/lib/http_digest.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -23,6 +23,8 @@
***************************************************************************/
#include "curl_setup.h"
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+
/* this is for digest header input */
CURLcode Curl_input_digest(struct connectdata *conn,
bool proxy, const char *header);
@@ -33,10 +35,8 @@ CURLcode Curl_output_digest(struct connectdata *conn,
const unsigned char *request,
const unsigned char *uripath);
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
-void Curl_digest_cleanup(struct Curl_easy *data);
-#else
-#define Curl_digest_cleanup(x) Curl_nop_stmt
-#endif
+void Curl_http_auth_cleanup_digest(struct Curl_easy *data);
+
+#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_CRYPTO_AUTH */
#endif /* HEADER_CURL_HTTP_DIGEST_H */
diff --git a/Utilities/cmcurl/lib/http_negotiate.c b/Utilities/cmcurl/lib/http_negotiate.c
index ddcd65b3b..c8f406444 100644
--- a/Utilities/cmcurl/lib/http_negotiate.c
+++ b/Utilities/cmcurl/lib/http_negotiate.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -49,6 +49,7 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy,
/* Point to the correct struct with this */
struct negotiatedata *neg_ctx;
+ curlnegotiate state;
if(proxy) {
userp = conn->http_proxy.user;
@@ -56,7 +57,8 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy,
service = data->set.str[STRING_PROXY_SERVICE_NAME] ?
data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP";
host = conn->http_proxy.host.name;
- neg_ctx = &data->state.proxyneg;
+ neg_ctx = &conn->proxyneg;
+ state = conn->proxy_negotiate_state;
}
else {
userp = conn->user;
@@ -64,7 +66,8 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy,
service = data->set.str[STRING_SERVICE_NAME] ?
data->set.str[STRING_SERVICE_NAME] : "HTTP";
host = conn->host.name;
- neg_ctx = &data->state.negotiate;
+ neg_ctx = &conn->negotiate;
+ state = conn->http_negotiate_state;
}
/* Not set means empty */
@@ -80,59 +83,138 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy,
header++;
len = strlen(header);
+ neg_ctx->havenegdata = len != 0;
if(!len) {
- /* Is this the first call in a new negotiation? */
- if(neg_ctx->context) {
- /* The server rejected our authentication and hasn't suppled any more
+ if(state == GSS_AUTHSUCC) {
+ infof(conn->data, "Negotiate auth restarted\n");
+ Curl_http_auth_cleanup_negotiate(conn);
+ }
+ else if(state != GSS_AUTHNONE) {
+ /* The server rejected our authentication and hasn't supplied any more
negotiation mechanisms */
+ Curl_http_auth_cleanup_negotiate(conn);
return CURLE_LOGIN_DENIED;
}
}
+ /* Supports SSL channel binding for Windows ISS extended protection */
+#if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS)
+ neg_ctx->sslContext = conn->sslContext;
+#endif
+
/* Initialize the security context and decode our challenge */
result = Curl_auth_decode_spnego_message(data, userp, passwdp, service,
host, header, neg_ctx);
if(result)
- Curl_auth_spnego_cleanup(neg_ctx);
+ Curl_http_auth_cleanup_negotiate(conn);
return result;
}
CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy)
{
- struct negotiatedata *neg_ctx = proxy ? &conn->data->state.proxyneg :
- &conn->data->state.negotiate;
+ struct negotiatedata *neg_ctx = proxy ? &conn->proxyneg :
+ &conn->negotiate;
+ struct auth *authp = proxy ? &conn->data->state.authproxy :
+ &conn->data->state.authhost;
+ curlnegotiate *state = proxy ? &conn->proxy_negotiate_state :
+ &conn->http_negotiate_state;
char *base64 = NULL;
size_t len = 0;
char *userp;
CURLcode result;
- result = Curl_auth_create_spnego_message(conn->data, neg_ctx, &base64, &len);
- if(result)
- return result;
+ authp->done = FALSE;
- userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "",
- base64);
+ if(*state == GSS_AUTHRECV) {
+ if(neg_ctx->havenegdata) {
+ neg_ctx->havemultiplerequests = TRUE;
+ }
+ }
+ else if(*state == GSS_AUTHSUCC) {
+ if(!neg_ctx->havenoauthpersist) {
+ neg_ctx->noauthpersist = !neg_ctx->havemultiplerequests;
+ }
+ }
- if(proxy) {
- Curl_safefree(conn->allocptr.proxyuserpwd);
- conn->allocptr.proxyuserpwd = userp;
+ if(neg_ctx->noauthpersist ||
+ (*state != GSS_AUTHDONE && *state != GSS_AUTHSUCC)) {
+
+ if(neg_ctx->noauthpersist && *state == GSS_AUTHSUCC) {
+ infof(conn->data, "Curl_output_negotiate, "
+ "no persistent authentication: cleanup existing context");
+ Curl_http_auth_cleanup_negotiate(conn);
+ }
+ if(!neg_ctx->context) {
+ result = Curl_input_negotiate(conn, proxy, "Negotiate");
+ if(result == CURLE_LOGIN_DENIED) {
+ /* negotiate auth failed, let's continue unauthenticated to stay
+ * compatible with the behavior before curl-7_64_0-158-g6c6035532 */
+ conn->data->state.authproblem = TRUE;
+ return CURLE_OK;
+ }
+ else if(result)
+ return result;
+ }
+
+ result = Curl_auth_create_spnego_message(conn->data,
+ neg_ctx, &base64, &len);
+ if(result)
+ return result;
+
+ userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "",
+ base64);
+
+ if(proxy) {
+ Curl_safefree(conn->allocptr.proxyuserpwd);
+ conn->allocptr.proxyuserpwd = userp;
+ }
+ else {
+ Curl_safefree(conn->allocptr.userpwd);
+ conn->allocptr.userpwd = userp;
+ }
+
+ free(base64);
+
+ if(userp == NULL) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ *state = GSS_AUTHSENT;
+ #ifdef HAVE_GSSAPI
+ if(neg_ctx->status == GSS_S_COMPLETE ||
+ neg_ctx->status == GSS_S_CONTINUE_NEEDED) {
+ *state = GSS_AUTHDONE;
+ }
+ #else
+ #ifdef USE_WINDOWS_SSPI
+ if(neg_ctx->status == SEC_E_OK ||
+ neg_ctx->status == SEC_I_CONTINUE_NEEDED) {
+ *state = GSS_AUTHDONE;
+ }
+ #endif
+ #endif
}
- else {
- Curl_safefree(conn->allocptr.userpwd);
- conn->allocptr.userpwd = userp;
+
+ if(*state == GSS_AUTHDONE || *state == GSS_AUTHSUCC) {
+ /* connection is already authenticated,
+ * don't send a header in future requests */
+ authp->done = TRUE;
}
- free(base64);
+ neg_ctx->havenegdata = FALSE;
- return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK;
+ return CURLE_OK;
}
-void Curl_cleanup_negotiate(struct Curl_easy *data)
+void Curl_http_auth_cleanup_negotiate(struct connectdata *conn)
{
- Curl_auth_spnego_cleanup(&data->state.negotiate);
- Curl_auth_spnego_cleanup(&data->state.proxyneg);
+ conn->http_negotiate_state = GSS_AUTHNONE;
+ conn->proxy_negotiate_state = GSS_AUTHNONE;
+
+ Curl_auth_cleanup_spnego(&conn->negotiate);
+ Curl_auth_cleanup_spnego(&conn->proxyneg);
}
#endif /* !CURL_DISABLE_HTTP && USE_SPNEGO */
diff --git a/Utilities/cmcurl/lib/http_negotiate.h b/Utilities/cmcurl/lib/http_negotiate.h
index c64e54825..4f0ac1686 100644
--- a/Utilities/cmcurl/lib/http_negotiate.h
+++ b/Utilities/cmcurl/lib/http_negotiate.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -22,7 +22,7 @@
*
***************************************************************************/
-#ifdef USE_SPNEGO
+#if !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO)
/* this is for Negotiate header input */
CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy,
@@ -31,8 +31,8 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy,
/* this is for creating Negotiate header output */
CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy);
-void Curl_cleanup_negotiate(struct Curl_easy *data);
+void Curl_http_auth_cleanup_negotiate(struct connectdata *conn);
-#endif /* USE_SPNEGO */
+#endif /* !CURL_DISABLE_HTTP && USE_SPNEGO */
#endif /* HEADER_CURL_HTTP_NEGOTIATE_H */
diff --git a/Utilities/cmcurl/lib/http_ntlm.c b/Utilities/cmcurl/lib/http_ntlm.c
index a9b33f98e..e4a4fe05d 100644
--- a/Utilities/cmcurl/lib/http_ntlm.c
+++ b/Utilities/cmcurl/lib/http_ntlm.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -68,9 +68,11 @@ CURLcode Curl_input_ntlm(struct connectdata *conn,
{
/* point to the correct struct with this */
struct ntlmdata *ntlm;
+ curlntlm *state;
CURLcode result = CURLE_OK;
ntlm = proxy ? &conn->proxyntlm : &conn->ntlm;
+ state = proxy ? &conn->proxy_ntlm_state : &conn->http_ntlm_state;
if(checkprefix("NTLM", header)) {
header += strlen("NTLM");
@@ -83,25 +85,25 @@ CURLcode Curl_input_ntlm(struct connectdata *conn,
if(result)
return result;
- ntlm->state = NTLMSTATE_TYPE2; /* We got a type-2 message */
+ *state = NTLMSTATE_TYPE2; /* We got a type-2 message */
}
else {
- if(ntlm->state == NTLMSTATE_LAST) {
+ if(*state == NTLMSTATE_LAST) {
infof(conn->data, "NTLM auth restarted\n");
- Curl_http_ntlm_cleanup(conn);
+ Curl_http_auth_cleanup_ntlm(conn);
}
- else if(ntlm->state == NTLMSTATE_TYPE3) {
+ else if(*state == NTLMSTATE_TYPE3) {
infof(conn->data, "NTLM handshake rejected\n");
- Curl_http_ntlm_cleanup(conn);
- ntlm->state = NTLMSTATE_NONE;
+ Curl_http_auth_cleanup_ntlm(conn);
+ *state = NTLMSTATE_NONE;
return CURLE_REMOTE_ACCESS_DENIED;
}
- else if(ntlm->state >= NTLMSTATE_TYPE1) {
+ else if(*state >= NTLMSTATE_TYPE1) {
infof(conn->data, "NTLM handshake failure (internal error)\n");
return CURLE_REMOTE_ACCESS_DENIED;
}
- ntlm->state = NTLMSTATE_TYPE1; /* We should send away a type-1 */
+ *state = NTLMSTATE_TYPE1; /* We should send away a type-1 */
}
}
@@ -129,6 +131,7 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy)
/* point to the correct struct with this */
struct ntlmdata *ntlm;
+ curlntlm *state;
struct auth *authp;
DEBUGASSERT(conn);
@@ -147,6 +150,7 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy)
conn->data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP";
hostname = conn->http_proxy.host.name;
ntlm = &conn->proxyntlm;
+ state = &conn->proxy_ntlm_state;
authp = &conn->data->state.authproxy;
}
else {
@@ -157,6 +161,7 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy)
conn->data->set.str[STRING_SERVICE_NAME] : "HTTP";
hostname = conn->host.name;
ntlm = &conn->ntlm;
+ state = &conn->http_ntlm_state;
authp = &conn->data->state.authhost;
}
authp->done = FALSE;
@@ -175,9 +180,12 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy)
if(s_hSecDll == NULL)
return err;
}
+#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
+ ntlm->sslContext = conn->sslContext;
+#endif
#endif
- switch(ntlm->state) {
+ switch(*state) {
case NTLMSTATE_TYPE1:
default: /* for the weird cases we (re)start here */
/* Create a type-1 message */
@@ -219,7 +227,7 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy)
DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd));
- ntlm->state = NTLMSTATE_TYPE3; /* we send a type-3 */
+ *state = NTLMSTATE_TYPE3; /* we send a type-3 */
authp->done = TRUE;
}
break;
@@ -227,7 +235,7 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy)
case NTLMSTATE_TYPE3:
/* connection is already authenticated,
* don't send a header in future requests */
- ntlm->state = NTLMSTATE_LAST;
+ *state = NTLMSTATE_LAST;
/* FALLTHROUGH */
case NTLMSTATE_LAST:
Curl_safefree(*allocuserpwd);
@@ -238,13 +246,13 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy)
return CURLE_OK;
}
-void Curl_http_ntlm_cleanup(struct connectdata *conn)
+void Curl_http_auth_cleanup_ntlm(struct connectdata *conn)
{
- Curl_auth_ntlm_cleanup(&conn->ntlm);
- Curl_auth_ntlm_cleanup(&conn->proxyntlm);
+ Curl_auth_cleanup_ntlm(&conn->ntlm);
+ Curl_auth_cleanup_ntlm(&conn->proxyntlm);
#if defined(NTLM_WB_ENABLED)
- Curl_ntlm_wb_cleanup(conn);
+ Curl_http_auth_cleanup_ntlm_wb(conn);
#endif
}
diff --git a/Utilities/cmcurl/lib/http_ntlm.h b/Utilities/cmcurl/lib/http_ntlm.h
index d186bbe37..003714dbd 100644
--- a/Utilities/cmcurl/lib/http_ntlm.h
+++ b/Utilities/cmcurl/lib/http_ntlm.h
@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_NTLM_H
-#define HEADER_CURL_NTLM_H
+#ifndef HEADER_CURL_HTTP_NTLM_H
+#define HEADER_CURL_HTTP_NTLM_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -33,8 +33,8 @@ CURLcode Curl_input_ntlm(struct connectdata *conn, bool proxy,
/* this is for creating ntlm header output */
CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy);
-void Curl_http_ntlm_cleanup(struct connectdata *conn);
+void Curl_http_auth_cleanup_ntlm(struct connectdata *conn);
#endif /* !CURL_DISABLE_HTTP && USE_NTLM */
-#endif /* HEADER_CURL_NTLM_H */
+#endif /* HEADER_CURL_HTTP_NTLM_H */
diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c
index 2e0d92edd..d7ed11761 100644
--- a/Utilities/cmcurl/lib/http_proxy.c
+++ b/Utilities/cmcurl/lib/http_proxy.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -643,7 +643,7 @@ static CURLcode CONNECT(struct connectdata *conn,
void Curl_connect_free(struct Curl_easy *data)
{
- struct connectdata *conn = data->easy_conn;
+ struct connectdata *conn = data->conn;
struct http_connect_state *s = conn->connect_state;
if(s) {
free(s);
diff --git a/Utilities/cmcurl/lib/if2ip.c b/Utilities/cmcurl/lib/if2ip.c
index ce38ea117..d003de678 100644
--- a/Utilities/cmcurl/lib/if2ip.c
+++ b/Utilities/cmcurl/lib/if2ip.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -96,26 +96,8 @@ unsigned int Curl_ipv6_scope(const struct sockaddr *sa)
#if defined(HAVE_GETIFADDRS)
-bool Curl_if_is_interface_name(const char *interf)
-{
- bool result = FALSE;
-
- struct ifaddrs *iface, *head;
-
- if(getifaddrs(&head) >= 0) {
- for(iface = head; iface != NULL; iface = iface->ifa_next) {
- if(strcasecompare(iface->ifa_name, interf)) {
- result = TRUE;
- break;
- }
- }
- freeifaddrs(head);
- }
- return result;
-}
-
if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
- unsigned int remote_scope_id, const char *interf,
+ unsigned int local_scope_id, const char *interf,
char *buf, int buf_size)
{
struct ifaddrs *iface, *head;
@@ -127,7 +109,7 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
#if !defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) || \
!defined(ENABLE_IPV6)
- (void) remote_scope_id;
+ (void) local_scope_id;
#endif
if(getifaddrs(&head) >= 0) {
@@ -141,7 +123,9 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
char ipstr[64];
#ifdef ENABLE_IPV6
if(af == AF_INET6) {
+#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
unsigned int scopeid = 0;
+#endif
unsigned int ifscope = Curl_ipv6_scope(iface->ifa_addr);
if(ifscope != remote_scope) {
@@ -161,15 +145,16 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
->sin6_scope_id;
/* If given, scope id should match. */
- if(remote_scope_id && scopeid != remote_scope_id) {
+ if(local_scope_id && scopeid != local_scope_id) {
if(res == IF2IP_NOT_FOUND)
res = IF2IP_AF_NOT_SUPPORTED;
continue;
}
-#endif
+
if(scopeid)
- snprintf(scope, sizeof(scope), "%%%u", scopeid);
+ msnprintf(scope, sizeof(scope), "%%%u", scopeid);
+#endif
}
else
#endif
@@ -177,7 +162,7 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
&((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr;
res = IF2IP_FOUND;
ip = (char *) Curl_inet_ntop(af, addr, ipstr, sizeof(ipstr));
- snprintf(buf, buf_size, "%s%s", ip, scope);
+ msnprintf(buf, buf_size, "%s%s", ip, scope);
break;
}
}
@@ -196,17 +181,8 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
#elif defined(HAVE_IOCTL_SIOCGIFADDR)
-bool Curl_if_is_interface_name(const char *interf)
-{
- /* This is here just to support the old interfaces */
- char buf[256];
-
- return (Curl_if2ip(AF_INET, 0 /* unused */, 0, interf, buf, sizeof(buf)) ==
- IF2IP_NOT_FOUND) ? FALSE : TRUE;
-}
-
if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
- unsigned int remote_scope_id, const char *interf,
+ unsigned int local_scope_id, const char *interf,
char *buf, int buf_size)
{
struct ifreq req;
@@ -216,7 +192,7 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
size_t len;
(void)remote_scope;
- (void)remote_scope_id;
+ (void)local_scope_id;
if(!interf || (af != AF_INET))
return IF2IP_NOT_FOUND;
@@ -251,20 +227,13 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
#else
-bool Curl_if_is_interface_name(const char *interf)
-{
- (void) interf;
-
- return FALSE;
-}
-
if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
- unsigned int remote_scope_id, const char *interf,
+ unsigned int local_scope_id, const char *interf,
char *buf, int buf_size)
{
(void) af;
(void) remote_scope;
- (void) remote_scope_id;
+ (void) local_scope_id;
(void) interf;
(void) buf;
(void) buf_size;
diff --git a/Utilities/cmcurl/lib/if2ip.h b/Utilities/cmcurl/lib/if2ip.h
index a90e66216..f193d4257 100644
--- a/Utilities/cmcurl/lib/if2ip.h
+++ b/Utilities/cmcurl/lib/if2ip.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -32,8 +32,6 @@
unsigned int Curl_ipv6_scope(const struct sockaddr *sa);
-bool Curl_if_is_interface_name(const char *interf);
-
typedef enum {
IF2IP_NOT_FOUND = 0, /* Interface not found */
IF2IP_AF_NOT_SUPPORTED = 1, /* Int. exists but has no address for this af */
@@ -41,7 +39,7 @@ typedef enum {
} if2ip_result_t;
if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
- unsigned int remote_scope_id, const char *interf,
+ unsigned int local_scope_id, const char *interf,
char *buf, int buf_size);
#ifdef __INTERIX
diff --git a/Utilities/cmcurl/lib/imap.c b/Utilities/cmcurl/lib/imap.c
index 3ef89097f..bdcc69c67 100644
--- a/Utilities/cmcurl/lib/imap.c
+++ b/Utilities/cmcurl/lib/imap.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -28,6 +28,7 @@
* RFC4959 IMAP Extension for SASL Initial Client Response
* RFC5092 IMAP URL Scheme
* RFC6749 OAuth 2.0 Authorization Framework
+ * RFC8314 Use of TLS for Email Submission and Access
* Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
*
***************************************************************************/
@@ -316,7 +317,7 @@ static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
a space and optionally some text as per RFC-3501 for the AUTHENTICATE and
APPEND commands and as outlined in Section 4. Examples of RFC-4959 but
some e-mail servers ignore this and only send a single + instead. */
- if(imap && !imap->custom && ((len == 3 && !memcmp("+", line, 1)) ||
+ if(imap && !imap->custom && ((len == 3 && line[0] == '+') ||
(len >= 2 && !memcmp("+ ", line, 2)))) {
switch(imapc->state) {
/* States which are interested in continuation responses */
@@ -1042,7 +1043,7 @@ static CURLcode imap_state_listsearch_resp(struct connectdata *conn,
line[len] = '\0';
}
else if(imapcode != IMAP_RESP_OK)
- result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
+ result = CURLE_QUOTE_ERROR;
else
/* End of DO phase */
state(conn, IMAP_STOP);
@@ -1114,7 +1115,7 @@ static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
if(imapcode != '*') {
Curl_pgrsSetDownloadSize(data, -1);
state(conn, IMAP_STOP);
- return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
+ return CURLE_REMOTE_FILE_NOT_FOUND;
}
/* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
@@ -1177,11 +1178,11 @@ static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
if(data->req.bytecount == size)
/* The entire data is already transferred! */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
else {
/* IMAP download */
data->req.maxdownload = size;
- Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(data, FIRSTSOCKET, size, FALSE, -1);
}
}
else {
@@ -1231,7 +1232,7 @@ static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
Curl_pgrsSetUploadSize(data, data->state.infilesize);
/* IMAP upload */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
+ Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
/* End of DO phase */
state(conn, IMAP_STOP);
@@ -1362,19 +1363,20 @@ static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
return result;
}
- result = Curl_pp_statemach(&imapc->pp, FALSE);
+ result = Curl_pp_statemach(&imapc->pp, FALSE, FALSE);
*done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
return result;
}
-static CURLcode imap_block_statemach(struct connectdata *conn)
+static CURLcode imap_block_statemach(struct connectdata *conn,
+ bool disconnecting)
{
CURLcode result = CURLE_OK;
struct imap_conn *imapc = &conn->proto.imapc;
while(imapc->state != IMAP_STOP && !result)
- result = Curl_pp_statemach(&imapc->pp, TRUE);
+ result = Curl_pp_statemach(&imapc->pp, TRUE, disconnecting);
return result;
}
@@ -1490,14 +1492,9 @@ static CURLcode imap_done(struct connectdata *conn, CURLcode status,
state(conn, IMAP_APPEND_FINAL);
}
- /* Run the state-machine
-
- TODO: when the multi interface is used, this _really_ should be using
- the imap_multi_statemach function but we have no general support for
- non-blocking DONE operations!
- */
+ /* Run the state-machine */
if(!result)
- result = imap_block_statemach(conn);
+ result = imap_block_statemach(conn, FALSE);
}
/* Cleanup our per-request based variables */
@@ -1635,7 +1632,7 @@ static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
point! */
if(!dead_connection && imapc->pp.conn && imapc->pp.conn->bits.protoconnstart)
if(!imap_perform_logout(conn))
- (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
+ (void)imap_block_statemach(conn, TRUE); /* ignore errors on LOGOUT */
/* Disconnect from the server */
Curl_pp_disconnect(&imapc->pp);
@@ -1659,7 +1656,7 @@ static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
if(imap->transfer != FTPTRANSFER_BODY)
/* no data to transfer */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(conn->data, -1, -1, FALSE, -1);
return CURLE_OK;
}
@@ -1749,8 +1746,8 @@ static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
imapc->cmdid = (imapc->cmdid + 1) % 1000;
/* Calculate the tag based on the connection ID and command ID */
- snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
- 'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid);
+ msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
+ 'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid);
/* Prefix the format with the tag */
taggedfmt = aprintf("%s %s", imapc->resptag, fmt);
@@ -1793,7 +1790,7 @@ static char *imap_atom(const char *str, bool escape_only)
return NULL;
/* Look for "atom-specials", counting the backslash and quote characters as
- these will need escapping */
+ these will need escaping */
p1 = str;
while(*p1) {
if(*p1 == '\\')
diff --git a/Utilities/cmcurl/lib/inet_ntop.c b/Utilities/cmcurl/lib/inet_ntop.c
index ac5d2d4d6..855981c66 100644
--- a/Utilities/cmcurl/lib/inet_ntop.c
+++ b/Utilities/cmcurl/lib/inet_ntop.c
@@ -55,11 +55,11 @@ static char *inet_ntop4 (const unsigned char *src, char *dst, size_t size)
DEBUGASSERT(size >= 16);
tmp[0] = '\0';
- (void)snprintf(tmp, sizeof(tmp), "%d.%d.%d.%d",
- ((int)((unsigned char)src[0])) & 0xff,
- ((int)((unsigned char)src[1])) & 0xff,
- ((int)((unsigned char)src[2])) & 0xff,
- ((int)((unsigned char)src[3])) & 0xff);
+ (void)msnprintf(tmp, sizeof(tmp), "%d.%d.%d.%d",
+ ((int)((unsigned char)src[0])) & 0xff,
+ ((int)((unsigned char)src[1])) & 0xff,
+ ((int)((unsigned char)src[2])) & 0xff,
+ ((int)((unsigned char)src[3])) & 0xff);
len = strlen(tmp);
if(len == 0 || len >= size) {
@@ -148,7 +148,7 @@ static char *inet_ntop6 (const unsigned char *src, char *dst, size_t size)
tp += strlen(tp);
break;
}
- tp += snprintf(tp, 5, "%lx", words[i]);
+ tp += msnprintf(tp, 5, "%lx", words[i]);
}
/* Was it a trailing run of 0x00's?
diff --git a/Utilities/cmcurl/lib/inet_pton.c b/Utilities/cmcurl/lib/inet_pton.c
index fef9610d1..0d65ae0ec 100644
--- a/Utilities/cmcurl/lib/inet_pton.c
+++ b/Utilities/cmcurl/lib/inet_pton.c
@@ -153,7 +153,7 @@ inet_pton6(const char *src, unsigned char *dst)
static const char xdigits_l[] = "0123456789abcdef",
xdigits_u[] = "0123456789ABCDEF";
unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
- const char *xdigits, *curtok;
+ const char *curtok;
int ch, saw_xdigit;
size_t val;
@@ -168,6 +168,7 @@ inet_pton6(const char *src, unsigned char *dst)
saw_xdigit = 0;
val = 0;
while((ch = *src++) != '\0') {
+ const char *xdigits;
const char *pch;
pch = strchr((xdigits = xdigits_l), ch);
diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c
index ceaa71d08..fd31faa3e 100644
--- a/Utilities/cmcurl/lib/ldap.c
+++ b/Utilities/cmcurl/lib/ldap.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -744,7 +744,7 @@ quit:
#endif
/* no data to transfer */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
connclose(conn, "LDAP connection always disable re-use");
return result;
@@ -839,6 +839,7 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp)
{
int rc = LDAP_SUCCESS;
char *path;
+ char *query;
char *p;
char *q;
size_t i;
@@ -846,7 +847,7 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp)
if(!conn->data ||
!conn->data->state.up.path ||
conn->data->state.up.path[0] != '/' ||
- !strcasecompare("LDAP", conn->data->state.up.scheme))
+ !strncasecompare("LDAP", conn->data->state.up.scheme, 4))
return LDAP_INVALID_SYNTAX;
ludp->lud_scope = LDAP_SCOPE_BASE;
@@ -858,11 +859,14 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp)
if(!path)
return LDAP_NO_MEMORY;
- /* Parse the DN (Distinguished Name) */
- q = strchr(p, '?');
- if(q)
- *q++ = '\0';
+ /* Duplicate the query */
+ q = query = strdup(conn->data->state.up.query);
+ if(!query) {
+ free(path);
+ return LDAP_NO_MEMORY;
+ }
+ /* Parse the DN (Distinguished Name) */
if(*p) {
char *dn = p;
char *unescaped;
@@ -1039,6 +1043,7 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp)
quit:
free(path);
+ free(query);
return rc;
}
@@ -1064,8 +1069,6 @@ static int _ldap_url_parse(const struct connectdata *conn,
static void _ldap_free_urldesc(LDAPURLDesc *ludp)
{
- size_t i;
-
if(!ludp)
return;
@@ -1073,6 +1076,7 @@ static void _ldap_free_urldesc(LDAPURLDesc *ludp)
free(ludp->lud_filter);
if(ludp->lud_attrs) {
+ size_t i;
for(i = 0; i < ludp->lud_attrs_dups; i++)
free(ludp->lud_attrs[i]);
free(ludp->lud_attrs);
diff --git a/Utilities/cmcurl/lib/libcurl.rc b/Utilities/cmcurl/lib/libcurl.rc
index 3316fba19..4839d0a65 100644
--- a/Utilities/cmcurl/lib/libcurl.rc
+++ b/Utilities/cmcurl/lib/libcurl.rc
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -22,22 +22,22 @@
#include <winver.h>
#include "../include/curl/curlver.h"
-LANGUAGE 0x09,0x01
+LANGUAGE 0, 0
#define RC_VERSION LIBCURL_VERSION_MAJOR, LIBCURL_VERSION_MINOR, LIBCURL_VERSION_PATCH, 0
VS_VERSION_INFO VERSIONINFO
FILEVERSION RC_VERSION
PRODUCTVERSION RC_VERSION
- FILEFLAGSMASK 0x3fL
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#if defined(DEBUGBUILD) || defined(_DEBUG)
- FILEFLAGS 1
+ FILEFLAGS VS_FF_DEBUG
#else
- FILEFLAGS 0
+ FILEFLAGS 0L
#endif
FILEOS VOS__WINDOWS32
FILETYPE VFT_DLL
- FILESUBTYPE 0x0L
+ FILESUBTYPE 0L
BEGIN
BLOCK "StringFileInfo"
diff --git a/Utilities/cmcurl/lib/md4.c b/Utilities/cmcurl/lib/md4.c
index d35060216..e7c77bc36 100644
--- a/Utilities/cmcurl/lib/md4.c
+++ b/Utilities/cmcurl/lib/md4.c
@@ -1,4 +1,5 @@
/*
+ * !checksrc! disable COPYRIGHT
* This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
* MD4 Message-Digest Algorithm (RFC 1320).
*
@@ -37,9 +38,11 @@
#include "curl_setup.h"
-/* The NSS, OS/400 and sometimes mbed TLS crypto libraries do not provide the
- * MD4 hash algorithm, so we have a local implementation of it */
+/* The NSS, OS/400, and when not included, OpenSSL and mbed TLS crypto
+ * libraries do not provide the MD4 hash algorithm, so we use this
+ * implementation of it */
#if defined(USE_NSS) || defined(USE_OS400CRYPTO) || \
+ (defined(USE_OPENSSL) && defined(OPENSSL_NO_MD4)) || \
(defined(USE_MBEDTLS) && !defined(MBEDTLS_MD4_C))
#include "curl_md4.h"
@@ -112,7 +115,6 @@ static const void *body(MD4_CTX *ctx, const void *data, unsigned long size)
{
const unsigned char *ptr;
MD4_u32plus a, b, c, d;
- MD4_u32plus saved_a, saved_b, saved_c, saved_d;
ptr = (const unsigned char *)data;
@@ -122,6 +124,8 @@ static const void *body(MD4_CTX *ctx, const void *data, unsigned long size)
d = ctx->d;
do {
+ MD4_u32plus saved_a, saved_b, saved_c, saved_d;
+
saved_a = a;
saved_b = b;
saved_c = c;
@@ -129,59 +133,59 @@ static const void *body(MD4_CTX *ctx, const void *data, unsigned long size)
/* Round 1 */
STEP(F, a, b, c, d, SET(0), 3)
- STEP(F, d, a, b, c, SET(1), 7)
- STEP(F, c, d, a, b, SET(2), 11)
- STEP(F, b, c, d, a, SET(3), 19)
- STEP(F, a, b, c, d, SET(4), 3)
- STEP(F, d, a, b, c, SET(5), 7)
- STEP(F, c, d, a, b, SET(6), 11)
- STEP(F, b, c, d, a, SET(7), 19)
- STEP(F, a, b, c, d, SET(8), 3)
- STEP(F, d, a, b, c, SET(9), 7)
- STEP(F, c, d, a, b, SET(10), 11)
- STEP(F, b, c, d, a, SET(11), 19)
- STEP(F, a, b, c, d, SET(12), 3)
- STEP(F, d, a, b, c, SET(13), 7)
- STEP(F, c, d, a, b, SET(14), 11)
- STEP(F, b, c, d, a, SET(15), 19)
+ STEP(F, d, a, b, c, SET(1), 7)
+ STEP(F, c, d, a, b, SET(2), 11)
+ STEP(F, b, c, d, a, SET(3), 19)
+ STEP(F, a, b, c, d, SET(4), 3)
+ STEP(F, d, a, b, c, SET(5), 7)
+ STEP(F, c, d, a, b, SET(6), 11)
+ STEP(F, b, c, d, a, SET(7), 19)
+ STEP(F, a, b, c, d, SET(8), 3)
+ STEP(F, d, a, b, c, SET(9), 7)
+ STEP(F, c, d, a, b, SET(10), 11)
+ STEP(F, b, c, d, a, SET(11), 19)
+ STEP(F, a, b, c, d, SET(12), 3)
+ STEP(F, d, a, b, c, SET(13), 7)
+ STEP(F, c, d, a, b, SET(14), 11)
+ STEP(F, b, c, d, a, SET(15), 19)
/* Round 2 */
- STEP(G, a, b, c, d, GET(0) + 0x5a827999, 3)
- STEP(G, d, a, b, c, GET(4) + 0x5a827999, 5)
- STEP(G, c, d, a, b, GET(8) + 0x5a827999, 9)
- STEP(G, b, c, d, a, GET(12) + 0x5a827999, 13)
- STEP(G, a, b, c, d, GET(1) + 0x5a827999, 3)
- STEP(G, d, a, b, c, GET(5) + 0x5a827999, 5)
- STEP(G, c, d, a, b, GET(9) + 0x5a827999, 9)
- STEP(G, b, c, d, a, GET(13) + 0x5a827999, 13)
- STEP(G, a, b, c, d, GET(2) + 0x5a827999, 3)
- STEP(G, d, a, b, c, GET(6) + 0x5a827999, 5)
- STEP(G, c, d, a, b, GET(10) + 0x5a827999, 9)
- STEP(G, b, c, d, a, GET(14) + 0x5a827999, 13)
- STEP(G, a, b, c, d, GET(3) + 0x5a827999, 3)
- STEP(G, d, a, b, c, GET(7) + 0x5a827999, 5)
- STEP(G, c, d, a, b, GET(11) + 0x5a827999, 9)
- STEP(G, b, c, d, a, GET(15) + 0x5a827999, 13)
+ STEP(G, a, b, c, d, GET(0) + 0x5a827999, 3)
+ STEP(G, d, a, b, c, GET(4) + 0x5a827999, 5)
+ STEP(G, c, d, a, b, GET(8) + 0x5a827999, 9)
+ STEP(G, b, c, d, a, GET(12) + 0x5a827999, 13)
+ STEP(G, a, b, c, d, GET(1) + 0x5a827999, 3)
+ STEP(G, d, a, b, c, GET(5) + 0x5a827999, 5)
+ STEP(G, c, d, a, b, GET(9) + 0x5a827999, 9)
+ STEP(G, b, c, d, a, GET(13) + 0x5a827999, 13)
+ STEP(G, a, b, c, d, GET(2) + 0x5a827999, 3)
+ STEP(G, d, a, b, c, GET(6) + 0x5a827999, 5)
+ STEP(G, c, d, a, b, GET(10) + 0x5a827999, 9)
+ STEP(G, b, c, d, a, GET(14) + 0x5a827999, 13)
+ STEP(G, a, b, c, d, GET(3) + 0x5a827999, 3)
+ STEP(G, d, a, b, c, GET(7) + 0x5a827999, 5)
+ STEP(G, c, d, a, b, GET(11) + 0x5a827999, 9)
+ STEP(G, b, c, d, a, GET(15) + 0x5a827999, 13)
/* Round 3 */
- STEP(H, a, b, c, d, GET(0) + 0x6ed9eba1, 3)
- STEP(H, d, a, b, c, GET(8) + 0x6ed9eba1, 9)
- STEP(H, c, d, a, b, GET(4) + 0x6ed9eba1, 11)
- STEP(H, b, c, d, a, GET(12) + 0x6ed9eba1, 15)
- STEP(H, a, b, c, d, GET(2) + 0x6ed9eba1, 3)
- STEP(H, d, a, b, c, GET(10) + 0x6ed9eba1, 9)
- STEP(H, c, d, a, b, GET(6) + 0x6ed9eba1, 11)
- STEP(H, b, c, d, a, GET(14) + 0x6ed9eba1, 15)
- STEP(H, a, b, c, d, GET(1) + 0x6ed9eba1, 3)
- STEP(H, d, a, b, c, GET(9) + 0x6ed9eba1, 9)
- STEP(H, c, d, a, b, GET(5) + 0x6ed9eba1, 11)
- STEP(H, b, c, d, a, GET(13) + 0x6ed9eba1, 15)
- STEP(H, a, b, c, d, GET(3) + 0x6ed9eba1, 3)
- STEP(H, d, a, b, c, GET(11) + 0x6ed9eba1, 9)
- STEP(H, c, d, a, b, GET(7) + 0x6ed9eba1, 11)
- STEP(H, b, c, d, a, GET(15) + 0x6ed9eba1, 15)
-
- a += saved_a;
+ STEP(H, a, b, c, d, GET(0) + 0x6ed9eba1, 3)
+ STEP(H, d, a, b, c, GET(8) + 0x6ed9eba1, 9)
+ STEP(H, c, d, a, b, GET(4) + 0x6ed9eba1, 11)
+ STEP(H, b, c, d, a, GET(12) + 0x6ed9eba1, 15)
+ STEP(H, a, b, c, d, GET(2) + 0x6ed9eba1, 3)
+ STEP(H, d, a, b, c, GET(10) + 0x6ed9eba1, 9)
+ STEP(H, c, d, a, b, GET(6) + 0x6ed9eba1, 11)
+ STEP(H, b, c, d, a, GET(14) + 0x6ed9eba1, 15)
+ STEP(H, a, b, c, d, GET(1) + 0x6ed9eba1, 3)
+ STEP(H, d, a, b, c, GET(9) + 0x6ed9eba1, 9)
+ STEP(H, c, d, a, b, GET(5) + 0x6ed9eba1, 11)
+ STEP(H, b, c, d, a, GET(13) + 0x6ed9eba1, 15)
+ STEP(H, a, b, c, d, GET(3) + 0x6ed9eba1, 3)
+ STEP(H, d, a, b, c, GET(11) + 0x6ed9eba1, 9)
+ STEP(H, c, d, a, b, GET(7) + 0x6ed9eba1, 11)
+ STEP(H, b, c, d, a, GET(15) + 0x6ed9eba1, 15)
+
+ a += saved_a;
b += saved_b;
c += saved_c;
d += saved_d;
@@ -211,7 +215,7 @@ static void MD4_Init(MD4_CTX *ctx)
static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
{
MD4_u32plus saved_lo;
- unsigned long used, available;
+ unsigned long used;
saved_lo = ctx->lo;
ctx->lo = (saved_lo + size) & 0x1fffffff;
@@ -222,7 +226,7 @@ static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
used = saved_lo & 0x3f;
if(used) {
- available = 64 - used;
+ unsigned long available = 64 - used;
if(size < available) {
memcpy(&ctx->buffer[used], data, size);
@@ -303,5 +307,7 @@ void Curl_md4it(unsigned char *output, const unsigned char *input, size_t len)
MD4_Update(&ctx, input, curlx_uztoui(len));
MD4_Final(output, &ctx);
}
+
#endif /* defined(USE_NSS) || defined(USE_OS400CRYPTO) ||
+ (defined(USE_OPENSSL) && defined(OPENSSL_NO_MD4)) ||
(defined(USE_MBEDTLS) && !defined(MBEDTLS_MD4_C)) */
diff --git a/Utilities/cmcurl/lib/md5.c b/Utilities/cmcurl/lib/md5.c
index 45f45bbd9..2b81ca455 100644
--- a/Utilities/cmcurl/lib/md5.c
+++ b/Utilities/cmcurl/lib/md5.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -39,19 +39,19 @@
typedef struct md5_ctx MD5_CTX;
-static void MD5_Init(MD5_CTX * ctx)
+static void MD5_Init(MD5_CTX *ctx)
{
md5_init(ctx);
}
-static void MD5_Update(MD5_CTX * ctx,
+static void MD5_Update(MD5_CTX *ctx,
const unsigned char *input,
unsigned int inputLen)
{
md5_update(ctx, inputLen, input);
}
-static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx)
+static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx)
{
md5_digest(ctx, 16, digest);
}
@@ -65,25 +65,25 @@ static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx)
typedef gcry_md_hd_t MD5_CTX;
-static void MD5_Init(MD5_CTX * ctx)
+static void MD5_Init(MD5_CTX *ctx)
{
gcry_md_open(ctx, GCRY_MD_MD5, 0);
}
-static void MD5_Update(MD5_CTX * ctx,
+static void MD5_Update(MD5_CTX *ctx,
const unsigned char *input,
unsigned int inputLen)
{
gcry_md_write(*ctx, input, inputLen);
}
-static void MD5_Final(unsigned char digest[16], MD5_CTX * ctx)
+static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx)
{
memcpy(digest, gcry_md_read(*ctx, 0), 16);
gcry_md_close(*ctx);
}
-#elif defined(USE_OPENSSL)
+#elif defined(USE_OPENSSL) && !defined(USE_AMISSL)
/* When OpenSSL is available we use the MD5-function from OpenSSL */
#include <openssl/md5.h>
#include "curl_memory.h"
@@ -124,7 +124,7 @@ static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx)
CC_MD5_Final(digest, ctx);
}
-#elif defined(_WIN32) && !defined(CURL_WINDOWS_APP)
+#elif defined(WIN32) && !defined(CURL_WINDOWS_APP)
#include <wincrypt.h>
#include "curl_memory.h"
@@ -163,13 +163,6 @@ static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx)
CryptReleaseContext(ctx->hCryptProv, 0);
}
-#elif defined(USE_AXTLS)
-#include <axTLS/config.h>
-#include <axTLS/os_int.h>
-#include <axTLS/crypto.h>
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "memdebug.h"
#else
/* When no other crypto library is available we use this code segment */
/*
@@ -282,7 +275,6 @@ static const void *body(MD5_CTX *ctx, const void *data, unsigned long size)
{
const unsigned char *ptr;
MD5_u32plus a, b, c, d;
- MD5_u32plus saved_a, saved_b, saved_c, saved_d;
ptr = (const unsigned char *)data;
@@ -292,6 +284,8 @@ static const void *body(MD5_CTX *ctx, const void *data, unsigned long size)
d = ctx->d;
do {
+ MD5_u32plus saved_a, saved_b, saved_c, saved_d;
+
saved_a = a;
saved_b = b;
saved_c = c;
@@ -299,77 +293,77 @@ static const void *body(MD5_CTX *ctx, const void *data, unsigned long size)
/* Round 1 */
STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
- STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
- STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
- STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
- STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
- STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
- STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
- STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
- STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
- STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
- STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
- STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
- STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
- STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
- STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
- STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
+ STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
+ STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
+ STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
+ STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
+ STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
+ STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
+ STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
+ STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
+ STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
+ STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
+ STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
+ STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
+ STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
+ STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
+ STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
/* Round 2 */
- STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
- STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
- STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
- STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
- STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
- STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
- STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
- STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
- STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
- STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
- STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
- STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
- STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
- STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
- STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
- STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
+ STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
+ STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
+ STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
+ STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
+ STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
+ STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
+ STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
+ STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
+ STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
+ STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
+ STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
+ STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
+ STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
+ STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
+ STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
+ STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
/* Round 3 */
- STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
- STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11)
- STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
- STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23)
- STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
- STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11)
- STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
- STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23)
- STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
- STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11)
- STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
- STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23)
- STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
- STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11)
- STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
- STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23)
+ STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
+ STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11)
+ STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
+ STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23)
+ STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
+ STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11)
+ STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
+ STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23)
+ STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
+ STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11)
+ STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
+ STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23)
+ STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
+ STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11)
+ STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
+ STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23)
/* Round 4 */
- STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
- STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
- STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
- STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
- STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
- STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
- STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
- STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
- STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
- STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
- STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
- STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
- STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
- STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
- STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
- STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
-
- a += saved_a;
+ STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
+ STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
+ STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
+ STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
+ STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
+ STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
+ STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
+ STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
+ STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
+ STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
+ STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
+ STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
+ STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
+ STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
+ STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
+ STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
+
+ a += saved_a;
b += saved_b;
c += saved_c;
d += saved_d;
@@ -399,7 +393,7 @@ static void MD5_Init(MD5_CTX *ctx)
static void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size)
{
MD5_u32plus saved_lo;
- unsigned long used, available;
+ unsigned long used;
saved_lo = ctx->lo;
ctx->lo = (saved_lo + size) & 0x1fffffff;
@@ -410,7 +404,7 @@ static void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size)
used = saved_lo & 0x3f;
if(used) {
- available = 64 - used;
+ unsigned long available = 64 - used;
if(size < available) {
memcpy(&ctx->buffer[used], data, size);
@@ -552,23 +546,23 @@ MD5_context *Curl_MD5_init(const MD5_params *md5params)
return ctxt;
}
-int Curl_MD5_update(MD5_context *context,
- const unsigned char *data,
- unsigned int len)
+CURLcode Curl_MD5_update(MD5_context *context,
+ const unsigned char *data,
+ unsigned int len)
{
(*context->md5_hash->md5_update_func)(context->md5_hashctx, data, len);
- return 0;
+ return CURLE_OK;
}
-int Curl_MD5_final(MD5_context *context, unsigned char *result)
+CURLcode Curl_MD5_final(MD5_context *context, unsigned char *result)
{
(*context->md5_hash->md5_final_func)(result, context->md5_hashctx);
free(context->md5_hashctx);
free(context);
- return 0;
+ return CURLE_OK;
}
#endif /* CURL_DISABLE_CRYPTO_AUTH */
diff --git a/Utilities/cmcurl/lib/memdebug.c b/Utilities/cmcurl/lib/memdebug.c
index 2b81c26a6..ede60094b 100644
--- a/Utilities/cmcurl/lib/memdebug.c
+++ b/Utilities/cmcurl/lib/memdebug.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -100,30 +100,29 @@ struct memdebug {
* Don't use these with multithreaded test programs!
*/
-#define logfile curl_debuglogfile
-FILE *curl_debuglogfile = NULL;
+FILE *curl_dbg_logfile = NULL;
static bool memlimit = FALSE; /* enable memory limit */
static long memsize = 0; /* set number of mallocs allowed */
/* this sets the log file name */
-void curl_memdebug(const char *logname)
+void curl_dbg_memdebug(const char *logname)
{
- if(!logfile) {
+ if(!curl_dbg_logfile) {
if(logname && *logname)
- logfile = fopen(logname, FOPEN_WRITETEXT);
+ curl_dbg_logfile = fopen(logname, FOPEN_WRITETEXT);
else
- logfile = stderr;
+ curl_dbg_logfile = stderr;
#ifdef MEMDEBUG_LOG_SYNC
/* Flush the log file after every line so the log isn't lost in a crash */
- if(logfile)
- setbuf(logfile, (char *)NULL);
+ if(curl_dbg_logfile)
+ setbuf(curl_dbg_logfile, (char *)NULL);
#endif
}
}
/* This function sets the number of malloc() calls that should return
successfully! */
-void curl_memlimit(long limit)
+void curl_dbg_memlimit(long limit)
{
if(!memlimit) {
memlimit = TRUE;
@@ -140,12 +139,12 @@ static bool countcheck(const char *func, int line, const char *source)
if(!memsize) {
if(source) {
/* log to file */
- curl_memlog("LIMIT %s:%d %s reached memlimit\n",
- source, line, func);
+ curl_dbg_log("LIMIT %s:%d %s reached memlimit\n",
+ source, line, func);
/* log to stderr also */
fprintf(stderr, "LIMIT %s:%d %s reached memlimit\n",
source, line, func);
- fflush(logfile); /* because it might crash now */
+ fflush(curl_dbg_logfile); /* because it might crash now */
}
errno = ENOMEM;
return TRUE; /* RETURN ERROR! */
@@ -159,7 +158,7 @@ static bool countcheck(const char *func, int line, const char *source)
return FALSE; /* allow this */
}
-void *curl_domalloc(size_t wantedsize, int line, const char *source)
+void *curl_dbg_malloc(size_t wantedsize, int line, const char *source)
{
struct memdebug *mem;
size_t size;
@@ -180,15 +179,15 @@ void *curl_domalloc(size_t wantedsize, int line, const char *source)
}
if(source)
- curl_memlog("MEM %s:%d malloc(%zu) = %p\n",
- source, line, wantedsize,
- mem ? (void *)mem->mem : (void *)0);
+ curl_dbg_log("MEM %s:%d malloc(%zu) = %p\n",
+ source, line, wantedsize,
+ mem ? (void *)mem->mem : (void *)0);
return (mem ? mem->mem : NULL);
}
-void *curl_docalloc(size_t wanted_elements, size_t wanted_size,
- int line, const char *source)
+void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size,
+ int line, const char *source)
{
struct memdebug *mem;
size_t size, user_size;
@@ -208,14 +207,14 @@ void *curl_docalloc(size_t wanted_elements, size_t wanted_size,
mem->size = user_size;
if(source)
- curl_memlog("MEM %s:%d calloc(%zu,%zu) = %p\n",
- source, line, wanted_elements, wanted_size,
- mem ? (void *)mem->mem : (void *)0);
+ curl_dbg_log("MEM %s:%d calloc(%zu,%zu) = %p\n",
+ source, line, wanted_elements, wanted_size,
+ mem ? (void *)mem->mem : (void *)0);
return (mem ? mem->mem : NULL);
}
-char *curl_dostrdup(const char *str, int line, const char *source)
+char *curl_dbg_strdup(const char *str, int line, const char *source)
{
char *mem;
size_t len;
@@ -227,19 +226,19 @@ char *curl_dostrdup(const char *str, int line, const char *source)
len = strlen(str) + 1;
- mem = curl_domalloc(len, 0, NULL); /* NULL prevents logging */
+ mem = curl_dbg_malloc(len, 0, NULL); /* NULL prevents logging */
if(mem)
memcpy(mem, str, len);
if(source)
- curl_memlog("MEM %s:%d strdup(%p) (%zu) = %p\n",
- source, line, (const void *)str, len, (const void *)mem);
+ curl_dbg_log("MEM %s:%d strdup(%p) (%zu) = %p\n",
+ source, line, (const void *)str, len, (const void *)mem);
return mem;
}
#if defined(WIN32) && defined(UNICODE)
-wchar_t *curl_dowcsdup(const wchar_t *str, int line, const char *source)
+wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source)
{
wchar_t *mem;
size_t wsiz, bsiz;
@@ -252,12 +251,12 @@ wchar_t *curl_dowcsdup(const wchar_t *str, int line, const char *source)
wsiz = wcslen(str) + 1;
bsiz = wsiz * sizeof(wchar_t);
- mem = curl_domalloc(bsiz, 0, NULL); /* NULL prevents logging */
+ mem = curl_dbg_malloc(bsiz, 0, NULL); /* NULL prevents logging */
if(mem)
memcpy(mem, str, bsiz);
if(source)
- curl_memlog("MEM %s:%d wcsdup(%p) (%zu) = %p\n",
+ curl_dbg_log("MEM %s:%d wcsdup(%p) (%zu) = %p\n",
source, line, (void *)str, bsiz, (void *)mem);
return mem;
@@ -266,8 +265,8 @@ wchar_t *curl_dowcsdup(const wchar_t *str, int line, const char *source)
/* We provide a realloc() that accepts a NULL as pointer, which then
performs a malloc(). In order to work with ares. */
-void *curl_dorealloc(void *ptr, size_t wantedsize,
- int line, const char *source)
+void *curl_dbg_realloc(void *ptr, size_t wantedsize,
+ int line, const char *source)
{
struct memdebug *mem = NULL;
@@ -293,7 +292,7 @@ void *curl_dorealloc(void *ptr, size_t wantedsize,
mem = (Curl_crealloc)(mem, size);
if(source)
- curl_memlog("MEM %s:%d realloc(%p, %zu) = %p\n",
+ curl_dbg_log("MEM %s:%d realloc(%p, %zu) = %p\n",
source, line, (void *)ptr, wantedsize,
mem ? (void *)mem->mem : (void *)0);
@@ -305,11 +304,10 @@ void *curl_dorealloc(void *ptr, size_t wantedsize,
return NULL;
}
-void curl_dofree(void *ptr, int line, const char *source)
+void curl_dbg_free(void *ptr, int line, const char *source)
{
- struct memdebug *mem;
-
if(ptr) {
+ struct memdebug *mem;
#ifdef __INTEL_COMPILER
# pragma warning(push)
@@ -331,11 +329,11 @@ void curl_dofree(void *ptr, int line, const char *source)
}
if(source)
- curl_memlog("MEM %s:%d free(%p)\n", source, line, (void *)ptr);
+ curl_dbg_log("MEM %s:%d free(%p)\n", source, line, (void *)ptr);
}
-curl_socket_t curl_socket(int domain, int type, int protocol,
- int line, const char *source)
+curl_socket_t curl_dbg_socket(int domain, int type, int protocol,
+ int line, const char *source)
{
const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
"FD %s:%d socket() = %d\n" :
@@ -351,44 +349,44 @@ curl_socket_t curl_socket(int domain, int type, int protocol,
sockfd = socket(domain, type, protocol);
if(source && (sockfd != CURL_SOCKET_BAD))
- curl_memlog(fmt, source, line, sockfd);
+ curl_dbg_log(fmt, source, line, sockfd);
return sockfd;
}
-SEND_TYPE_RETV curl_dosend(SEND_TYPE_ARG1 sockfd,
- SEND_QUAL_ARG2 SEND_TYPE_ARG2 buf,
- SEND_TYPE_ARG3 len, SEND_TYPE_ARG4 flags, int line,
- const char *source)
+SEND_TYPE_RETV curl_dbg_send(SEND_TYPE_ARG1 sockfd,
+ SEND_QUAL_ARG2 SEND_TYPE_ARG2 buf,
+ SEND_TYPE_ARG3 len, SEND_TYPE_ARG4 flags, int line,
+ const char *source)
{
SEND_TYPE_RETV rc;
if(countcheck("send", line, source))
return -1;
rc = send(sockfd, buf, len, flags);
if(source)
- curl_memlog("SEND %s:%d send(%lu) = %ld\n",
+ curl_dbg_log("SEND %s:%d send(%lu) = %ld\n",
source, line, (unsigned long)len, (long)rc);
return rc;
}
-RECV_TYPE_RETV curl_dorecv(RECV_TYPE_ARG1 sockfd, RECV_TYPE_ARG2 buf,
- RECV_TYPE_ARG3 len, RECV_TYPE_ARG4 flags, int line,
- const char *source)
+RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd, RECV_TYPE_ARG2 buf,
+ RECV_TYPE_ARG3 len, RECV_TYPE_ARG4 flags, int line,
+ const char *source)
{
RECV_TYPE_RETV rc;
if(countcheck("recv", line, source))
return -1;
rc = recv(sockfd, buf, len, flags);
if(source)
- curl_memlog("RECV %s:%d recv(%lu) = %ld\n",
+ curl_dbg_log("RECV %s:%d recv(%lu) = %ld\n",
source, line, (unsigned long)len, (long)rc);
return rc;
}
#ifdef HAVE_SOCKETPAIR
-int curl_socketpair(int domain, int type, int protocol,
- curl_socket_t socket_vector[2],
- int line, const char *source)
+int curl_dbg_socketpair(int domain, int type, int protocol,
+ curl_socket_t socket_vector[2],
+ int line, const char *source)
{
const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
"FD %s:%d socketpair() = %d %d\n" :
@@ -399,14 +397,14 @@ int curl_socketpair(int domain, int type, int protocol,
int res = socketpair(domain, type, protocol, socket_vector);
if(source && (0 == res))
- curl_memlog(fmt, source, line, socket_vector[0], socket_vector[1]);
+ curl_dbg_log(fmt, source, line, socket_vector[0], socket_vector[1]);
return res;
}
#endif
-curl_socket_t curl_accept(curl_socket_t s, void *saddr, void *saddrlen,
- int line, const char *source)
+curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen,
+ int line, const char *source)
{
const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
"FD %s:%d accept() = %d\n" :
@@ -420,13 +418,13 @@ curl_socket_t curl_accept(curl_socket_t s, void *saddr, void *saddrlen,
curl_socket_t sockfd = accept(s, addr, addrlen);
if(source && (sockfd != CURL_SOCKET_BAD))
- curl_memlog(fmt, source, line, sockfd);
+ curl_dbg_log(fmt, source, line, sockfd);
return sockfd;
}
/* separate function to allow libcurl to mark a "faked" close */
-void curl_mark_sclose(curl_socket_t sockfd, int line, const char *source)
+void curl_dbg_mark_sclose(curl_socket_t sockfd, int line, const char *source)
{
const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
"FD %s:%d sclose(%d)\n":
@@ -435,54 +433,40 @@ void curl_mark_sclose(curl_socket_t sockfd, int line, const char *source)
"FD %s:%d sclose(%zd)\n";
if(source)
- curl_memlog(fmt, source, line, sockfd);
+ curl_dbg_log(fmt, source, line, sockfd);
}
/* this is our own defined way to close sockets on *ALL* platforms */
-int curl_sclose(curl_socket_t sockfd, int line, const char *source)
+int curl_dbg_sclose(curl_socket_t sockfd, int line, const char *source)
{
int res = sclose(sockfd);
- curl_mark_sclose(sockfd, line, source);
+ curl_dbg_mark_sclose(sockfd, line, source);
return res;
}
-FILE *curl_fopen(const char *file, const char *mode,
- int line, const char *source)
+FILE *curl_dbg_fopen(const char *file, const char *mode,
+ int line, const char *source)
{
FILE *res = fopen(file, mode);
if(source)
- curl_memlog("FILE %s:%d fopen(\"%s\",\"%s\") = %p\n",
+ curl_dbg_log("FILE %s:%d fopen(\"%s\",\"%s\") = %p\n",
source, line, file, mode, (void *)res);
return res;
}
-#ifdef HAVE_FDOPEN
-FILE *curl_fdopen(int filedes, const char *mode,
- int line, const char *source)
-{
- FILE *res = fdopen(filedes, mode);
-
- if(source)
- curl_memlog("FILE %s:%d fdopen(\"%d\",\"%s\") = %p\n",
- source, line, filedes, mode, (void *)res);
-
- return res;
-}
-#endif
-
-int curl_fclose(FILE *file, int line, const char *source)
+int curl_dbg_fclose(FILE *file, int line, const char *source)
{
int res;
DEBUGASSERT(file != NULL);
- res = fclose(file);
-
if(source)
- curl_memlog("FILE %s:%d fclose(%p)\n",
- source, line, (void *)file);
+ curl_dbg_log("FILE %s:%d fclose(%p)\n",
+ source, line, (void *)file);
+
+ res = fclose(file);
return res;
}
@@ -490,13 +474,13 @@ int curl_fclose(FILE *file, int line, const char *source)
#define LOGLINE_BUFSIZE 1024
/* this does the writing to the memory tracking log file */
-void curl_memlog(const char *format, ...)
+void curl_dbg_log(const char *format, ...)
{
char *buf;
int nchars;
va_list ap;
- if(!logfile)
+ if(!curl_dbg_logfile)
return;
buf = (Curl_cmalloc)(LOGLINE_BUFSIZE);
@@ -504,14 +488,14 @@ void curl_memlog(const char *format, ...)
return;
va_start(ap, format);
- nchars = vsnprintf(buf, LOGLINE_BUFSIZE, format, ap);
+ nchars = mvsnprintf(buf, LOGLINE_BUFSIZE, format, ap);
va_end(ap);
if(nchars > LOGLINE_BUFSIZE - 1)
nchars = LOGLINE_BUFSIZE - 1;
if(nchars > 0)
- fwrite(buf, 1, (size_t)nchars, logfile);
+ fwrite(buf, 1, (size_t)nchars, curl_dbg_logfile);
(Curl_cfree)(buf);
}
diff --git a/Utilities/cmcurl/lib/memdebug.h b/Utilities/cmcurl/lib/memdebug.h
index 233de65a4..5236f60fa 100644
--- a/Utilities/cmcurl/lib/memdebug.h
+++ b/Utilities/cmcurl/lib/memdebug.h
@@ -8,7 +8,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -30,97 +30,92 @@
#define CURL_MT_LOGFNAME_BUFSIZE 512
-#define logfile curl_debuglogfile
-
-extern FILE *logfile;
+extern FILE *curl_dbg_logfile;
/* memory functions */
-CURL_EXTERN void *curl_domalloc(size_t size, int line, const char *source);
-CURL_EXTERN void *curl_docalloc(size_t elements, size_t size, int line,
- const char *source);
-CURL_EXTERN void *curl_dorealloc(void *ptr, size_t size, int line,
- const char *source);
-CURL_EXTERN void curl_dofree(void *ptr, int line, const char *source);
-CURL_EXTERN char *curl_dostrdup(const char *str, int line, const char *source);
-#if defined(WIN32) && defined(UNICODE)
-CURL_EXTERN wchar_t *curl_dowcsdup(const wchar_t *str, int line,
+CURL_EXTERN void *curl_dbg_malloc(size_t size, int line, const char *source);
+CURL_EXTERN void *curl_dbg_calloc(size_t elements, size_t size, int line,
+ const char *source);
+CURL_EXTERN void *curl_dbg_realloc(void *ptr, size_t size, int line,
const char *source);
+CURL_EXTERN void curl_dbg_free(void *ptr, int line, const char *source);
+CURL_EXTERN char *curl_dbg_strdup(const char *str, int line, const char *src);
+#if defined(WIN32) && defined(UNICODE)
+CURL_EXTERN wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line,
+ const char *source);
#endif
-CURL_EXTERN void curl_memdebug(const char *logname);
-CURL_EXTERN void curl_memlimit(long limit);
-CURL_EXTERN void curl_memlog(const char *format, ...);
+CURL_EXTERN void curl_dbg_memdebug(const char *logname);
+CURL_EXTERN void curl_dbg_memlimit(long limit);
+CURL_EXTERN void curl_dbg_log(const char *format, ...);
/* file descriptor manipulators */
-CURL_EXTERN curl_socket_t curl_socket(int domain, int type, int protocol,
- int line, const char *source);
-CURL_EXTERN void curl_mark_sclose(curl_socket_t sockfd,
- int line, const char *source);
-CURL_EXTERN int curl_sclose(curl_socket_t sockfd,
- int line, const char *source);
-CURL_EXTERN curl_socket_t curl_accept(curl_socket_t s, void *a, void *alen,
+CURL_EXTERN curl_socket_t curl_dbg_socket(int domain, int type, int protocol,
+ int line, const char *source);
+CURL_EXTERN void curl_dbg_mark_sclose(curl_socket_t sockfd,
int line, const char *source);
-#ifdef HAVE_SOCKETPAIR
-CURL_EXTERN int curl_socketpair(int domain, int type, int protocol,
- curl_socket_t socket_vector[2],
+CURL_EXTERN int curl_dbg_sclose(curl_socket_t sockfd,
int line, const char *source);
+CURL_EXTERN curl_socket_t curl_dbg_accept(curl_socket_t s, void *a, void *alen,
+ int line, const char *source);
+#ifdef HAVE_SOCKETPAIR
+CURL_EXTERN int curl_dbg_socketpair(int domain, int type, int protocol,
+ curl_socket_t socket_vector[2],
+ int line, const char *source);
#endif
/* send/receive sockets */
-CURL_EXTERN SEND_TYPE_RETV curl_dosend(SEND_TYPE_ARG1 sockfd,
- SEND_QUAL_ARG2 SEND_TYPE_ARG2 buf,
- SEND_TYPE_ARG3 len,
- SEND_TYPE_ARG4 flags, int line,
- const char *source);
-CURL_EXTERN RECV_TYPE_RETV curl_dorecv(RECV_TYPE_ARG1 sockfd,
- RECV_TYPE_ARG2 buf, RECV_TYPE_ARG3 len,
- RECV_TYPE_ARG4 flags, int line,
- const char *source);
+CURL_EXTERN SEND_TYPE_RETV curl_dbg_send(SEND_TYPE_ARG1 sockfd,
+ SEND_QUAL_ARG2 SEND_TYPE_ARG2 buf,
+ SEND_TYPE_ARG3 len,
+ SEND_TYPE_ARG4 flags, int line,
+ const char *source);
+CURL_EXTERN RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd,
+ RECV_TYPE_ARG2 buf,
+ RECV_TYPE_ARG3 len,
+ RECV_TYPE_ARG4 flags, int line,
+ const char *source);
/* FILE functions */
-CURL_EXTERN FILE *curl_fopen(const char *file, const char *mode, int line,
- const char *source);
-#ifdef HAVE_FDOPEN
-CURL_EXTERN FILE *curl_fdopen(int filedes, const char *mode, int line,
- const char *source);
-#endif
-CURL_EXTERN int curl_fclose(FILE *file, int line, const char *source);
+CURL_EXTERN FILE *curl_dbg_fopen(const char *file, const char *mode, int line,
+ const char *source);
+CURL_EXTERN int curl_dbg_fclose(FILE *file, int line, const char *source);
#ifndef MEMDEBUG_NODEFINES
/* Set this symbol on the command-line, recompile all lib-sources */
#undef strdup
-#define strdup(ptr) curl_dostrdup(ptr, __LINE__, __FILE__)
-#define malloc(size) curl_domalloc(size, __LINE__, __FILE__)
-#define calloc(nbelem,size) curl_docalloc(nbelem, size, __LINE__, __FILE__)
-#define realloc(ptr,size) curl_dorealloc(ptr, size, __LINE__, __FILE__)
-#define free(ptr) curl_dofree(ptr, __LINE__, __FILE__)
-#define send(a,b,c,d) curl_dosend(a,b,c,d, __LINE__, __FILE__)
-#define recv(a,b,c,d) curl_dorecv(a,b,c,d, __LINE__, __FILE__)
+#define strdup(ptr) curl_dbg_strdup(ptr, __LINE__, __FILE__)
+#define malloc(size) curl_dbg_malloc(size, __LINE__, __FILE__)
+#define calloc(nbelem,size) curl_dbg_calloc(nbelem, size, __LINE__, __FILE__)
+#define realloc(ptr,size) curl_dbg_realloc(ptr, size, __LINE__, __FILE__)
+#define free(ptr) curl_dbg_free(ptr, __LINE__, __FILE__)
+#define send(a,b,c,d) curl_dbg_send(a,b,c,d, __LINE__, __FILE__)
+#define recv(a,b,c,d) curl_dbg_recv(a,b,c,d, __LINE__, __FILE__)
#ifdef WIN32
# ifdef UNICODE
# undef wcsdup
-# define wcsdup(ptr) curl_dowcsdup(ptr, __LINE__, __FILE__)
+# define wcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__)
# undef _wcsdup
-# define _wcsdup(ptr) curl_dowcsdup(ptr, __LINE__, __FILE__)
+# define _wcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__)
# undef _tcsdup
-# define _tcsdup(ptr) curl_dowcsdup(ptr, __LINE__, __FILE__)
+# define _tcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__)
# else
# undef _tcsdup
-# define _tcsdup(ptr) curl_dostrdup(ptr, __LINE__, __FILE__)
+# define _tcsdup(ptr) curl_dbg_strdup(ptr, __LINE__, __FILE__)
# endif
#endif
#undef socket
#define socket(domain,type,protocol)\
- curl_socket(domain, type, protocol, __LINE__, __FILE__)
+ curl_dbg_socket(domain, type, protocol, __LINE__, __FILE__)
#undef accept /* for those with accept as a macro */
#define accept(sock,addr,len)\
- curl_accept(sock, addr, len, __LINE__, __FILE__)
+ curl_dbg_accept(sock, addr, len, __LINE__, __FILE__)
#ifdef HAVE_SOCKETPAIR
#define socketpair(domain,type,protocol,socket_vector)\
- curl_socketpair(domain, type, protocol, socket_vector, __LINE__, __FILE__)
+ curl_dbg_socketpair(domain, type, protocol, socket_vector, __LINE__, __FILE__)
#endif
#ifdef HAVE_GETADDRINFO
@@ -129,31 +124,31 @@ CURL_EXTERN int curl_fclose(FILE *file, int line, const char *source);
our macro as for other platforms. Instead, we redefine the new name they
define getaddrinfo to become! */
#define ogetaddrinfo(host,serv,hint,res) \
- curl_dogetaddrinfo(host, serv, hint, res, __LINE__, __FILE__)
+ curl_dbg_getaddrinfo(host, serv, hint, res, __LINE__, __FILE__)
#else
#undef getaddrinfo
#define getaddrinfo(host,serv,hint,res) \
- curl_dogetaddrinfo(host, serv, hint, res, __LINE__, __FILE__)
+ curl_dbg_getaddrinfo(host, serv, hint, res, __LINE__, __FILE__)
#endif
#endif /* HAVE_GETADDRINFO */
#ifdef HAVE_FREEADDRINFO
#undef freeaddrinfo
#define freeaddrinfo(data) \
- curl_dofreeaddrinfo(data, __LINE__, __FILE__)
+ curl_dbg_freeaddrinfo(data, __LINE__, __FILE__)
#endif /* HAVE_FREEADDRINFO */
/* sclose is probably already defined, redefine it! */
#undef sclose
-#define sclose(sockfd) curl_sclose(sockfd,__LINE__,__FILE__)
+#define sclose(sockfd) curl_dbg_sclose(sockfd,__LINE__,__FILE__)
-#define fake_sclose(sockfd) curl_mark_sclose(sockfd,__LINE__,__FILE__)
+#define fake_sclose(sockfd) curl_dbg_mark_sclose(sockfd,__LINE__,__FILE__)
#undef fopen
-#define fopen(file,mode) curl_fopen(file,mode,__LINE__,__FILE__)
+#define fopen(file,mode) curl_dbg_fopen(file,mode,__LINE__,__FILE__)
#undef fdopen
-#define fdopen(file,mode) curl_fdopen(file,mode,__LINE__,__FILE__)
-#define fclose(file) curl_fclose(file,__LINE__,__FILE__)
+#define fdopen(file,mode) curl_dbg_fdopen(file,mode,__LINE__,__FILE__)
+#define fclose(file) curl_dbg_fclose(file,__LINE__,__FILE__)
#endif /* MEMDEBUG_NODEFINES */
diff --git a/Utilities/cmcurl/lib/mime.c b/Utilities/cmcurl/lib/mime.c
index ca492d11a..2135f72c2 100644
--- a/Utilities/cmcurl/lib/mime.c
+++ b/Utilities/cmcurl/lib/mime.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -29,8 +29,8 @@
#include "urldata.h"
#include "sendf.h"
-#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \
- !defined(CURL_DISABLE_IMAP)
+#if (!defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_MIME)) || \
+ !defined(CURL_DISABLE_SMTP) || !defined(CURL_DISABLE_IMAP)
#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
#include <libgen.h>
@@ -821,8 +821,10 @@ static size_t readback_part(curl_mimepart *part,
struct curl_slist *hdr = (struct curl_slist *) part->state.ptr;
switch(part->state.state) {
case MIMESTATE_BEGIN:
- mimesetstate(&part->state, part->flags & MIME_BODY_ONLY? MIMESTATE_BODY:
- MIMESTATE_CURLHEADERS, part->curlheaders);
+ mimesetstate(&part->state,
+ (part->flags & MIME_BODY_ONLY)?
+ MIMESTATE_BODY: MIMESTATE_CURLHEADERS,
+ part->curlheaders);
break;
case MIMESTATE_USERHEADERS:
if(!hdr) {
@@ -1122,8 +1124,6 @@ void curl_mime_free(curl_mime *mime)
Curl_mime_cleanpart(part);
free(part);
}
-
- free(mime->boundary);
free(mime);
}
}
@@ -1220,18 +1220,10 @@ curl_mime *curl_mime_init(struct Curl_easy *easy)
mime->firstpart = NULL;
mime->lastpart = NULL;
- /* Get a part boundary. */
- mime->boundary = malloc(24 + MIME_RAND_BOUNDARY_CHARS + 1);
- if(!mime->boundary) {
- free(mime);
- return NULL;
- }
-
memset(mime->boundary, '-', 24);
- if(Curl_rand_hex(easy, (unsigned char *) mime->boundary + 24,
+ if(Curl_rand_hex(easy, (unsigned char *) &mime->boundary[24],
MIME_RAND_BOUNDARY_CHARS + 1)) {
/* failed to get random separator, bail out */
- free(mime->boundary);
free(mime);
return NULL;
}
@@ -1909,72 +1901,4 @@ CURLcode curl_mime_headers(curl_mimepart *part,
return CURLE_NOT_BUILT_IN;
}
-void Curl_mime_initpart(curl_mimepart *part, struct Curl_easy *easy)
-{
- (void) part;
- (void) easy;
-}
-
-void Curl_mime_cleanpart(curl_mimepart *part)
-{
- (void) part;
-}
-
-CURLcode Curl_mime_duppart(curl_mimepart *dst, const curl_mimepart *src)
-{
- (void) dst;
- (void) src;
- return CURLE_OK; /* Nothing to duplicate: always succeed. */
-}
-
-CURLcode Curl_mime_set_subparts(curl_mimepart *part,
- curl_mime *subparts, int take_ownership)
-{
- (void) part;
- (void) subparts;
- (void) take_ownership;
- return CURLE_NOT_BUILT_IN;
-}
-
-CURLcode Curl_mime_prepare_headers(curl_mimepart *part,
- const char *contenttype,
- const char *disposition,
- enum mimestrategy strategy)
-{
- (void) part;
- (void) contenttype;
- (void) disposition;
- (void) strategy;
- return CURLE_NOT_BUILT_IN;
-}
-
-curl_off_t Curl_mime_size(curl_mimepart *part)
-{
- (void) part;
- return (curl_off_t) -1;
-}
-
-size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream)
-{
- (void) buffer;
- (void) size;
- (void) nitems;
- (void) instream;
- return 0;
-}
-
-CURLcode Curl_mime_rewind(curl_mimepart *part)
-{
- (void) part;
- return CURLE_NOT_BUILT_IN;
-}
-
-/* VARARGS2 */
-CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...)
-{
- (void) slp;
- (void) fmt;
- return CURLE_NOT_BUILT_IN;
-}
-
-#endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP */
+#endif /* if disabled */
diff --git a/Utilities/cmcurl/lib/mime.h b/Utilities/cmcurl/lib/mime.h
index 4d5c70404..4c9a5fb71 100644
--- a/Utilities/cmcurl/lib/mime.h
+++ b/Utilities/cmcurl/lib/mime.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -22,6 +22,8 @@
*
***************************************************************************/
+#include "curl_setup.h"
+
#define MIME_RAND_BOUNDARY_CHARS 16 /* Nb. of random boundary chars. */
#define MAX_ENCODED_LINE_LENGTH 76 /* Maximum encoded line length. */
#define ENCODING_BUFFER_SIZE 256 /* Encoding temp buffers size. */
@@ -69,7 +71,7 @@ enum mimestrategy {
typedef struct {
const char * name; /* Encoding name. */
size_t (*encodefunc)(char *buffer, size_t size, bool ateof,
- curl_mimepart *part); /* Encoded read. */
+ curl_mimepart *part); /* Encoded read. */
curl_off_t (*sizefunc)(curl_mimepart *part); /* Encoded size. */
} mime_encoder;
@@ -88,13 +90,16 @@ typedef struct {
size_t offset; /* State-dependent offset. */
} mime_state;
+/* minimum buffer size for the boundary string */
+#define MIME_BOUNDARY_LEN (24 + MIME_RAND_BOUNDARY_CHARS + 1)
+
/* A mime multipart. */
struct curl_mime_s {
struct Curl_easy *easy; /* The associated easy handle. */
curl_mimepart *parent; /* Parent part. */
curl_mimepart *firstpart; /* First part. */
curl_mimepart *lastpart; /* Last part. */
- char *boundary; /* The part boundary. */
+ char boundary[MIME_BOUNDARY_LEN]; /* The part boundary. */
mime_state state; /* Current readback state. */
};
@@ -122,6 +127,8 @@ struct curl_mimepart_s {
mime_encoder_state encstate; /* Data encoder state. */
};
+#if (!defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_MIME)) || \
+ !defined(CURL_DISABLE_SMTP) || !defined(CURL_DISABLE_IMAP)
/* Prototypes. */
void Curl_mime_initpart(curl_mimepart *part, struct Curl_easy *easy);
@@ -140,4 +147,18 @@ CURLcode Curl_mime_rewind(curl_mimepart *part);
CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...);
const char *Curl_mime_contenttype(const char *filename);
+#else
+/* if disabled */
+#define Curl_mime_initpart(x,y)
+#define Curl_mime_cleanpart(x)
+#define Curl_mime_duppart(x,y) CURLE_OK /* Nothing to duplicate. Succeed */
+#define Curl_mime_set_subparts(a,b,c) CURLE_NOT_BUILT_IN
+#define Curl_mime_prepare_headers(a,b,c,d) CURLE_NOT_BUILT_IN
+#define Curl_mime_size(x) (curl_off_t) -1
+#define Curl_mime_read NULL
+#define Curl_mime_rewind(x) ((void)x, CURLE_NOT_BUILT_IN)
+#define Curl_mime_add_header(x,y,...) CURLE_NOT_BUILT_IN
+#endif
+
+
#endif /* HEADER_CURL_MIME_H */
diff --git a/Utilities/cmcurl/lib/mprintf.c b/Utilities/cmcurl/lib/mprintf.c
index d2d91d743..e19093678 100644
--- a/Utilities/cmcurl/lib/mprintf.c
+++ b/Utilities/cmcurl/lib/mprintf.c
@@ -835,7 +835,7 @@ static int dprintf_formatf(
while(width-- > 0)
OUTCHAR(' ');
- while((len-- > 0) && *str)
+ for(; len && *str; len--)
OUTCHAR(*str++);
if(p->flags&FLAGS_LEFT)
while(width-- > 0)
diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c
index 0db2a9730..c7c46eefc 100644
--- a/Utilities/cmcurl/lib/multi.c
+++ b/Utilities/cmcurl/lib/multi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -41,11 +41,11 @@
#include "speedcheck.h"
#include "conncache.h"
#include "multihandle.h"
-#include "pipeline.h"
#include "sigpipe.h"
#include "vtls/vtls.h"
#include "connect.h"
#include "http_proxy.h"
+#include "http2.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@@ -79,6 +79,7 @@ static CURLMcode add_next_timeout(struct curltime now,
static CURLMcode multi_timeout(struct Curl_multi *multi,
long *timeout_ms);
static void process_pending_handles(struct Curl_multi *multi);
+static void detach_connnection(struct Curl_easy *data);
#ifdef DEBUGBUILD
static const char * const statename[]={
@@ -90,12 +91,10 @@ static const char * const statename[]={
"WAITPROXYCONNECT",
"SENDPROTOCONNECT",
"PROTOCONNECT",
- "WAITDO",
"DO",
"DOING",
"DO_MORE",
"DO_DONE",
- "WAITPERFORM",
"PERFORM",
"TOOFAST",
"DONE",
@@ -113,7 +112,7 @@ static void Curl_init_completed(struct Curl_easy *data)
/* Important: reset the conn pointer so that we don't point to memory
that could be freed anytime */
- data->easy_conn = NULL;
+ detach_connnection(data);
Curl_expire_clear(data); /* stop all timers */
}
@@ -134,12 +133,10 @@ static void mstate(struct Curl_easy *data, CURLMstate state
NULL, /* WAITPROXYCONNECT */
NULL, /* SENDPROTOCONNECT */
NULL, /* PROTOCONNECT */
- NULL, /* WAITDO */
Curl_connect_free, /* DO */
NULL, /* DOING */
NULL, /* DO_MORE */
NULL, /* DO_DONE */
- NULL, /* WAITPERFORM */
NULL, /* PERFORM */
NULL, /* TOOFAST */
NULL, /* DONE */
@@ -162,8 +159,8 @@ static void mstate(struct Curl_easy *data, CURLMstate state
data->mstate < CURLM_STATE_COMPLETED) {
long connection_id = -5000;
- if(data->easy_conn)
- connection_id = data->easy_conn->connection_id;
+ if(data->conn)
+ connection_id = data->conn->connection_id;
infof(data,
"STATE: %s => %s handle %p; line %d (connection #%ld)\n",
@@ -188,14 +185,17 @@ static void mstate(struct Curl_easy *data, CURLMstate state
#endif
/*
- * We add one of these structs to the sockhash for a particular socket
+ * We add one of these structs to the sockhash for each socket
*/
struct Curl_sh_entry {
- struct Curl_easy *easy;
- int action; /* what action READ/WRITE this socket waits for */
- curl_socket_t socket; /* mainly to ease debugging */
+ struct curl_llist list; /* list of easy handles using this socket */
+ unsigned int action; /* what combined action READ/WRITE this socket waits
+ for */
void *socketp; /* settable by users with curl_multi_assign() */
+ unsigned int users; /* number of transfers using this */
+ unsigned int readers; /* this many transfers want to read */
+ unsigned int writers; /* this many transfers want to write */
};
/* bits for 'action' having no bits means this socket is not expecting any
action */
@@ -214,8 +214,7 @@ static struct Curl_sh_entry *sh_getentry(struct curl_hash *sh,
/* make sure this socket is present in the hash for this handle */
static struct Curl_sh_entry *sh_addentry(struct curl_hash *sh,
- curl_socket_t s,
- struct Curl_easy *data)
+ curl_socket_t s)
{
struct Curl_sh_entry *there = sh_getentry(sh, s);
struct Curl_sh_entry *check;
@@ -229,8 +228,7 @@ static struct Curl_sh_entry *sh_addentry(struct curl_hash *sh,
if(!check)
return NULL; /* major failure */
- check->easy = data;
- check->socket = s;
+ Curl_llist_init(&check->list, NULL);
/* make/add new hash entry */
if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) {
@@ -346,9 +344,6 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
Curl_llist_init(&multi->msglist, multi_freeamsg);
Curl_llist_init(&multi->pending, multi_freeamsg);
- multi->max_pipeline_length = 5;
- multi->pipelining = CURLPIPE_MULTIPLEX;
-
/* -1 means it not set by user, use the default value */
multi->maxconnects = -1;
return multi;
@@ -405,19 +400,9 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
/* set the easy handle */
multistate(data, CURLM_STATE_INIT);
- if((data->set.global_dns_cache) &&
- (data->dns.hostcachetype != HCACHE_GLOBAL)) {
- /* global dns cache was requested but still isn't */
- struct curl_hash *global = Curl_global_host_cache_init();
- if(global) {
- /* only do this if the global cache init works */
- data->dns.hostcache = global;
- data->dns.hostcachetype = HCACHE_GLOBAL;
- }
- }
/* for multi interface connections, we share DNS cache automatically if the
easy handle's one is currently not set. */
- else if(!data->dns.hostcache ||
+ if(!data->dns.hostcache ||
(data->dns.hostcachetype == HCACHE_NONE)) {
data->dns.hostcache = &multi->hostcache;
data->dns.hostcachetype = HCACHE_MULTI;
@@ -437,12 +422,7 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
data->psl = &multi->psl;
#endif
- /* This adds the new entry at the 'end' of the doubly-linked circular
- list of Curl_easy structs to try and maintain a FIFO queue so
- the pipelined requests are in order. */
-
- /* We add this new entry last in the list. */
-
+ /* We add the new entry last in the list. */
data->next = NULL; /* end of the line */
if(multi->easyp) {
struct Curl_easy *last = multi->easylp;
@@ -515,33 +495,23 @@ static void debug_print_sock_hash(void *p)
}
#endif
-static CURLcode multi_done(struct connectdata **connp,
- CURLcode status, /* an error if this is called
- after an error was detected */
- bool premature)
+static CURLcode multi_done(struct Curl_easy *data,
+ CURLcode status, /* an error if this is called
+ after an error was detected */
+ bool premature)
{
CURLcode result;
- struct connectdata *conn;
- struct Curl_easy *data;
+ struct connectdata *conn = data->conn;
unsigned int i;
- DEBUGASSERT(*connp);
-
- conn = *connp;
- data = conn->data;
-
DEBUGF(infof(data, "multi_done\n"));
if(data->state.done)
/* Stop if multi_done() has already been called */
return CURLE_OK;
- if(data->mstate == CURLM_STATE_WAITRESOLVE) {
- /* still waiting for the resolve to complete */
- (void)Curl_resolver_wait_resolv(conn, NULL);
- }
-
- Curl_getoff_all_pipelines(data, conn);
+ /* Stop the resolver and free its own resources (but not dns_entry yet). */
+ Curl_resolver_kill(conn);
/* Cleanup possible redirect junk */
Curl_safefree(data->req.newurl);
@@ -576,17 +546,16 @@ static CURLcode multi_done(struct connectdata **connp,
process_pending_handles(data->multi); /* connection / multiplex */
- if(conn->send_pipe.size || conn->recv_pipe.size) {
- /* Stop if pipeline is not empty . */
- data->easy_conn = NULL;
- DEBUGF(infof(data, "Connection still in use %zu/%zu, "
+ detach_connnection(data);
+ if(CONN_INUSE(conn)) {
+ /* Stop if still used. */
+ DEBUGF(infof(data, "Connection still in use %zu, "
"no more multi_done now!\n",
- conn->send_pipe.size, conn->recv_pipe.size));
+ conn->easyq.size));
return CURLE_OK;
}
data->state.done = TRUE; /* called just now! */
- Curl_resolver_cancel(conn);
if(conn->dns_entry) {
Curl_resolv_unlock(data, conn->dns_entry); /* done with this */
@@ -604,7 +573,7 @@ static CURLcode multi_done(struct connectdata **connp,
/* if data->set.reuse_forbid is TRUE, it means the libcurl client has
forced us to close this connection. This is ignored for requests taking
- place in a NTLM authentication handshake
+ place in a NTLM/NEGOTIATE authentication handshake
if conn->bits.close is TRUE, it means that the connection should be
closed in spite of all our efforts to be nice, due to protocol
@@ -619,8 +588,12 @@ static CURLcode multi_done(struct connectdata **connp,
if((data->set.reuse_forbid
#if defined(USE_NTLM)
- && !(conn->ntlm.state == NTLMSTATE_TYPE2 ||
- conn->proxyntlm.state == NTLMSTATE_TYPE2)
+ && !(conn->http_ntlm_state == NTLMSTATE_TYPE2 ||
+ conn->proxy_ntlm_state == NTLMSTATE_TYPE2)
+#endif
+#if defined(USE_SPNEGO)
+ && !(conn->http_negotiate_state == GSS_AUTHRECV ||
+ conn->proxy_negotiate_state == GSS_AUTHRECV)
#endif
) || conn->bits.close
|| (premature && !(conn->handler->flags & PROTOPT_STREAM))) {
@@ -634,13 +607,13 @@ static CURLcode multi_done(struct connectdata **connp,
else {
char buffer[256];
/* create string before returning the connection */
- snprintf(buffer, sizeof(buffer),
- "Connection #%ld to host %s left intact",
- conn->connection_id,
- conn->bits.socksproxy ? conn->socks_proxy.host.dispname :
- conn->bits.httpproxy ? conn->http_proxy.host.dispname :
- conn->bits.conn_to_host ? conn->conn_to_host.dispname :
- conn->host.dispname);
+ msnprintf(buffer, sizeof(buffer),
+ "Connection #%ld to host %s left intact",
+ conn->connection_id,
+ conn->bits.socksproxy ? conn->socks_proxy.host.dispname :
+ conn->bits.httpproxy ? conn->http_proxy.host.dispname :
+ conn->bits.conn_to_host ? conn->conn_to_host.dispname :
+ conn->host.dispname);
/* the connection is no longer in use by this transfer */
if(Curl_conncache_return_conn(conn)) {
@@ -652,10 +625,6 @@ static CURLcode multi_done(struct connectdata **connp,
data->state.lastconnect = NULL;
}
- *connp = NULL; /* to make the caller of this function better detect that
- this was either closed or handed over to the connection
- cache here, and therefore cannot be used from this point on
- */
Curl_free_request_state(data);
return result;
}
@@ -684,7 +653,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
return CURLM_RECURSIVE_API_CALL;
premature = (data->mstate < CURLM_STATE_COMPLETED) ? TRUE : FALSE;
- easy_owns_conn = (data->easy_conn && (data->easy_conn->data == easy)) ?
+ easy_owns_conn = (data->conn && (data->conn->data == easy)) ?
TRUE : FALSE;
/* If the 'state' is not INIT or COMPLETED, we might need to do something
@@ -695,16 +664,13 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
multi->num_alive--;
}
- if(data->easy_conn &&
+ if(data->conn &&
data->mstate > CURLM_STATE_DO &&
data->mstate < CURLM_STATE_COMPLETED) {
/* Set connection owner so that the DONE function closes it. We can
safely do this here since connection is killed. */
- data->easy_conn->data = easy;
- /* If the handle is in a pipeline and has started sending off its
- request but not received its response yet, we need to close
- connection. */
- streamclose(data->easy_conn, "Removed with partial response");
+ data->conn->data = easy;
+ streamclose(data->conn, "Removed with partial response");
easy_owns_conn = TRUE;
}
@@ -713,7 +679,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
curl_easy_cleanup is called. */
Curl_expire_clear(data);
- if(data->easy_conn) {
+ if(data->conn) {
/* we must call multi_done() here (if we still own the connection) so that
we don't leave a half-baked one around */
@@ -724,11 +690,8 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
Note that this ignores the return code simply because there's
nothing really useful to do with it anyway! */
- (void)multi_done(&data->easy_conn, data->result, premature);
+ (void)multi_done(data, data->result, premature);
}
- else
- /* Clear connection pipelines, if multi_done above was not called */
- Curl_getoff_all_pipelines(data, data->easy_conn);
}
if(data->connect_queue.ptr)
@@ -760,9 +723,9 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
vanish with this handle */
/* Remove the association between the connection and the handle */
- if(data->easy_conn) {
- data->easy_conn->data = NULL;
- data->easy_conn = NULL;
+ if(data->conn) {
+ data->conn->data = NULL;
+ detach_connnection(data);
}
#ifdef USE_LIBPSL
@@ -806,15 +769,31 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
return CURLM_OK;
}
-/* Return TRUE if the application asked for a certain set of pipelining */
-bool Curl_pipeline_wanted(const struct Curl_multi *multi, int bits)
+/* Return TRUE if the application asked for multiplexing */
+bool Curl_multiplex_wanted(const struct Curl_multi *multi)
+{
+ return (multi && (multi->multiplexing));
+}
+
+/* This is the only function that should clear data->conn. This will
+ occasionally be called with the pointer already cleared. */
+static void detach_connnection(struct Curl_easy *data)
{
- return (multi && (multi->pipelining & bits)) ? TRUE : FALSE;
+ struct connectdata *conn = data->conn;
+ if(conn)
+ Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL);
+ data->conn = NULL;
}
-void Curl_multi_handlePipeBreak(struct Curl_easy *data)
+/* This is the only function that should assign data->conn */
+void Curl_attach_connnection(struct Curl_easy *data,
+ struct connectdata *conn)
{
- data->easy_conn = NULL;
+ DEBUGASSERT(!data->conn);
+ DEBUGASSERT(conn);
+ data->conn = conn;
+ Curl_llist_insert_next(&conn->easyq, conn->easyq.tail, data,
+ &data->conn_queue);
}
static int waitconnect_getsock(struct connectdata *conn,
@@ -878,13 +857,13 @@ static int multi_getsock(struct Curl_easy *data,
/* The no connection case can happen when this is called from
curl_multi_remove_handle() => singlesocket() => multi_getsock().
*/
- if(!data->easy_conn)
+ if(!data->conn)
return 0;
if(data->mstate > CURLM_STATE_CONNECT &&
data->mstate < CURLM_STATE_COMPLETED) {
/* Set up ownership correctly */
- data->easy_conn->data = data;
+ data->conn->data = data;
}
switch(data->mstate) {
@@ -905,31 +884,30 @@ static int multi_getsock(struct Curl_easy *data,
return 0;
case CURLM_STATE_WAITRESOLVE:
- return Curl_resolv_getsock(data->easy_conn, socks, numsocks);
+ return Curl_resolv_getsock(data->conn, socks, numsocks);
case CURLM_STATE_PROTOCONNECT:
case CURLM_STATE_SENDPROTOCONNECT:
- return Curl_protocol_getsock(data->easy_conn, socks, numsocks);
+ return Curl_protocol_getsock(data->conn, socks, numsocks);
case CURLM_STATE_DO:
case CURLM_STATE_DOING:
- return Curl_doing_getsock(data->easy_conn, socks, numsocks);
+ return Curl_doing_getsock(data->conn, socks, numsocks);
case CURLM_STATE_WAITPROXYCONNECT:
- return waitproxyconnect_getsock(data->easy_conn, socks, numsocks);
+ return waitproxyconnect_getsock(data->conn, socks, numsocks);
case CURLM_STATE_WAITCONNECT:
- return waitconnect_getsock(data->easy_conn, socks, numsocks);
+ return waitconnect_getsock(data->conn, socks, numsocks);
case CURLM_STATE_DO_MORE:
- return domore_getsock(data->easy_conn, socks, numsocks);
+ return domore_getsock(data->conn, socks, numsocks);
case CURLM_STATE_DO_DONE: /* since is set after DO is completed, we switch
to waiting for the same as the *PERFORM
states */
case CURLM_STATE_PERFORM:
- case CURLM_STATE_WAITPERFORM:
- return Curl_single_getsock(data->easy_conn, socks, numsocks);
+ return Curl_single_getsock(data->conn, socks, numsocks);
}
}
@@ -985,11 +963,12 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi,
#define NUM_POLLS_ON_STACK 10
-CURLMcode curl_multi_wait(struct Curl_multi *multi,
+CURLMcode Curl_multi_wait(struct Curl_multi *multi,
struct curl_waitfd extra_fds[],
unsigned int extra_nfds,
int timeout_ms,
- int *ret)
+ int *ret,
+ bool *gotsocket) /* if any socket was checked */
{
struct Curl_easy *data;
curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
@@ -997,11 +976,14 @@ CURLMcode curl_multi_wait(struct Curl_multi *multi,
unsigned int i;
unsigned int nfds = 0;
unsigned int curlfds;
- struct pollfd *ufds = NULL;
bool ufds_malloc = FALSE;
long timeout_internal;
int retcode = 0;
struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
+ struct pollfd *ufds = &a_few_on_stack[0];
+
+ if(gotsocket)
+ *gotsocket = FALSE;
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
@@ -1043,19 +1025,15 @@ CURLMcode curl_multi_wait(struct Curl_multi *multi,
curlfds = nfds; /* number of internal file descriptors */
nfds += extra_nfds; /* add the externally provided ones */
- if(nfds) {
- if(nfds > NUM_POLLS_ON_STACK) {
- /* 'nfds' is a 32 bit value and 'struct pollfd' is typically 8 bytes
- big, so at 2^29 sockets this value might wrap. When a process gets
- the capability to actually handle over 500 million sockets this
- calculation needs a integer overflow check. */
- ufds = malloc(nfds * sizeof(struct pollfd));
- if(!ufds)
- return CURLM_OUT_OF_MEMORY;
- ufds_malloc = TRUE;
- }
- else
- ufds = &a_few_on_stack[0];
+ if(nfds > NUM_POLLS_ON_STACK) {
+ /* 'nfds' is a 32 bit value and 'struct pollfd' is typically 8 bytes
+ big, so at 2^29 sockets this value might wrap. When a process gets
+ the capability to actually handle over 500 million sockets this
+ calculation needs a integer overflow check. */
+ ufds = malloc(nfds * sizeof(struct pollfd));
+ if(!ufds)
+ return CURLM_OUT_OF_MEMORY;
+ ufds_malloc = TRUE;
}
nfds = 0;
@@ -1135,19 +1113,20 @@ CURLMcode curl_multi_wait(struct Curl_multi *multi,
free(ufds);
if(ret)
*ret = retcode;
+ if(gotsocket && (extra_fds || curlfds))
+ /* if any socket was checked */
+ *gotsocket = TRUE;
+
return CURLM_OK;
}
-/*
- * Curl_multi_connchanged() is called to tell that there is a connection in
- * this multi handle that has changed state (pipelining become possible, the
- * number of allowed streams changed or similar), and a subsequent use of this
- * multi handle should move CONNECT_PEND handles back to CONNECT to have them
- * retry.
- */
-void Curl_multi_connchanged(struct Curl_multi *multi)
+CURLMcode curl_multi_wait(struct Curl_multi *multi,
+ struct curl_waitfd extra_fds[],
+ unsigned int extra_nfds,
+ int timeout_ms,
+ int *ret)
{
- multi->recheckstate = TRUE;
+ return Curl_multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, NULL);
}
/*
@@ -1185,111 +1164,36 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
/* take this handle to the perform state right away */
multistate(data, CURLM_STATE_PERFORM);
- data->easy_conn = conn;
+ Curl_attach_connnection(data, conn);
k->keepon |= KEEP_RECV; /* setup to receive! */
}
return rc;
}
-static CURLcode multi_reconnect_request(struct connectdata **connp)
-{
- CURLcode result = CURLE_OK;
- struct connectdata *conn = *connp;
- struct Curl_easy *data = conn->data;
-
- /* This was a re-use of a connection and we got a write error in the
- * DO-phase. Then we DISCONNECT this connection and have another attempt to
- * CONNECT and then DO again! The retry cannot possibly find another
- * connection to re-use, since we only keep one possible connection for
- * each. */
-
- infof(data, "Re-used connection seems dead, get a new one\n");
-
- connclose(conn, "Reconnect dead connection"); /* enforce close */
- result = multi_done(&conn, result, FALSE); /* we are so done with this */
-
- /* conn may no longer be a good pointer, clear it to avoid mistakes by
- parent functions */
- *connp = NULL;
-
- /*
- * We need to check for CURLE_SEND_ERROR here as well. This could happen
- * when the request failed on a FTP connection and thus multi_done() itself
- * tried to use the connection (again).
- */
- if(!result || (CURLE_SEND_ERROR == result)) {
- bool async;
- bool protocol_done = TRUE;
-
- /* Now, redo the connect and get a new connection */
- result = Curl_connect(data, connp, &async, &protocol_done);
- if(!result) {
- /* We have connected or sent away a name resolve query fine */
-
- conn = *connp; /* setup conn to again point to something nice */
- if(async) {
- /* Now, if async is TRUE here, we need to wait for the name
- to resolve */
- result = Curl_resolver_wait_resolv(conn, NULL);
- if(result)
- return result;
-
- /* Resolved, continue with the connection */
- result = Curl_once_resolved(conn, &protocol_done);
- if(result)
- return result;
- }
- }
- }
-
- return result;
-}
-
/*
* do_complete is called when the DO actions are complete.
*
* We init chunking and trailer bits to their default values here immediately
- * before receiving any header data for the current request in the pipeline.
+ * before receiving any header data for the current request.
*/
static void do_complete(struct connectdata *conn)
{
conn->data->req.chunk = FALSE;
- conn->data->req.maxfd = (conn->sockfd>conn->writesockfd?
- conn->sockfd:conn->writesockfd) + 1;
Curl_pgrsTime(conn->data, TIMER_PRETRANSFER);
}
-static CURLcode multi_do(struct connectdata **connp, bool *done)
+static CURLcode multi_do(struct Curl_easy *data, bool *done)
{
CURLcode result = CURLE_OK;
- struct connectdata *conn = *connp;
- struct Curl_easy *data = conn->data;
+ struct connectdata *conn = data->conn;
+
+ DEBUGASSERT(conn);
+ DEBUGASSERT(conn->handler);
if(conn->handler->do_it) {
/* generic protocol-specific function pointer set in curl_connect() */
result = conn->handler->do_it(conn, done);
- /* This was formerly done in transfer.c, but we better do it here */
- if((CURLE_SEND_ERROR == result) && conn->bits.reuse) {
- /*
- * If the connection is using an easy handle, call reconnect
- * to re-establish the connection. Otherwise, let the multi logic
- * figure out how to re-establish the connection.
- */
- if(!data->multi) {
- result = multi_reconnect_request(connp);
-
- if(!result) {
- /* ... finally back to actually retry the DO phase */
- conn = *connp; /* re-assign conn since multi_reconnect_request
- creates a new connection */
- result = conn->handler->do_it(conn, done);
- }
- }
- else
- return result;
- }
-
if(!result && *done)
/* do_complete must be called after the protocol-specific DO function */
do_complete(conn);
@@ -1302,8 +1206,6 @@ static CURLcode multi_do(struct connectdata **connp, bool *done)
* second stage DO state which (wrongly) was introduced to support FTP's
* second connection.
*
- * TODO: A future libcurl should be able to work away this state.
- *
* 'complete' can return 0 for incomplete, 1 for done and -1 for go back to
* DOING state there's more work to do!
*/
@@ -1336,9 +1238,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
bool done = FALSE;
CURLMcode rc;
CURLcode result = CURLE_OK;
- struct SingleRequest *k;
- time_t timeout_ms;
- time_t recv_timeout_ms;
+ timediff_t timeout_ms;
+ timediff_t recv_timeout_ms;
timediff_t send_timeout_ms;
int control;
@@ -1351,56 +1252,59 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
bool stream_error = FALSE;
rc = CURLM_OK;
- if(!data->easy_conn &&
+ if(!data->conn &&
data->mstate > CURLM_STATE_CONNECT &&
data->mstate < CURLM_STATE_DONE) {
- /* In all these states, the code will blindly access 'data->easy_conn'
+ /* In all these states, the code will blindly access 'data->conn'
so this is precaution that it isn't NULL. And it silences static
analyzers. */
- failf(data, "In state %d with no easy_conn, bail out!\n", data->mstate);
+ failf(data, "In state %d with no conn, bail out!\n", data->mstate);
return CURLM_INTERNAL_ERROR;
}
if(multi_ischanged(multi, TRUE)) {
DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue!\n"));
- process_pending_handles(multi); /* pipelined/multiplexed */
+ process_pending_handles(multi); /* multiplexed */
}
- if(data->easy_conn && data->mstate > CURLM_STATE_CONNECT &&
+ if(data->conn && data->mstate > CURLM_STATE_CONNECT &&
data->mstate < CURLM_STATE_COMPLETED) {
/* Make sure we set the connection's current owner */
- data->easy_conn->data = data;
+ data->conn->data = data;
}
- if(data->easy_conn &&
+ if(data->conn &&
(data->mstate >= CURLM_STATE_CONNECT) &&
(data->mstate < CURLM_STATE_COMPLETED)) {
/* we need to wait for the connect state as only then is the start time
stored, but we must not check already completed handles */
timeout_ms = Curl_timeleft(data, &now,
- (data->mstate <= CURLM_STATE_WAITDO)?
+ (data->mstate <= CURLM_STATE_DO)?
TRUE:FALSE);
if(timeout_ms < 0) {
/* Handle timed out */
if(data->mstate == CURLM_STATE_WAITRESOLVE)
- failf(data, "Resolving timed out after %ld milliseconds",
+ failf(data, "Resolving timed out after %" CURL_FORMAT_TIMEDIFF_T
+ " milliseconds",
Curl_timediff(now, data->progress.t_startsingle));
else if(data->mstate == CURLM_STATE_WAITCONNECT)
- failf(data, "Connection timed out after %ld milliseconds",
+ failf(data, "Connection timed out after %" CURL_FORMAT_TIMEDIFF_T
+ " milliseconds",
Curl_timediff(now, data->progress.t_startsingle));
else {
- k = &data->req;
+ struct SingleRequest *k = &data->req;
if(k->size != -1) {
- failf(data, "Operation timed out after %ld milliseconds with %"
- CURL_FORMAT_CURL_OFF_T " out of %"
+ failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
+ " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %"
CURL_FORMAT_CURL_OFF_T " bytes received",
Curl_timediff(now, data->progress.t_startsingle),
k->bytecount, k->size);
}
else {
- failf(data, "Operation timed out after %ld milliseconds with %"
- CURL_FORMAT_CURL_OFF_T " bytes received",
+ failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
+ " milliseconds with %" CURL_FORMAT_CURL_OFF_T
+ " bytes received",
Curl_timediff(now, data->progress.t_startsingle),
k->bytecount);
}
@@ -1408,11 +1312,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* Force connection closed if the connection has indeed been used */
if(data->mstate > CURLM_STATE_DO) {
- streamclose(data->easy_conn, "Disconnected with pending data");
+ streamclose(data->conn, "Disconnected with pending data");
stream_error = TRUE;
}
result = CURLE_OPERATION_TIMEDOUT;
- (void)multi_done(&data->easy_conn, result, TRUE);
+ (void)multi_done(data, result, TRUE);
/* Skip the statemachine and go directly to error handling section. */
goto statemachine_end;
}
@@ -1439,8 +1343,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
case CURLM_STATE_CONNECT:
/* Connect. We want to get a connection identifier filled in. */
Curl_pgrsTime(data, TIMER_STARTSINGLE);
- result = Curl_connect(data, &data->easy_conn,
- &async, &protocol_connect);
+ if(data->set.timeout)
+ Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT);
+
+ if(data->set.connecttimeout)
+ Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT);
+
+ result = Curl_connect(data, &async, &protocol_connect);
if(CURLE_NO_CONNECTION_AVAILABLE == result) {
/* There was no connection available. We will go to the pending
state and wait for an available connection. */
@@ -1452,33 +1361,31 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
result = CURLE_OK;
break;
}
+ else if(data->state.previouslypending) {
+ /* this transfer comes from the pending queue so try move another */
+ infof(data, "Transfer was pending, now try another\n");
+ process_pending_handles(data->multi);
+ }
if(!result) {
- /* Add this handle to the send or pend pipeline */
- result = Curl_add_handle_to_pipeline(data, data->easy_conn);
- if(result)
- stream_error = TRUE;
+ if(async)
+ /* We're now waiting for an asynchronous name lookup */
+ multistate(data, CURLM_STATE_WAITRESOLVE);
else {
- if(async)
- /* We're now waiting for an asynchronous name lookup */
- multistate(data, CURLM_STATE_WAITRESOLVE);
- else {
- /* after the connect has been sent off, go WAITCONNECT unless the
- protocol connect is already done and we can go directly to
- WAITDO or DO! */
- rc = CURLM_CALL_MULTI_PERFORM;
+ /* after the connect has been sent off, go WAITCONNECT unless the
+ protocol connect is already done and we can go directly to
+ WAITDO or DO! */
+ rc = CURLM_CALL_MULTI_PERFORM;
- if(protocol_connect)
- multistate(data, Curl_pipeline_wanted(multi, CURLPIPE_HTTP1)?
- CURLM_STATE_WAITDO:CURLM_STATE_DO);
- else {
+ if(protocol_connect)
+ multistate(data, CURLM_STATE_DO);
+ else {
#ifndef CURL_DISABLE_HTTP
- if(Curl_connect_ongoing(data->easy_conn))
- multistate(data, CURLM_STATE_WAITPROXYCONNECT);
- else
+ if(Curl_connect_ongoing(data->conn))
+ multistate(data, CURLM_STATE_WAITPROXYCONNECT);
+ else
#endif
- multistate(data, CURLM_STATE_WAITCONNECT);
- }
+ multistate(data, CURLM_STATE_WAITCONNECT);
}
}
}
@@ -1488,9 +1395,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* awaiting an asynch name resolve to complete */
{
struct Curl_dns_entry *dns = NULL;
- struct connectdata *conn = data->easy_conn;
+ struct connectdata *conn = data->conn;
const char *hostname;
+ DEBUGASSERT(conn);
if(conn->bits.httpproxy)
hostname = conn->http_proxy.host.name;
else if(conn->bits.conn_to_host)
@@ -1511,7 +1419,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
if(!dns)
- result = Curl_resolv_check(data->easy_conn, &dns);
+ result = Curl_resolv_check(data->conn, &dns);
/* Update sockets here, because the socket(s) may have been
closed and the application thus needs to be told, even if it
@@ -1524,21 +1432,20 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
if(dns) {
/* Perform the next step in the connection phase, and then move on
to the WAITCONNECT state */
- result = Curl_once_resolved(data->easy_conn, &protocol_connect);
+ result = Curl_once_resolved(data->conn, &protocol_connect);
if(result)
/* if Curl_once_resolved() returns failure, the connection struct
is already freed and gone */
- data->easy_conn = NULL; /* no more connection */
+ data->conn = NULL; /* no more connection */
else {
/* call again please so that we get the next socket setup */
rc = CURLM_CALL_MULTI_PERFORM;
if(protocol_connect)
- multistate(data, Curl_pipeline_wanted(multi, CURLPIPE_HTTP1)?
- CURLM_STATE_WAITDO:CURLM_STATE_DO);
+ multistate(data, CURLM_STATE_DO);
else {
#ifndef CURL_DISABLE_HTTP
- if(Curl_connect_ongoing(data->easy_conn))
+ if(Curl_connect_ongoing(data->conn))
multistate(data, CURLM_STATE_WAITPROXYCONNECT);
else
#endif
@@ -1558,19 +1465,20 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
#ifndef CURL_DISABLE_HTTP
case CURLM_STATE_WAITPROXYCONNECT:
/* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */
- result = Curl_http_connect(data->easy_conn, &protocol_connect);
+ DEBUGASSERT(data->conn);
+ result = Curl_http_connect(data->conn, &protocol_connect);
- if(data->easy_conn->bits.proxy_connect_closed) {
+ if(data->conn->bits.proxy_connect_closed) {
rc = CURLM_CALL_MULTI_PERFORM;
/* connect back to proxy again */
result = CURLE_OK;
- multi_done(&data->easy_conn, CURLE_OK, FALSE);
+ multi_done(data, CURLE_OK, FALSE);
multistate(data, CURLM_STATE_CONNECT);
}
else if(!result) {
- if((data->easy_conn->http_proxy.proxytype != CURLPROXY_HTTPS ||
- data->easy_conn->bits.proxy_ssl_connected[FIRSTSOCKET]) &&
- Curl_connect_complete(data->easy_conn)) {
+ if((data->conn->http_proxy.proxytype != CURLPROXY_HTTPS ||
+ data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) &&
+ Curl_connect_complete(data->conn)) {
rc = CURLM_CALL_MULTI_PERFORM;
/* initiate protocol connect phase */
multistate(data, CURLM_STATE_SENDPROTOCONNECT);
@@ -1583,101 +1491,94 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
case CURLM_STATE_WAITCONNECT:
/* awaiting a completion of an asynch TCP connect */
- result = Curl_is_connected(data->easy_conn, FIRSTSOCKET, &connected);
+ DEBUGASSERT(data->conn);
+ result = Curl_is_connected(data->conn, FIRSTSOCKET, &connected);
if(connected && !result) {
#ifndef CURL_DISABLE_HTTP
- if((data->easy_conn->http_proxy.proxytype == CURLPROXY_HTTPS &&
- !data->easy_conn->bits.proxy_ssl_connected[FIRSTSOCKET]) ||
- Curl_connect_ongoing(data->easy_conn)) {
+ if((data->conn->http_proxy.proxytype == CURLPROXY_HTTPS &&
+ !data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) ||
+ Curl_connect_ongoing(data->conn)) {
multistate(data, CURLM_STATE_WAITPROXYCONNECT);
break;
}
#endif
rc = CURLM_CALL_MULTI_PERFORM;
- multistate(data, data->easy_conn->bits.tunnel_proxy?
+ multistate(data, data->conn->bits.tunnel_proxy?
CURLM_STATE_WAITPROXYCONNECT:
CURLM_STATE_SENDPROTOCONNECT);
}
else if(result) {
/* failure detected */
- /* Just break, the cleaning up is handled all in one place */
+ Curl_posttransfer(data);
+ multi_done(data, result, TRUE);
stream_error = TRUE;
break;
}
break;
case CURLM_STATE_SENDPROTOCONNECT:
- result = Curl_protocol_connect(data->easy_conn, &protocol_connect);
+ result = Curl_protocol_connect(data->conn, &protocol_connect);
if(!result && !protocol_connect)
/* switch to waiting state */
multistate(data, CURLM_STATE_PROTOCONNECT);
else if(!result) {
/* protocol connect has completed, go WAITDO or DO */
- multistate(data, Curl_pipeline_wanted(multi, CURLPIPE_HTTP1)?
- CURLM_STATE_WAITDO:CURLM_STATE_DO);
+ multistate(data, CURLM_STATE_DO);
rc = CURLM_CALL_MULTI_PERFORM;
}
else if(result) {
/* failure detected */
Curl_posttransfer(data);
- multi_done(&data->easy_conn, result, TRUE);
+ multi_done(data, result, TRUE);
stream_error = TRUE;
}
break;
case CURLM_STATE_PROTOCONNECT:
/* protocol-specific connect phase */
- result = Curl_protocol_connecting(data->easy_conn, &protocol_connect);
+ result = Curl_protocol_connecting(data->conn, &protocol_connect);
if(!result && protocol_connect) {
/* after the connect has completed, go WAITDO or DO */
- multistate(data, Curl_pipeline_wanted(multi, CURLPIPE_HTTP1)?
- CURLM_STATE_WAITDO:CURLM_STATE_DO);
+ multistate(data, CURLM_STATE_DO);
rc = CURLM_CALL_MULTI_PERFORM;
}
else if(result) {
/* failure detected */
Curl_posttransfer(data);
- multi_done(&data->easy_conn, result, TRUE);
+ multi_done(data, result, TRUE);
stream_error = TRUE;
}
break;
- case CURLM_STATE_WAITDO:
- /* Wait for our turn to DO when we're pipelining requests */
- if(Curl_pipeline_checkget_write(data, data->easy_conn)) {
- /* Grabbed the channel */
- multistate(data, CURLM_STATE_DO);
- rc = CURLM_CALL_MULTI_PERFORM;
- }
- break;
-
case CURLM_STATE_DO:
if(data->set.connect_only) {
/* keep connection open for application to use the socket */
- connkeep(data->easy_conn, "CONNECT_ONLY");
+ connkeep(data->conn, "CONNECT_ONLY");
multistate(data, CURLM_STATE_DONE);
result = CURLE_OK;
rc = CURLM_CALL_MULTI_PERFORM;
}
else {
/* Perform the protocol's DO action */
- result = multi_do(&data->easy_conn, &dophase_done);
+ result = multi_do(data, &dophase_done);
- /* When multi_do() returns failure, data->easy_conn might be NULL! */
+ /* When multi_do() returns failure, data->conn might be NULL! */
if(!result) {
if(!dophase_done) {
+#ifndef CURL_DISABLE_FTP
/* some steps needed for wildcard matching */
if(data->state.wildcardmatch) {
struct WildcardData *wc = &data->wildcard;
if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) {
/* skip some states if it is important */
- multi_done(&data->easy_conn, CURLE_OK, FALSE);
+ multi_done(data, CURLE_OK, FALSE);
multistate(data, CURLM_STATE_DONE);
rc = CURLM_CALL_MULTI_PERFORM;
break;
}
}
+#endif
/* DO was not completed in one function call, we must continue
DOING... */
multistate(data, CURLM_STATE_DOING);
@@ -1685,7 +1586,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
/* after DO, go DO_DONE... or DO_MORE */
- else if(data->easy_conn->bits.do_more) {
+ else if(data->conn->bits.do_more) {
/* we're supposed to do more, but we need to sit down, relax
and wait a little while first */
multistate(data, CURLM_STATE_DO_MORE);
@@ -1698,7 +1599,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
}
else if((CURLE_SEND_ERROR == result) &&
- data->easy_conn->bits.reuse) {
+ data->conn->bits.reuse) {
/*
* In this situation, a connection that we were trying to use
* may have unexpectedly died. If possible, send the connection
@@ -1708,7 +1609,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
followtype follow = FOLLOW_NONE;
CURLcode drc;
- drc = Curl_retry_request(data->easy_conn, &newurl);
+ drc = Curl_retry_request(data->conn, &newurl);
if(drc) {
/* a failure here pretty much implies an out of memory */
result = drc;
@@ -1716,7 +1617,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
Curl_posttransfer(data);
- drc = multi_done(&data->easy_conn, result, FALSE);
+ drc = multi_done(data, result, FALSE);
/* When set to retry the connection, we must to go back to
* the CONNECT state */
@@ -1748,8 +1649,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
else {
/* failure detected */
Curl_posttransfer(data);
- if(data->easy_conn)
- multi_done(&data->easy_conn, result, FALSE);
+ if(data->conn)
+ multi_done(data, result, FALSE);
stream_error = TRUE;
}
}
@@ -1757,12 +1658,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
case CURLM_STATE_DOING:
/* we continue DOING until the DO phase is complete */
- result = Curl_protocol_doing(data->easy_conn,
+ DEBUGASSERT(data->conn);
+ result = Curl_protocol_doing(data->conn,
&dophase_done);
if(!result) {
if(dophase_done) {
/* after DO, go DO_DONE or DO_MORE */
- multistate(data, data->easy_conn->bits.do_more?
+ multistate(data, data->conn->bits.do_more?
CURLM_STATE_DO_MORE:
CURLM_STATE_DO_DONE);
rc = CURLM_CALL_MULTI_PERFORM;
@@ -1771,7 +1673,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
else {
/* failure detected */
Curl_posttransfer(data);
- multi_done(&data->easy_conn, result, FALSE);
+ multi_done(data, result, FALSE);
stream_error = TRUE;
}
break;
@@ -1780,10 +1682,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/*
* When we are connected, DO MORE and then go DO_DONE
*/
- result = multi_do_more(data->easy_conn, &control);
+ DEBUGASSERT(data->conn);
+ result = multi_do_more(data->conn, &control);
- /* No need to remove this handle from the send pipeline here since that
- is done in multi_done() */
if(!result) {
if(control) {
/* if positive, advance to DO_DONE
@@ -1800,46 +1701,38 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
else {
/* failure detected */
Curl_posttransfer(data);
- multi_done(&data->easy_conn, result, FALSE);
+ multi_done(data, result, FALSE);
stream_error = TRUE;
}
break;
case CURLM_STATE_DO_DONE:
- /* Move ourselves from the send to recv pipeline */
- Curl_move_handle_from_send_to_recv_pipe(data, data->easy_conn);
-
- if(data->easy_conn->bits.multiplex || data->easy_conn->send_pipe.size)
+ DEBUGASSERT(data->conn);
+ if(data->conn->bits.multiplex)
/* Check if we can move pending requests to send pipe */
- process_pending_handles(multi); /* pipelined/multiplexed */
+ process_pending_handles(multi); /* multiplexed */
/* Only perform the transfer if there's a good socket to work with.
Having both BAD is a signal to skip immediately to DONE */
- if((data->easy_conn->sockfd != CURL_SOCKET_BAD) ||
- (data->easy_conn->writesockfd != CURL_SOCKET_BAD))
- multistate(data, CURLM_STATE_WAITPERFORM);
+ if((data->conn->sockfd != CURL_SOCKET_BAD) ||
+ (data->conn->writesockfd != CURL_SOCKET_BAD))
+ multistate(data, CURLM_STATE_PERFORM);
else {
+#ifndef CURL_DISABLE_FTP
if(data->state.wildcardmatch &&
- ((data->easy_conn->handler->flags & PROTOPT_WILDCARD) == 0)) {
- data->wildcard.state = CURLWC_DONE;
+ ((data->conn->handler->flags & PROTOPT_WILDCARD) == 0)) {
+ data->wildcard.state = CURLWC_DONE;
}
+#endif
multistate(data, CURLM_STATE_DONE);
}
rc = CURLM_CALL_MULTI_PERFORM;
break;
- case CURLM_STATE_WAITPERFORM:
- /* Wait for our turn to PERFORM */
- if(Curl_pipeline_checkget_read(data, data->easy_conn)) {
- /* Grabbed the channel */
- multistate(data, CURLM_STATE_PERFORM);
- rc = CURLM_CALL_MULTI_PERFORM;
- }
- break;
-
case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */
+ DEBUGASSERT(data->conn);
/* if both rates are within spec, resume transfer */
- if(Curl_pgrsUpdate(data->easy_conn))
+ if(Curl_pgrsUpdate(data->conn))
result = CURLE_ABORTED_BY_CALLBACK;
else
result = Curl_speedcheck(data, now);
@@ -1909,24 +1802,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
/* read/write data if it is ready to do so */
- result = Curl_readwrite(data->easy_conn, data, &done, &comeback);
-
- k = &data->req;
-
- if(!(k->keepon & KEEP_RECV))
- /* We're done receiving */
- Curl_pipeline_leave_read(data->easy_conn);
-
- if(!(k->keepon & KEEP_SEND))
- /* We're done sending */
- Curl_pipeline_leave_write(data->easy_conn);
+ result = Curl_readwrite(data->conn, data, &done, &comeback);
if(done || (result == CURLE_RECV_ERROR)) {
/* If CURLE_RECV_ERROR happens early enough, we assume it was a race
* condition and the server closed the re-used connection exactly when
* we wanted to use it, so figure out if that is indeed the case.
*/
- CURLcode ret = Curl_retry_request(data->easy_conn, &newurl);
+ CURLcode ret = Curl_retry_request(data->conn, &newurl);
if(!ret)
retry = (newurl)?TRUE:FALSE;
else if(!result)
@@ -1939,6 +1822,27 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
done = TRUE;
}
}
+ else if((CURLE_HTTP2_STREAM == result) &&
+ Curl_h2_http_1_1_error(data->conn)) {
+ CURLcode ret = Curl_retry_request(data->conn, &newurl);
+
+ if(!ret) {
+ infof(data, "Downgrades to HTTP/1.1!\n");
+ data->set.httpversion = CURL_HTTP_VERSION_1_1;
+ /* clear the error message bit too as we ignore the one we got */
+ data->state.errorbuf = FALSE;
+ if(!newurl)
+ /* typically for HTTP_1_1_REQUIRED error on first flight */
+ newurl = strdup(data->change.url);
+ /* if we are to retry, set the result to OK and consider the request
+ as done */
+ retry = TRUE;
+ result = CURLE_OK;
+ done = TRUE;
+ }
+ else
+ result = ret;
+ }
if(result) {
/*
@@ -1949,12 +1853,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
* happened in the data connection.
*/
- if(!(data->easy_conn->handler->flags & PROTOPT_DUAL) &&
+ if(!(data->conn->handler->flags & PROTOPT_DUAL) &&
result != CURLE_HTTP2_STREAM)
- streamclose(data->easy_conn, "Transfer returned error");
+ streamclose(data->conn, "Transfer returned error");
Curl_posttransfer(data);
- multi_done(&data->easy_conn, result, TRUE);
+ multi_done(data, result, TRUE);
}
else if(done) {
followtype follow = FOLLOW_NONE;
@@ -1962,13 +1866,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* call this even if the readwrite function returned error */
Curl_posttransfer(data);
- /* we're no longer receiving */
- Curl_removeHandleFromPipeline(data, &data->easy_conn->recv_pipe);
-
- /* expire the new receiving pipeline head */
- if(data->easy_conn->recv_pipe.head)
- Curl_expire(data->easy_conn->recv_pipe.head->ptr, 0, EXPIRE_RUN_NOW);
-
/* When we follow redirects or is set to retry the connection, we must
to go back to the CONNECT state */
if(data->req.newurl || retry) {
@@ -1982,13 +1879,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
}
else
follow = FOLLOW_RETRY;
- result = multi_done(&data->easy_conn, CURLE_OK, FALSE);
+ (void)multi_done(data, CURLE_OK, FALSE);
+ /* multi_done() might return CURLE_GOT_NOTHING */
+ result = Curl_follow(data, newurl, follow);
if(!result) {
- result = Curl_follow(data, newurl, follow);
- if(!result) {
- multistate(data, CURLM_STATE_CONNECT);
- rc = CURLM_CALL_MULTI_PERFORM;
- }
+ multistate(data, CURLM_STATE_CONNECT);
+ rc = CURLM_CALL_MULTI_PERFORM;
}
free(newurl);
}
@@ -2005,7 +1901,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
free(newurl);
if(result) {
stream_error = TRUE;
- result = multi_done(&data->easy_conn, result, TRUE);
+ result = multi_done(data, result, TRUE);
}
}
@@ -2024,33 +1920,31 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* this state is highly transient, so run another loop after this */
rc = CURLM_CALL_MULTI_PERFORM;
- if(data->easy_conn) {
+ if(data->conn) {
CURLcode res;
- /* Remove ourselves from the receive pipeline, if we are there. */
- Curl_removeHandleFromPipeline(data, &data->easy_conn->recv_pipe);
-
- if(data->easy_conn->bits.multiplex || data->easy_conn->send_pipe.size)
+ if(data->conn->bits.multiplex)
/* Check if we can move pending requests to connection */
- process_pending_handles(multi); /* pipelined/multiplexing */
+ process_pending_handles(multi); /* multiplexing */
/* post-transfer command */
- res = multi_done(&data->easy_conn, result, FALSE);
+ res = multi_done(data, result, FALSE);
/* allow a previously set error code take precedence */
if(!result)
result = res;
/*
- * If there are other handles on the pipeline, multi_done won't set
- * easy_conn to NULL. In such a case, curl_multi_remove_handle() can
+ * If there are other handles on the connection, multi_done won't set
+ * conn to NULL. In such a case, curl_multi_remove_handle() can
* access free'd data, if the connection is free'd and the handle
* removed before we perform the processing in CURLM_STATE_COMPLETED
*/
- if(data->easy_conn)
- data->easy_conn = NULL;
+ if(data->conn)
+ detach_connnection(data);
}
+#ifndef CURL_DISABLE_FTP
if(data->state.wildcardmatch) {
if(data->wildcard.state != CURLWC_DONE) {
/* if a wildcard is set and we are not ending -> lets start again
@@ -2059,7 +1953,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
break;
}
}
-
+#endif
/* after we have DONE what we're supposed to do, go COMPLETED, and
it doesn't matter what the multi_done() returned! */
multistate(data, CURLM_STATE_COMPLETED);
@@ -2090,23 +1984,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
/* Check if we can move pending requests to send pipe */
process_pending_handles(multi); /* connection */
- if(data->easy_conn) {
- /* if this has a connection, unsubscribe from the pipelines */
- Curl_pipeline_leave_write(data->easy_conn);
- Curl_pipeline_leave_read(data->easy_conn);
- Curl_removeHandleFromPipeline(data, &data->easy_conn->send_pipe);
- Curl_removeHandleFromPipeline(data, &data->easy_conn->recv_pipe);
-
+ if(data->conn) {
if(stream_error) {
/* Don't attempt to send data over a connection that timed out */
bool dead_connection = result == CURLE_OPERATION_TIMEDOUT;
/* disconnect properly */
- Curl_disconnect(data, data->easy_conn, dead_connection);
+ Curl_disconnect(data, data->conn, dead_connection);
- /* This is where we make sure that the easy_conn pointer is reset.
+ /* This is where we make sure that the conn pointer is reset.
We don't have to do this in every case block above where a
failure is detected */
- data->easy_conn = NULL;
+ detach_connnection(data);
}
}
else if(data->mstate == CURLM_STATE_CONNECT) {
@@ -2118,11 +2006,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
rc = CURLM_CALL_MULTI_PERFORM;
}
/* if there's still a connection to use, call the progress function */
- else if(data->easy_conn && Curl_pgrsUpdate(data->easy_conn)) {
+ else if(data->conn && Curl_pgrsUpdate(data->conn)) {
/* aborted due to progress callback return code must close the
connection */
result = CURLE_ABORTED_BY_CALLBACK;
- streamclose(data->easy_conn, "Aborted by callback");
+ streamclose(data->conn, "Aborted by callback");
/* if not yet in DONE state, go there, otherwise COMPLETED */
multistate(data, (data->mstate < CURLM_STATE_DONE)?
@@ -2145,7 +2033,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
msg->extmsg.data.result = result;
rc = multi_addmsg(multi, msg);
- DEBUGASSERT(!data->easy_conn);
+ DEBUGASSERT(!data->conn);
}
multistate(data, CURLM_STATE_MSGSENT);
}
@@ -2225,9 +2113,9 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
data = multi->easyp;
while(data) {
nextdata = data->next;
- if(!data->state.done && data->easy_conn)
+ if(!data->state.done && data->conn)
/* if DONE was never called for this handle */
- (void)multi_done(&data->easy_conn, CURLE_OK, TRUE);
+ (void)multi_done(data, CURLE_OK, TRUE);
if(data->dns.hostcachetype == HCACHE_MULTI) {
/* clear out the usage of the shared DNS cache */
Curl_hostcache_clean(data, data->dns.hostcache);
@@ -2257,11 +2145,6 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
Curl_hash_destroy(&multi->hostcache);
Curl_psl_destroy(&multi->psl);
-
- /* Free the blacklists by setting them to NULL */
- Curl_pipeline_set_site_blacklist(NULL, &multi->pipelining_site_bl);
- Curl_pipeline_set_server_blacklist(NULL, &multi->pipelining_server_bl);
-
free(multi);
return CURLM_OK;
@@ -2320,6 +2203,7 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
curl_socket_t s;
int num;
unsigned int curraction;
+ int actions[MAX_SOCKSPEREASYHANDLE];
for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++)
socks[i] = CURL_SOCKET_BAD;
@@ -2336,7 +2220,10 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
for(i = 0; (i< MAX_SOCKSPEREASYHANDLE) &&
(curraction & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i)));
i++) {
- int action = CURL_POLL_NONE;
+ unsigned int action = CURL_POLL_NONE;
+ unsigned int prevaction = 0;
+ unsigned int comboaction;
+ bool sincebefore = FALSE;
s = socks[i];
@@ -2348,29 +2235,70 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
if(curraction & GETSOCK_WRITESOCK(i))
action |= CURL_POLL_OUT;
+ actions[i] = action;
if(entry) {
- /* yeps, already present so check if it has the same action set */
- if(entry->action == action)
- /* same, continue */
- continue;
+ /* check if new for this transfer */
+ for(i = 0; i< data->numsocks; i++) {
+ if(s == data->sockets[i]) {
+ prevaction = data->actions[i];
+ sincebefore = TRUE;
+ break;
+ }
+ }
+
}
else {
- /* this is a socket we didn't have before, add it! */
- entry = sh_addentry(&multi->sockhash, s, data);
+ /* this is a socket we didn't have before, add it to the hash! */
+ entry = sh_addentry(&multi->sockhash, s);
if(!entry)
/* fatal */
return CURLM_OUT_OF_MEMORY;
}
+ if(sincebefore && (prevaction != action)) {
+ /* Socket was used already, but different action now */
+ if(prevaction & CURL_POLL_IN)
+ entry->readers--;
+ if(prevaction & CURL_POLL_OUT)
+ entry->writers--;
+ if(action & CURL_POLL_IN)
+ entry->readers++;
+ if(action & CURL_POLL_OUT)
+ entry->writers++;
+ }
+ else if(!sincebefore) {
+ /* a new user */
+ entry->users++;
+ if(action & CURL_POLL_IN)
+ entry->readers++;
+ if(action & CURL_POLL_OUT)
+ entry->writers++;
+
+ /* add 'data' to the list of handles using this socket! */
+ Curl_llist_insert_next(&entry->list, entry->list.tail,
+ data, &data->sh_queue);
+ }
+
+ comboaction = (entry->writers? CURL_POLL_OUT : 0) |
+ (entry->readers ? CURL_POLL_IN : 0);
+
+#if 0
+ infof(data, "--- Comboaction: %u readers %u writers\n",
+ entry->readers, entry->writers);
+#endif
+ /* check if it has the same action set */
+ if(entry->action == comboaction)
+ /* same, continue */
+ continue;
/* we know (entry != NULL) at this point, see the logic above */
if(multi->socket_cb)
multi->socket_cb(data,
s,
- action,
+ comboaction,
multi->socket_userp,
entry->socketp);
- entry->action = action; /* store the current action state */
+ entry->action = comboaction; /* store the current action state */
}
num = i; /* number of sockets */
@@ -2379,73 +2307,45 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
make sure to detect sockets that are removed */
for(i = 0; i< data->numsocks; i++) {
int j;
+ bool stillused = FALSE;
s = data->sockets[i];
- for(j = 0; j<num; j++) {
+ for(j = 0; j < num; j++) {
if(s == socks[j]) {
/* this is still supervised */
- s = CURL_SOCKET_BAD;
+ stillused = TRUE;
break;
}
}
+ if(stillused)
+ continue;
entry = sh_getentry(&multi->sockhash, s);
+ /* if this is NULL here, the socket has been closed and notified so
+ already by Curl_multi_closed() */
if(entry) {
- /* this socket has been removed. Tell the app to remove it */
- bool remove_sock_from_hash = TRUE;
-
- /* check if the socket to be removed serves a connection which has
- other easy-s in a pipeline. In this case the socket should not be
- removed. */
- struct connectdata *easy_conn = data->easy_conn;
- if(easy_conn) {
- if(easy_conn->recv_pipe.size > 1) {
- /* the handle should not be removed from the pipe yet */
- remove_sock_from_hash = FALSE;
-
- /* Update the sockhash entry to instead point to the next in line
- for the recv_pipe, or the first (in case this particular easy
- isn't already) */
- if(entry->easy == data) {
- if(Curl_recvpipe_head(data, easy_conn))
- entry->easy = easy_conn->recv_pipe.head->next->ptr;
- else
- entry->easy = easy_conn->recv_pipe.head->ptr;
- }
- }
- if(easy_conn->send_pipe.size > 1) {
- /* the handle should not be removed from the pipe yet */
- remove_sock_from_hash = FALSE;
-
- /* Update the sockhash entry to instead point to the next in line
- for the send_pipe, or the first (in case this particular easy
- isn't already) */
- if(entry->easy == data) {
- if(Curl_sendpipe_head(data, easy_conn))
- entry->easy = easy_conn->send_pipe.head->next->ptr;
- else
- entry->easy = easy_conn->send_pipe.head->ptr;
- }
- }
- /* Don't worry about overwriting recv_pipe head with send_pipe_head,
- when action will be asked on the socket (see multi_socket()), the
- head of the correct pipe will be taken according to the
- action. */
- }
-
- if(remove_sock_from_hash) {
- /* in this case 'entry' is always non-NULL */
+ int oldactions = data->actions[i];
+ /* this socket has been removed. Decrease user count */
+ entry->users--;
+ if(oldactions & CURL_POLL_OUT)
+ entry->writers--;
+ if(oldactions & CURL_POLL_IN)
+ entry->readers--;
+ if(!entry->users) {
if(multi->socket_cb)
- multi->socket_cb(data,
- s,
- CURL_POLL_REMOVE,
+ multi->socket_cb(data, s, CURL_POLL_REMOVE,
multi->socket_userp,
entry->socketp);
sh_delentry(&multi->sockhash, s);
}
- } /* if sockhash entry existed */
+ else {
+ /* remove this transfer as a user of this socket */
+ Curl_llist_remove(&entry->list, &data->sh_queue, NULL);
+ }
+ }
} /* for loop over numsocks */
memcpy(data->sockets, socks, num*sizeof(curl_socket_t));
+ memcpy(data->actions, actions, num*sizeof(int));
data->numsocks = num;
return CURLM_OK;
}
@@ -2466,11 +2366,11 @@ void Curl_updatesocket(struct Curl_easy *data)
* socket again and it gets the same file descriptor number.
*/
-void Curl_multi_closed(struct connectdata *conn, curl_socket_t s)
+void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s)
{
- if(conn->data) {
+ if(data) {
/* if there's still an easy handle associated with this connection */
- struct Curl_multi *multi = conn->data->multi;
+ struct Curl_multi *multi = data->multi;
if(multi) {
/* this is set if this connection is part of a handle that is added to
a multi handle, and only then this is necessary */
@@ -2478,7 +2378,7 @@ void Curl_multi_closed(struct connectdata *conn, curl_socket_t s)
if(entry) {
if(multi->socket_cb)
- multi->socket_cb(conn->data, s, CURL_POLL_REMOVE,
+ multi->socket_cb(data, s, CURL_POLL_REMOVE,
multi->socket_userp,
entry->socketp);
@@ -2585,46 +2485,37 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
and just move on. */
;
else {
+ struct curl_llist *list = &entry->list;
+ struct curl_llist_element *e;
SIGPIPE_VARIABLE(pipe_st);
- data = entry->easy;
-
- if(data->magic != CURLEASY_MAGIC_NUMBER)
- /* bad bad bad bad bad bad bad */
- return CURLM_INTERNAL_ERROR;
-
- /* If the pipeline is enabled, take the handle which is in the head of
- the pipeline. If we should write into the socket, take the send_pipe
- head. If we should read from the socket, take the recv_pipe head. */
- if(data->easy_conn) {
- if((ev_bitmask & CURL_POLL_OUT) &&
- data->easy_conn->send_pipe.head)
- data = data->easy_conn->send_pipe.head->ptr;
- else if((ev_bitmask & CURL_POLL_IN) &&
- data->easy_conn->recv_pipe.head)
- data = data->easy_conn->recv_pipe.head->ptr;
- }
+ /* the socket can be shared by many transfers, iterate */
+ for(e = list->head; e; e = e->next) {
+ data = (struct Curl_easy *)e->ptr;
- if(data->easy_conn &&
- !(data->easy_conn->handler->flags & PROTOPT_DIRLOCK))
- /* set socket event bitmask if they're not locked */
- data->easy_conn->cselect_bits = ev_bitmask;
+ if(data->magic != CURLEASY_MAGIC_NUMBER)
+ /* bad bad bad bad bad bad bad */
+ return CURLM_INTERNAL_ERROR;
- sigpipe_ignore(data, &pipe_st);
- result = multi_runsingle(multi, now, data);
- sigpipe_restore(&pipe_st);
+ if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK))
+ /* set socket event bitmask if they're not locked */
+ data->conn->cselect_bits = ev_bitmask;
- if(data->easy_conn &&
- !(data->easy_conn->handler->flags & PROTOPT_DIRLOCK))
- /* clear the bitmask only if not locked */
- data->easy_conn->cselect_bits = 0;
+ sigpipe_ignore(data, &pipe_st);
+ result = multi_runsingle(multi, now, data);
+ sigpipe_restore(&pipe_st);
- if(CURLM_OK >= result) {
- /* get the socket(s) and check if the state has been changed since
- last */
- result = singlesocket(multi, data);
- if(result)
- return result;
+ if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK))
+ /* clear the bitmask only if not locked */
+ data->conn->cselect_bits = 0;
+
+ if(CURLM_OK >= result) {
+ /* get the socket(s) and check if the state has been changed since
+ last */
+ result = singlesocket(multi, data);
+ if(result)
+ return result;
+ }
}
/* Now we fall-through and do the timer-based stuff, since we don't want
@@ -2712,7 +2603,7 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi,
multi->push_userp = va_arg(param, void *);
break;
case CURLMOPT_PIPELINING:
- multi->pipelining = va_arg(param, long) & CURLPIPE_MULTIPLEX;
+ multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX;
break;
case CURLMOPT_TIMERFUNCTION:
multi->timer_cb = va_arg(param, curl_multi_timer_callback);
@@ -2726,25 +2617,19 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi,
case CURLMOPT_MAX_HOST_CONNECTIONS:
multi->max_host_connections = va_arg(param, long);
break;
+ case CURLMOPT_MAX_TOTAL_CONNECTIONS:
+ multi->max_total_connections = va_arg(param, long);
+ break;
+ /* options formerly used for pipelining */
case CURLMOPT_MAX_PIPELINE_LENGTH:
- multi->max_pipeline_length = va_arg(param, long);
break;
case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE:
- multi->content_length_penalty_size = va_arg(param, long);
break;
case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE:
- multi->chunk_length_penalty_size = va_arg(param, long);
break;
case CURLMOPT_PIPELINING_SITE_BL:
- res = Curl_pipeline_set_site_blacklist(va_arg(param, char **),
- &multi->pipelining_site_bl);
break;
case CURLMOPT_PIPELINING_SERVER_BL:
- res = Curl_pipeline_set_server_blacklist(va_arg(param, char **),
- &multi->pipelining_server_bl);
- break;
- case CURLMOPT_MAX_TOTAL_CONNECTIONS:
- multi->max_total_connections = va_arg(param, long);
break;
default:
res = CURLM_UNKNOWN_OPTION;
@@ -3059,7 +2944,7 @@ void Curl_expire_clear(struct Curl_easy *data)
}
#ifdef DEBUGBUILD
- infof(data, "Expire cleared\n");
+ infof(data, "Expire cleared (transfer %p)\n", data);
#endif
nowp->tv_sec = 0;
nowp->tv_usec = 0;
@@ -3097,24 +2982,20 @@ size_t Curl_multi_max_total_connections(struct Curl_multi *multi)
return multi ? multi->max_total_connections : 0;
}
-curl_off_t Curl_multi_content_length_penalty_size(struct Curl_multi *multi)
-{
- return multi ? multi->content_length_penalty_size : 0;
-}
-
-curl_off_t Curl_multi_chunk_length_penalty_size(struct Curl_multi *multi)
-{
- return multi ? multi->chunk_length_penalty_size : 0;
-}
+/*
+ * When information about a connection has appeared, call this!
+ */
-struct curl_llist *Curl_multi_pipelining_site_bl(struct Curl_multi *multi)
+void Curl_multiuse_state(struct connectdata *conn,
+ int bundlestate) /* use BUNDLE_* defines */
{
- return &multi->pipelining_site_bl;
-}
+ DEBUGASSERT(conn);
+ DEBUGASSERT(conn->bundle);
+ DEBUGASSERT(conn->data);
+ DEBUGASSERT(conn->data->multi);
-struct curl_llist *Curl_multi_pipelining_server_bl(struct Curl_multi *multi)
-{
- return &multi->pipelining_server_bl;
+ conn->bundle->multiuse = bundlestate;
+ process_pending_handles(conn->data->multi);
}
static void process_pending_handles(struct Curl_multi *multi)
@@ -3132,6 +3013,9 @@ static void process_pending_handles(struct Curl_multi *multi)
/* Make sure that the handle will be processed soonish. */
Curl_expire(data, 0, EXPIRE_RUN_NOW);
+
+ /* mark this as having been in the pending queue */
+ data->state.previouslypending = TRUE;
}
}
@@ -3175,8 +3059,8 @@ void Curl_multi_dump(struct Curl_multi *multi)
continue;
}
fprintf(stderr, "[%s %s] ",
- entry->action&CURL_POLL_IN?"RECVING":"",
- entry->action&CURL_POLL_OUT?"SENDING":"");
+ (entry->action&CURL_POLL_IN)?"RECVING":"",
+ (entry->action&CURL_POLL_OUT)?"SENDING":"");
}
if(data->numsocks)
fprintf(stderr, "\n");
diff --git a/Utilities/cmcurl/lib/multihandle.h b/Utilities/cmcurl/lib/multihandle.h
index ea2bf352d..279379ae0 100644
--- a/Utilities/cmcurl/lib/multihandle.h
+++ b/Utilities/cmcurl/lib/multihandle.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -46,18 +46,16 @@ typedef enum {
CURLM_STATE_SENDPROTOCONNECT, /* 6 - initiate protocol connect procedure */
CURLM_STATE_PROTOCONNECT, /* 7 - completing the protocol-specific connect
phase */
- CURLM_STATE_WAITDO, /* 8 - wait for our turn to send the request */
- CURLM_STATE_DO, /* 9 - start send off the request (part 1) */
- CURLM_STATE_DOING, /* 10 - sending off the request (part 1) */
- CURLM_STATE_DO_MORE, /* 11 - send off the request (part 2) */
- CURLM_STATE_DO_DONE, /* 12 - done sending off request */
- CURLM_STATE_WAITPERFORM, /* 13 - wait for our turn to read the response */
- CURLM_STATE_PERFORM, /* 14 - transfer data */
- CURLM_STATE_TOOFAST, /* 15 - wait because limit-rate exceeded */
- CURLM_STATE_DONE, /* 16 - post data transfer operation */
- CURLM_STATE_COMPLETED, /* 17 - operation complete */
- CURLM_STATE_MSGSENT, /* 18 - the operation complete message is sent */
- CURLM_STATE_LAST /* 19 - not a true state, never use this */
+ CURLM_STATE_DO, /* 8 - start send off the request (part 1) */
+ CURLM_STATE_DOING, /* 9 - sending off the request (part 1) */
+ CURLM_STATE_DO_MORE, /* 10 - send off the request (part 2) */
+ CURLM_STATE_DO_DONE, /* 11 - done sending off request */
+ CURLM_STATE_PERFORM, /* 12 - transfer data */
+ CURLM_STATE_TOOFAST, /* 13 - wait because limit-rate exceeded */
+ CURLM_STATE_DONE, /* 14 - post data transfer operation */
+ CURLM_STATE_COMPLETED, /* 15 - operation complete */
+ CURLM_STATE_MSGSENT, /* 16 - the operation complete message is sent */
+ CURLM_STATE_LAST /* 17 - not a true state, never use this */
} CURLMstate;
/* we support N sockets per easy handle. Set the corresponding bit to what
@@ -66,7 +64,7 @@ typedef enum {
#define GETSOCK_READABLE (0x00ff)
#define GETSOCK_WRITABLE (0xff00)
-#define CURLPIPE_ANY (CURLPIPE_HTTP1 | CURLPIPE_MULTIPLEX)
+#define CURLPIPE_ANY (CURLPIPE_MULTIPLEX)
/* This is the struct known as CURLM on the outside */
struct Curl_multi {
@@ -112,8 +110,8 @@ struct Curl_multi {
same actual socket) */
struct curl_hash sockhash;
- /* pipelining wanted bits (CURLPIPE*) */
- long pipelining;
+ /* multiplexing wanted */
+ bool multiplexing;
bool recheckstate; /* see Curl_multi_connchanged */
@@ -129,24 +127,6 @@ struct Curl_multi {
long max_total_connections; /* if >0, a fixed limit of the maximum number
of connections in total */
- long max_pipeline_length; /* if >0, maximum number of requests in a
- pipeline */
-
- long content_length_penalty_size; /* a connection with a
- content-length bigger than
- this is not considered
- for pipelining */
-
- long chunk_length_penalty_size; /* a connection with a chunk length
- bigger than this is not
- considered for pipelining */
-
- struct curl_llist pipelining_site_bl; /* List of sites that are blacklisted
- from pipelining */
-
- struct curl_llist pipelining_server_bl; /* List of server types that are
- blacklisted from pipelining */
-
/* timer callback and user data pointer for the *socket() API */
curl_multi_timer_callback timer_cb;
void *timer_userp;
diff --git a/Utilities/cmcurl/lib/multiif.h b/Utilities/cmcurl/lib/multiif.h
index c8fb5ca0d..e8a5e7062 100644
--- a/Utilities/cmcurl/lib/multiif.h
+++ b/Utilities/cmcurl/lib/multiif.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -30,8 +30,10 @@ void Curl_updatesocket(struct Curl_easy *data);
void Curl_expire(struct Curl_easy *data, time_t milli, expire_id);
void Curl_expire_clear(struct Curl_easy *data);
void Curl_expire_done(struct Curl_easy *data, expire_id id);
-bool Curl_pipeline_wanted(const struct Curl_multi* multi, int bits);
-void Curl_multi_handlePipeBreak(struct Curl_easy *data);
+void Curl_detach_connnection(struct Curl_easy *data);
+void Curl_attach_connnection(struct Curl_easy *data,
+ struct connectdata *conn);
+bool Curl_multiplex_wanted(const struct Curl_multi *multi);
void Curl_set_in_callback(struct Curl_easy *data, bool value);
bool Curl_is_in_callback(struct Curl_easy *easy);
@@ -62,22 +64,11 @@ void Curl_multi_dump(struct Curl_multi *multi);
/* Return the value of the CURLMOPT_MAX_HOST_CONNECTIONS option */
size_t Curl_multi_max_host_connections(struct Curl_multi *multi);
-/* Return the value of the CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE option */
-curl_off_t Curl_multi_content_length_penalty_size(struct Curl_multi *multi);
-
-/* Return the value of the CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE option */
-curl_off_t Curl_multi_chunk_length_penalty_size(struct Curl_multi *multi);
-
-/* Return the value of the CURLMOPT_PIPELINING_SITE_BL option */
-struct curl_llist *Curl_multi_pipelining_site_bl(struct Curl_multi *multi);
-
-/* Return the value of the CURLMOPT_PIPELINING_SERVER_BL option */
-struct curl_llist *Curl_multi_pipelining_server_bl(struct Curl_multi *multi);
-
/* Return the value of the CURLMOPT_MAX_TOTAL_CONNECTIONS option */
size_t Curl_multi_max_total_connections(struct Curl_multi *multi);
-void Curl_multi_connchanged(struct Curl_multi *multi);
+void Curl_multiuse_state(struct connectdata *conn,
+ int bundlestate); /* use BUNDLE_* defines */
/*
* Curl_multi_closed()
@@ -89,7 +80,7 @@ void Curl_multi_connchanged(struct Curl_multi *multi);
* socket again and it gets the same file descriptor number.
*/
-void Curl_multi_closed(struct connectdata *conn, curl_socket_t s);
+void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s);
/*
* Add a handle and move it into PERFORM state at once. For pushed streams.
@@ -97,4 +88,12 @@ void Curl_multi_closed(struct connectdata *conn, curl_socket_t s);
CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
struct Curl_easy *data,
struct connectdata *conn);
+
+CURLMcode Curl_multi_wait(struct Curl_multi *multi,
+ struct curl_waitfd extra_fds[],
+ unsigned int extra_nfds,
+ int timeout_ms,
+ int *ret,
+ bool *gotsocket); /* if any socket was checked */
+
#endif /* HEADER_CURL_MULTIIF_H */
diff --git a/Utilities/cmcurl/lib/netrc.c b/Utilities/cmcurl/lib/netrc.c
index 1724b35b0..1bd998f9c 100644
--- a/Utilities/cmcurl/lib/netrc.c
+++ b/Utilities/cmcurl/lib/netrc.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -21,6 +21,7 @@
***************************************************************************/
#include "curl_setup.h"
+#ifndef CURL_DISABLE_NETRC
#ifdef HAVE_PWD_H
#include <pwd.h>
@@ -53,6 +54,8 @@ enum host_lookup_state {
int Curl_parsenetrc(const char *host,
char **loginp,
char **passwordp,
+ bool *login_changed,
+ bool *password_changed,
char *netrcfile)
{
FILE *file;
@@ -164,7 +167,7 @@ int Curl_parsenetrc(const char *host,
if(specific_login) {
state_our_login = strcasecompare(login, tok);
}
- else {
+ else if(!login || strcmp(login, tok)) {
if(login_alloc) {
free(login);
login_alloc = FALSE;
@@ -179,7 +182,8 @@ int Curl_parsenetrc(const char *host,
state_login = 0;
}
else if(state_password) {
- if(state_our_login || !specific_login) {
+ if((state_our_login || !specific_login)
+ && (!password || strcmp(password, tok))) {
if(password_alloc) {
free(password);
password_alloc = FALSE;
@@ -211,15 +215,19 @@ int Curl_parsenetrc(const char *host,
out:
if(!retcode) {
+ *login_changed = FALSE;
+ *password_changed = FALSE;
if(login_alloc) {
if(*loginp)
free(*loginp);
*loginp = login;
+ *login_changed = TRUE;
}
if(password_alloc) {
if(*passwordp)
free(*passwordp);
*passwordp = password;
+ *password_changed = TRUE;
}
}
else {
@@ -233,3 +241,5 @@ int Curl_parsenetrc(const char *host,
return retcode;
}
+
+#endif
diff --git a/Utilities/cmcurl/lib/netrc.h b/Utilities/cmcurl/lib/netrc.h
index d980166e6..7f56c4b4d 100644
--- a/Utilities/cmcurl/lib/netrc.h
+++ b/Utilities/cmcurl/lib/netrc.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -22,15 +22,24 @@
*
***************************************************************************/
+#include "curl_setup.h"
+#ifndef CURL_DISABLE_NETRC
+
/* returns -1 on failure, 0 if the host is found, 1 is the host isn't found */
int Curl_parsenetrc(const char *host,
char **loginp,
char **passwordp,
+ bool *login_changed,
+ bool *password_changed,
char *filename);
/* Assume: (*passwordp)[0]=0, host[0] != 0.
* If (*loginp)[0] = 0, search for login and password within a machine
* section in the netrc.
* If (*loginp)[0] != 0, search for password within machine and login.
*/
+#else
+/* disabled */
+#define Curl_parsenetrc(a,b,c,d,e,f) 1
+#endif
#endif /* HEADER_CURL_NETRC_H */
diff --git a/Utilities/cmcurl/lib/non-ascii.c b/Utilities/cmcurl/lib/non-ascii.c
index 14143248f..42beaec45 100644
--- a/Utilities/cmcurl/lib/non-ascii.c
+++ b/Utilities/cmcurl/lib/non-ascii.c
@@ -78,7 +78,7 @@ CURLcode Curl_convert_clone(struct Curl_easy *data,
/*
* Curl_convert_to_network() is an internal function for performing ASCII
- * conversions on non-ASCII platforms. It convers the buffer _in place_.
+ * conversions on non-ASCII platforms. It converts the buffer _in place_.
*/
CURLcode Curl_convert_to_network(struct Curl_easy *data,
char *buffer, size_t length)
@@ -144,7 +144,7 @@ CURLcode Curl_convert_to_network(struct Curl_easy *data,
/*
* Curl_convert_from_network() is an internal function for performing ASCII
- * conversions on non-ASCII platforms. It convers the buffer _in place_.
+ * conversions on non-ASCII platforms. It converts the buffer _in place_.
*/
CURLcode Curl_convert_from_network(struct Curl_easy *data,
char *buffer, size_t length)
diff --git a/Utilities/cmcurl/lib/openldap.c b/Utilities/cmcurl/lib/openldap.c
index c6cb79434..eeab2c7a7 100644
--- a/Utilities/cmcurl/lib/openldap.c
+++ b/Utilities/cmcurl/lib/openldap.c
@@ -6,7 +6,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2010, Howard Chu, <hyc@openldap.org>
- * Copyright (C) 2011 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2011 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -196,9 +196,6 @@ static CURLcode ldap_setup_connection(struct connectdata *conn)
li->proto = proto;
conn->proto.generic = li;
connkeep(conn, "OpenLDAP default");
- /* TODO:
- * - provide option to choose SASL Binds instead of Simple
- */
return CURLE_OK;
}
@@ -220,8 +217,8 @@ static CURLcode ldap_connect(struct connectdata *conn, bool *done)
ptr = hosturl + 4;
if(conn->handler->flags & PROTOPT_SSL)
*ptr++ = 's';
- snprintf(ptr, sizeof(hosturl)-(ptr-hosturl), "://%s:%d",
- conn->host.name, conn->remote_port);
+ msnprintf(ptr, sizeof(hosturl)-(ptr-hosturl), "://%s:%d",
+ conn->host.name, conn->remote_port);
#ifdef CURL_OPENLDAP_DEBUG
static int do_trace = 0;
@@ -414,7 +411,7 @@ static CURLcode ldap_do(struct connectdata *conn, bool *done)
return CURLE_OUT_OF_MEMORY;
lr->msgid = msgid;
data->req.protop = lr;
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
*done = TRUE;
return CURLE_OK;
}
@@ -510,8 +507,6 @@ static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf,
lr->nument++;
rc = ldap_get_dn_ber(li->ld, ent, &ber, &bv);
if(rc < 0) {
- /* TODO: verify that this is really how this return code should be
- handled */
*err = CURLE_RECV_ERROR;
return -1;
}
diff --git a/Utilities/cmcurl/lib/parsedate.c b/Utilities/cmcurl/lib/parsedate.c
index 3d3c00b4f..7ae5eb8cd 100644
--- a/Utilities/cmcurl/lib/parsedate.c
+++ b/Utilities/cmcurl/lib/parsedate.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -82,20 +82,6 @@
#include "warnless.h"
#include "parsedate.h"
-const char * const Curl_wkday[] =
-{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
-static const char * const weekday[] =
-{ "Monday", "Tuesday", "Wednesday", "Thursday",
- "Friday", "Saturday", "Sunday" };
-const char * const Curl_month[]=
-{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
-
-struct tzinfo {
- char name[5];
- int offset; /* +/- in minutes */
-};
-
/*
* parsedate()
*
@@ -114,6 +100,22 @@ static int parsedate(const char *date, time_t *output);
#define PARSEDATE_LATER 1
#define PARSEDATE_SOONER 2
+#ifndef CURL_DISABLE_PARSEDATE
+
+const char * const Curl_wkday[] =
+{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
+static const char * const weekday[] =
+{ "Monday", "Tuesday", "Wednesday", "Thursday",
+ "Friday", "Saturday", "Sunday" };
+const char * const Curl_month[]=
+{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+struct tzinfo {
+ char name[5];
+ int offset; /* +/- in minutes */
+};
+
/* Here's a bunch of frequently used time zone names. These were supported
by the old getdate parser. */
#define tDAYZONE -60 /* offset for daylight savings time */
@@ -555,6 +557,15 @@ static int parsedate(const char *date, time_t *output)
return PARSEDATE_OK;
}
+#else
+/* disabled */
+static int parsedate(const char *date, time_t *output)
+{
+ (void)date;
+ *output = 0;
+ return PARSEDATE_OK; /* a lie */
+}
+#endif
time_t curl_getdate(const char *p, const time_t *now)
{
diff --git a/Utilities/cmcurl/lib/pingpong.c b/Utilities/cmcurl/lib/pingpong.c
index 2e93d201f..e9568ee3d 100644
--- a/Utilities/cmcurl/lib/pingpong.c
+++ b/Utilities/cmcurl/lib/pingpong.c
@@ -44,7 +44,7 @@
/* Returns timeout in ms. 0 or negative number means the timeout has already
triggered */
-time_t Curl_pp_state_timeout(struct pingpong *pp)
+time_t Curl_pp_state_timeout(struct pingpong *pp, bool disconnecting)
{
struct connectdata *conn = pp->conn;
struct Curl_easy *data = conn->data;
@@ -62,7 +62,7 @@ time_t Curl_pp_state_timeout(struct pingpong *pp)
timeout_ms = response_time -
Curl_timediff(Curl_now(), pp->response); /* spent time */
- if(data->set.timeout) {
+ if(data->set.timeout && !disconnecting) {
/* if timeout is requested, find out how much remaining time we have */
time_t timeout2_ms = data->set.timeout - /* timeout time */
Curl_timediff(Curl_now(), conn->now); /* spent time */
@@ -77,13 +77,14 @@ time_t Curl_pp_state_timeout(struct pingpong *pp)
/*
* Curl_pp_statemach()
*/
-CURLcode Curl_pp_statemach(struct pingpong *pp, bool block)
+CURLcode Curl_pp_statemach(struct pingpong *pp, bool block,
+ bool disconnecting)
{
struct connectdata *conn = pp->conn;
curl_socket_t sock = conn->sock[FIRSTSOCKET];
int rc;
time_t interval_ms;
- time_t timeout_ms = Curl_pp_state_timeout(pp);
+ time_t timeout_ms = Curl_pp_state_timeout(pp, disconnecting);
struct Curl_easy *data = conn->data;
CURLcode result = CURLE_OK;
diff --git a/Utilities/cmcurl/lib/pingpong.h b/Utilities/cmcurl/lib/pingpong.h
index 5ac8df876..dbe1f8d3d 100644
--- a/Utilities/cmcurl/lib/pingpong.h
+++ b/Utilities/cmcurl/lib/pingpong.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -81,14 +81,15 @@ struct pingpong {
* called repeatedly until done. Set 'wait' to make it wait a while on the
* socket if there's no traffic.
*/
-CURLcode Curl_pp_statemach(struct pingpong *pp, bool block);
+CURLcode Curl_pp_statemach(struct pingpong *pp, bool block,
+ bool disconnecting);
/* initialize stuff to prepare for reading a fresh new response */
void Curl_pp_init(struct pingpong *pp);
/* Returns timeout in ms. 0 or negative number means the timeout has already
triggered */
-time_t Curl_pp_state_timeout(struct pingpong *pp);
+time_t Curl_pp_state_timeout(struct pingpong *pp, bool disconnecting);
/***********************************************************************
diff --git a/Utilities/cmcurl/lib/pipeline.c b/Utilities/cmcurl/lib/pipeline.c
deleted file mode 100644
index 8de3babd7..000000000
--- a/Utilities/cmcurl/lib/pipeline.c
+++ /dev/null
@@ -1,404 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) 2013, Linus Nielsen Feltzing, <linus@haxx.se>
- * Copyright (C) 2013 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include <curl/curl.h>
-
-#include "urldata.h"
-#include "url.h"
-#include "progress.h"
-#include "multiif.h"
-#include "pipeline.h"
-#include "sendf.h"
-#include "strcase.h"
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "memdebug.h"
-
-struct site_blacklist_entry {
- struct curl_llist_element list;
- unsigned short port;
- char hostname[1];
-};
-
-static void site_blacklist_llist_dtor(void *user, void *element)
-{
- struct site_blacklist_entry *entry = element;
- (void)user;
- free(entry);
-}
-
-static void server_blacklist_llist_dtor(void *user, void *element)
-{
- (void)user;
- free(element);
-}
-
-bool Curl_pipeline_penalized(struct Curl_easy *data,
- struct connectdata *conn)
-{
- if(data) {
- bool penalized = FALSE;
- curl_off_t penalty_size =
- Curl_multi_content_length_penalty_size(data->multi);
- curl_off_t chunk_penalty_size =
- Curl_multi_chunk_length_penalty_size(data->multi);
- curl_off_t recv_size = -2; /* Make it easy to spot in the log */
-
- /* Find the head of the recv pipe, if any */
- if(conn->recv_pipe.head) {
- struct Curl_easy *recv_handle = conn->recv_pipe.head->ptr;
-
- recv_size = recv_handle->req.size;
-
- if(penalty_size > 0 && recv_size > penalty_size)
- penalized = TRUE;
- }
-
- if(chunk_penalty_size > 0 &&
- (curl_off_t)conn->chunk.datasize > chunk_penalty_size)
- penalized = TRUE;
-
- infof(data, "Conn: %ld (%p) Receive pipe weight: (%"
- CURL_FORMAT_CURL_OFF_T "/%" CURL_FORMAT_CURL_OFF_T
- "), penalized: %s\n",
- conn->connection_id, (void *)conn, recv_size,
- conn->chunk.datasize, penalized?"TRUE":"FALSE");
- return penalized;
- }
- return FALSE;
-}
-
-static CURLcode addHandleToPipeline(struct Curl_easy *data,
- struct curl_llist *pipeline)
-{
- Curl_llist_insert_next(pipeline, pipeline->tail, data,
- &data->pipeline_queue);
- return CURLE_OK;
-}
-
-
-CURLcode Curl_add_handle_to_pipeline(struct Curl_easy *handle,
- struct connectdata *conn)
-{
- struct curl_llist_element *sendhead = conn->send_pipe.head;
- struct curl_llist *pipeline;
- CURLcode result;
-
- pipeline = &conn->send_pipe;
-
- result = addHandleToPipeline(handle, pipeline);
- if((conn->bundle->multiuse == BUNDLE_PIPELINING) &&
- (pipeline == &conn->send_pipe && sendhead != conn->send_pipe.head)) {
- /* this is a new one as head, expire it */
- Curl_pipeline_leave_write(conn); /* not in use yet */
- Curl_expire(conn->send_pipe.head->ptr, 0, EXPIRE_RUN_NOW);
- }
-
-#if 0 /* enable for pipeline debugging */
- print_pipeline(conn);
-#endif
-
- return result;
-}
-
-/* Move this transfer from the sending list to the receiving list.
-
- Pay special attention to the new sending list "leader" as it needs to get
- checked to update what sockets it acts on.
-
-*/
-void Curl_move_handle_from_send_to_recv_pipe(struct Curl_easy *handle,
- struct connectdata *conn)
-{
- struct curl_llist_element *curr;
-
- curr = conn->send_pipe.head;
- while(curr) {
- if(curr->ptr == handle) {
- Curl_llist_move(&conn->send_pipe, curr,
- &conn->recv_pipe, conn->recv_pipe.tail);
-
- if(conn->send_pipe.head) {
- /* Since there's a new easy handle at the start of the send pipeline,
- set its timeout value to 1ms to make it trigger instantly */
- Curl_pipeline_leave_write(conn); /* not used now */
-#ifdef DEBUGBUILD
- infof(conn->data, "%p is at send pipe head B!\n",
- (void *)conn->send_pipe.head->ptr);
-#endif
- Curl_expire(conn->send_pipe.head->ptr, 0, EXPIRE_RUN_NOW);
- }
-
- /* The receiver's list is not really interesting here since either this
- handle is now first in the list and we'll deal with it soon, or
- another handle is already first and thus is already taken care of */
-
- break; /* we're done! */
- }
- curr = curr->next;
- }
-}
-
-bool Curl_pipeline_site_blacklisted(struct Curl_easy *handle,
- struct connectdata *conn)
-{
- if(handle->multi) {
- struct curl_llist *blacklist =
- Curl_multi_pipelining_site_bl(handle->multi);
-
- if(blacklist) {
- struct curl_llist_element *curr;
-
- curr = blacklist->head;
- while(curr) {
- struct site_blacklist_entry *site;
-
- site = curr->ptr;
- if(strcasecompare(site->hostname, conn->host.name) &&
- site->port == conn->remote_port) {
- infof(handle, "Site %s:%d is pipeline blacklisted\n",
- conn->host.name, conn->remote_port);
- return TRUE;
- }
- curr = curr->next;
- }
- }
- }
- return FALSE;
-}
-
-CURLMcode Curl_pipeline_set_site_blacklist(char **sites,
- struct curl_llist *list)
-{
- /* Free the old list */
- if(list->size)
- Curl_llist_destroy(list, NULL);
-
- if(sites) {
- Curl_llist_init(list, (curl_llist_dtor) site_blacklist_llist_dtor);
-
- /* Parse the URLs and populate the list */
- while(*sites) {
- char *port;
- struct site_blacklist_entry *entry;
-
- entry = malloc(sizeof(struct site_blacklist_entry) + strlen(*sites));
- if(!entry) {
- Curl_llist_destroy(list, NULL);
- return CURLM_OUT_OF_MEMORY;
- }
- strcpy(entry->hostname, *sites);
-
- port = strchr(entry->hostname, ':');
- if(port) {
- *port = '\0';
- port++;
- entry->port = (unsigned short)strtol(port, NULL, 10);
- }
- else {
- /* Default port number for HTTP */
- entry->port = 80;
- }
-
- Curl_llist_insert_next(list, list->tail, entry, &entry->list);
- sites++;
- }
- }
-
- return CURLM_OK;
-}
-
-struct blacklist_node {
- struct curl_llist_element list;
- char server_name[1];
-};
-
-bool Curl_pipeline_server_blacklisted(struct Curl_easy *handle,
- char *server_name)
-{
- if(handle->multi && server_name) {
- struct curl_llist *list =
- Curl_multi_pipelining_server_bl(handle->multi);
-
- struct curl_llist_element *e = list->head;
- while(e) {
- struct blacklist_node *bl = (struct blacklist_node *)e;
- if(strncasecompare(bl->server_name, server_name,
- strlen(bl->server_name))) {
- infof(handle, "Server %s is blacklisted\n", server_name);
- return TRUE;
- }
- e = e->next;
- }
-
- DEBUGF(infof(handle, "Server %s is not blacklisted\n", server_name));
- }
- return FALSE;
-}
-
-CURLMcode Curl_pipeline_set_server_blacklist(char **servers,
- struct curl_llist *list)
-{
- /* Free the old list */
- if(list->size)
- Curl_llist_destroy(list, NULL);
-
- if(servers) {
- Curl_llist_init(list, (curl_llist_dtor) server_blacklist_llist_dtor);
-
- /* Parse the URLs and populate the list */
- while(*servers) {
- struct blacklist_node *n;
- size_t len = strlen(*servers);
-
- n = malloc(sizeof(struct blacklist_node) + len);
- if(!n) {
- Curl_llist_destroy(list, NULL);
- return CURLM_OUT_OF_MEMORY;
- }
- strcpy(n->server_name, *servers);
-
- Curl_llist_insert_next(list, list->tail, n, &n->list);
- servers++;
- }
- }
-
-
- return CURLM_OK;
-}
-
-static bool pipe_head(struct Curl_easy *data,
- struct curl_llist *pipeline)
-{
- if(pipeline) {
- struct curl_llist_element *curr = pipeline->head;
- if(curr)
- return (curr->ptr == data) ? TRUE : FALSE;
- }
- return FALSE;
-}
-
-/* returns TRUE if the given handle is head of the recv pipe */
-bool Curl_recvpipe_head(struct Curl_easy *data,
- struct connectdata *conn)
-{
- return pipe_head(data, &conn->recv_pipe);
-}
-
-/* returns TRUE if the given handle is head of the send pipe */
-bool Curl_sendpipe_head(struct Curl_easy *data,
- struct connectdata *conn)
-{
- return pipe_head(data, &conn->send_pipe);
-}
-
-
-/*
- * Check if the write channel is available and this handle as at the head,
- * then grab the channel and return TRUE.
- *
- * If not available, return FALSE.
- */
-
-bool Curl_pipeline_checkget_write(struct Curl_easy *data,
- struct connectdata *conn)
-{
- if(conn->bits.multiplex)
- /* when multiplexing, we can use it at once */
- return TRUE;
-
- if(!conn->writechannel_inuse && Curl_sendpipe_head(data, conn)) {
- /* Grab the channel */
- conn->writechannel_inuse = TRUE;
- return TRUE;
- }
- return FALSE;
-}
-
-
-/*
- * Check if the read channel is available and this handle as at the head, then
- * grab the channel and return TRUE.
- *
- * If not available, return FALSE.
- */
-
-bool Curl_pipeline_checkget_read(struct Curl_easy *data,
- struct connectdata *conn)
-{
- if(conn->bits.multiplex)
- /* when multiplexing, we can use it at once */
- return TRUE;
-
- if(!conn->readchannel_inuse && Curl_recvpipe_head(data, conn)) {
- /* Grab the channel */
- conn->readchannel_inuse = TRUE;
- return TRUE;
- }
- return FALSE;
-}
-
-/*
- * The current user of the pipeline write channel gives it up.
- */
-void Curl_pipeline_leave_write(struct connectdata *conn)
-{
- conn->writechannel_inuse = FALSE;
-}
-
-/*
- * The current user of the pipeline read channel gives it up.
- */
-void Curl_pipeline_leave_read(struct connectdata *conn)
-{
- conn->readchannel_inuse = FALSE;
-}
-
-
-#if 0
-void print_pipeline(struct connectdata *conn)
-{
- struct curl_llist_element *curr;
- struct connectbundle *cb_ptr;
- struct Curl_easy *data = conn->data;
-
- cb_ptr = conn->bundle;
-
- if(cb_ptr) {
- curr = cb_ptr->conn_list->head;
- while(curr) {
- conn = curr->ptr;
- infof(data, "- Conn %ld (%p) send_pipe: %zu, recv_pipe: %zu\n",
- conn->connection_id,
- (void *)conn,
- conn->send_pipe->size,
- conn->recv_pipe->size);
- curr = curr->next;
- }
- }
-}
-
-#endif
diff --git a/Utilities/cmcurl/lib/pipeline.h b/Utilities/cmcurl/lib/pipeline.h
deleted file mode 100644
index 413ba31a0..000000000
--- a/Utilities/cmcurl/lib/pipeline.h
+++ /dev/null
@@ -1,56 +0,0 @@
-#ifndef HEADER_CURL_PIPELINE_H
-#define HEADER_CURL_PIPELINE_H
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) 2015 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2013 - 2014, Linus Nielsen Feltzing, <linus@haxx.se>
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-CURLcode Curl_add_handle_to_pipeline(struct Curl_easy *handle,
- struct connectdata *conn);
-void Curl_move_handle_from_send_to_recv_pipe(struct Curl_easy *handle,
- struct connectdata *conn);
-bool Curl_pipeline_penalized(struct Curl_easy *data,
- struct connectdata *conn);
-
-bool Curl_pipeline_site_blacklisted(struct Curl_easy *handle,
- struct connectdata *conn);
-
-CURLMcode Curl_pipeline_set_site_blacklist(char **sites,
- struct curl_llist *list_ptr);
-
-bool Curl_pipeline_server_blacklisted(struct Curl_easy *handle,
- char *server_name);
-
-CURLMcode Curl_pipeline_set_server_blacklist(char **servers,
- struct curl_llist *list_ptr);
-
-bool Curl_pipeline_checkget_write(struct Curl_easy *data,
- struct connectdata *conn);
-bool Curl_pipeline_checkget_read(struct Curl_easy *data,
- struct connectdata *conn);
-void Curl_pipeline_leave_write(struct connectdata *conn);
-void Curl_pipeline_leave_read(struct connectdata *conn);
-bool Curl_recvpipe_head(struct Curl_easy *data,
- struct connectdata *conn);
-bool Curl_sendpipe_head(struct Curl_easy *data,
- struct connectdata *conn);
-
-#endif /* HEADER_CURL_PIPELINE_H */
diff --git a/Utilities/cmcurl/lib/pop3.c b/Utilities/cmcurl/lib/pop3.c
index 5e0fd2299..c8f3965e4 100644
--- a/Utilities/cmcurl/lib/pop3.c
+++ b/Utilities/cmcurl/lib/pop3.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -30,6 +30,7 @@
* RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
* RFC5034 POP3 SASL Authentication Mechanism
* RFC6749 OAuth 2.0 Authorization Framework
+ * RFC8314 Use of TLS for Email Submission and Access
* Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
*
***************************************************************************/
@@ -208,7 +209,7 @@ static bool pop3_endofresp(struct connectdata *conn, char *line, size_t len,
/* Are we processing CAPA command responses? */
if(pop3c->state == POP3_CAPA) {
/* Do we have the terminating line? */
- if(len >= 1 && !memcmp(line, ".", 1))
+ if(len >= 1 && line[0] == '.')
/* Treat the response as a success */
*resp = '+';
else
@@ -226,7 +227,7 @@ static bool pop3_endofresp(struct connectdata *conn, char *line, size_t len,
}
/* Do we have a continuation response? */
- if(len >= 1 && !memcmp("+", line, 1)) {
+ if(len >= 1 && line[0] == '+') {
*resp = '*';
return TRUE;
@@ -443,7 +444,7 @@ static CURLcode pop3_perform_apop(struct connectdata *conn)
/* Convert the calculated 16 octet digest into a 32 byte hex string */
for(i = 0; i < MD5_DIGEST_LEN; i++)
- snprintf(&secret[2 * i], 3, "%02x", digest[i]);
+ msnprintf(&secret[2 * i], 3, "%02x", digest[i]);
result = Curl_pp_sendf(&pop3c->pp, "APOP %s %s", conn->user, secret);
@@ -629,6 +630,7 @@ static CURLcode pop3_state_servergreet_resp(struct connectdata *conn,
if(line[i] == '<') {
/* Calculate the length of the timestamp */
size_t timestamplen = len - 1 - i;
+ char *at;
if(!timestamplen)
break;
@@ -642,8 +644,15 @@ static CURLcode pop3_state_servergreet_resp(struct connectdata *conn,
memcpy(pop3c->apoptimestamp, line + i, timestamplen);
pop3c->apoptimestamp[timestamplen] = '\0';
- /* Store the APOP capability */
- pop3c->authtypes |= POP3_TYPE_APOP;
+ /* If the timestamp does not contain '@' it is not (as required by
+ RFC-1939) conformant to the RFC-822 message id syntax, and we
+ therefore do not use APOP authentication. */
+ at = strchr(pop3c->apoptimestamp, '@');
+ if(!at)
+ Curl_safefree(pop3c->apoptimestamp);
+ else
+ /* Store the APOP capability */
+ pop3c->authtypes |= POP3_TYPE_APOP;
break;
}
}
@@ -904,7 +913,7 @@ static CURLcode pop3_state_command_resp(struct connectdata *conn,
if(pop3->transfer == FTPTRANSFER_BODY) {
/* POP3 download */
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
if(pp->cache) {
/* The header "cache" contains a bunch of data that is actually body
@@ -1017,19 +1026,20 @@ static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
return result;
}
- result = Curl_pp_statemach(&pop3c->pp, FALSE);
+ result = Curl_pp_statemach(&pop3c->pp, FALSE, FALSE);
*done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
return result;
}
-static CURLcode pop3_block_statemach(struct connectdata *conn)
+static CURLcode pop3_block_statemach(struct connectdata *conn,
+ bool disconnecting)
{
CURLcode result = CURLE_OK;
struct pop3_conn *pop3c = &conn->proto.pop3c;
while(pop3c->state != POP3_STOP && !result)
- result = Curl_pp_statemach(&pop3c->pp, TRUE);
+ result = Curl_pp_statemach(&pop3c->pp, TRUE, disconnecting);
return result;
}
@@ -1227,7 +1237,7 @@ static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection)
point! */
if(!dead_connection && pop3c->pp.conn && pop3c->pp.conn->bits.protoconnstart)
if(!pop3_perform_quit(conn))
- (void)pop3_block_statemach(conn); /* ignore errors on QUIT */
+ (void)pop3_block_statemach(conn, TRUE); /* ignore errors on QUIT */
/* Disconnect from the server */
Curl_pp_disconnect(&pop3c->pp);
diff --git a/Utilities/cmcurl/lib/progress.c b/Utilities/cmcurl/lib/progress.c
index a94668dc2..f586d59b4 100644
--- a/Utilities/cmcurl/lib/progress.c
+++ b/Utilities/cmcurl/lib/progress.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -31,6 +31,7 @@
/* check rate limits within this many recent milliseconds, at minimum. */
#define MIN_RATE_LIMIT_PERIOD 3000
+#ifndef CURL_DISABLE_PROGRESS_METER
/* Provide a string that is 2 + 1 + 2 + 1 + 2 = 8 letters long (plus the zero
byte) */
static void time2str(char *r, curl_off_t seconds)
@@ -44,8 +45,8 @@ static void time2str(char *r, curl_off_t seconds)
if(h <= CURL_OFF_T_C(99)) {
curl_off_t m = (seconds - (h*CURL_OFF_T_C(3600))) / CURL_OFF_T_C(60);
curl_off_t s = (seconds - (h*CURL_OFF_T_C(3600))) - (m*CURL_OFF_T_C(60));
- snprintf(r, 9, "%2" CURL_FORMAT_CURL_OFF_T ":%02" CURL_FORMAT_CURL_OFF_T
- ":%02" CURL_FORMAT_CURL_OFF_T, h, m, s);
+ msnprintf(r, 9, "%2" CURL_FORMAT_CURL_OFF_T ":%02" CURL_FORMAT_CURL_OFF_T
+ ":%02" CURL_FORMAT_CURL_OFF_T, h, m, s);
}
else {
/* this equals to more than 99 hours, switch to a more suitable output
@@ -53,10 +54,10 @@ static void time2str(char *r, curl_off_t seconds)
curl_off_t d = seconds / CURL_OFF_T_C(86400);
h = (seconds - (d*CURL_OFF_T_C(86400))) / CURL_OFF_T_C(3600);
if(d <= CURL_OFF_T_C(999))
- snprintf(r, 9, "%3" CURL_FORMAT_CURL_OFF_T
- "d %02" CURL_FORMAT_CURL_OFF_T "h", d, h);
+ msnprintf(r, 9, "%3" CURL_FORMAT_CURL_OFF_T
+ "d %02" CURL_FORMAT_CURL_OFF_T "h", d, h);
else
- snprintf(r, 9, "%7" CURL_FORMAT_CURL_OFF_T "d", d);
+ msnprintf(r, 9, "%7" CURL_FORMAT_CURL_OFF_T "d", d);
}
}
@@ -72,40 +73,40 @@ static char *max5data(curl_off_t bytes, char *max5)
#define ONE_PETABYTE (CURL_OFF_T_C(1024) * ONE_TERABYTE)
if(bytes < CURL_OFF_T_C(100000))
- snprintf(max5, 6, "%5" CURL_FORMAT_CURL_OFF_T, bytes);
+ msnprintf(max5, 6, "%5" CURL_FORMAT_CURL_OFF_T, bytes);
else if(bytes < CURL_OFF_T_C(10000) * ONE_KILOBYTE)
- snprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "k", bytes/ONE_KILOBYTE);
+ msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "k", bytes/ONE_KILOBYTE);
else if(bytes < CURL_OFF_T_C(100) * ONE_MEGABYTE)
/* 'XX.XM' is good as long as we're less than 100 megs */
- snprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0"
- CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE,
- (bytes%ONE_MEGABYTE) / (ONE_MEGABYTE/CURL_OFF_T_C(10)) );
+ msnprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0"
+ CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE,
+ (bytes%ONE_MEGABYTE) / (ONE_MEGABYTE/CURL_OFF_T_C(10)) );
#if (CURL_SIZEOF_CURL_OFF_T > 4)
else if(bytes < CURL_OFF_T_C(10000) * ONE_MEGABYTE)
/* 'XXXXM' is good until we're at 10000MB or above */
- snprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE);
+ msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE);
else if(bytes < CURL_OFF_T_C(100) * ONE_GIGABYTE)
/* 10000 MB - 100 GB, we show it as XX.XG */
- snprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0"
- CURL_FORMAT_CURL_OFF_T "G", bytes/ONE_GIGABYTE,
- (bytes%ONE_GIGABYTE) / (ONE_GIGABYTE/CURL_OFF_T_C(10)) );
+ msnprintf(max5, 6, "%2" CURL_FORMAT_CURL_OFF_T ".%0"
+ CURL_FORMAT_CURL_OFF_T "G", bytes/ONE_GIGABYTE,
+ (bytes%ONE_GIGABYTE) / (ONE_GIGABYTE/CURL_OFF_T_C(10)) );
else if(bytes < CURL_OFF_T_C(10000) * ONE_GIGABYTE)
/* up to 10000GB, display without decimal: XXXXG */
- snprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "G", bytes/ONE_GIGABYTE);
+ msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "G", bytes/ONE_GIGABYTE);
else if(bytes < CURL_OFF_T_C(10000) * ONE_TERABYTE)
/* up to 10000TB, display without decimal: XXXXT */
- snprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "T", bytes/ONE_TERABYTE);
+ msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "T", bytes/ONE_TERABYTE);
else
/* up to 10000PB, display without decimal: XXXXP */
- snprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "P", bytes/ONE_PETABYTE);
+ msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "P", bytes/ONE_PETABYTE);
/* 16384 petabytes (16 exabytes) is the maximum a 64 bit unsigned number
can hold, but our data type is signed so 8192PB will be the maximum. */
@@ -113,12 +114,13 @@ static char *max5data(curl_off_t bytes, char *max5)
#else
else
- snprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE);
+ msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE);
#endif
return max5;
}
+#endif
/*
@@ -362,17 +364,13 @@ void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size)
}
}
-/*
- * Curl_pgrsUpdate() returns 0 for success or the value returned by the
- * progress callback!
- */
-int Curl_pgrsUpdate(struct connectdata *conn)
+#ifndef CURL_DISABLE_PROGRESS_METER
+static void progress_meter(struct connectdata *conn)
{
struct curltime now;
curl_off_t timespent;
curl_off_t timespent_ms; /* milliseconds */
struct Curl_easy *data = conn->data;
- int nowindex = data->progress.speeder_c% CURR_TIME;
bool shownow = FALSE;
curl_off_t dl = data->progress.downloaded;
curl_off_t ul = data->progress.uploaded;
@@ -399,7 +397,9 @@ int Curl_pgrsUpdate(struct connectdata *conn)
/* Calculations done at most once a second, unless end is reached */
if(data->progress.lastshow != now.tv_sec) {
int countindex; /* amount of seconds stored in the speeder array */
- shownow = TRUE;
+ int nowindex = data->progress.speeder_c% CURR_TIME;
+ if(!(data->progress.flags & PGRS_HIDE))
+ shownow = TRUE;
data->progress.lastshow = now.tv_sec;
@@ -461,8 +461,12 @@ int Curl_pgrsUpdate(struct connectdata *conn)
data->progress.ulspeed + data->progress.dlspeed;
} /* Calculations end */
-
- if(!(data->progress.flags & PGRS_HIDE)) {
+ if(!shownow)
+ /* only show the internal progress meter once per second */
+ return;
+ else {
+ /* If there's no external callback set, use internal code to show
+ progress */
/* progress meter has not been shut off */
char max5[6][10];
curl_off_t dlpercen = 0;
@@ -477,42 +481,6 @@ int Curl_pgrsUpdate(struct connectdata *conn)
curl_off_t dlestimate = 0;
curl_off_t total_estimate;
- if(data->set.fxferinfo) {
- int result;
- /* There's a callback set, call that */
- Curl_set_in_callback(data, true);
- result = data->set.fxferinfo(data->set.progress_client,
- data->progress.size_dl,
- data->progress.downloaded,
- data->progress.size_ul,
- data->progress.uploaded);
- Curl_set_in_callback(data, false);
- if(result)
- failf(data, "Callback aborted");
- return result;
- }
- if(data->set.fprogress) {
- int result;
- /* The older deprecated callback is set, call that */
- Curl_set_in_callback(data, true);
- result = data->set.fprogress(data->set.progress_client,
- (double)data->progress.size_dl,
- (double)data->progress.downloaded,
- (double)data->progress.size_ul,
- (double)data->progress.uploaded);
- Curl_set_in_callback(data, false);
- if(result)
- failf(data, "Callback aborted");
- return result;
- }
-
- if(!shownow)
- /* only show the internal progress meter once per second */
- return 0;
-
- /* If there's no external callback set, use internal code to show
- progress */
-
if(!(data->progress.flags & PGRS_HEADERS_OUT)) {
if(data->state.resume_from) {
fprintf(data->set.err,
@@ -564,9 +532,9 @@ int Curl_pgrsUpdate(struct connectdata *conn)
/* Get the total amount of data expected to get transferred */
total_expected_transfer =
- (data->progress.flags & PGRS_UL_SIZE_KNOWN?
+ ((data->progress.flags & PGRS_UL_SIZE_KNOWN)?
data->progress.size_ul:data->progress.uploaded)+
- (data->progress.flags & PGRS_DL_SIZE_KNOWN?
+ ((data->progress.flags & PGRS_DL_SIZE_KNOWN)?
data->progress.size_dl:data->progress.downloaded);
/* We have transferred this much so far */
@@ -595,13 +563,57 @@ int Curl_pgrsUpdate(struct connectdata *conn)
time_total, /* 8 letters */ /* total time */
time_spent, /* 8 letters */ /* time spent */
time_left, /* 8 letters */ /* time left */
- max5data(data->progress.current_speed, max5[5]) /* current speed */
- );
+ max5data(data->progress.current_speed, max5[5])
+ );
/* we flush the output stream to make it appear as soon as possible */
fflush(data->set.err);
+ } /* don't show now */
+}
+#else
+ /* progress bar disabled */
+#define progress_meter(x)
+#endif
+
- } /* !(data->progress.flags & PGRS_HIDE) */
+/*
+ * Curl_pgrsUpdate() returns 0 for success or the value returned by the
+ * progress callback!
+ */
+int Curl_pgrsUpdate(struct connectdata *conn)
+{
+ struct Curl_easy *data = conn->data;
+ if(!(data->progress.flags & PGRS_HIDE)) {
+ if(data->set.fxferinfo) {
+ int result;
+ /* There's a callback set, call that */
+ Curl_set_in_callback(data, true);
+ result = data->set.fxferinfo(data->set.progress_client,
+ data->progress.size_dl,
+ data->progress.downloaded,
+ data->progress.size_ul,
+ data->progress.uploaded);
+ Curl_set_in_callback(data, false);
+ if(result)
+ failf(data, "Callback aborted");
+ return result;
+ }
+ if(data->set.fprogress) {
+ int result;
+ /* The older deprecated callback is set, call that */
+ Curl_set_in_callback(data, true);
+ result = data->set.fprogress(data->set.progress_client,
+ (double)data->progress.size_dl,
+ (double)data->progress.downloaded,
+ (double)data->progress.size_ul,
+ (double)data->progress.uploaded);
+ Curl_set_in_callback(data, false);
+ if(result)
+ failf(data, "Callback aborted");
+ return result;
+ }
+ }
+ progress_meter(conn);
return 0;
}
diff --git a/Utilities/cmcurl/lib/rand.h b/Utilities/cmcurl/lib/rand.h
index c6fae3553..5deb04161 100644
--- a/Utilities/cmcurl/lib/rand.h
+++ b/Utilities/cmcurl/lib/rand.h
@@ -39,8 +39,11 @@
*/
CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num);
-/* Same as above but outputs only random lowercase hex characters.
- Does NOT terminate.*/
+/*
+ * Curl_rand_hex() fills the 'rnd' buffer with a given 'num' size with random
+ * hexadecimal digits PLUS a zero terminating byte. It must be an odd number
+ * size.
+ */
CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd,
size_t num);
diff --git a/Utilities/cmcurl/lib/rtsp.c b/Utilities/cmcurl/lib/rtsp.c
index 01dfce640..74cf23244 100644
--- a/Utilities/cmcurl/lib/rtsp.c
+++ b/Utilities/cmcurl/lib/rtsp.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -42,16 +42,6 @@
#include "curl_memory.h"
#include "memdebug.h"
-/*
- * TODO (general)
- * -incoming server requests
- * -server CSeq counter
- * -digest authentication
- * -connect through proxy
- * -pipelining?
- */
-
-
#define RTP_PKT_CHANNEL(p) ((int)((unsigned char)((p)[1])))
#define RTP_PKT_LENGTH(p) ((((int)((unsigned char)((p)[2]))) << 8) | \
@@ -80,8 +70,6 @@ static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
bool *readmore);
static CURLcode rtsp_setup_connection(struct connectdata *conn);
-
-bool rtsp_connisdead(struct connectdata *check);
static unsigned int rtsp_conncheck(struct connectdata *check,
unsigned int checks_to_perform);
@@ -147,7 +135,7 @@ static CURLcode rtsp_setup_connection(struct connectdata *conn)
* Instead, if it is readable, run Curl_connalive() to peek at the socket
* and distinguish between closed and data.
*/
-bool rtsp_connisdead(struct connectdata *check)
+static bool rtsp_connisdead(struct connectdata *check)
{
int sval;
bool ret_val = TRUE;
@@ -238,7 +226,6 @@ static CURLcode rtsp_done(struct connectdata *conn,
if(data->set.rtspreq == RTSPREQ_RECEIVE &&
(conn->proto.rtspc.rtp_channel == -1)) {
infof(data, "Got an RTP Receive with a CSeq of %ld\n", CSeq_recv);
- /* TODO CPC: Server -> Client logic here */
}
}
@@ -251,7 +238,6 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)
CURLcode result = CURLE_OK;
Curl_RtspReq rtspreq = data->set.rtspreq;
struct RTSP *rtsp = data->req.protop;
- struct HTTP *http;
Curl_send_buffer *req_buffer;
curl_off_t postsize = 0; /* for ANNOUNCE and SET_PARAMETER */
curl_off_t putsize = 0; /* for ANNOUNCE and SET_PARAMETER */
@@ -270,10 +256,6 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)
*done = TRUE;
- http = &(rtsp->http_wrapper);
- /* Assert that no one has changed the RTSP struct in an evil way */
- DEBUGASSERT((void *)http == (void *)rtsp);
-
rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq;
rtsp->CSeq_recv = 0;
@@ -330,8 +312,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)
}
if(rtspreq == RTSPREQ_RECEIVE) {
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
- &http->readbytecount, -1, NULL);
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1);
return result;
}
@@ -344,8 +325,6 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)
return CURLE_BAD_FUNCTION_ARGUMENT;
}
- /* TODO: proxy? */
-
/* Stream URI. Default to server '*' if not specified */
if(data->set.str[STRING_RTSP_STREAM_URI]) {
p_stream_uri = data->set.str[STRING_RTSP_STREAM_URI];
@@ -599,17 +578,15 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)
return result;
}
- Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
- putsize?FIRSTSOCKET:-1,
- putsize?&http->writebytecount:NULL);
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, putsize?FIRSTSOCKET:-1);
/* Increment the CSeq on success */
data->state.rtsp_next_client_CSeq++;
- if(http->writebytecount) {
+ if(data->req.writebytecount) {
/* if a request-body has been sent off, we make sure this progress is
noted properly */
- Curl_pgrsSetUploadCounter(data, http->writebytecount);
+ Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
if(Curl_pgrsUpdate(conn))
result = CURLE_ABORTED_BY_CALLBACK;
}
diff --git a/Utilities/cmcurl/lib/security.c b/Utilities/cmcurl/lib/security.c
index c278406ba..76951548d 100644
--- a/Utilities/cmcurl/lib/security.c
+++ b/Utilities/cmcurl/lib/security.c
@@ -10,7 +10,7 @@
* Copyright (c) 1998, 1999, 2017 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
*
- * Copyright (C) 2001 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2001 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* All rights reserved.
*
@@ -120,7 +120,7 @@ static int ftp_send_command(struct connectdata *conn, const char *message, ...)
char print_buffer[50];
va_start(args, message);
- vsnprintf(print_buffer, sizeof(print_buffer), message, args);
+ mvsnprintf(print_buffer, sizeof(print_buffer), message, args);
va_end(args);
if(Curl_ftpsend(conn, print_buffer)) {
@@ -142,7 +142,7 @@ socket_read(curl_socket_t fd, void *to, size_t len)
{
char *to_p = to;
CURLcode result;
- ssize_t nread;
+ ssize_t nread = 0;
while(len > 0) {
result = Curl_read_plain(fd, to_p, len, &nread);
@@ -151,7 +151,6 @@ socket_read(curl_socket_t fd, void *to, size_t len)
to_p += nread;
}
else {
- /* FIXME: We are doing a busy wait */
if(result == CURLE_AGAIN)
continue;
return result;
@@ -179,7 +178,6 @@ socket_write(struct connectdata *conn, curl_socket_t fd, const void *to,
to_p += written;
}
else {
- /* FIXME: We are doing a busy wait */
if(result == CURLE_AGAIN)
continue;
return result;
@@ -265,13 +263,11 @@ static ssize_t sec_recv(struct connectdata *conn, int sockindex,
total_read += bytes_read;
buffer += bytes_read;
}
- /* FIXME: Check for overflow */
return total_read;
}
/* Send |length| bytes from |from| to the |fd| socket taking care of encoding
and negociating with the server. |from| can be NULL. */
-/* FIXME: We don't check for errors nor report any! */
static void do_sec_send(struct connectdata *conn, curl_socket_t fd,
const char *from, int length)
{
@@ -406,18 +402,14 @@ int Curl_sec_read_msg(struct connectdata *conn, char *buffer,
if(buf[decoded_len - 1] == '\n')
buf[decoded_len - 1] = '\0';
- /* FIXME: Is |buffer| length always greater than |decoded_len|? */
strcpy(buffer, buf);
free(buf);
return ret_code;
}
-/* FIXME: The error code returned here is never checked. */
static int sec_set_protection_level(struct connectdata *conn)
{
int code;
- char *pbsz;
- static unsigned int buffer_size = 1 << 20; /* 1048576 */
enum protection_level level = conn->request_data_prot;
DEBUGASSERT(level > PROT_NONE && level < PROT_LAST);
@@ -433,6 +425,9 @@ static int sec_set_protection_level(struct connectdata *conn)
return 0;
if(level) {
+ char *pbsz;
+ static unsigned int buffer_size = 1 << 20; /* 1048576 */
+
code = ftp_send_command(conn, "PBSZ %u", buffer_size);
if(code < 0)
return -1;
@@ -508,7 +503,6 @@ static CURLcode choose_mech(struct connectdata *conn)
infof(data, "Trying mechanism %s...\n", mech->name);
ret = ftp_send_command(conn, "AUTH %s", mech->name);
if(ret < 0)
- /* FIXME: This error is too generic but it is OK for now. */
return CURLE_COULDNT_CONNECT;
if(ret/100 != 3) {
@@ -575,7 +569,6 @@ Curl_sec_end(struct connectdata *conn)
conn->in_buffer.data = NULL;
conn->in_buffer.size = 0;
conn->in_buffer.index = 0;
- /* FIXME: Is this really needed? */
conn->in_buffer.eof_flag = 0;
}
conn->sec_complete = 0;
diff --git a/Utilities/cmcurl/lib/sendf.c b/Utilities/cmcurl/lib/sendf.c
index d3c10b369..5913ea406 100644
--- a/Utilities/cmcurl/lib/sendf.c
+++ b/Utilities/cmcurl/lib/sendf.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -237,7 +237,18 @@ void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
size_t len;
char print_buffer[2048 + 1];
va_start(ap, fmt);
- vsnprintf(print_buffer, sizeof(print_buffer), fmt, ap);
+ len = mvsnprintf(print_buffer, sizeof(print_buffer), fmt, ap);
+ /*
+ * Indicate truncation of the input by replacing the last 3 characters
+ * with "...", and transfer the newline over in case the format had one.
+ */
+ if(len >= sizeof(print_buffer)) {
+ len = strlen(fmt);
+ if(fmt[--len] == '\n')
+ msnprintf(print_buffer + (sizeof(print_buffer) - 5), 5, "...\n");
+ else
+ msnprintf(print_buffer + (sizeof(print_buffer) - 4), 4, "...");
+ }
va_end(ap);
len = strlen(print_buffer);
Curl_debug(data, CURLINFO_TEXT, print_buffer, len);
@@ -255,7 +266,7 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
size_t len;
char error[CURL_ERROR_SIZE + 2];
va_start(ap, fmt);
- vsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
+ mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
len = strlen(error);
if(data->set.errorbuffer && !data->state.errorbuf) {
@@ -400,8 +411,9 @@ ssize_t Curl_send_plain(struct connectdata *conn, int num,
*code = CURLE_AGAIN;
}
else {
+ char buffer[STRERROR_LEN];
failf(conn->data, "Send failure: %s",
- Curl_strerror(conn, err));
+ Curl_strerror(err, buffer, sizeof(buffer)));
conn->data->state.os_errno = err;
*code = CURLE_SEND_ERROR;
}
@@ -465,8 +477,9 @@ ssize_t Curl_recv_plain(struct connectdata *conn, int num, char *buf,
*code = CURLE_AGAIN;
}
else {
+ char buffer[STRERROR_LEN];
failf(conn->data, "Recv failure: %s",
- Curl_strerror(conn, err));
+ Curl_strerror(err, buffer, sizeof(buffer)));
conn->data->state.os_errno = err;
*code = CURLE_RECV_ERROR;
}
@@ -582,7 +595,10 @@ static CURLcode chop_write(struct connectdata *conn,
size_t chunklen = len <= CURL_MAX_WRITE_SIZE? len: CURL_MAX_WRITE_SIZE;
if(writebody) {
- size_t wrote = writebody(ptr, 1, chunklen, data->set.out);
+ size_t wrote;
+ Curl_set_in_callback(data, true);
+ wrote = writebody(ptr, 1, chunklen, data->set.out);
+ Curl_set_in_callback(data, false);
if(CURL_WRITEFUNC_PAUSE == wrote) {
if(conn->handler->flags & PROTOPT_NONETWORK) {
@@ -711,10 +727,6 @@ CURLcode Curl_read(struct connectdata *conn, /* connection data */
char *buffertofill = NULL;
struct Curl_easy *data = conn->data;
- /* if HTTP/1 pipelining is both wanted and possible */
- bool pipelining = Curl_pipeline_wanted(data->multi, CURLPIPE_HTTP1) &&
- (conn->bundle->multiuse == BUNDLE_PIPELINING);
-
/* Set 'num' to 0 or 1, depending on which socket that has been sent here.
If it is the second socket, we set num to 1. Otherwise to 0. This lets
us use the correct ssl handle. */
@@ -722,40 +734,13 @@ CURLcode Curl_read(struct connectdata *conn, /* connection data */
*n = 0; /* reset amount to zero */
- /* If session can pipeline, check connection buffer */
- if(pipelining) {
- size_t bytestocopy = CURLMIN(conn->buf_len - conn->read_pos,
- sizerequested);
-
- /* Copy from our master buffer first if we have some unread data there*/
- if(bytestocopy > 0) {
- memcpy(buf, conn->master_buffer + conn->read_pos, bytestocopy);
- conn->read_pos += bytestocopy;
- conn->bits.stream_was_rewound = FALSE;
-
- *n = (ssize_t)bytestocopy;
- return CURLE_OK;
- }
- /* If we come here, it means that there is no data to read from the buffer,
- * so we read from the socket */
- bytesfromsocket = CURLMIN(sizerequested, MASTERBUF_SIZE);
- buffertofill = conn->master_buffer;
- }
- else {
- bytesfromsocket = CURLMIN(sizerequested, (size_t)data->set.buffer_size);
- buffertofill = buf;
- }
+ bytesfromsocket = CURLMIN(sizerequested, (size_t)data->set.buffer_size);
+ buffertofill = buf;
nread = conn->recv[num](conn, num, buffertofill, bytesfromsocket, &result);
if(nread < 0)
return result;
- if(pipelining) {
- memcpy(buf, conn->master_buffer, nread);
- conn->buf_len = nread;
- conn->read_pos = nread;
- }
-
*n += nread;
return CURLE_OK;
diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c
index 22956a20f..92cd5b271 100644
--- a/Utilities/cmcurl/lib/setopt.c
+++ b/Utilities/cmcurl/lib/setopt.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -44,6 +44,7 @@
#include "http2.h"
#include "setopt.h"
#include "multiif.h"
+#include "altsvc.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -60,6 +61,13 @@ CURLcode Curl_setstropt(char **charp, const char *s)
if(s) {
char *str = strdup(s);
+ if(str) {
+ size_t len = strlen(str);
+ if(len > CURL_MAX_INPUT_LENGTH) {
+ free(str);
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+ }
if(!str)
return CURLE_OUT_OF_MEMORY;
@@ -111,12 +119,13 @@ static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp)
#define C_SSLVERSION_VALUE(x) (x & 0xffff)
#define C_SSLVERSION_MAX_VALUE(x) (x & 0xffff0000)
-CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
- va_list param)
+static CURLcode vsetopt(struct Curl_easy *data, CURLoption option,
+ va_list param)
{
char *argptr;
CURLcode result = CURLE_OK;
long arg;
+ unsigned long uarg;
curl_off_t bigsize;
switch(option) {
@@ -127,23 +136,20 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
data->set.dns_cache_timeout = arg;
break;
case CURLOPT_DNS_USE_GLOBAL_CACHE:
-#if 0 /* deprecated */
- /* remember we want this enabled */
- arg = va_arg(param, long);
- data->set.global_dns_cache = (0 != arg) ? TRUE : FALSE;
-#endif
+ /* deprecated */
break;
case CURLOPT_SSL_CIPHER_LIST:
/* set a list of cipher we want to use in the SSL connection */
result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER_LIST_ORIG],
va_arg(param, char *));
break;
+#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_SSL_CIPHER_LIST:
/* set a list of cipher we want to use in the SSL connection for proxy */
result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER_LIST_PROXY],
va_arg(param, char *));
break;
-
+#endif
case CURLOPT_TLS13_CIPHERS:
if(Curl_ssl_tls13_ciphersuites()) {
/* set preferred list of TLS 1.3 cipher suites */
@@ -153,6 +159,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
else
return CURLE_NOT_BUILT_IN;
break;
+#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_TLS13_CIPHERS:
if(Curl_ssl_tls13_ciphersuites()) {
/* set preferred list of TLS 1.3 cipher suites for proxy */
@@ -162,7 +169,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
else
return CURLE_NOT_BUILT_IN;
break;
-
+#endif
case CURLOPT_RANDOM_FILE:
/*
* This is the path name to a file that contains random data to seed
@@ -270,27 +277,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
*/
data->set.get_filetime = (0 != va_arg(param, long)) ? TRUE : FALSE;
break;
- case CURLOPT_FTP_CREATE_MISSING_DIRS:
- /*
- * An FTP option that modifies an upload to create missing directories on
- * the server.
- */
- switch(va_arg(param, long)) {
- case 0:
- data->set.ftp_create_missing_dirs = 0;
- break;
- case 1:
- data->set.ftp_create_missing_dirs = 1;
- break;
- case 2:
- data->set.ftp_create_missing_dirs = 2;
- break;
- default:
- /* reserve other values for future use */
- result = CURLE_UNKNOWN_OPTION;
- break;
- }
- break;
case CURLOPT_SERVER_RESPONSE_TIMEOUT:
/*
* Option that specifies how quickly an server response must be obtained
@@ -302,6 +288,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
else
return CURLE_BAD_FUNCTION_ARGUMENT;
break;
+#ifndef CURL_DISABLE_TFTP
case CURLOPT_TFTP_NO_OPTIONS:
/*
* Option that prevents libcurl from sending TFTP option requests to the
@@ -318,28 +305,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
return CURLE_BAD_FUNCTION_ARGUMENT;
data->set.tftp_blksize = arg;
break;
- case CURLOPT_DIRLISTONLY:
- /*
- * An option that changes the command to one that asks for a list
- * only, no file info details.
- */
- data->set.ftp_list_only = (0 != va_arg(param, long)) ? TRUE : FALSE;
- break;
- case CURLOPT_APPEND:
- /*
- * We want to upload and append to an existing file.
- */
- data->set.ftp_append = (0 != va_arg(param, long)) ? TRUE : FALSE;
- break;
- case CURLOPT_FTP_FILEMETHOD:
- /*
- * How do access files over FTP.
- */
- arg = va_arg(param, long);
- if((arg < CURLFTPMETHOD_DEFAULT) || (arg > CURLFTPMETHOD_SINGLECWD))
- return CURLE_BAD_FUNCTION_ARGUMENT;
- data->set.ftp_filemethod = (curl_ftpfile)arg;
- break;
+#endif
+#ifndef CURL_DISABLE_NETRC
case CURLOPT_NETRC:
/*
* Parse the $HOME/.netrc file
@@ -356,6 +323,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
result = Curl_setstropt(&data->set.str[STRING_NETRC_FILE],
va_arg(param, char *));
break;
+#endif
case CURLOPT_TRANSFERTEXT:
/*
* This option was previously named 'FTPASCII'. Renamed to work with
@@ -663,6 +631,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
break;
#ifndef CURL_DISABLE_HTTP
+#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXYHEADER:
/*
* Set a list with proxy headers to use (or replace internals with)
@@ -676,13 +645,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
*/
data->set.proxyheaders = va_arg(param, struct curl_slist *);
break;
-
+#endif
case CURLOPT_HEADEROPT:
/*
* Set header option.
*/
arg = va_arg(param, long);
- data->set.sep_headers = (arg & CURLHEADER_SEPARATE)? TRUE: FALSE;
+ data->set.sep_headers = (bool)((arg & CURLHEADER_SEPARATE)? TRUE: FALSE);
break;
case CURLOPT_HTTP200ALIASES:
@@ -803,12 +772,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
if(checkprefix("Set-Cookie:", argptr))
/* HTTP Header format line */
Curl_cookie_add(data, data->cookies, TRUE, FALSE, argptr + 11, NULL,
- NULL);
+ NULL, TRUE);
else
/* Netscape format line */
Curl_cookie_add(data, data->cookies, FALSE, FALSE, argptr, NULL,
- NULL);
+ NULL, TRUE);
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
free(argptr);
@@ -860,6 +829,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
data->set.expect_100_timeout = arg;
break;
+ case CURLOPT_HTTP09_ALLOWED:
+ arg = va_arg(param, unsigned long);
+ if(arg > 1L)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.http09_allowed = arg ? TRUE : FALSE;
+ break;
#endif /* CURL_DISABLE_HTTP */
case CURLOPT_HTTPAUTH:
@@ -878,7 +853,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
/* the DIGEST_IE bit is only used to set a special marker, for all the
rest we need to handle it as normal DIGEST */
- data->state.authhost.iestyle = (auth & CURLAUTH_DIGEST_IE) ? TRUE : FALSE;
+ data->state.authhost.iestyle =
+ (bool)((auth & CURLAUTH_DIGEST_IE) ? TRUE : FALSE);
if(auth & CURLAUTH_DIGEST_IE) {
auth |= CURLAUTH_DIGEST; /* set standard digest bit */
@@ -961,7 +937,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
/* the DIGEST_IE bit is only used to set a special marker, for all the
rest we need to handle it as normal DIGEST */
- data->state.authproxy.iestyle = (auth & CURLAUTH_DIGEST_IE) ? TRUE : FALSE;
+ data->state.authproxy.iestyle =
+ (bool)((auth & CURLAUTH_DIGEST_IE) ? TRUE : FALSE);
if(auth & CURLAUTH_DIGEST_IE) {
auth |= CURLAUTH_DIGEST; /* set standard digest bit */
@@ -1063,7 +1040,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
data->set.socks5_gssapi_nec = (0 != va_arg(param, long)) ? TRUE : FALSE;
break;
#endif
-
+#ifndef CURL_DISABLE_PROXY
case CURLOPT_SOCKS5_GSSAPI_SERVICE:
case CURLOPT_PROXY_SERVICE_NAME:
/*
@@ -1072,7 +1049,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
result = Curl_setstropt(&data->set.str[STRING_PROXY_SERVICE_NAME],
va_arg(param, char *));
break;
-
+#endif
case CURLOPT_SERVICE_NAME:
/*
* Set authentication service name for DIGEST-MD5, Kerberos 5 and SPNEGO
@@ -1101,6 +1078,33 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
*/
data->set.out = va_arg(param, void *);
break;
+
+ case CURLOPT_DIRLISTONLY:
+ /*
+ * An option that changes the command to one that asks for a list only, no
+ * file info details. Used for FTP, POP3 and SFTP.
+ */
+ data->set.ftp_list_only = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+
+ case CURLOPT_APPEND:
+ /*
+ * We want to upload and append to an existing file. Used for FTP and
+ * SFTP.
+ */
+ data->set.ftp_append = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ break;
+
+#ifndef CURL_DISABLE_FTP
+ case CURLOPT_FTP_FILEMETHOD:
+ /*
+ * How do access files over FTP.
+ */
+ arg = va_arg(param, long);
+ if((arg < CURLFTPMETHOD_DEFAULT) || (arg > CURLFTPMETHOD_SINGLECWD))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.ftp_filemethod = (curl_ftpfile)arg;
+ break;
case CURLOPT_FTPPORT:
/*
* Use FTP PORT, this also specifies which IP address to use
@@ -1137,6 +1141,55 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
data->set.ftp_skip_ip = (0 != va_arg(param, long)) ? TRUE : FALSE;
break;
+ case CURLOPT_FTP_ACCOUNT:
+ result = Curl_setstropt(&data->set.str[STRING_FTP_ACCOUNT],
+ va_arg(param, char *));
+ break;
+
+ case CURLOPT_FTP_ALTERNATIVE_TO_USER:
+ result = Curl_setstropt(&data->set.str[STRING_FTP_ALTERNATIVE_TO_USER],
+ va_arg(param, char *));
+ break;
+
+ case CURLOPT_FTPSSLAUTH:
+ /*
+ * Set a specific auth for FTP-SSL transfers.
+ */
+ arg = va_arg(param, long);
+ if((arg < CURLFTPAUTH_DEFAULT) || (arg > CURLFTPAUTH_TLS))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.ftpsslauth = (curl_ftpauth)arg;
+ break;
+ case CURLOPT_KRBLEVEL:
+ /*
+ * A string that defines the kerberos security level.
+ */
+ result = Curl_setstropt(&data->set.str[STRING_KRB_LEVEL],
+ va_arg(param, char *));
+ data->set.krb = (data->set.str[STRING_KRB_LEVEL]) ? TRUE : FALSE;
+ break;
+#endif
+ case CURLOPT_FTP_CREATE_MISSING_DIRS:
+ /*
+ * An FTP/SFTP option that modifies an upload to create missing
+ * directories on the server.
+ */
+ switch(va_arg(param, long)) {
+ case 0:
+ data->set.ftp_create_missing_dirs = 0;
+ break;
+ case 1:
+ data->set.ftp_create_missing_dirs = 1;
+ break;
+ case 2:
+ data->set.ftp_create_missing_dirs = 2;
+ break;
+ default:
+ /* reserve other values for future use */
+ result = CURLE_UNKNOWN_OPTION;
+ break;
+ }
+ break;
case CURLOPT_READDATA:
/*
* FILE pointer to read the file to be uploaded from. Or possibly
@@ -1204,6 +1257,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
return CURLE_BAD_FUNCTION_ARGUMENT;
data->set.low_speed_time = arg;
break;
+ case CURLOPT_CURLU:
+ /*
+ * pass CURLU to set URL
+ */
+ data->set.uh = va_arg(param, CURLU *);
+ break;
case CURLOPT_URL:
/*
* The URL to fetch.
@@ -1539,6 +1598,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
result = Curl_setstropt(&data->set.str[STRING_CERT_ORIG],
va_arg(param, char *));
break;
+#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_SSLCERT:
/*
* String that holds file name of the SSL certificate to use for proxy
@@ -1546,6 +1606,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
result = Curl_setstropt(&data->set.str[STRING_CERT_PROXY],
va_arg(param, char *));
break;
+#endif
case CURLOPT_SSLCERTTYPE:
/*
* String that holds file type of the SSL certificate to use
@@ -1553,6 +1614,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
result = Curl_setstropt(&data->set.str[STRING_CERT_TYPE_ORIG],
va_arg(param, char *));
break;
+#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_SSLCERTTYPE:
/*
* String that holds file type of the SSL certificate to use for proxy
@@ -1560,6 +1622,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
result = Curl_setstropt(&data->set.str[STRING_CERT_TYPE_PROXY],
va_arg(param, char *));
break;
+#endif
case CURLOPT_SSLKEY:
/*
* String that holds file name of the SSL key to use
@@ -1567,6 +1630,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
result = Curl_setstropt(&data->set.str[STRING_KEY_ORIG],
va_arg(param, char *));
break;
+#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_SSLKEY:
/*
* String that holds file name of the SSL key to use for proxy
@@ -1574,6 +1638,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
result = Curl_setstropt(&data->set.str[STRING_KEY_PROXY],
va_arg(param, char *));
break;
+#endif
case CURLOPT_SSLKEYTYPE:
/*
* String that holds file type of the SSL key to use
@@ -1581,6 +1646,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
result = Curl_setstropt(&data->set.str[STRING_KEY_TYPE_ORIG],
va_arg(param, char *));
break;
+#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_SSLKEYTYPE:
/*
* String that holds file type of the SSL key to use for proxy
@@ -1588,6 +1654,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
result = Curl_setstropt(&data->set.str[STRING_KEY_TYPE_PROXY],
va_arg(param, char *));
break;
+#endif
case CURLOPT_KEYPASSWD:
/*
* String that holds the SSL or SSH private key password.
@@ -1595,6 +1662,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
result = Curl_setstropt(&data->set.str[STRING_KEY_PASSWD_ORIG],
va_arg(param, char *));
break;
+#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_KEYPASSWD:
/*
* String that holds the SSL private key password for proxy.
@@ -1602,6 +1670,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
result = Curl_setstropt(&data->set.str[STRING_KEY_PASSWD_PROXY],
va_arg(param, char *));
break;
+#endif
case CURLOPT_SSLENGINE:
/*
* String that holds the SSL crypto engine.
@@ -1628,14 +1697,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
*/
data->set.crlf = (0 != va_arg(param, long)) ? TRUE : FALSE;
break;
-
+#ifndef CURL_DISABLE_PROXY
case CURLOPT_HAPROXYPROTOCOL:
/*
* Set to send the HAProxy Proxy Protocol header
*/
data->set.haproxyprotocol = (0 != va_arg(param, long)) ? TRUE : FALSE;
break;
-
+#endif
case CURLOPT_INTERFACE:
/*
* Set what interface or address/hostname to bind the socket to when
@@ -1662,14 +1731,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
return CURLE_BAD_FUNCTION_ARGUMENT;
data->set.localportrange = curlx_sltosi(arg);
break;
- case CURLOPT_KRBLEVEL:
- /*
- * A string that defines the kerberos security level.
- */
- result = Curl_setstropt(&data->set.str[STRING_KRB_LEVEL],
- va_arg(param, char *));
- data->set.krb = (data->set.str[STRING_KRB_LEVEL]) ? TRUE : FALSE;
- break;
case CURLOPT_GSSAPI_DELEGATION:
/*
* GSS-API credential delegation bitmask
@@ -1687,11 +1748,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
TRUE : FALSE;
/* Update the current connection ssl_config. */
- if(data->easy_conn) {
- data->easy_conn->ssl_config.verifypeer =
+ if(data->conn) {
+ data->conn->ssl_config.verifypeer =
data->set.ssl.primary.verifypeer;
}
break;
+#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_SSL_VERIFYPEER:
/*
* Enable peer SSL verifying for proxy.
@@ -1700,11 +1762,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
(0 != va_arg(param, long))?TRUE:FALSE;
/* Update the current connection proxy_ssl_config. */
- if(data->easy_conn) {
- data->easy_conn->proxy_ssl_config.verifypeer =
+ if(data->conn) {
+ data->conn->proxy_ssl_config.verifypeer =
data->set.proxy_ssl.primary.verifypeer;
}
break;
+#endif
case CURLOPT_SSL_VERIFYHOST:
/*
* Enable verification of the host name in the peer certificate
@@ -1724,11 +1787,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
data->set.ssl.primary.verifyhost = (0 != arg) ? TRUE : FALSE;
/* Update the current connection ssl_config. */
- if(data->easy_conn) {
- data->easy_conn->ssl_config.verifyhost =
+ if(data->conn) {
+ data->conn->ssl_config.verifyhost =
data->set.ssl.primary.verifyhost;
}
break;
+#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_SSL_VERIFYHOST:
/*
* Enable verification of the host name in the peer certificate for proxy
@@ -1748,11 +1812,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
data->set.proxy_ssl.primary.verifyhost = (0 != arg)?TRUE:FALSE;
/* Update the current connection proxy_ssl_config. */
- if(data->easy_conn) {
- data->easy_conn->proxy_ssl_config.verifyhost =
+ if(data->conn) {
+ data->conn->proxy_ssl_config.verifyhost =
data->set.proxy_ssl.primary.verifyhost;
}
break;
+#endif
case CURLOPT_SSL_VERIFYSTATUS:
/*
* Enable certificate status verifying.
@@ -1766,8 +1831,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
TRUE : FALSE;
/* Update the current connection ssl_config. */
- if(data->easy_conn) {
- data->easy_conn->ssl_config.verifystatus =
+ if(data->conn) {
+ data->conn->ssl_config.verifystatus =
data->set.ssl.primary.verifystatus;
}
break;
@@ -1825,6 +1890,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
#endif
result = CURLE_NOT_BUILT_IN;
break;
+#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_PINNEDPUBLICKEY:
/*
* Set pinned public key for SSL connection.
@@ -1838,6 +1904,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
#endif
result = CURLE_NOT_BUILT_IN;
break;
+#endif
case CURLOPT_CAINFO:
/*
* Set CA info for SSL connection. Specify file name of the CA certificate
@@ -1845,6 +1912,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
result = Curl_setstropt(&data->set.str[STRING_SSL_CAFILE_ORIG],
va_arg(param, char *));
break;
+#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_CAINFO:
/*
* Set CA info SSL connection for proxy. Specify file name of the
@@ -1853,6 +1921,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
result = Curl_setstropt(&data->set.str[STRING_SSL_CAFILE_PROXY],
va_arg(param, char *));
break;
+#endif
case CURLOPT_CAPATH:
/*
* Set CA path info for SSL connection. Specify directory name of the CA
@@ -1867,6 +1936,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
#endif
result = CURLE_NOT_BUILT_IN;
break;
+#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_CAPATH:
/*
* Set CA path info for SSL connection proxy. Specify directory name of the
@@ -1881,6 +1951,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
#endif
result = CURLE_NOT_BUILT_IN;
break;
+#endif
case CURLOPT_CRLFILE:
/*
* Set CRL file info for SSL connection. Specify file name of the CRL
@@ -1889,6 +1960,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
result = Curl_setstropt(&data->set.str[STRING_SSL_CRLFILE_ORIG],
va_arg(param, char *));
break;
+#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_CRLFILE:
/*
* Set CRL file info for SSL connection for proxy. Specify file name of the
@@ -1897,6 +1969,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
result = Curl_setstropt(&data->set.str[STRING_SSL_CRLFILE_PROXY],
va_arg(param, char *));
break;
+#endif
case CURLOPT_ISSUERCERT:
/*
* Set Issuer certificate file
@@ -1905,13 +1978,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
result = Curl_setstropt(&data->set.str[STRING_SSL_ISSUERCERT_ORIG],
va_arg(param, char *));
break;
+#ifndef CURL_DISABLE_TELNET
case CURLOPT_TELNETOPTIONS:
/*
* Set a linked list of telnet options
*/
data->set.telnet_options = va_arg(param, struct curl_slist *);
break;
-
+#endif
case CURLOPT_BUFFERSIZE:
/*
* The application kindly asks for a differently sized receive buffer.
@@ -2064,27 +2138,21 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
case CURLOPT_SSL_OPTIONS:
arg = va_arg(param, long);
- data->set.ssl.enable_beast = arg&CURLSSLOPT_ALLOW_BEAST?TRUE:FALSE;
+ data->set.ssl.enable_beast =
+ (bool)((arg&CURLSSLOPT_ALLOW_BEAST) ? TRUE : FALSE);
data->set.ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE);
break;
+#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_SSL_OPTIONS:
arg = va_arg(param, long);
- data->set.proxy_ssl.enable_beast = arg&CURLSSLOPT_ALLOW_BEAST?TRUE:FALSE;
+ data->set.proxy_ssl.enable_beast =
+ (bool)((arg&CURLSSLOPT_ALLOW_BEAST) ? TRUE : FALSE);
data->set.proxy_ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE);
break;
-
#endif
- case CURLOPT_FTPSSLAUTH:
- /*
- * Set a specific auth for FTP-SSL transfers.
- */
- arg = va_arg(param, long);
- if((arg < CURLFTPAUTH_DEFAULT) || (arg > CURLFTPAUTH_TLS))
- return CURLE_BAD_FUNCTION_ARGUMENT;
- data->set.ftpsslauth = (curl_ftpauth)arg;
- break;
+#endif
case CURLOPT_IPRESOLVE:
arg = va_arg(param, long);
if((arg < CURL_IPRESOLVE_WHATEVER) || (arg > CURL_IPRESOLVE_V6))
@@ -2110,11 +2178,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
data->set.tcp_nodelay = (0 != va_arg(param, long)) ? TRUE : FALSE;
break;
- case CURLOPT_FTP_ACCOUNT:
- result = Curl_setstropt(&data->set.str[STRING_FTP_ACCOUNT],
- va_arg(param, char *));
- break;
-
case CURLOPT_IGNORE_CONTENT_LENGTH:
data->set.ignorecl = (0 != va_arg(param, long)) ? TRUE : FALSE;
break;
@@ -2126,11 +2189,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
data->set.connect_only = (0 != va_arg(param, long)) ? TRUE : FALSE;
break;
- case CURLOPT_FTP_ALTERNATIVE_TO_USER:
- result = Curl_setstropt(&data->set.str[STRING_FTP_ALTERNATIVE_TO_USER],
- va_arg(param, char *));
- break;
-
case CURLOPT_SOCKOPTFUNCTION:
/*
* socket callback function: called after socket() but before connect()
@@ -2196,7 +2254,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
data->set.proxy_ssl.primary.sessionid = data->set.ssl.primary.sessionid;
break;
-#if defined(USE_LIBSSH2) || defined(USE_LIBSSH)
+#ifdef USE_SSH
/* we only include SSH options if explicitly built to support SSH */
case CURLOPT_SSH_AUTH_TYPES:
data->set.ssh_auth_types = va_arg(param, long);
@@ -2225,7 +2283,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
result = Curl_setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5],
va_arg(param, char *));
break;
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
+
case CURLOPT_SSH_KNOWNHOSTS:
/*
* Store the file name to read known hosts from.
@@ -2246,8 +2304,11 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
*/
data->set.ssh_keyfunc_userp = va_arg(param, void *);
break;
-#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
-#endif /* USE_LIBSSH2 */
+
+ case CURLOPT_SSH_COMPRESSION:
+ data->set.ssh_compression = (0 != va_arg(param, long))?TRUE:FALSE;
+ break;
+#endif /* USE_SSH */
case CURLOPT_HTTP_TRANSFER_DECODING:
/*
@@ -2263,6 +2324,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
data->set.http_ce_skip = (0 == va_arg(param, long)) ? TRUE : FALSE;
break;
+#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH)
case CURLOPT_NEW_FILE_PERMS:
/*
* Uses these permissions instead of 0644
@@ -2282,17 +2344,20 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
return CURLE_BAD_FUNCTION_ARGUMENT;
data->set.new_directory_perms = arg;
break;
+#endif
case CURLOPT_ADDRESS_SCOPE:
/*
- * We always get longs when passed plain numericals, but for this value we
- * know that an unsigned int will always hold the value so we blindly
- * typecast to this type
+ * Use this scope id when using IPv6
+ * We always get longs when passed plain numericals so we should check
+ * that the value fits into an unsigned 32 bit integer.
*/
- arg = va_arg(param, long);
- if((arg < 0) || (arg > 0xf))
+ uarg = va_arg(param, unsigned long);
+#if SIZEOF_LONG > 4
+ if(uarg > UINT_MAX)
return CURLE_BAD_FUNCTION_ARGUMENT;
- data->set.scope_id = curlx_sltoui(arg);
+#endif
+ data->set.scope_id = (unsigned int)uarg;
break;
case CURLOPT_PROTOCOLS:
@@ -2316,7 +2381,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
result = Curl_setstropt(&data->set.str[STRING_DEFAULT_PROTOCOL],
va_arg(param, char *));
break;
-
+#ifndef CURL_DISABLE_SMTP
case CURLOPT_MAIL_FROM:
/* Set the SMTP mail originator */
result = Curl_setstropt(&data->set.str[STRING_MAIL_FROM],
@@ -2333,12 +2398,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
/* Set the list of mail recipients */
data->set.mail_rcpt = va_arg(param, struct curl_slist *);
break;
+#endif
case CURLOPT_SASL_IR:
/* Enable/disable SASL initial response */
data->set.sasl_ir = (0 != va_arg(param, long)) ? TRUE : FALSE;
break;
-
+#ifndef CURL_DISABLE_RTSP
case CURLOPT_RTSP_REQUEST:
{
/*
@@ -2447,7 +2513,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
/* Set the user defined RTP write function */
data->set.fwrite_rtp = va_arg(param, curl_write_callback);
break;
-
+#endif
+#ifndef CURL_DISABLE_FTP
case CURLOPT_WILDCARDMATCH:
data->set.wildcard_enabled = (0 != va_arg(param, long)) ? TRUE : FALSE;
break;
@@ -2466,6 +2533,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
case CURLOPT_FNMATCH_DATA:
data->set.fnmatch_data = va_arg(param, void *);
break;
+#endif
#ifdef USE_TLS_SRP
case CURLOPT_TLSAUTH_USERNAME:
result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME_ORIG],
@@ -2510,6 +2578,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
data->set.proxy_ssl.authtype = CURL_TLSAUTH_NONE;
break;
#endif
+#ifdef USE_ARES
case CURLOPT_DNS_SERVERS:
result = Curl_set_dns_servers(data, va_arg(param, char *));
break;
@@ -2522,7 +2591,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
case CURLOPT_DNS_LOCAL_IP6:
result = Curl_set_dns_local_ip6(data, va_arg(param, char *));
break;
-
+#endif
case CURLOPT_TCP_KEEPALIVE:
data->set.tcp_keepalive = (0 != va_arg(param, long)) ? TRUE : FALSE;
break;
@@ -2546,13 +2615,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
result = CURLE_NOT_BUILT_IN;
#endif
break;
+#ifdef USE_NGHTTP2
case CURLOPT_SSL_ENABLE_NPN:
data->set.ssl_enable_npn = (0 != va_arg(param, long)) ? TRUE : FALSE;
break;
case CURLOPT_SSL_ENABLE_ALPN:
data->set.ssl_enable_alpn = (0 != va_arg(param, long)) ? TRUE : FALSE;
break;
-
+#endif
#ifdef USE_UNIX_SOCKETS
case CURLOPT_UNIX_SOCKET_PATH:
data->set.abstract_unix_socket = FALSE;
@@ -2603,33 +2673,75 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
case CURLOPT_SUPPRESS_CONNECT_HEADERS:
data->set.suppress_connect_headers = (0 != va_arg(param, long))?TRUE:FALSE;
break;
- case CURLOPT_SSH_COMPRESSION:
- data->set.ssh_compression = (0 != va_arg(param, long))?TRUE:FALSE;
- break;
case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS:
arg = va_arg(param, long);
if(arg < 0)
return CURLE_BAD_FUNCTION_ARGUMENT;
data->set.happy_eyeballs_timeout = arg;
break;
+#ifndef CURL_DISABLE_SHUFFLE_DNS
case CURLOPT_DNS_SHUFFLE_ADDRESSES:
data->set.dns_shuffle_addresses = (0 != va_arg(param, long)) ? TRUE:FALSE;
break;
+#endif
case CURLOPT_DISALLOW_USERNAME_IN_URL:
data->set.disallow_username_in_url =
(0 != va_arg(param, long)) ? TRUE : FALSE;
break;
+#ifndef CURL_DISABLE_DOH
case CURLOPT_DOH_URL:
result = Curl_setstropt(&data->set.str[STRING_DOH],
va_arg(param, char *));
data->set.doh = data->set.str[STRING_DOH]?TRUE:FALSE;
break;
+#endif
case CURLOPT_UPKEEP_INTERVAL_MS:
arg = va_arg(param, long);
if(arg < 0)
return CURLE_BAD_FUNCTION_ARGUMENT;
data->set.upkeep_interval_ms = arg;
break;
+ case CURLOPT_MAXAGE_CONN:
+ arg = va_arg(param, long);
+ if(arg < 0)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.maxage_conn = arg;
+ break;
+ case CURLOPT_TRAILERFUNCTION:
+#ifndef CURL_DISABLE_HTTP
+ data->set.trailer_callback = va_arg(param, curl_trailer_callback);
+#endif
+ break;
+ case CURLOPT_TRAILERDATA:
+#ifndef CURL_DISABLE_HTTP
+ data->set.trailer_data = va_arg(param, void *);
+#endif
+ break;
+#ifdef USE_ALTSVC
+ case CURLOPT_ALTSVC:
+ if(!data->asi) {
+ data->asi = Curl_altsvc_init();
+ if(!data->asi)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ argptr = va_arg(param, char *);
+ result = Curl_setstropt(&data->set.str[STRING_ALTSVC], argptr);
+ if(result)
+ return result;
+ (void)Curl_altsvc_load(data->asi, argptr);
+ break;
+ case CURLOPT_ALTSVC_CTRL:
+ if(!data->asi) {
+ data->asi = Curl_altsvc_init();
+ if(!data->asi)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ arg = va_arg(param, long);
+ result = Curl_altsvc_ctrl(data->asi, arg);
+ if(result)
+ return result;
+ break;
+#endif
default:
/* unknown tag and its companion, just ignore: */
result = CURLE_UNKNOWN_OPTION;
@@ -2658,7 +2770,7 @@ CURLcode curl_easy_setopt(struct Curl_easy *data, CURLoption tag, ...)
va_start(arg, tag);
- result = Curl_vsetopt(data, tag, arg);
+ result = vsetopt(data, tag, arg);
va_end(arg);
return result;
diff --git a/Utilities/cmcurl/lib/sigpipe.h b/Utilities/cmcurl/lib/sigpipe.h
index 800f9d3b4..3960a139d 100644
--- a/Utilities/cmcurl/lib/sigpipe.h
+++ b/Utilities/cmcurl/lib/sigpipe.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -23,7 +23,8 @@
***************************************************************************/
#include "curl_setup.h"
-#if defined(HAVE_SIGNAL_H) && defined(HAVE_SIGACTION) && defined(USE_OPENSSL)
+#if defined(HAVE_SIGNAL_H) && defined(HAVE_SIGACTION) && \
+ (defined(USE_OPENSSL) || defined(USE_MBEDTLS))
#include <signal.h>
struct sigpipe_ignore {
diff --git a/Utilities/cmcurl/lib/smb.c b/Utilities/cmcurl/lib/smb.c
index e4f266e19..76c99a230 100644
--- a/Utilities/cmcurl/lib/smb.c
+++ b/Utilities/cmcurl/lib/smb.c
@@ -947,15 +947,10 @@ static int smb_getsock(struct connectdata *conn, curl_socket_t *socks,
static CURLcode smb_do(struct connectdata *conn, bool *done)
{
struct smb_conn *smbc = &conn->proto.smbc;
- struct smb_request *req = conn->data->req.protop;
*done = FALSE;
if(smbc->share) {
- req->path = strchr(smbc->share, '\0');
- if(req->path) {
- req->path++;
- return CURLE_OK;
- }
+ return CURLE_OK;
}
return CURLE_URL_MALFORMAT;
}
@@ -964,6 +959,7 @@ static CURLcode smb_parse_url_path(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
struct Curl_easy *data = conn->data;
+ struct smb_request *req = data->req.protop;
struct smb_conn *smbc = &conn->proto.smbc;
char *path;
char *slash;
@@ -992,6 +988,7 @@ static CURLcode smb_parse_url_path(struct connectdata *conn)
/* Parse the path for the file path converting any forward slashes into
backslashes */
*slash++ = 0;
+ req->path = slash;
for(; *slash; slash++) {
if(*slash == '/')
diff --git a/Utilities/cmcurl/lib/smtp.c b/Utilities/cmcurl/lib/smtp.c
index 587562306..4a3462b84 100644
--- a/Utilities/cmcurl/lib/smtp.c
+++ b/Utilities/cmcurl/lib/smtp.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -28,6 +28,7 @@
* RFC4954 SMTP Authentication
* RFC5321 SMTP protocol
* RFC6749 OAuth 2.0 Authorization Framework
+ * RFC8314 Use of TLS for Email Submission and Access
* Draft SMTP URL Interface <draft-earhart-url-smtp-00.txt>
* Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
*
@@ -207,8 +208,12 @@ static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len,
Section 4. Examples of RFC-4954 but some e-mail servers ignore this and
only send the response code instead as per Section 4.2. */
if(line[3] == ' ' || len == 5) {
+ char tmpline[6];
+
result = TRUE;
- *resp = curlx_sltosi(strtol(line, NULL, 10));
+ memset(tmpline, '\0', sizeof(tmpline));
+ memcpy(tmpline, line, (len == 5 ? 5 : 3));
+ *resp = curlx_sltosi(strtol(tmpline, NULL, 10));
/* Make sure real server never sends internal value */
if(*resp == 1)
@@ -955,7 +960,7 @@ static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
Curl_pgrsSetUploadSize(data, data->state.infilesize);
/* SMTP upload */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
+ Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
/* End of DO phase */
state(conn, SMTP_STOP);
@@ -1080,19 +1085,20 @@ static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
return result;
}
- result = Curl_pp_statemach(&smtpc->pp, FALSE);
+ result = Curl_pp_statemach(&smtpc->pp, FALSE, FALSE);
*done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
return result;
}
-static CURLcode smtp_block_statemach(struct connectdata *conn)
+static CURLcode smtp_block_statemach(struct connectdata *conn,
+ bool disconnecting)
{
CURLcode result = CURLE_OK;
struct smtp_conn *smtpc = &conn->proto.smtpc;
while(smtpc->state != SMTP_STOP && !result)
- result = Curl_pp_statemach(&smtpc->pp, TRUE);
+ result = Curl_pp_statemach(&smtpc->pp, TRUE, disconnecting);
return result;
}
@@ -1213,7 +1219,7 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
returned CURLE_AGAIN, we duplicate the EOB now rather than when the
bytes written doesn't equal len. */
if(smtp->trailing_crlf || !conn->data->state.infilesize) {
- eob = strdup(SMTP_EOB + 2);
+ eob = strdup(&SMTP_EOB[2]);
len = SMTP_EOB_LEN - 2;
}
else {
@@ -1247,13 +1253,8 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
state(conn, SMTP_POSTDATA);
- /* Run the state-machine
-
- TODO: when the multi interface is used, this _really_ should be using
- the smtp_multi_statemach function but we have no general support for
- non-blocking DONE operations!
- */
- result = smtp_block_statemach(conn);
+ /* Run the state-machine */
+ result = smtp_block_statemach(conn, FALSE);
}
/* Clear the transfer mode for the next request */
@@ -1360,7 +1361,7 @@ static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
point! */
if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart)
if(!smtp_perform_quit(conn))
- (void)smtp_block_statemach(conn); /* ignore errors on QUIT */
+ (void)smtp_block_statemach(conn, TRUE); /* ignore errors on QUIT */
/* Disconnect from the server */
Curl_pp_disconnect(&smtpc->pp);
@@ -1383,7 +1384,7 @@ static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
if(smtp->transfer != FTPTRANSFER_BODY)
/* no data to transfer */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(conn->data, -1, -1, FALSE, -1);
return CURLE_OK;
}
diff --git a/Utilities/cmcurl/lib/socks.c b/Utilities/cmcurl/lib/socks.c
index d2209ad89..d8fcc3bbb 100644
--- a/Utilities/cmcurl/lib/socks.c
+++ b/Utilities/cmcurl/lib/socks.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -54,7 +54,7 @@ int Curl_blockread_all(struct connectdata *conn, /* connection data */
ssize_t buffersize, /* max amount to read */
ssize_t *n) /* amount bytes read */
{
- ssize_t nread;
+ ssize_t nread = 0;
ssize_t allread = 0;
int result;
*n = 0;
@@ -155,7 +155,7 @@ CURLcode Curl_SOCKS4(const char *proxy_user,
Curl_addrinfo *hp = NULL;
int rc;
- rc = Curl_resolv(conn, hostname, remote_port, &dns);
+ rc = Curl_resolv(conn, hostname, remote_port, FALSE, &dns);
if(rc == CURLRESOLV_ERROR)
return CURLE_COULDNT_RESOLVE_PROXY;
@@ -290,7 +290,7 @@ CURLcode Curl_SOCKS4(const char *proxy_user,
/* wrong version ? */
if(socksreq[0] != 0) {
failf(data,
- "SOCKS4 reply has wrong version, version should be 4.");
+ "SOCKS4 reply has wrong version, version should be 0.");
return CURLE_COULDNT_CONNECT;
}
@@ -527,12 +527,24 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
len = 0;
socksreq[len++] = 1; /* username/pw subnegotiation version */
socksreq[len++] = (unsigned char) proxy_user_len;
- if(proxy_user && proxy_user_len)
+ if(proxy_user && proxy_user_len) {
+ /* the length must fit in a single byte */
+ if(proxy_user_len >= 255) {
+ failf(data, "Excessive user name length for proxy auth");
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
memcpy(socksreq + len, proxy_user, proxy_user_len);
+ }
len += proxy_user_len;
socksreq[len++] = (unsigned char) proxy_password_len;
- if(proxy_password && proxy_password_len)
+ if(proxy_password && proxy_password_len) {
+ /* the length must fit in a single byte */
+ if(proxy_password_len > 255) {
+ failf(data, "Excessive password length for proxy auth");
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
memcpy(socksreq + len, proxy_password, proxy_password_len);
+ }
len += proxy_password_len;
code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
@@ -597,7 +609,7 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
else {
struct Curl_dns_entry *dns;
Curl_addrinfo *hp = NULL;
- int rc = Curl_resolv(conn, hostname, remote_port, &dns);
+ int rc = Curl_resolv(conn, hostname, remote_port, FALSE, &dns);
if(rc == CURLRESOLV_ERROR)
return CURLE_COULDNT_RESOLVE_HOST;
diff --git a/Utilities/cmcurl/lib/socks_gssapi.c b/Utilities/cmcurl/lib/socks_gssapi.c
index 96948ac4b..65294bbeb 100644
--- a/Utilities/cmcurl/lib/socks_gssapi.c
+++ b/Utilities/cmcurl/lib/socks_gssapi.c
@@ -6,7 +6,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2009, Markus Moeller, <markus_moeller@compuserve.com>
- * Copyright (C) 2012 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -151,8 +151,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
return CURLE_OUT_OF_MEMORY;
service.length = serviceptr_length +
strlen(conn->socks_proxy.host.name) + 1;
- snprintf(service.value, service.length + 1, "%s@%s",
- serviceptr, conn->socks_proxy.host.name);
+ msnprintf(service.value, service.length + 1, "%s@%s",
+ serviceptr, conn->socks_proxy.host.name);
gss_major_status = gss_import_name(&gss_minor_status, &service,
GSS_C_NT_HOSTBASED_SERVICE, &server);
diff --git a/Utilities/cmcurl/lib/socks_sspi.c b/Utilities/cmcurl/lib/socks_sspi.c
index 34699d374..57027ef68 100644
--- a/Utilities/cmcurl/lib/socks_sspi.c
+++ b/Utilities/cmcurl/lib/socks_sspi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 2009, 2011, Markus Moeller, <markus_moeller@compuserve.com>
*
* This software is licensed as described in the file COPYING, which
@@ -51,8 +51,9 @@ static int check_sspi_err(struct connectdata *conn,
status != SEC_I_COMPLETE_AND_CONTINUE &&
status != SEC_I_COMPLETE_NEEDED &&
status != SEC_I_CONTINUE_NEEDED) {
+ char buffer[STRERROR_LEN];
failf(conn->data, "SSPI error: %s failed: %s", function,
- Curl_sspi_strerror(conn, status));
+ Curl_sspi_strerror(status, buffer, sizeof(buffer)));
return 1;
}
return 0;
@@ -107,9 +108,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
strlen(conn->socks_proxy.host.name) + 2);
if(!service_name)
return CURLE_OUT_OF_MEMORY;
- snprintf(service_name, service_length +
- strlen(conn->socks_proxy.host.name) + 2, "%s/%s",
- service, conn->socks_proxy.host.name);
+ msnprintf(service_name, service_length +
+ strlen(conn->socks_proxy.host.name) + 2, "%s/%s",
+ service, conn->socks_proxy.host.name);
}
input_desc.cBuffers = 1;
diff --git a/Utilities/cmcurl/lib/splay.c b/Utilities/cmcurl/lib/splay.c
index baf07e00d..0f5fcd1e8 100644
--- a/Utilities/cmcurl/lib/splay.c
+++ b/Utilities/cmcurl/lib/splay.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1997 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1997 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -198,7 +198,7 @@ struct Curl_tree *Curl_splaygetbest(struct curltime i,
/* Deletes the very node we point out from the tree if it's there. Stores a
* pointer to the new resulting tree in 'newroot'.
*
- * Returns zero on success and non-zero on errors! TODO: document error codes.
+ * Returns zero on success and non-zero on errors!
* When returning error, it does not touch the 'newroot' pointer.
*
* NOTE: when the last node of the tree is removed, there's no tree left so
diff --git a/Utilities/cmcurl/lib/ssh-libssh.c b/Utilities/cmcurl/lib/ssh-libssh.c
index 7d590891c..6cfd6bda8 100644
--- a/Utilities/cmcurl/lib/ssh-libssh.c
+++ b/Utilities/cmcurl/lib/ssh-libssh.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2017 - 2018 Red Hat, Inc.
+ * Copyright (C) 2017 - 2019 Red Hat, Inc.
*
* Authors: Nikos Mavrogiannopoulos, Tomas Mraz, Stanislav Zidek,
* Robert Kolcun, Andreas Schneider
@@ -95,6 +95,13 @@
#include "memdebug.h"
#include "curl_path.h"
+/* A recent macro provided by libssh. Or make our own. */
+#ifndef SSH_STRING_FREE_CHAR
+/* !checksrc! disable ASSIGNWITHINCONDITION 1 */
+#define SSH_STRING_FREE_CHAR(x) \
+ do { if((x) != NULL) { ssh_string_free_char(x); x = NULL; } } while(0)
+#endif
+
/* Local functions: */
static CURLcode myssh_connect(struct connectdata *conn, bool *done);
static CURLcode myssh_multi_statemach(struct connectdata *conn,
@@ -549,6 +556,7 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block)
struct Curl_easy *data = conn->data;
struct SSHPROTO *protop = data->req.protop;
struct ssh_conn *sshc = &conn->proto.sshc;
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
int rc = SSH_NO_ERROR, err;
char *new_readdir_line;
int seekerr = CURL_SEEKFUNC_OK;
@@ -792,7 +800,7 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block)
Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSH is connected */
- conn->sockfd = ssh_get_fd(sshc->ssh_session);
+ conn->sockfd = sock;
conn->writesockfd = CURL_SOCKET_BAD;
if(conn->handler->protocol == CURLPROTO_SFTP) {
@@ -1192,7 +1200,7 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block)
Curl_pgrsSetUploadSize(data, data->state.infilesize);
}
/* upload data */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
+ Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
/* not set by Curl_setup_transfer to preserve keepon bits */
conn->sockfd = conn->writesockfd;
@@ -1342,8 +1350,8 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block)
break;
}
- snprintf(sshc->readdir_linkPath, PATH_MAX, "%s%s", protop->path,
- sshc->readdir_filename);
+ msnprintf(sshc->readdir_linkPath, PATH_MAX, "%s%s", protop->path,
+ sshc->readdir_filename);
state(conn, SSH_SFTP_READDIR_LINK);
break;
@@ -1406,12 +1414,12 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block)
}
sshc->readdir_line = new_readdir_line;
- sshc->readdir_currLen += snprintf(sshc->readdir_line +
- sshc->readdir_currLen,
- sshc->readdir_totalLen -
- sshc->readdir_currLen,
- " -> %s",
- sshc->readdir_filename);
+ sshc->readdir_currLen += msnprintf(sshc->readdir_line +
+ sshc->readdir_currLen,
+ sshc->readdir_totalLen -
+ sshc->readdir_currLen,
+ " -> %s",
+ sshc->readdir_filename);
sftp_attributes_free(sshc->readdir_link_attrs);
sshc->readdir_link_attrs = NULL;
@@ -1421,10 +1429,10 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block)
state(conn, SSH_SFTP_READDIR_BOTTOM);
/* FALLTHROUGH */
case SSH_SFTP_READDIR_BOTTOM:
- sshc->readdir_currLen += snprintf(sshc->readdir_line +
- sshc->readdir_currLen,
- sshc->readdir_totalLen -
- sshc->readdir_currLen, "\n");
+ sshc->readdir_currLen += msnprintf(sshc->readdir_line +
+ sshc->readdir_currLen,
+ sshc->readdir_totalLen -
+ sshc->readdir_currLen, "\n");
result = Curl_client_write(conn, CLIENTWRITE_BODY,
sshc->readdir_line,
sshc->readdir_currLen);
@@ -1454,7 +1462,7 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block)
sshc->sftp_dir = NULL;
/* no data to transfer */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
state(conn, SSH_STOP);
break;
@@ -1595,13 +1603,12 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block)
/* Setup the actual download */
if(data->req.size == 0) {
/* no data to transfer */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
infof(data, "File already completely downloaded\n");
state(conn, SSH_STOP);
break;
}
- Curl_setup_transfer(conn, FIRSTSOCKET, data->req.size,
- FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1);
/* not set by Curl_setup_transfer to preserve keepon bits */
conn->writesockfd = conn->sockfd;
@@ -1661,7 +1668,7 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block)
sshc->sftp_session = NULL;
}
- Curl_safefree(sshc->homedir);
+ SSH_STRING_FREE_CHAR(sshc->homedir);
conn->data->state.most_recent_ftp_entrypath = NULL;
state(conn, SSH_SESSION_DISCONNECT);
@@ -1723,8 +1730,7 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block)
}
/* upload data */
- Curl_setup_transfer(conn, -1, data->req.size, FALSE, NULL,
- FIRSTSOCKET, NULL);
+ Curl_setup_transfer(data, -1, data->req.size, FALSE, FIRSTSOCKET);
/* not set by Curl_setup_transfer to preserve keepon bits */
conn->sockfd = conn->writesockfd;
@@ -1767,8 +1773,7 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block)
/* download data */
bytecount = ssh_scp_request_get_size(sshc->scp_session);
data->req.maxdownload = (curl_off_t) bytecount;
- Curl_setup_transfer(conn, FIRSTSOCKET, bytecount, FALSE, NULL, -1,
- NULL);
+ Curl_setup_transfer(data, FIRSTSOCKET, bytecount, FALSE, -1);
/* not set by Curl_setup_transfer to preserve keepon bits */
conn->writesockfd = conn->sockfd;
@@ -1829,7 +1834,7 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block)
ssh_disconnect(sshc->ssh_session);
- Curl_safefree(sshc->homedir);
+ SSH_STRING_FREE_CHAR(sshc->homedir);
conn->data->state.most_recent_ftp_entrypath = NULL;
state(conn, SSH_SESSION_FREE);
@@ -1866,14 +1871,11 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block)
Curl_safefree(sshc->rsa_pub);
Curl_safefree(sshc->rsa);
-
Curl_safefree(sshc->quote_path1);
Curl_safefree(sshc->quote_path2);
-
- Curl_safefree(sshc->homedir);
-
Curl_safefree(sshc->readdir_line);
Curl_safefree(sshc->readdir_linkPath);
+ SSH_STRING_FREE_CHAR(sshc->homedir);
/* the code we are about to return */
result = sshc->actualcode;
@@ -1944,14 +1946,13 @@ static int myssh_getsock(struct connectdata *conn,
static void myssh_block2waitfor(struct connectdata *conn, bool block)
{
struct ssh_conn *sshc = &conn->proto.sshc;
- int dir;
/* If it didn't block, or nothing was returned by ssh_get_poll_flags
* have the original set */
conn->waitfor = sshc->orig_waitfor;
if(block) {
- dir = ssh_get_poll_flags(sshc->ssh_session);
+ int dir = ssh_get_poll_flags(sshc->ssh_session);
if(dir & SSH_READ_PENDING) {
/* translate the libssh define bits into our own bit defines */
conn->waitfor = KEEP_RECV;
@@ -2010,9 +2011,7 @@ static CURLcode myssh_block_statemach(struct connectdata *conn,
}
if(!result && block) {
- curl_socket_t sock = conn->sock[FIRSTSOCKET];
- curl_socket_t fd_read = CURL_SOCKET_BAD;
- fd_read = sock;
+ curl_socket_t fd_read = conn->sock[FIRSTSOCKET];
/* wait for the socket to become ready */
(void) Curl_socket_check(fd_read, CURL_SOCKET_BAD,
CURL_SOCKET_BAD, left > 1000 ? 1000 : left);
@@ -2048,8 +2047,8 @@ static CURLcode myssh_connect(struct connectdata *conn, bool *done)
{
struct ssh_conn *ssh;
CURLcode result;
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
struct Curl_easy *data = conn->data;
- int rc;
/* initialize per-handle data if not already */
if(!data->req.protop)
@@ -2076,6 +2075,8 @@ static CURLcode myssh_connect(struct connectdata *conn, bool *done)
return CURLE_FAILED_INIT;
}
+ ssh_options_set(ssh->ssh_session, SSH_OPTIONS_FD, &sock);
+
if(conn->user) {
infof(data, "User: %s\n", conn->user);
ssh_options_set(ssh->ssh_session, SSH_OPTIONS_USER, conn->user);
@@ -2101,8 +2102,8 @@ static CURLcode myssh_connect(struct connectdata *conn, bool *done)
ssh->pubkey = NULL;
if(data->set.str[STRING_SSH_PUBLIC_KEY]) {
- rc = ssh_pki_import_pubkey_file(data->set.str[STRING_SSH_PUBLIC_KEY],
- &ssh->pubkey);
+ int rc = ssh_pki_import_pubkey_file(data->set.str[STRING_SSH_PUBLIC_KEY],
+ &ssh->pubkey);
if(rc != SSH_OK) {
failf(data, "Could not load public key file");
/* ignore */
@@ -2222,12 +2223,7 @@ static CURLcode myssh_done(struct connectdata *conn, CURLcode status)
struct SSHPROTO *protop = conn->data->req.protop;
if(!status) {
- /* run the state-machine
-
- TODO: when the multi interface is used, this _really_ should be using
- the ssh_multi_statemach function but we have no general support for
- non-blocking DONE operations!
- */
+ /* run the state-machine */
result = myssh_block_statemach(conn, FALSE);
}
else
@@ -2390,13 +2386,9 @@ static CURLcode sftp_done(struct connectdata *conn, CURLcode status,
/* Post quote commands are executed after the SFTP_CLOSE state to avoid
errors that could happen due to open file handles during POSTQUOTE
operation */
- if(!status && !premature && conn->data->set.postquote &&
- !conn->bits.retry) {
+ if(!premature && conn->data->set.postquote && !conn->bits.retry)
sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT;
- state(conn, SSH_SFTP_CLOSE);
- }
- else
- state(conn, SSH_SFTP_CLOSE);
+ state(conn, SSH_SFTP_CLOSE);
}
return myssh_done(conn, status);
}
diff --git a/Utilities/cmcurl/lib/ssh.c b/Utilities/cmcurl/lib/ssh.c
index da896196f..a265c3c9a 100644
--- a/Utilities/cmcurl/lib/ssh.c
+++ b/Utilities/cmcurl/lib/ssh.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -81,11 +81,11 @@
#include "multiif.h"
#include "select.h"
#include "warnless.h"
+#include "curl_path.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
-#include "curl_path.h"
#include "memdebug.h"
#if LIBSSH2_VERSION_NUM >= 0x010206
@@ -290,10 +290,6 @@ static CURLcode libssh2_session_error_to_CURLE(int err)
return CURLE_AGAIN;
}
- /* TODO: map some more of the libssh2 errors to the more appropriate CURLcode
- error code, and possibly add a few new SSH-related one. We must however
- not return or even depend on libssh2 errors in the public libcurl API */
-
return CURLE_SSH;
}
@@ -591,15 +587,15 @@ static CURLcode ssh_check_fingerprint(struct connectdata *conn)
struct Curl_easy *data = conn->data;
const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5];
char md5buffer[33];
- int i;
const char *fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
LIBSSH2_HOSTKEY_HASH_MD5);
if(fingerprint) {
/* The fingerprint points to static storage (!), don't free() it. */
+ int i;
for(i = 0; i < 16; i++)
- snprintf(&md5buffer[i*2], 3, "%02x", (unsigned char) fingerprint[i]);
+ msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char) fingerprint[i]);
infof(data, "SSH MD5 fingerprint: %s\n", md5buffer);
}
@@ -667,7 +663,10 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
break;
}
if(rc) {
- failf(data, "Failure establishing ssh session");
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0);
+ failf(data, "Failure establishing ssh session: %d, %s", rc, err_msg);
+
state(conn, SSH_SESSION_FREE);
sshc->actualcode = CURLE_FAILED_INIT;
break;
@@ -734,18 +733,17 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) &&
(strstr(sshc->authlist, "publickey") != NULL)) {
- char *home = NULL;
bool out_of_memory = FALSE;
sshc->rsa_pub = sshc->rsa = NULL;
- /* To ponder about: should really the lib be messing about with the
- HOME environment variable etc? */
- home = curl_getenv("HOME");
-
if(data->set.str[STRING_SSH_PRIVATE_KEY])
sshc->rsa = strdup(data->set.str[STRING_SSH_PRIVATE_KEY]);
else {
+ /* To ponder about: should really the lib be messing about with the
+ HOME environment variable etc? */
+ char *home = curl_getenv("HOME");
+
/* If no private key file is specified, try some common paths. */
if(home) {
/* Try ~/.ssh first. */
@@ -761,6 +759,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
Curl_safefree(sshc->rsa);
}
}
+ free(home);
}
if(!out_of_memory && !sshc->rsa) {
/* Nothing found; try the current dir. */
@@ -792,7 +791,6 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
}
if(out_of_memory || sshc->rsa == NULL) {
- free(home);
Curl_safefree(sshc->rsa);
Curl_safefree(sshc->rsa_pub);
state(conn, SSH_SESSION_FREE);
@@ -804,8 +802,6 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
if(!sshc->passphrase)
sshc->passphrase = "";
- free(home);
-
if(sshc->rsa_pub)
infof(data, "Using SSH public key file '%s'\n", sshc->rsa_pub);
infof(data, "Using SSH private key file '%s'\n", sshc->rsa);
@@ -1805,7 +1801,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
Curl_pgrsSetUploadSize(data, data->state.infilesize);
}
/* upload data */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
+ Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
/* not set by Curl_setup_transfer to preserve keepon bits */
conn->sockfd = conn->writesockfd;
@@ -1999,8 +1995,8 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
break;
}
- snprintf(sshc->readdir_linkPath, PATH_MAX, "%s%s", sftp_scp->path,
- sshc->readdir_filename);
+ msnprintf(sshc->readdir_linkPath, PATH_MAX, "%s%s", sftp_scp->path,
+ sshc->readdir_filename);
state(conn, SSH_SFTP_READDIR_LINK);
break;
}
@@ -2055,21 +2051,21 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
}
sshc->readdir_line = new_readdir_line;
- sshc->readdir_currLen += snprintf(sshc->readdir_line +
- sshc->readdir_currLen,
- sshc->readdir_totalLen -
- sshc->readdir_currLen,
- " -> %s",
- sshc->readdir_filename);
+ sshc->readdir_currLen += msnprintf(sshc->readdir_line +
+ sshc->readdir_currLen,
+ sshc->readdir_totalLen -
+ sshc->readdir_currLen,
+ " -> %s",
+ sshc->readdir_filename);
state(conn, SSH_SFTP_READDIR_BOTTOM);
break;
case SSH_SFTP_READDIR_BOTTOM:
- sshc->readdir_currLen += snprintf(sshc->readdir_line +
- sshc->readdir_currLen,
- sshc->readdir_totalLen -
- sshc->readdir_currLen, "\n");
+ sshc->readdir_currLen += msnprintf(sshc->readdir_line +
+ sshc->readdir_currLen,
+ sshc->readdir_totalLen -
+ sshc->readdir_currLen, "\n");
result = Curl_client_write(conn, CLIENTWRITE_BODY,
sshc->readdir_line,
sshc->readdir_currLen);
@@ -2102,7 +2098,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
Curl_safefree(sshc->readdir_longentry);
/* no data to transfer */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
state(conn, SSH_STOP);
break;
@@ -2242,13 +2238,12 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
/* Setup the actual download */
if(data->req.size == 0) {
/* no data to transfer */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
infof(data, "File already completely downloaded\n");
state(conn, SSH_STOP);
break;
}
- Curl_setup_transfer(conn, FIRSTSOCKET, data->req.size,
- FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1);
/* not set by Curl_setup_transfer to preserve keepon bits */
conn->writesockfd = conn->sockfd;
@@ -2392,8 +2387,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
}
/* upload data */
- Curl_setup_transfer(conn, -1, data->req.size, FALSE, NULL,
- FIRSTSOCKET, NULL);
+ Curl_setup_transfer(data, -1, data->req.size, FALSE, FIRSTSOCKET);
/* not set by Curl_setup_transfer to preserve keepon bits */
conn->sockfd = conn->writesockfd;
@@ -2464,7 +2458,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
/* download data */
bytecount = (curl_off_t)sb.st_size;
data->req.maxdownload = (curl_off_t)sb.st_size;
- Curl_setup_transfer(conn, FIRSTSOCKET, bytecount, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(data, FIRSTSOCKET, bytecount, FALSE, -1);
/* not set by Curl_setup_transfer to preserve keepon bits */
conn->writesockfd = conn->sockfd;
@@ -2788,9 +2782,12 @@ static CURLcode ssh_multi_statemach(struct connectdata *conn, bool *done)
CURLcode result = CURLE_OK;
bool block; /* we store the status and use that to provide a ssh_getsock()
implementation */
-
- result = ssh_statemach_act(conn, &block);
- *done = (sshc->state == SSH_STOP) ? TRUE : FALSE;
+ do {
+ result = ssh_statemach_act(conn, &block);
+ *done = (sshc->state == SSH_STOP) ? TRUE : FALSE;
+ /* if there's no error, it isn't done and it didn't EWOULDBLOCK, then
+ try again */
+ } while(!result && !*done && !block);
ssh_block2waitfor(conn, block);
return result;
@@ -3061,12 +3058,7 @@ static CURLcode ssh_done(struct connectdata *conn, CURLcode status)
struct SSHPROTO *sftp_scp = conn->data->req.protop;
if(!status) {
- /* run the state-machine
-
- TODO: when the multi interface is used, this _really_ should be using
- the ssh_multi_statemach function but we have no general support for
- non-blocking DONE operations!
- */
+ /* run the state-machine */
result = ssh_block_statemach(conn, FALSE);
}
else
@@ -3219,13 +3211,9 @@ static CURLcode sftp_done(struct connectdata *conn, CURLcode status,
/* Post quote commands are executed after the SFTP_CLOSE state to avoid
errors that could happen due to open file handles during POSTQUOTE
operation */
- if(!status && !premature && conn->data->set.postquote &&
- !conn->bits.retry) {
+ if(!premature && conn->data->set.postquote && !conn->bits.retry)
sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT;
- state(conn, SSH_SFTP_CLOSE);
- }
- else
- state(conn, SSH_SFTP_CLOSE);
+ state(conn, SSH_SFTP_CLOSE);
}
return ssh_done(conn, status);
}
diff --git a/Utilities/cmcurl/lib/strerror.c b/Utilities/cmcurl/lib/strerror.c
index 47ef44a66..e273f3765 100644
--- a/Utilities/cmcurl/lib/strerror.c
+++ b/Utilities/cmcurl/lib/strerror.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2004 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2004 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -646,20 +646,18 @@ get_winsock_error (int err, char *buf, size_t len)
* We don't do range checking (on systems other than Windows) since there is
* no good reliable and portable way to do it.
*/
-const char *Curl_strerror(struct connectdata *conn, int err)
+const char *Curl_strerror(int err, char *buf, size_t buflen)
{
#ifdef PRESERVE_WINDOWS_ERROR_CODE
DWORD old_win_err = GetLastError();
#endif
int old_errno = errno;
- char *buf, *p;
+ char *p;
size_t max;
- DEBUGASSERT(conn);
DEBUGASSERT(err >= 0);
- buf = conn->syserr_buf;
- max = sizeof(conn->syserr_buf)-1;
+ max = buflen - 1;
*buf = '\0';
#ifdef USE_WINSOCK
@@ -681,7 +679,7 @@ const char *Curl_strerror(struct connectdata *conn, int err)
if(!get_winsock_error(err, buf, max) &&
!FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
LANG_NEUTRAL, buf, (DWORD)max, NULL))
- snprintf(buf, max, "Unknown error %d (%#x)", err, err);
+ msnprintf(buf, max, "Unknown error %d (%#x)", err, err);
}
#endif
@@ -695,7 +693,7 @@ const char *Curl_strerror(struct connectdata *conn, int err)
*/
if(0 != strerror_r(err, buf, max)) {
if('\0' == buf[0])
- snprintf(buf, max, "Unknown error %d", err);
+ msnprintf(buf, max, "Unknown error %d", err);
}
#elif defined(HAVE_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R)
/*
@@ -709,7 +707,7 @@ const char *Curl_strerror(struct connectdata *conn, int err)
if(msg)
strncpy(buf, msg, max);
else
- snprintf(buf, max, "Unknown error %d", err);
+ msnprintf(buf, max, "Unknown error %d", err);
}
#elif defined(HAVE_STRERROR_R) && defined(HAVE_VXWORKS_STRERROR_R)
/*
@@ -721,7 +719,7 @@ const char *Curl_strerror(struct connectdata *conn, int err)
if(OK == strerror_r(err, buffer))
strncpy(buf, buffer, max);
else
- snprintf(buf, max, "Unknown error %d", err);
+ msnprintf(buf, max, "Unknown error %d", err);
}
#else
{
@@ -729,7 +727,7 @@ const char *Curl_strerror(struct connectdata *conn, int err)
if(msg)
strncpy(buf, msg, max);
else
- snprintf(buf, max, "Unknown error %d", err);
+ msnprintf(buf, max, "Unknown error %d", err);
}
#endif
@@ -757,7 +755,7 @@ const char *Curl_strerror(struct connectdata *conn, int err)
}
#ifdef USE_WINDOWS_SSPI
-const char *Curl_sspi_strerror (struct connectdata *conn, int err)
+const char *Curl_sspi_strerror(int err, char *buf, size_t buflen)
{
#ifdef PRESERVE_WINDOWS_ERROR_CODE
DWORD old_win_err = GetLastError();
@@ -768,15 +766,13 @@ const char *Curl_sspi_strerror (struct connectdata *conn, int err)
size_t outmax;
#ifndef CURL_DISABLE_VERBOSE_STRINGS
char txtbuf[80];
- char msgbuf[sizeof(conn->syserr_buf)];
+ char msgbuf[256];
char *p, *str, *msg = NULL;
bool msg_formatted = FALSE;
#endif
- DEBUGASSERT(conn);
-
- outbuf = conn->syserr_buf;
- outmax = sizeof(conn->syserr_buf)-1;
+ outbuf = buf;
+ outmax = buflen - 1;
*outbuf = '\0';
#ifndef CURL_DISABLE_VERBOSE_STRINGS
@@ -1032,14 +1028,14 @@ const char *Curl_sspi_strerror (struct connectdata *conn, int err)
if(err == SEC_E_OK)
strncpy(outbuf, txt, outmax);
else if(err == SEC_E_ILLEGAL_MESSAGE)
- snprintf(outbuf, outmax,
- "SEC_E_ILLEGAL_MESSAGE (0x%08X) - This error usually occurs "
- "when a fatal SSL/TLS alert is received (e.g. handshake failed). "
- "More detail may be available in the Windows System event log.",
- err);
+ msnprintf(outbuf, outmax,
+ "SEC_E_ILLEGAL_MESSAGE (0x%08X) - This error usually occurs "
+ "when a fatal SSL/TLS alert is received (e.g. handshake failed)."
+ " More detail may be available in the Windows System event log.",
+ err);
else {
str = txtbuf;
- snprintf(txtbuf, sizeof(txtbuf), "%s (0x%08X)", txt, err);
+ msnprintf(txtbuf, sizeof(txtbuf), "%s (0x%08X)", txt, err);
txtbuf[sizeof(txtbuf)-1] = '\0';
#ifdef _WIN32_WCE
@@ -1075,7 +1071,7 @@ const char *Curl_sspi_strerror (struct connectdata *conn, int err)
msg = msgbuf;
}
if(msg)
- snprintf(outbuf, outmax, "%s - %s", str, msg);
+ msnprintf(outbuf, outmax, "%s - %s", str, msg);
else
strncpy(outbuf, str, outmax);
}
diff --git a/Utilities/cmcurl/lib/strerror.h b/Utilities/cmcurl/lib/strerror.h
index 627273eb2..683b5b4a3 100644
--- a/Utilities/cmcurl/lib/strerror.h
+++ b/Utilities/cmcurl/lib/strerror.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -24,14 +24,11 @@
#include "urldata.h"
-const char *Curl_strerror (struct connectdata *conn, int err);
-
-#ifdef USE_LIBIDN2
-const char *Curl_idn_strerror (struct connectdata *conn, int err);
-#endif
+#define STRERROR_LEN 128 /* a suitable length */
+const char *Curl_strerror(int err, char *buf, size_t buflen);
#ifdef USE_WINDOWS_SSPI
-const char *Curl_sspi_strerror (struct connectdata *conn, int err);
+const char *Curl_sspi_strerror(int err, char *buf, size_t buflen);
#endif
#endif /* HEADER_CURL_STRERROR_H */
diff --git a/Utilities/cmcurl/lib/system_win32.c b/Utilities/cmcurl/lib/system_win32.c
index 6b8004e5b..f7f817dd4 100644
--- a/Utilities/cmcurl/lib/system_win32.c
+++ b/Utilities/cmcurl/lib/system_win32.c
@@ -26,12 +26,94 @@
#include <curl/curl.h>
#include "system_win32.h"
+#include "curl_sspi.h"
#include "warnless.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
+LARGE_INTEGER Curl_freq;
+bool Curl_isVistaOrGreater;
+
+/* Curl_win32_init() performs win32 global initialization */
+CURLcode Curl_win32_init(long flags)
+{
+ /* CURL_GLOBAL_WIN32 controls the *optional* part of the initialization which
+ is just for Winsock at the moment. Any required win32 initialization
+ should take place after this block. */
+ if(flags & CURL_GLOBAL_WIN32) {
+#ifdef USE_WINSOCK
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ int res;
+
+#if defined(ENABLE_IPV6) && (USE_WINSOCK < 2)
+#error IPV6_requires_winsock2
+#endif
+
+ wVersionRequested = MAKEWORD(USE_WINSOCK, USE_WINSOCK);
+
+ res = WSAStartup(wVersionRequested, &wsaData);
+
+ if(res != 0)
+ /* Tell the user that we couldn't find a usable */
+ /* winsock.dll. */
+ return CURLE_FAILED_INIT;
+
+ /* Confirm that the Windows Sockets DLL supports what we need.*/
+ /* Note that if the DLL supports versions greater */
+ /* than wVersionRequested, it will still return */
+ /* wVersionRequested in wVersion. wHighVersion contains the */
+ /* highest supported version. */
+
+ if(LOBYTE(wsaData.wVersion) != LOBYTE(wVersionRequested) ||
+ HIBYTE(wsaData.wVersion) != HIBYTE(wVersionRequested) ) {
+ /* Tell the user that we couldn't find a usable */
+
+ /* winsock.dll. */
+ WSACleanup();
+ return CURLE_FAILED_INIT;
+ }
+ /* The Windows Sockets DLL is acceptable. Proceed. */
+ #elif defined(USE_LWIPSOCK)
+ lwip_init();
+ #endif
+ } /* CURL_GLOBAL_WIN32 */
+
+#ifdef USE_WINDOWS_SSPI
+ {
+ CURLcode result = Curl_sspi_global_init();
+ if(result)
+ return result;
+ }
+#endif
+
+ if(Curl_verify_windows_version(6, 0, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL)) {
+ Curl_isVistaOrGreater = TRUE;
+ QueryPerformanceFrequency(&Curl_freq);
+ }
+ else
+ Curl_isVistaOrGreater = FALSE;
+
+ return CURLE_OK;
+}
+
+/* Curl_win32_cleanup() is the opposite of Curl_win32_init() */
+void Curl_win32_cleanup(long init_flags)
+{
+#ifdef USE_WINDOWS_SSPI
+ Curl_sspi_global_cleanup();
+#endif
+
+ if(init_flags & CURL_GLOBAL_WIN32) {
+#ifdef USE_WINSOCK
+ WSACleanup();
+#endif
+ }
+}
+
#if defined(USE_WINDOWS_SSPI) || (!defined(CURL_DISABLE_TELNET) && \
defined(USE_WINSOCK))
diff --git a/Utilities/cmcurl/lib/system_win32.h b/Utilities/cmcurl/lib/system_win32.h
index 1e772856b..926328a9a 100644
--- a/Utilities/cmcurl/lib/system_win32.h
+++ b/Utilities/cmcurl/lib/system_win32.h
@@ -26,6 +26,12 @@
#if defined(WIN32)
+extern LARGE_INTEGER Curl_freq;
+extern bool Curl_isVistaOrGreater;
+
+CURLcode Curl_win32_init(long flags);
+void Curl_win32_cleanup(long init_flags);
+
/* Version condition */
typedef enum {
VERSION_LESS_THAN,
diff --git a/Utilities/cmcurl/lib/telnet.c b/Utilities/cmcurl/lib/telnet.c
index 05fe744db..955255c36 100644
--- a/Utilities/cmcurl/lib/telnet.c
+++ b/Utilities/cmcurl/lib/telnet.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -829,7 +829,7 @@ static CURLcode check_telnet_options(struct connectdata *conn)
/* Add the user name as an environment variable if it
was given on the command line */
if(conn->bits.user_passwd) {
- snprintf(option_arg, sizeof(option_arg), "USER,%s", conn->user);
+ msnprintf(option_arg, sizeof(option_arg), "USER,%s", conn->user);
beg = curl_slist_append(tn->telnet_vars, option_arg);
if(!beg) {
curl_slist_free_all(tn->telnet_vars);
@@ -935,9 +935,9 @@ static void suboption(struct connectdata *conn)
switch(CURL_SB_GET(tn)) {
case CURL_TELOPT_TTYPE:
len = strlen(tn->subopt_ttype) + 4 + 2;
- snprintf((char *)temp, sizeof(temp),
- "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_TTYPE,
- CURL_TELQUAL_IS, tn->subopt_ttype, CURL_IAC, CURL_SE);
+ msnprintf((char *)temp, sizeof(temp),
+ "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_TTYPE,
+ CURL_TELQUAL_IS, tn->subopt_ttype, CURL_IAC, CURL_SE);
bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
if(bytes_written < 0) {
err = SOCKERRNO;
@@ -947,9 +947,9 @@ static void suboption(struct connectdata *conn)
break;
case CURL_TELOPT_XDISPLOC:
len = strlen(tn->subopt_xdisploc) + 4 + 2;
- snprintf((char *)temp, sizeof(temp),
- "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_XDISPLOC,
- CURL_TELQUAL_IS, tn->subopt_xdisploc, CURL_IAC, CURL_SE);
+ msnprintf((char *)temp, sizeof(temp),
+ "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_XDISPLOC,
+ CURL_TELQUAL_IS, tn->subopt_xdisploc, CURL_IAC, CURL_SE);
bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
if(bytes_written < 0) {
err = SOCKERRNO;
@@ -958,9 +958,9 @@ static void suboption(struct connectdata *conn)
printsub(data, '>', &temp[2], len-2);
break;
case CURL_TELOPT_NEW_ENVIRON:
- snprintf((char *)temp, sizeof(temp),
- "%c%c%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_NEW_ENVIRON,
- CURL_TELQUAL_IS);
+ msnprintf((char *)temp, sizeof(temp),
+ "%c%c%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_NEW_ENVIRON,
+ CURL_TELQUAL_IS);
len = 4;
for(v = tn->telnet_vars; v; v = v->next) {
@@ -968,15 +968,15 @@ static void suboption(struct connectdata *conn)
/* Add the variable only if it fits */
if(len + tmplen < (int)sizeof(temp)-6) {
if(sscanf(v->data, "%127[^,],%127s", varname, varval)) {
- snprintf((char *)&temp[len], sizeof(temp) - len,
- "%c%s%c%s", CURL_NEW_ENV_VAR, varname,
- CURL_NEW_ENV_VALUE, varval);
+ msnprintf((char *)&temp[len], sizeof(temp) - len,
+ "%c%s%c%s", CURL_NEW_ENV_VAR, varname,
+ CURL_NEW_ENV_VALUE, varval);
len += tmplen;
}
}
}
- snprintf((char *)&temp[len], sizeof(temp) - len,
- "%c%c", CURL_IAC, CURL_SE);
+ msnprintf((char *)&temp[len], sizeof(temp) - len,
+ "%c%c", CURL_IAC, CURL_SE);
len += 2;
bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
if(bytes_written < 0) {
@@ -1692,7 +1692,7 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done)
}
#endif
/* mark this as "no further transfer wanted" */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
return result;
}
diff --git a/Utilities/cmcurl/lib/tftp.c b/Utilities/cmcurl/lib/tftp.c
index 5b74e8e08..289cda282 100644
--- a/Utilities/cmcurl/lib/tftp.c
+++ b/Utilities/cmcurl/lib/tftp.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -496,9 +496,9 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event)
return CURLE_TFTP_ILLEGAL; /* too long file name field */
}
- snprintf((char *)state->spacket.data + 2,
- state->blksize,
- "%s%c%s%c", filename, '\0', mode, '\0');
+ msnprintf((char *)state->spacket.data + 2,
+ state->blksize,
+ "%s%c%s%c", filename, '\0', mode, '\0');
sbytes = 4 + strlen(filename) + strlen(mode);
/* optional addition of TFTP options */
@@ -506,8 +506,8 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event)
char buf[64];
/* add tsize option */
if(data->set.upload && (data->state.infilesize != -1))
- snprintf(buf, sizeof(buf), "%" CURL_FORMAT_CURL_OFF_T,
- data->state.infilesize);
+ msnprintf(buf, sizeof(buf), "%" CURL_FORMAT_CURL_OFF_T,
+ data->state.infilesize);
else
strcpy(buf, "0"); /* the destination is large enough */
@@ -517,7 +517,7 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event)
sbytes += tftp_option_add(state, sbytes,
(char *)state->spacket.data + sbytes, buf);
/* add blksize option */
- snprintf(buf, sizeof(buf), "%d", state->requested_blksize);
+ msnprintf(buf, sizeof(buf), "%d", state->requested_blksize);
sbytes += tftp_option_add(state, sbytes,
(char *)state->spacket.data + sbytes,
TFTP_OPTION_BLKSIZE);
@@ -525,7 +525,7 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event)
(char *)state->spacket.data + sbytes, buf);
/* add timeout option */
- snprintf(buf, sizeof(buf), "%d", state->retry_time);
+ msnprintf(buf, sizeof(buf), "%d", state->retry_time);
sbytes += tftp_option_add(state, sbytes,
(char *)state->spacket.data + sbytes,
TFTP_OPTION_INTERVAL);
@@ -540,7 +540,8 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event)
state->conn->ip_addr->ai_addr,
state->conn->ip_addr->ai_addrlen);
if(senddata != (ssize_t)sbytes) {
- failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
+ char buffer[STRERROR_LEN];
+ failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
}
free(filename);
break;
@@ -590,6 +591,7 @@ static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event)
ssize_t sbytes;
int rblock;
struct Curl_easy *data = state->conn->data;
+ char buffer[STRERROR_LEN];
switch(event) {
@@ -622,7 +624,7 @@ static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event)
(struct sockaddr *)&state->remote_addr,
state->remote_addrlen);
if(sbytes < 0) {
- failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
+ failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
return CURLE_SEND_ERROR;
}
@@ -647,7 +649,7 @@ static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event)
(struct sockaddr *)&state->remote_addr,
state->remote_addrlen);
if(sbytes < 0) {
- failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
+ failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
return CURLE_SEND_ERROR;
}
@@ -673,7 +675,7 @@ static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event)
(struct sockaddr *)&state->remote_addr,
state->remote_addrlen);
if(sbytes<0) {
- failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
+ failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
return CURLE_SEND_ERROR;
}
}
@@ -713,6 +715,7 @@ static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event)
CURLcode result = CURLE_OK;
struct SingleRequest *k = &data->req;
size_t cb; /* Bytes currently read */
+ char buffer[STRERROR_LEN];
switch(event) {
@@ -747,7 +750,8 @@ static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event)
state->remote_addrlen);
/* Check all sbytes were sent */
if(sbytes<0) {
- failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
+ failf(data, "%s", Curl_strerror(SOCKERRNO,
+ buffer, sizeof(buffer)));
result = CURLE_SEND_ERROR;
}
}
@@ -791,7 +795,7 @@ static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event)
state->remote_addrlen);
/* Check all sbytes were sent */
if(sbytes<0) {
- failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
+ failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
return CURLE_SEND_ERROR;
}
/* Update the progress meter */
@@ -817,7 +821,7 @@ static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event)
state->remote_addrlen);
/* Check all sbytes were sent */
if(sbytes<0) {
- failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
+ failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
return CURLE_SEND_ERROR;
}
/* since this was a re-send, we remain at the still byte position */
@@ -1005,7 +1009,7 @@ static CURLcode tftp_connect(struct connectdata *conn, bool *done)
state->sockfd = state->conn->sock[FIRSTSOCKET];
state->state = TFTP_STATE_START;
state->error = TFTP_ERR_NONE;
- state->blksize = TFTP_BLKSIZE_DEFAULT;
+ state->blksize = blksize;
state->requested_blksize = blksize;
((struct sockaddr *)&state->local_addr)->sa_family =
@@ -1030,8 +1034,9 @@ static CURLcode tftp_connect(struct connectdata *conn, bool *done)
int rc = bind(state->sockfd, (struct sockaddr *)&state->local_addr,
conn->ip_addr->ai_addrlen);
if(rc) {
+ char buffer[STRERROR_LEN];
failf(conn->data, "bind() failed; %s",
- Curl_strerror(conn, SOCKERRNO));
+ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
return CURLE_COULDNT_CONNECT;
}
conn->bits.bound = TRUE;
@@ -1242,7 +1247,7 @@ static CURLcode tftp_multi_statemach(struct connectdata *conn, bool *done)
*done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE;
if(*done)
/* Tell curl we're done */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
}
else {
/* no timeouts to handle, check our socket */
@@ -1251,7 +1256,8 @@ static CURLcode tftp_multi_statemach(struct connectdata *conn, bool *done)
if(rc == -1) {
/* bail out */
int error = SOCKERRNO;
- failf(data, "%s", Curl_strerror(conn, error));
+ char buffer[STRERROR_LEN];
+ failf(data, "%s", Curl_strerror(error, buffer, sizeof(buffer)));
state->event = TFTP_EVENT_ERROR;
}
else if(rc != 0) {
@@ -1264,7 +1270,7 @@ static CURLcode tftp_multi_statemach(struct connectdata *conn, bool *done)
*done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE;
if(*done)
/* Tell curl we're done */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
}
/* if rc == 0, then select() timed out */
}
diff --git a/Utilities/cmcurl/lib/timeval.c b/Utilities/cmcurl/lib/timeval.c
index dce1a761e..e2bd7fd14 100644
--- a/Utilities/cmcurl/lib/timeval.c
+++ b/Utilities/cmcurl/lib/timeval.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -24,26 +24,34 @@
#if defined(WIN32) && !defined(MSDOS)
+/* set in win32_init() */
+extern LARGE_INTEGER Curl_freq;
+extern bool Curl_isVistaOrGreater;
+
struct curltime Curl_now(void)
{
- /*
- ** GetTickCount() is available on _all_ Windows versions from W95 up
- ** to nowadays. Returns milliseconds elapsed since last system boot,
- ** increases monotonically and wraps once 49.7 days have elapsed.
- */
struct curltime now;
-#if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \
- (_WIN32_WINNT < _WIN32_WINNT_VISTA) || \
- (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
- DWORD milliseconds = GetTickCount();
- now.tv_sec = milliseconds / 1000;
- now.tv_usec = (milliseconds % 1000) * 1000;
-#else
- ULONGLONG milliseconds = GetTickCount64();
- now.tv_sec = (time_t) (milliseconds / 1000);
- now.tv_usec = (unsigned int) (milliseconds % 1000) * 1000;
+ if(Curl_isVistaOrGreater) { /* QPC timer might have issues pre-Vista */
+ LARGE_INTEGER count;
+ QueryPerformanceCounter(&count);
+ now.tv_sec = (time_t)(count.QuadPart / Curl_freq.QuadPart);
+ now.tv_usec = (int)((count.QuadPart % Curl_freq.QuadPart) * 1000000 /
+ Curl_freq.QuadPart);
+ }
+ else {
+ /* Disable /analyze warning that GetTickCount64 is preferred */
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:28159)
+#endif
+ DWORD milliseconds = GetTickCount();
+#if defined(_MSC_VER)
+#pragma warning(pop)
#endif
+ now.tv_sec = milliseconds / 1000;
+ now.tv_usec = (milliseconds % 1000) * 1000;
+ }
return now;
}
@@ -58,7 +66,9 @@ struct curltime Curl_now(void)
** in any case the time starting point does not change once that the
** system has started up.
*/
+#ifdef HAVE_GETTIMEOFDAY
struct timeval now;
+#endif
struct curltime cnow;
struct timespec tsnow;
@@ -180,7 +190,7 @@ struct curltime Curl_now(void)
*/
timediff_t Curl_timediff(struct curltime newer, struct curltime older)
{
- timediff_t diff = newer.tv_sec-older.tv_sec;
+ timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec;
if(diff >= (TIME_MAX/1000))
return TIME_MAX;
else if(diff <= (TIME_MIN/1000))
@@ -194,7 +204,7 @@ timediff_t Curl_timediff(struct curltime newer, struct curltime older)
*/
timediff_t Curl_timediff_us(struct curltime newer, struct curltime older)
{
- timediff_t diff = newer.tv_sec-older.tv_sec;
+ timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec;
if(diff >= (TIME_MAX/1000000))
return TIME_MAX;
else if(diff <= (TIME_MIN/1000000))
diff --git a/Utilities/cmcurl/lib/timeval.h b/Utilities/cmcurl/lib/timeval.h
index fb3f680c4..96867d713 100644
--- a/Utilities/cmcurl/lib/timeval.h
+++ b/Utilities/cmcurl/lib/timeval.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -26,8 +26,10 @@
#if SIZEOF_TIME_T < 8
typedef int timediff_t;
+#define CURL_FORMAT_TIMEDIFF_T "d"
#else
typedef curl_off_t timediff_t;
+#define CURL_FORMAT_TIMEDIFF_T CURL_FORMAT_CURL_OFF_T
#endif
struct curltime {
diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c
index 05ba862c2..514330e8c 100644
--- a/Utilities/cmcurl/lib/transfer.c
+++ b/Utilities/cmcurl/lib/transfer.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -117,6 +117,35 @@ CURLcode Curl_get_upload_buffer(struct Curl_easy *data)
return CURLE_OK;
}
+#ifndef CURL_DISABLE_HTTP
+/*
+ * This function will be called to loop through the trailers buffer
+ * until no more data is available for sending.
+ */
+static size_t Curl_trailers_read(char *buffer, size_t size, size_t nitems,
+ void *raw)
+{
+ struct Curl_easy *data = (struct Curl_easy *)raw;
+ Curl_send_buffer *trailers_buf = data->state.trailers_buf;
+ size_t bytes_left = trailers_buf->size_used-data->state.trailers_bytes_sent;
+ size_t to_copy = (size*nitems < bytes_left) ? size*nitems : bytes_left;
+ if(to_copy) {
+ memcpy(buffer,
+ &trailers_buf->buffer[data->state.trailers_bytes_sent],
+ to_copy);
+ data->state.trailers_bytes_sent += to_copy;
+ }
+ return to_copy;
+}
+
+static size_t Curl_trailers_left(void *raw)
+{
+ struct Curl_easy *data = (struct Curl_easy *)raw;
+ Curl_send_buffer *trailers_buf = data->state.trailers_buf;
+ return trailers_buf->size_used - data->state.trailers_bytes_sent;
+}
+#endif
+
/*
* This function will call the read callback to fill our buffer with data
* to upload.
@@ -127,6 +156,10 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes,
struct Curl_easy *data = conn->data;
size_t buffersize = bytes;
size_t nread;
+
+ curl_read_callback readfunc = NULL;
+ void *extra_data = NULL;
+
#ifdef CURL_DOES_CONVERSIONS
bool sending_http_headers = FALSE;
@@ -140,15 +173,75 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes,
}
#endif
- if(data->req.upload_chunky) {
+#ifndef CURL_DISABLE_HTTP
+ if(data->state.trailers_state == TRAILERS_INITIALIZED) {
+ struct curl_slist *trailers = NULL;
+ CURLcode c;
+ int trailers_ret_code;
+
+ /* at this point we already verified that the callback exists
+ so we compile and store the trailers buffer, then proceed */
+ infof(data,
+ "Moving trailers state machine from initialized to sending.\n");
+ data->state.trailers_state = TRAILERS_SENDING;
+ data->state.trailers_buf = Curl_add_buffer_init();
+ if(!data->state.trailers_buf) {
+ failf(data, "Unable to allocate trailing headers buffer !");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ data->state.trailers_bytes_sent = 0;
+ Curl_set_in_callback(data, true);
+ trailers_ret_code = data->set.trailer_callback(&trailers,
+ data->set.trailer_data);
+ Curl_set_in_callback(data, false);
+ if(trailers_ret_code == CURL_TRAILERFUNC_OK) {
+ c = Curl_http_compile_trailers(trailers, data->state.trailers_buf, data);
+ }
+ else {
+ failf(data, "operation aborted by trailing headers callback");
+ *nreadp = 0;
+ c = CURLE_ABORTED_BY_CALLBACK;
+ }
+ if(c != CURLE_OK) {
+ Curl_add_buffer_free(&data->state.trailers_buf);
+ curl_slist_free_all(trailers);
+ return c;
+ }
+ infof(data, "Successfully compiled trailers.\r\n");
+ curl_slist_free_all(trailers);
+ }
+#endif
+
+ /* if we are transmitting trailing data, we don't need to write
+ a chunk size so we skip this */
+ if(data->req.upload_chunky &&
+ data->state.trailers_state == TRAILERS_NONE) {
/* if chunked Transfer-Encoding */
buffersize -= (8 + 2 + 2); /* 32bit hex + CRLF + CRLF */
data->req.upload_fromhere += (8 + 2); /* 32bit hex + CRLF */
}
+#ifndef CURL_DISABLE_HTTP
+ if(data->state.trailers_state == TRAILERS_SENDING) {
+ /* if we're here then that means that we already sent the last empty chunk
+ but we didn't send a final CR LF, so we sent 0 CR LF. We then start
+ pulling trailing data until we ²have no more at which point we
+ simply return to the previous point in the state machine as if
+ nothing happened.
+ */
+ readfunc = Curl_trailers_read;
+ extra_data = (void *)data;
+ }
+ else
+#endif
+ {
+ readfunc = data->state.fread_func;
+ extra_data = data->state.in;
+ }
+
Curl_set_in_callback(data, true);
- nread = data->state.fread_func(data->req.upload_fromhere, 1,
- buffersize, data->state.in);
+ nread = readfunc(data->req.upload_fromhere, 1,
+ buffersize, extra_data);
Curl_set_in_callback(data, false);
if(nread == CURL_READFUNC_ABORT) {
@@ -200,10 +293,10 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes,
here, knowing they'll become CRLFs later on.
*/
- char hexbuffer[11];
+ bool added_crlf = FALSE;
+ int hexlen = 0;
const char *endofline_native;
const char *endofline_network;
- int hexlen;
if(
#ifdef CURL_DO_LINEEND_CONV
@@ -218,20 +311,37 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes,
endofline_native = "\r\n";
endofline_network = "\x0d\x0a";
}
- hexlen = snprintf(hexbuffer, sizeof(hexbuffer),
- "%x%s", nread, endofline_native);
- /* move buffer pointer */
- data->req.upload_fromhere -= hexlen;
- nread += hexlen;
+ /* if we're not handling trailing data, proceed as usual */
+ if(data->state.trailers_state != TRAILERS_SENDING) {
+ char hexbuffer[11] = "";
+ hexlen = msnprintf(hexbuffer, sizeof(hexbuffer),
+ "%zx%s", nread, endofline_native);
- /* copy the prefix to the buffer, leaving out the NUL */
- memcpy(data->req.upload_fromhere, hexbuffer, hexlen);
+ /* move buffer pointer */
+ data->req.upload_fromhere -= hexlen;
+ nread += hexlen;
- /* always append ASCII CRLF to the data */
- memcpy(data->req.upload_fromhere + nread,
- endofline_network,
- strlen(endofline_network));
+ /* copy the prefix to the buffer, leaving out the NUL */
+ memcpy(data->req.upload_fromhere, hexbuffer, hexlen);
+
+ /* always append ASCII CRLF to the data unless
+ we have a valid trailer callback */
+#ifndef CURL_DISABLE_HTTP
+ if((nread-hexlen) == 0 &&
+ data->set.trailer_callback != NULL &&
+ data->state.trailers_state == TRAILERS_NONE) {
+ data->state.trailers_state = TRAILERS_INITIALIZED;
+ }
+ else
+#endif
+ {
+ memcpy(data->req.upload_fromhere + nread,
+ endofline_network,
+ strlen(endofline_network));
+ added_crlf = TRUE;
+ }
+ }
#ifdef CURL_DOES_CONVERSIONS
{
@@ -242,22 +352,40 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes,
length = nread;
else
/* just translate the protocol portion */
- length = strlen(hexbuffer);
- result = Curl_convert_to_network(data, data->req.upload_fromhere,
- length);
- /* Curl_convert_to_network calls failf if unsuccessful */
- if(result)
- return result;
+ length = hexlen;
+ if(length) {
+ result = Curl_convert_to_network(data, data->req.upload_fromhere,
+ length);
+ /* Curl_convert_to_network calls failf if unsuccessful */
+ if(result)
+ return result;
+ }
}
#endif /* CURL_DOES_CONVERSIONS */
- if((nread - hexlen) == 0) {
- /* mark this as done once this chunk is transferred */
+#ifndef CURL_DISABLE_HTTP
+ if(data->state.trailers_state == TRAILERS_SENDING &&
+ !Curl_trailers_left(data)) {
+ Curl_add_buffer_free(&data->state.trailers_buf);
+ data->state.trailers_state = TRAILERS_DONE;
+ data->set.trailer_data = NULL;
+ data->set.trailer_callback = NULL;
+ /* mark the transfer as done */
data->req.upload_done = TRUE;
- infof(data, "Signaling end of chunked upload via terminating chunk.\n");
+ infof(data, "Signaling end of chunked upload after trailers.\n");
}
+ else
+#endif
+ if((nread - hexlen) == 0 &&
+ data->state.trailers_state != TRAILERS_INITIALIZED) {
+ /* mark this as done once this chunk is transferred */
+ data->req.upload_done = TRUE;
+ infof(data,
+ "Signaling end of chunked upload via terminating chunk.\n");
+ }
- nread += strlen(endofline_native); /* for the added end of line */
+ if(added_crlf)
+ nread += strlen(endofline_network); /* for the added end of line */
}
#ifdef CURL_DOES_CONVERSIONS
else if((data->set.prefer_ascii) && (!sending_http_headers)) {
@@ -333,7 +461,6 @@ CURLcode Curl_readrewind(struct connectdata *conn)
infof(data, "the ioctl callback returned %d\n", (int)err);
if(err) {
- /* FIXME: convert to a human readable error message */
failf(data, "ioctl callback returned error %d", (int)err);
return CURLE_SEND_FAIL_REWIND;
}
@@ -376,35 +503,6 @@ static int data_pending(const struct connectdata *conn)
#endif
}
-static void read_rewind(struct connectdata *conn,
- size_t thismuch)
-{
- DEBUGASSERT(conn->read_pos >= thismuch);
-
- conn->read_pos -= thismuch;
- conn->bits.stream_was_rewound = TRUE;
-
-#ifdef DEBUGBUILD
- {
- char buf[512 + 1];
- size_t show;
-
- show = CURLMIN(conn->buf_len - conn->read_pos, sizeof(buf)-1);
- if(conn->master_buffer) {
- memcpy(buf, conn->master_buffer + conn->read_pos, show);
- buf[show] = '\0';
- }
- else {
- buf[0] = '\0';
- }
-
- DEBUGF(infof(conn->data,
- "Buffer after stream rewind (read_pos = %zu): [%s]\n",
- conn->read_pos, buf));
- }
-#endif
-}
-
/*
* Check to see if CURLOPT_TIMECONDITION was met by comparing the time of the
* remote document with the time provided by CURLOPT_TIMEVAL
@@ -479,9 +577,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
conn->httpversion == 20) &&
#endif
k->size != -1 && !k->header) {
- /* make sure we don't read "too much" if we can help it since we
- might be pipelining and then someone else might want to read what
- follows! */
+ /* make sure we don't read too much */
curl_off_t totalleft = k->size - k->bytecount;
if(totalleft < (curl_off_t)bytestoread)
bytestoread = (size_t)totalleft;
@@ -520,7 +616,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
if(0 < nread || is_empty_data) {
k->buf[nread] = 0;
}
- else if(0 >= nread) {
+ else {
/* if we receive 0 or less here, the server closed the connection
and we bail out from this! */
DEBUGF(infof(data, "nread <= 0, server closed connection, bailing\n"));
@@ -563,20 +659,11 @@ static CURLcode readwrite_data(struct Curl_easy *data,
/* We've stopped dealing with input, get out of the do-while loop */
if(nread > 0) {
- if(Curl_pipeline_wanted(conn->data->multi, CURLPIPE_HTTP1)) {
- infof(data,
- "Rewinding stream by : %zd"
- " bytes on url %s (zero-length body)\n",
- nread, data->state.up.path);
- read_rewind(conn, (size_t)nread);
- }
- else {
- infof(data,
- "Excess found in a non pipelined read:"
- " excess = %zd"
- " url = %s (zero-length body)\n",
- nread, data->state.up.path);
- }
+ infof(data,
+ "Excess found:"
+ " excess = %zd"
+ " url = %s (zero-length body)\n",
+ nread, data->state.up.path);
}
break;
@@ -707,19 +794,12 @@ static CURLcode readwrite_data(struct Curl_easy *data,
/* There are now possibly N number of bytes at the end of the
str buffer that weren't written to the client.
-
- We DO care about this data if we are pipelining.
Push it back to be read on the next pass. */
dataleft = conn->chunk.dataleft;
if(dataleft != 0) {
infof(conn->data, "Leftovers after chunking: %zu bytes\n",
dataleft);
- if(Curl_pipeline_wanted(conn->data->multi, CURLPIPE_HTTP1)) {
- /* only attempt the rewind if we truly are pipelining */
- infof(conn->data, "Rewinding %zu bytes\n",dataleft);
- read_rewind(conn, dataleft);
- }
}
}
/* If it returned OK, we just keep going */
@@ -738,25 +818,13 @@ static CURLcode readwrite_data(struct Curl_easy *data,
excess = (size_t)(k->bytecount + nread - k->maxdownload);
if(excess > 0 && !k->ignorebody) {
- if(Curl_pipeline_wanted(conn->data->multi, CURLPIPE_HTTP1)) {
- infof(data,
- "Rewinding stream by : %zu"
- " bytes on url %s (size = %" CURL_FORMAT_CURL_OFF_T
- ", maxdownload = %" CURL_FORMAT_CURL_OFF_T
- ", bytecount = %" CURL_FORMAT_CURL_OFF_T ", nread = %zd)\n",
- excess, data->state.up.path,
- k->size, k->maxdownload, k->bytecount, nread);
- read_rewind(conn, excess);
- }
- else {
- infof(data,
- "Excess found in a non pipelined read:"
- " excess = %zu"
- ", size = %" CURL_FORMAT_CURL_OFF_T
- ", maxdownload = %" CURL_FORMAT_CURL_OFF_T
- ", bytecount = %" CURL_FORMAT_CURL_OFF_T "\n",
- excess, k->size, k->maxdownload, k->bytecount);
- }
+ infof(data,
+ "Excess found in a read:"
+ " excess = %zu"
+ ", size = %" CURL_FORMAT_CURL_OFF_T
+ ", maxdownload = %" CURL_FORMAT_CURL_OFF_T
+ ", bytecount = %" CURL_FORMAT_CURL_OFF_T "\n",
+ excess, k->size, k->maxdownload, k->bytecount);
}
nread = (ssize_t) (k->maxdownload - k->bytecount);
@@ -844,6 +912,11 @@ static CURLcode readwrite_data(struct Curl_easy *data,
k->keepon &= ~KEEP_RECV;
}
+ if(k->keepon & KEEP_RECV_PAUSE) {
+ /* this is a paused transfer */
+ break;
+ }
+
} while(data_pending(conn) && maxloops--);
if(maxloops <= 0) {
@@ -920,7 +993,6 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
*didwhat |= KEEP_SEND;
do {
-
/* only read more data if there's no upload data already
present in the upload buffer */
if(0 == k->upload_present) {
@@ -945,7 +1017,6 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
k->keepon &= ~KEEP_SEND; /* disable writing */
k->start100 = Curl_now(); /* timeout count starts now */
*didwhat &= ~KEEP_SEND; /* we didn't write anything actually */
-
/* set a timeout for the multi interface */
Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
break;
@@ -1065,6 +1136,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
(size_t)bytes_written);
k->writebytecount += bytes_written;
+ Curl_pgrsSetUploadCounter(data, k->writebytecount);
if((!k->upload_chunky || k->forbidchunk) &&
(k->writebytecount == data->state.infilesize)) {
@@ -1098,7 +1170,6 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
}
}
- Curl_pgrsSetUploadCounter(data, k->writebytecount);
} WHILE_FALSE; /* just to break out from! */
@@ -1176,11 +1247,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
k->now = Curl_now();
if(didwhat) {
- /* Update read/write counters */
- if(k->bytecountp)
- *k->bytecountp = k->bytecount; /* read count */
- if(k->writebytecountp)
- *k->writebytecountp = k->writebytecount; /* write count */
+ ;
}
else {
/* no read no write, this is a timeout? */
@@ -1219,15 +1286,15 @@ CURLcode Curl_readwrite(struct connectdata *conn,
if(k->keepon) {
if(0 > Curl_timeleft(data, &k->now, FALSE)) {
if(k->size != -1) {
- failf(data, "Operation timed out after %ld milliseconds with %"
- CURL_FORMAT_CURL_OFF_T " out of %"
+ failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
+ " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %"
CURL_FORMAT_CURL_OFF_T " bytes received",
Curl_timediff(k->now, data->progress.t_startsingle),
k->bytecount, k->size);
}
else {
- failf(data, "Operation timed out after %ld milliseconds with %"
- CURL_FORMAT_CURL_OFF_T " bytes received",
+ failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
+ " milliseconds with %" CURL_FORMAT_CURL_OFF_T " bytes received",
Curl_timediff(k->now, data->progress.t_startsingle),
k->bytecount);
}
@@ -1348,17 +1415,30 @@ void Curl_init_CONNECT(struct Curl_easy *data)
CURLcode Curl_pretransfer(struct Curl_easy *data)
{
CURLcode result;
- if(!data->change.url) {
+
+ if(!data->change.url && !data->set.uh) {
/* we can't do anything without URL */
failf(data, "No URL set!");
return CURLE_URL_MALFORMAT;
}
+
/* since the URL may have been redirected in a previous use of this handle */
if(data->change.url_alloc) {
/* the already set URL is allocated, free it first! */
Curl_safefree(data->change.url);
data->change.url_alloc = FALSE;
}
+
+ if(!data->change.url && data->set.uh) {
+ CURLUcode uc;
+ uc = curl_url_get(data->set.uh,
+ CURLUPART_URL, &data->set.str[STRING_SET_URL], 0);
+ if(uc) {
+ failf(data, "No URL set!");
+ return CURLE_URL_MALFORMAT;
+ }
+ }
+
data->change.url = data->set.str[STRING_SET_URL];
/* Init the SSL session ID cache here. We do it here since we want to do it
@@ -1382,11 +1462,14 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
if(data->set.httpreq == HTTPREQ_PUT)
data->state.infilesize = data->set.filesize;
- else {
+ else if((data->set.httpreq != HTTPREQ_GET) &&
+ (data->set.httpreq != HTTPREQ_HEAD)) {
data->state.infilesize = data->set.postfieldsize;
if(data->set.postfields && (data->state.infilesize == -1))
data->state.infilesize = (curl_off_t)strlen(data->set.postfields);
}
+ else
+ data->state.infilesize = 0;
/* If there is a list of cookie files to read, do it now! */
if(data->change.cookielist)
@@ -1414,18 +1497,13 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
Curl_pgrsResetTransferSizes(data);
Curl_pgrsStartNow(data);
- if(data->set.timeout)
- Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT);
-
- if(data->set.connecttimeout)
- Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT);
-
/* In case the handle is re-used and an authentication method was picked
in the session we need to make sure we only use the one(s) we now
consider to be fine */
data->state.authhost.picked &= data->state.authhost.want;
data->state.authproxy.picked &= data->state.authproxy.want;
+#ifndef CURL_DISABLE_FTP
if(data->state.wildcardmatch) {
struct WildcardData *wc = &data->wildcard;
if(wc->state < CURLWC_INIT) {
@@ -1434,6 +1512,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
return CURLE_OUT_OF_MEMORY;
}
}
+#endif
}
return result;
@@ -1516,12 +1595,22 @@ CURLcode Curl_follow(struct Curl_easy *data,
DEBUGASSERT(data->state.uh);
uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl,
(type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME : 0);
- if(uc)
- return Curl_uc_to_curlcode(uc);
+ if(uc) {
+ if(type != FOLLOW_FAKE)
+ return Curl_uc_to_curlcode(uc);
+
+ /* the URL could not be parsed for some reason, but since this is FAKE
+ mode, just duplicate the field as-is */
+ newurl = strdup(newurl);
+ if(!newurl)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else {
- uc = curl_url_get(data->state.uh, CURLUPART_URL, &newurl, 0);
- if(uc)
- return Curl_uc_to_curlcode(uc);
+ uc = curl_url_get(data->state.uh, CURLUPART_URL, &newurl, 0);
+ if(uc)
+ return Curl_uc_to_curlcode(uc);
+ }
if(type == FOLLOW_FAKE) {
/* we're only figuring out the new url if we would've followed locations
@@ -1704,8 +1793,7 @@ CURLcode Curl_retry_request(struct connectdata *conn,
if(conn->handler->protocol&PROTO_FAMILY_HTTP) {
- struct HTTP *http = data->req.protop;
- if(http->writebytecount) {
+ if(data->req.writebytecount) {
CURLcode result = Curl_readrewind(conn);
if(result) {
Curl_safefree(*url);
@@ -1723,24 +1811,17 @@ CURLcode Curl_retry_request(struct connectdata *conn,
*/
void
Curl_setup_transfer(
- struct connectdata *conn, /* connection data */
+ struct Curl_easy *data, /* transfer */
int sockindex, /* socket index to read from or -1 */
curl_off_t size, /* -1 if unknown at this point */
bool getheader, /* TRUE if header parsing is wanted */
- curl_off_t *bytecountp, /* return number of bytes read or NULL */
- int writesockindex, /* socket index to write to, it may very well be
+ int writesockindex /* socket index to write to, it may very well be
the same we read from. -1 disables */
- curl_off_t *writecountp /* return number of bytes written or NULL */
)
{
- struct Curl_easy *data;
- struct SingleRequest *k;
-
+ struct SingleRequest *k = &data->req;
+ struct connectdata *conn = data->conn;
DEBUGASSERT(conn != NULL);
-
- data = conn->data;
- k = &data->req;
-
DEBUGASSERT((sockindex <= 1) && (sockindex >= -1));
if(conn->bits.multiplex || conn->httpversion == 20) {
@@ -1759,8 +1840,6 @@ Curl_setup_transfer(
k->getheader = getheader;
k->size = size;
- k->bytecountp = bytecountp;
- k->writebytecountp = writecountp;
/* The code sequence below is placed in this function just because all
necessary input is not always known in do_complete() as this function may
diff --git a/Utilities/cmcurl/lib/transfer.h b/Utilities/cmcurl/lib/transfer.h
index 9742455ae..a9bff6348 100644
--- a/Utilities/cmcurl/lib/transfer.h
+++ b/Utilities/cmcurl/lib/transfer.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -59,15 +59,13 @@ CURLcode Curl_get_upload_buffer(struct Curl_easy *data);
/* This sets up a forthcoming transfer */
void
-Curl_setup_transfer (struct connectdata *data,
- int sockindex, /* socket index to read from or -1 */
- curl_off_t size, /* -1 if unknown at this point */
- bool getheader, /* TRUE if header parsing is wanted */
- curl_off_t *bytecountp, /* return number of bytes read */
- int writesockindex, /* socket index to write to, it may
- very well be the same we read from.
- -1 disables */
- curl_off_t *writecountp /* return number of bytes written */
-);
+Curl_setup_transfer (struct Curl_easy *data,
+ int sockindex, /* socket index to read from or -1 */
+ curl_off_t size, /* -1 if unknown at this point */
+ bool getheader, /* TRUE if header parsing is wanted */
+ int writesockindex /* socket index to write to. May be
+ the same we read from. -1
+ disables */
+ );
#endif /* HEADER_CURL_TRANSFER_H */
diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c
index 0d5a13f99..c441ae716 100644
--- a/Utilities/cmcurl/lib/url.c
+++ b/Utilities/cmcurl/lib/url.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -109,17 +109,16 @@ bool curl_win32_idn_to_ascii(const char *in, char **out);
#include "connect.h"
#include "inet_ntop.h"
#include "http_ntlm.h"
-#include "curl_ntlm_wb.h"
#include "socks.h"
#include "curl_rtmp.h"
#include "gopher.h"
#include "http_proxy.h"
#include "conncache.h"
#include "multihandle.h"
-#include "pipeline.h"
#include "dotdot.h"
#include "strdup.h"
#include "setopt.h"
+#include "altsvc.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -127,7 +126,7 @@ bool curl_win32_idn_to_ascii(const char *in, char **out);
#include "memdebug.h"
static void conn_free(struct connectdata *conn);
-static void free_fixed_hostname(struct hostname *host);
+static void free_idnconverted_hostname(struct hostname *host);
static unsigned int get_protocol_family(unsigned int protocol);
/* Some parts of the code (e.g. chunked encoding) assume this buffer has at
@@ -186,11 +185,11 @@ static const struct Curl_handler * const protocols[] = {
&Curl_handler_tftp,
#endif
-#if defined(USE_LIBSSH2) || defined(USE_LIBSSH)
+#if defined(USE_SSH)
&Curl_handler_scp,
#endif
-#if defined(USE_LIBSSH2) || defined(USE_LIBSSH)
+#if defined(USE_SSH)
&Curl_handler_sftp,
#endif
@@ -292,7 +291,7 @@ void Curl_freeset(struct Curl_easy *data)
}
/* free the URL pieces */
-void Curl_up_free(struct Curl_easy *data)
+static void up_free(struct Curl_easy *data)
{
struct urlpieces *up = &data->state.up;
Curl_safefree(up->scheme);
@@ -369,12 +368,19 @@ CURLcode Curl_close(struct Curl_easy *data)
}
data->change.referer = NULL;
- Curl_up_free(data);
+ up_free(data);
Curl_safefree(data->state.buffer);
Curl_safefree(data->state.headerbuff);
Curl_safefree(data->state.ulbuf);
Curl_flush_cookies(data, 1);
- Curl_digest_cleanup(data);
+#ifdef USE_ALTSVC
+ Curl_altsvc_save(data->asi, data->set.str[STRING_ALTSVC]);
+ Curl_altsvc_cleanup(data->asi);
+ data->asi = NULL;
+#endif
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+ Curl_http_auth_cleanup_digest(data);
+#endif
Curl_safefree(data->info.contenttype);
Curl_safefree(data->info.wouldredirect);
@@ -433,11 +439,12 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
set->httpreq = HTTPREQ_GET; /* Default HTTP request */
set->rtspreq = RTSPREQ_OPTIONS; /* Default RTSP request */
+#ifndef CURL_DISABLE_FILE
set->ftp_use_epsv = TRUE; /* FTP defaults to EPSV operations */
set->ftp_use_eprt = TRUE; /* FTP defaults to EPRT operations */
set->ftp_use_pret = FALSE; /* mainly useful for drftpd servers */
set->ftp_filemethod = FTPFILE_MULTICWD;
-
+#endif
set->dns_cache_timeout = 60; /* Timeout every 60 seconds by default */
/* Set the default size of the SSL session ID cache */
@@ -492,9 +499,9 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
/* Set the default CA cert bundle/path detected/specified at build time.
*
- * If Schannel (WinSSL) is the selected SSL backend then these locations
- * are ignored. We allow setting CA location for schannel only when
- * explicitly specified by the user via CURLOPT_CAINFO / --cacert.
+ * If Schannel is the selected SSL backend then these locations are
+ * ignored. We allow setting CA location for schannel only when explicitly
+ * specified by the user via CURLOPT_CAINFO / --cacert.
*/
if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) {
#if defined(CURL_CA_BUNDLE)
@@ -536,6 +543,8 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
set->fnmatch = ZERO_NULL;
set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT;
set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */
+ set->maxage_conn = 118;
+ set->http09_allowed = TRUE;
set->httpversion =
#ifdef USE_NGHTTP2
CURL_HTTP_VERSION_2TLS
@@ -570,7 +579,7 @@ CURLcode Curl_open(struct Curl_easy **curl)
data->magic = CURLEASY_MAGIC_NUMBER;
- result = Curl_resolver_init(&data->state.resolver);
+ result = Curl_resolver_init(data, &data->state.resolver);
if(result) {
DEBUGF(fprintf(stderr, "Error: resolver_init failed\n"));
free(data);
@@ -659,11 +668,15 @@ static void conn_reset_all_postponed_data(struct connectdata *conn)
#define conn_reset_all_postponed_data(c) do {} WHILE_FALSE
#endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */
-static void conn_free(struct connectdata *conn)
+
+static void conn_shutdown(struct connectdata *conn)
{
if(!conn)
return;
+ infof(conn->data, "Closing connection %ld\n", conn->connection_id);
+ DEBUGASSERT(conn->data);
+
/* possible left-overs from the async name resolvers */
Curl_resolver_cancel(conn);
@@ -682,10 +695,20 @@ static void conn_free(struct connectdata *conn)
if(CURL_SOCKET_BAD != conn->tempsock[1])
Curl_closesocket(conn, conn->tempsock[1]);
-#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \
- defined(NTLM_WB_ENABLED)
- Curl_ntlm_wb_cleanup(conn);
-#endif
+ /* unlink ourselves. this should be called last since other shutdown
+ procedures need a valid conn->data and this may clear it. */
+ Curl_conncache_remove_conn(conn->data, conn, TRUE);
+}
+
+static void conn_free(struct connectdata *conn)
+{
+ if(!conn)
+ return;
+
+ free_idnconverted_hostname(&conn->host);
+ free_idnconverted_hostname(&conn->conn_to_host);
+ free_idnconverted_hostname(&conn->http_proxy.host);
+ free_idnconverted_hostname(&conn->socks_proxy.host);
Curl_safefree(conn->user);
Curl_safefree(conn->passwd);
@@ -708,17 +731,14 @@ static void conn_free(struct connectdata *conn)
Curl_safefree(conn->trailer);
Curl_safefree(conn->host.rawalloc); /* host name buffer */
Curl_safefree(conn->conn_to_host.rawalloc); /* host name buffer */
+ Curl_safefree(conn->hostname_resolve);
Curl_safefree(conn->secondaryhostname);
Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */
Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */
- Curl_safefree(conn->master_buffer);
Curl_safefree(conn->connect_state);
conn_reset_all_postponed_data(conn);
-
- Curl_llist_destroy(&conn->send_pipe, NULL);
- Curl_llist_destroy(&conn->recv_pipe, NULL);
-
+ Curl_llist_destroy(&conn->easyq, NULL);
Curl_safefree(conn->localdev);
Curl_free_primary_ssl_config(&conn->ssl_config);
Curl_free_primary_ssl_config(&conn->proxy_ssl_config);
@@ -767,7 +787,6 @@ CURLcode Curl_disconnect(struct Curl_easy *data,
return CURLE_OK;
}
- conn->data = data;
if(conn->dns_entry != NULL) {
Curl_resolv_unlock(data, conn->dns_entry);
conn->dns_entry = NULL;
@@ -777,27 +796,26 @@ CURLcode Curl_disconnect(struct Curl_easy *data,
#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM)
/* Cleanup NTLM connection-related data */
- Curl_http_ntlm_cleanup(conn);
+ Curl_http_auth_cleanup_ntlm(conn);
#endif
+#if !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO)
+ /* Cleanup NEGOTIATE connection-related data */
+ Curl_http_auth_cleanup_negotiate(conn);
+#endif
+
+ /* the protocol specific disconnect handler and conn_shutdown need a transfer
+ for the connection! */
+ conn->data = data;
+
+ if(conn->bits.connect_only)
+ /* treat the connection as dead in CONNECT_ONLY situations */
+ dead_connection = TRUE;
if(conn->handler->disconnect)
/* This is set if protocol-specific cleanups should be made */
conn->handler->disconnect(conn, dead_connection);
- /* unlink ourselves! */
- infof(data, "Closing connection %ld\n", conn->connection_id);
- Curl_conncache_remove_conn(conn, TRUE);
-
- free_fixed_hostname(&conn->host);
- free_fixed_hostname(&conn->conn_to_host);
- free_fixed_hostname(&conn->http_proxy.host);
- free_fixed_hostname(&conn->socks_proxy.host);
-
- DEBUGASSERT(conn->data == data);
- /* this assumes that the pointer is still there after the connection was
- detected from the cache */
- Curl_ssl_close(conn, FIRSTSOCKET);
-
+ conn_shutdown(conn);
conn_free(conn);
return CURLE_OK;
}
@@ -821,28 +839,21 @@ static bool SocketIsDead(curl_socket_t sock)
}
/*
- * IsPipeliningPossible()
+ * IsMultiplexingPossible()
*
- * Return a bitmask with the available pipelining and multiplexing options for
- * the given requested connection.
+ * Return a bitmask with the available multiplexing options for the given
+ * requested connection.
*/
-static int IsPipeliningPossible(const struct Curl_easy *handle,
- const struct connectdata *conn)
+static int IsMultiplexingPossible(const struct Curl_easy *handle,
+ const struct connectdata *conn)
{
int avail = 0;
- /* If a HTTP protocol and pipelining is enabled */
+ /* If a HTTP protocol and multiplexing is enabled */
if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
(!conn->bits.protoconnstart || !conn->bits.close)) {
- if(Curl_pipeline_wanted(handle->multi, CURLPIPE_HTTP1) &&
- (handle->set.httpversion != CURL_HTTP_VERSION_1_0) &&
- (handle->set.httpreq == HTTPREQ_GET ||
- handle->set.httpreq == HTTPREQ_HEAD))
- /* didn't ask for HTTP/1.0 and a GET or HEAD */
- avail |= CURLPIPE_HTTP1;
-
- if(Curl_pipeline_wanted(handle->multi, CURLPIPE_MULTIPLEX) &&
+ if(Curl_multiplex_wanted(handle->multi) &&
(handle->set.httpversion >= CURL_HTTP_VERSION_2))
/* allows HTTP/2 */
avail |= CURLPIPE_MULTIPLEX;
@@ -850,84 +861,7 @@ static int IsPipeliningPossible(const struct Curl_easy *handle,
return avail;
}
-/* Returns non-zero if a handle was removed */
-int Curl_removeHandleFromPipeline(struct Curl_easy *handle,
- struct curl_llist *pipeline)
-{
- if(pipeline) {
- struct curl_llist_element *curr;
-
- curr = pipeline->head;
- while(curr) {
- if(curr->ptr == handle) {
- Curl_llist_remove(pipeline, curr, NULL);
- return 1; /* we removed a handle */
- }
- curr = curr->next;
- }
- }
-
- return 0;
-}
-
-#if 0 /* this code is saved here as it is useful for debugging purposes */
-static void Curl_printPipeline(struct curl_llist *pipeline)
-{
- struct curl_llist_element *curr;
-
- curr = pipeline->head;
- while(curr) {
- struct Curl_easy *data = (struct Curl_easy *) curr->ptr;
- infof(data, "Handle in pipeline: %s\n", data->state.path);
- curr = curr->next;
- }
-}
-#endif
-
-static struct Curl_easy* gethandleathead(struct curl_llist *pipeline)
-{
- struct curl_llist_element *curr = pipeline->head;
-#ifdef DEBUGBUILD
- {
- struct curl_llist_element *p = pipeline->head;
- while(p) {
- struct Curl_easy *e = p->ptr;
- DEBUGASSERT(GOOD_EASY_HANDLE(e));
- p = p->next;
- }
- }
-#endif
- if(curr) {
- return (struct Curl_easy *) curr->ptr;
- }
-
- return NULL;
-}
-
-/* remove the specified connection from all (possible) pipelines and related
- queues */
-void Curl_getoff_all_pipelines(struct Curl_easy *data,
- struct connectdata *conn)
-{
- if(!conn->bundle)
- return;
- if(conn->bundle->multiuse == BUNDLE_PIPELINING) {
- bool recv_head = (conn->readchannel_inuse &&
- Curl_recvpipe_head(data, conn));
- bool send_head = (conn->writechannel_inuse &&
- Curl_sendpipe_head(data, conn));
-
- if(Curl_removeHandleFromPipeline(data, &conn->recv_pipe) && recv_head)
- Curl_pipeline_leave_read(conn);
- if(Curl_removeHandleFromPipeline(data, &conn->send_pipe) && send_head)
- Curl_pipeline_leave_write(conn);
- }
- else {
- (void)Curl_removeHandleFromPipeline(data, &conn->recv_pipe);
- (void)Curl_removeHandleFromPipeline(data, &conn->send_pipe);
- }
-}
-
+#ifndef CURL_DISABLE_PROXY
static bool
proxy_info_matches(const struct proxy_info* data,
const struct proxy_info* needle)
@@ -939,6 +873,10 @@ proxy_info_matches(const struct proxy_info* data,
return FALSE;
}
+#else
+/* disabled, won't get called */
+#define proxy_info_matches(x,y) FALSE
+#endif
/*
* This function checks if the given connection is dead and extracts it from
@@ -952,20 +890,18 @@ proxy_info_matches(const struct proxy_info* data,
static bool extract_if_dead(struct connectdata *conn,
struct Curl_easy *data)
{
- size_t pipeLen = conn->send_pipe.size + conn->recv_pipe.size;
- if(!pipeLen && !CONN_INUSE(conn)) {
- /* The check for a dead socket makes sense only if there are no
- handles in pipeline and the connection isn't already marked in
+ if(!CONN_INUSE(conn) && !conn->data) {
+ /* The check for a dead socket makes sense only if the connection isn't in
use */
bool dead;
-
- conn->data = data;
if(conn->handler->connection_check) {
/* The protocol has a special method for checking the state of the
connection. Use it to check if the connection is dead. */
unsigned int state;
-
+ struct Curl_easy *olddata = conn->data;
+ conn->data = data; /* use this transfer for now */
state = conn->handler->connection_check(conn, CONNCHECK_ISDEAD);
+ conn->data = olddata;
dead = (state & CONNRESULT_DEAD);
}
else {
@@ -975,8 +911,7 @@ static bool extract_if_dead(struct connectdata *conn,
if(dead) {
infof(data, "Connection %ld seems to be dead!\n", conn->connection_id);
- Curl_conncache_remove_conn(conn, FALSE);
- conn->data = NULL; /* detach */
+ Curl_conncache_remove_conn(data, conn, FALSE);
return TRUE;
}
}
@@ -1026,13 +961,25 @@ static void prune_dead_connections(struct Curl_easy *data)
}
}
+/* A connection has to have been idle for a shorter time than 'maxage_conn' to
+ be subject for reuse. The success rate is just too low after this. */
-static size_t max_pipeline_length(struct Curl_multi *multi)
+static bool conn_maxage(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct curltime now)
{
- return multi ? multi->max_pipeline_length : 0;
-}
-
+ if(!conn->data) {
+ timediff_t idletime = Curl_timediff(now, conn->lastused);
+ idletime /= 1000; /* integer seconds is fine */
+ if(idletime/1000 > data->set.maxage_conn) {
+ infof(data, "Too old connection (%ld seconds), disconnect it\n",
+ idletime);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
/*
* Given one filled in connection struct (named needle), this function should
* detect if there already is one that has all the significant details
@@ -1042,8 +989,7 @@ static size_t max_pipeline_length(struct Curl_multi *multi)
* connection as 'in-use'. It must later be called with ConnectionDone() to
* return back to 'idle' (unused) state.
*
- * The force_reuse flag is set if the connection must be used, even if
- * the pipelining strategy wants to open a new connection instead of reusing.
+ * The force_reuse flag is set if the connection must be used.
*/
static bool
ConnectionExists(struct Curl_easy *data,
@@ -1055,8 +1001,9 @@ ConnectionExists(struct Curl_easy *data,
struct connectdata *check;
struct connectdata *chosen = 0;
bool foundPendingCandidate = FALSE;
- int canpipe = IsPipeliningPossible(data, needle);
+ bool canmultiplex = IsMultiplexingPossible(data, needle);
struct connectbundle *bundle;
+ struct curltime now = Curl_now();
#ifdef USE_NTLM
bool wantNTLMhttp = ((data->state.authhost.want &
@@ -1071,59 +1018,47 @@ ConnectionExists(struct Curl_easy *data,
*force_reuse = FALSE;
*waitpipe = FALSE;
- /* We can't pipeline if the site is blacklisted */
- if((canpipe & CURLPIPE_HTTP1) &&
- Curl_pipeline_site_blacklisted(data, needle))
- canpipe &= ~ CURLPIPE_HTTP1;
-
/* Look up the bundle with all the connections to this particular host.
Locks the connection cache, beware of early returns! */
bundle = Curl_conncache_find_bundle(needle, data->state.conn_cache);
if(bundle) {
/* Max pipe length is zero (unlimited) for multiplexed connections */
- size_t max_pipe_len = (bundle->multiuse != BUNDLE_MULTIPLEX)?
- max_pipeline_length(data->multi):0;
- size_t best_pipe_len = max_pipe_len;
struct curl_llist_element *curr;
infof(data, "Found bundle for host %s: %p [%s]\n",
(needle->bits.conn_to_host ? needle->conn_to_host.name :
needle->host.name), (void *)bundle,
- (bundle->multiuse == BUNDLE_PIPELINING ?
- "can pipeline" :
- (bundle->multiuse == BUNDLE_MULTIPLEX ?
- "can multiplex" : "serially")));
-
- /* We can't pipeline if we don't know anything about the server */
- if(canpipe) {
- if(bundle->multiuse <= BUNDLE_UNKNOWN) {
+ (bundle->multiuse == BUNDLE_MULTIPLEX ?
+ "can multiplex" : "serially"));
+
+ /* We can't multiplex if we don't know anything about the server */
+ if(canmultiplex) {
+ if(bundle->multiuse == BUNDLE_UNKNOWN) {
if((bundle->multiuse == BUNDLE_UNKNOWN) && data->set.pipewait) {
- infof(data, "Server doesn't support multi-use yet, wait\n");
+ infof(data, "Server doesn't support multiplex yet, wait\n");
*waitpipe = TRUE;
- Curl_conncache_unlock(needle);
+ Curl_conncache_unlock(data);
return FALSE; /* no re-use */
}
- infof(data, "Server doesn't support multi-use (yet)\n");
- canpipe = 0;
+ infof(data, "Server doesn't support multiplex (yet)\n");
+ canmultiplex = FALSE;
}
- if((bundle->multiuse == BUNDLE_PIPELINING) &&
- !Curl_pipeline_wanted(data->multi, CURLPIPE_HTTP1)) {
- /* not asked for, switch off */
- infof(data, "Could pipeline, but not asked to!\n");
- canpipe = 0;
- }
- else if((bundle->multiuse == BUNDLE_MULTIPLEX) &&
- !Curl_pipeline_wanted(data->multi, CURLPIPE_MULTIPLEX)) {
+ if((bundle->multiuse == BUNDLE_MULTIPLEX) &&
+ !Curl_multiplex_wanted(data->multi)) {
infof(data, "Could multiplex, but not asked to!\n");
- canpipe = 0;
+ canmultiplex = FALSE;
+ }
+ if(bundle->multiuse == BUNDLE_NO_MULTIUSE) {
+ infof(data, "Can not multiplex, even if we wanted to!\n");
+ canmultiplex = FALSE;
}
}
curr = bundle->conn_list.head;
while(curr) {
bool match = FALSE;
- size_t pipeLen;
+ size_t multiplexed;
/*
* Note that if we use a HTTP proxy in normal mode (no tunneling), we
@@ -1132,35 +1067,25 @@ ConnectionExists(struct Curl_easy *data,
check = curr->ptr;
curr = curr->next;
- if(extract_if_dead(check, data)) {
+ if(check->bits.connect_only)
+ /* connect-only connections will not be reused */
+ continue;
+
+ if(conn_maxage(data, check, now) || extract_if_dead(check, data)) {
/* disconnect it */
(void)Curl_disconnect(data, check, /* dead_connection */TRUE);
continue;
}
- pipeLen = check->send_pipe.size + check->recv_pipe.size;
+ multiplexed = CONN_INUSE(check) &&
+ (bundle->multiuse == BUNDLE_MULTIPLEX);
- if(canpipe) {
+ if(canmultiplex) {
if(check->bits.protoconnstart && check->bits.close)
continue;
-
- if(!check->bits.multiplex) {
- /* If not multiplexing, make sure the connection is fine for HTTP/1
- pipelining */
- struct Curl_easy* sh = gethandleathead(&check->send_pipe);
- struct Curl_easy* rh = gethandleathead(&check->recv_pipe);
- if(sh) {
- if(!(IsPipeliningPossible(sh, check) & CURLPIPE_HTTP1))
- continue;
- }
- else if(rh) {
- if(!(IsPipeliningPossible(rh, check) & CURLPIPE_HTTP1))
- continue;
- }
- }
}
else {
- if(pipeLen > 0) {
+ if(multiplexed) {
/* can only happen within multi handles, and means that another easy
handle is using this connection */
continue;
@@ -1185,13 +1110,6 @@ ConnectionExists(struct Curl_easy *data,
to get closed. */
infof(data, "Connection #%ld isn't open enough, can't reuse\n",
check->connection_id);
-#ifdef DEBUGBUILD
- if(check->recv_pipe.size > 0) {
- infof(data,
- "BAD! Unconnected #%ld has a non-empty recv pipeline!\n",
- check->connection_id);
- }
-#endif
continue;
}
}
@@ -1262,14 +1180,15 @@ ConnectionExists(struct Curl_easy *data,
}
}
- if(!canpipe && CONN_INUSE(check))
- /* this request can't be pipelined but the checked connection is
+ if(!canmultiplex && check->data)
+ /* this request can't be multiplexed but the checked connection is
already in use so we skip it */
continue;
- if(CONN_INUSE(check) && (check->data->multi != needle->data->multi))
- /* this could be subject for pipeline/multiplex use, but only
- if they belong to the same multi handle */
+ if(CONN_INUSE(check) && check->data &&
+ (check->data->multi != needle->data->multi))
+ /* this could be subject for multiplex use, but only if they belong to
+ * the same multi handle */
continue;
if(needle->localdev || needle->localport) {
@@ -1360,7 +1279,7 @@ ConnectionExists(struct Curl_easy *data,
strcmp(needle->passwd, check->passwd))
continue;
}
- else if(check->ntlm.state != NTLMSTATE_NONE) {
+ else if(check->http_ntlm_state != NTLMSTATE_NONE) {
/* Connection is using NTLM auth but we don't want NTLM */
continue;
}
@@ -1376,7 +1295,7 @@ ConnectionExists(struct Curl_easy *data,
strcmp(needle->http_proxy.passwd, check->http_proxy.passwd))
continue;
}
- else if(check->proxyntlm.state != NTLMSTATE_NONE) {
+ else if(check->proxy_ntlm_state != NTLMSTATE_NONE) {
/* Proxy connection is using NTLM auth but we don't want NTLM */
continue;
}
@@ -1386,9 +1305,9 @@ ConnectionExists(struct Curl_easy *data,
chosen = check;
if((wantNTLMhttp &&
- (check->ntlm.state != NTLMSTATE_NONE)) ||
+ (check->http_ntlm_state != NTLMSTATE_NONE)) ||
(wantProxyNTLMhttp &&
- (check->proxyntlm.state != NTLMSTATE_NONE))) {
+ (check->proxy_ntlm_state != NTLMSTATE_NONE))) {
/* We must use this connection, no other */
*force_reuse = TRUE;
break;
@@ -1398,55 +1317,32 @@ ConnectionExists(struct Curl_easy *data,
continue;
}
#endif
- if(canpipe) {
- /* We can pipeline if we want to. Let's continue looking for
- the optimal connection to use, i.e the shortest pipe that is not
- blacklisted. */
+ if(canmultiplex) {
+ /* We can multiplex if we want to. Let's continue looking for
+ the optimal connection to use. */
- if(pipeLen == 0) {
+ if(!multiplexed) {
/* We have the optimal connection. Let's stop looking. */
chosen = check;
break;
}
- /* We can't use the connection if the pipe is full */
- if(max_pipe_len && (pipeLen >= max_pipe_len)) {
- infof(data, "Pipe is full, skip (%zu)\n", pipeLen);
- continue;
- }
#ifdef USE_NGHTTP2
/* If multiplexed, make sure we don't go over concurrency limit */
if(check->bits.multiplex) {
/* Multiplexed connections can only be HTTP/2 for now */
struct http_conn *httpc = &check->proto.httpc;
- if(pipeLen >= httpc->settings.max_concurrent_streams) {
+ if(multiplexed >= httpc->settings.max_concurrent_streams) {
infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)\n",
- pipeLen);
+ multiplexed);
continue;
}
}
#endif
- /* We can't use the connection if the pipe is penalized */
- if(Curl_pipeline_penalized(data, check)) {
- infof(data, "Penalized, skip\n");
- continue;
- }
-
- if(max_pipe_len) {
- if(pipeLen < best_pipe_len) {
- /* This connection has a shorter pipe so far. We'll pick this
- and continue searching */
- chosen = check;
- best_pipe_len = pipeLen;
- continue;
- }
- }
- else {
- /* When not pipelining (== multiplexed), we have a match here! */
- chosen = check;
- infof(data, "Multiplexed connection found!\n");
- break;
- }
+ /* When not multiplexed, we have a match here! */
+ chosen = check;
+ infof(data, "Multiplexed connection found!\n");
+ break;
}
else {
/* We have found a connection. Let's stop searching. */
@@ -1460,11 +1356,11 @@ ConnectionExists(struct Curl_easy *data,
if(chosen) {
/* mark it as used before releasing the lock */
chosen->data = data; /* own it! */
- Curl_conncache_unlock(needle);
+ Curl_conncache_unlock(data);
*usethis = chosen;
return TRUE; /* yes, we found one to use! */
}
- Curl_conncache_unlock(needle);
+ Curl_conncache_unlock(data);
if(foundPendingCandidate && data->set.pipewait) {
infof(data,
@@ -1679,11 +1575,25 @@ static bool is_ASCII_name(const char *hostname)
}
/*
- * Perform any necessary IDN conversion of hostname
+ * Strip single trailing dot in the hostname,
+ * primarily for SNI and http host header.
*/
-static CURLcode fix_hostname(struct connectdata *conn, struct hostname *host)
+static void strip_trailing_dot(struct hostname *host)
{
size_t len;
+ if(!host || !host->name)
+ return;
+ len = strlen(host->name);
+ if(len && (host->name[len-1] == '.'))
+ host->name[len-1] = 0;
+}
+
+/*
+ * Perform any necessary IDN conversion of hostname
+ */
+static CURLcode idnconvert_hostname(struct connectdata *conn,
+ struct hostname *host)
+{
struct Curl_easy *data = conn->data;
#ifndef USE_LIBIDN2
@@ -1696,12 +1606,6 @@ static CURLcode fix_hostname(struct connectdata *conn, struct hostname *host)
/* set the name we use to display the host name */
host->dispname = host->name;
- len = strlen(host->name);
- if(len && (host->name[len-1] == '.'))
- /* strip off a single trailing dot if present, primarily for SNI but
- there's no use for it */
- host->name[len-1] = 0;
-
/* Check name for non-ASCII and convert hostname to ACE form if we can */
if(!is_ASCII_name(host->name)) {
#ifdef USE_LIBIDN2
@@ -1743,22 +1647,13 @@ static CURLcode fix_hostname(struct connectdata *conn, struct hostname *host)
infof(data, "IDN support not present, can't parse Unicode domains\n");
#endif
}
- {
- char *hostp;
- for(hostp = host->name; *hostp; hostp++) {
- if(*hostp <= 32) {
- failf(data, "Host name '%s' contains bad letter", host->name);
- return CURLE_URL_MALFORMAT;
- }
- }
- }
return CURLE_OK;
}
/*
- * Frees data allocated by fix_hostname()
+ * Frees data allocated by idnconvert_hostname()
*/
-static void free_fixed_hostname(struct hostname *host)
+static void free_idnconverted_hostname(struct hostname *host)
{
#if defined(USE_LIBIDN2)
if(host->encalloc) {
@@ -1849,16 +1744,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
conn->http_proxy.proxytype = data->set.proxytype;
conn->socks_proxy.proxytype = CURLPROXY_SOCKS4;
-#ifdef CURL_DISABLE_PROXY
-
- conn->bits.proxy = FALSE;
- conn->bits.httpproxy = FALSE;
- conn->bits.socksproxy = FALSE;
- conn->bits.proxy_user_passwd = FALSE;
- conn->bits.tunnel_proxy = FALSE;
-
-#else /* CURL_DISABLE_PROXY */
-
+#if !defined(CURL_DISABLE_PROXY)
/* note that these two proxy bits are now just on what looks to be
requested, they may be altered down the road */
conn->bits.proxy = (data->set.str[STRING_PROXY] &&
@@ -1879,13 +1765,13 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
conn->bits.proxy_user_passwd =
(data->set.str[STRING_PROXYUSERNAME]) ? TRUE : FALSE;
conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy;
-
#endif /* CURL_DISABLE_PROXY */
conn->bits.user_passwd = (data->set.str[STRING_USERNAME]) ? TRUE : FALSE;
+#ifndef CURL_DISABLE_FTP
conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
-
+#endif
conn->ssl_config.verifystatus = data->set.ssl.primary.verifystatus;
conn->ssl_config.verifypeer = data->set.ssl.primary.verifypeer;
conn->ssl_config.verifyhost = data->set.ssl.primary.verifyhost;
@@ -1893,28 +1779,16 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
data->set.proxy_ssl.primary.verifystatus;
conn->proxy_ssl_config.verifypeer = data->set.proxy_ssl.primary.verifypeer;
conn->proxy_ssl_config.verifyhost = data->set.proxy_ssl.primary.verifyhost;
-
conn->ip_version = data->set.ipver;
+ conn->bits.connect_only = data->set.connect_only;
#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \
defined(NTLM_WB_ENABLED)
conn->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD;
- conn->ntlm_auth_hlpr_pid = 0;
- conn->challenge_header = NULL;
- conn->response_header = NULL;
#endif
- if(Curl_pipeline_wanted(data->multi, CURLPIPE_HTTP1) &&
- !conn->master_buffer) {
- /* Allocate master_buffer to be used for HTTP/1 pipelining */
- conn->master_buffer = calloc(MASTERBUF_SIZE, sizeof(char));
- if(!conn->master_buffer)
- goto error;
- }
-
- /* Initialize the pipeline lists */
- Curl_llist_init(&conn->send_pipe, (curl_llist_dtor) llist_dtor);
- Curl_llist_init(&conn->recv_pipe, (curl_llist_dtor) llist_dtor);
+ /* Initialize the easy handle list */
+ Curl_llist_init(&conn->easyq, (curl_llist_dtor) llist_dtor);
#ifdef HAVE_GSSAPI
conn->data_prot = PROT_CLEAR;
@@ -1937,10 +1811,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
return conn;
error:
- Curl_llist_destroy(&conn->send_pipe, NULL);
- Curl_llist_destroy(&conn->recv_pipe, NULL);
-
- free(conn->master_buffer);
+ Curl_llist_destroy(&conn->easyq, NULL);
free(conn->localdev);
#ifdef USE_SSL
free(conn->ssl_extra);
@@ -2023,10 +1894,16 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
CURLUcode uc;
char *hostname;
- Curl_up_free(data); /* cleanup previous leftovers first */
+ up_free(data); /* cleanup previous leftovers first */
/* parse the URL */
- uh = data->state.uh = curl_url();
+ if(data->set.uh) {
+ uh = data->state.uh = curl_url_dup(data->set.uh);
+ }
+ else {
+ uh = data->state.uh = curl_url();
+ }
+
if(!uh)
return CURLE_OUT_OF_MEMORY;
@@ -2043,14 +1920,18 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
data->change.url_alloc = TRUE;
}
- uc = curl_url_set(uh, CURLUPART_URL, data->change.url,
+ if(!data->set.uh) {
+ uc = curl_url_set(uh, CURLUPART_URL, data->change.url,
CURLU_GUESS_SCHEME |
CURLU_NON_SUPPORT_SCHEME |
(data->set.disallow_username_in_url ?
CURLU_DISALLOW_USER : 0) |
(data->set.path_as_is ? CURLU_PATH_AS_IS : 0));
- if(uc)
- return Curl_uc_to_curlcode(uc);
+ if(uc) {
+ DEBUGF(infof(data, "curl_url_set rejected %s\n", data->change.url));
+ return Curl_uc_to_curlcode(uc);
+ }
+ }
uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0);
if(uc)
@@ -2121,61 +2002,40 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
hostname = (char *)"";
if(hostname[0] == '[') {
- /* This looks like an IPv6 address literal. See if there is an address
+ /* This looks like an IPv6 address literal. See if there is an address
scope. */
- char *percent = strchr(++hostname, '%');
+ char *zoneid;
+ size_t hlen;
+ uc = curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0);
conn->bits.ipv6_ip = TRUE;
- if(percent) {
- unsigned int identifier_offset = 3;
+
+ /* cut off the brackets! */
+ hostname++;
+ hlen = strlen(hostname);
+ hostname[hlen - 1] = 0;
+ if(!uc && zoneid) {
char *endp;
unsigned long scope;
- if(strncmp("%25", percent, 3) != 0) {
- infof(data,
- "Please URL encode %% as %%25, see RFC 6874.\n");
- identifier_offset = 1;
- }
- scope = strtoul(percent + identifier_offset, &endp, 10);
- if(*endp == ']') {
- /* The address scope was well formed. Knock it out of the
- hostname. */
- memmove(percent, endp, strlen(endp) + 1);
+ scope = strtoul(zoneid, &endp, 10);
+ if(!*endp && (scope < UINT_MAX)) {
+ /* A plain number, use it direcly as a scope id. */
conn->scope_id = (unsigned int)scope;
}
+#ifdef HAVE_IF_NAMETOINDEX
else {
/* Zone identifier is not numeric */
-#if defined(HAVE_NET_IF_H) && defined(IFNAMSIZ) && defined(HAVE_IF_NAMETOINDEX)
- char ifname[IFNAMSIZ + 2];
- char *square_bracket;
unsigned int scopeidx = 0;
- strncpy(ifname, percent + identifier_offset, IFNAMSIZ + 2);
- /* Ensure nullbyte termination */
- ifname[IFNAMSIZ + 1] = '\0';
- square_bracket = strchr(ifname, ']');
- if(square_bracket) {
- /* Remove ']' */
- *square_bracket = '\0';
- scopeidx = if_nametoindex(ifname);
- if(scopeidx == 0) {
- infof(data, "Invalid network interface: %s; %s\n", ifname,
- strerror(errno));
- }
- }
- if(scopeidx > 0) {
- char *p = percent + identifier_offset + strlen(ifname);
-
- /* Remove zone identifier from hostname */
- memmove(percent, p, strlen(p) + 1);
- conn->scope_id = scopeidx;
- }
+ scopeidx = if_nametoindex(zoneid);
+ if(!scopeidx)
+ infof(data, "Invalid zoneid id: %s; %s\n", zoneid,
+ strerror(errno));
else
-#endif /* HAVE_NET_IF_H && IFNAMSIZ */
- infof(data, "Invalid IPv6 address format\n");
+ conn->scope_id = scopeidx;
+
}
+#endif /* HAVE_IF_NAMETOINDEX */
+ free(zoneid);
}
- percent = strchr(hostname, ']');
- if(percent)
- /* terminate IPv6 numerical at end bracket */
- *percent = 0;
}
/* make sure the connect struct gets its own copy of the host name */
@@ -2191,6 +2051,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
return CURLE_OK;
}
+
/*
* If we're doing a resumed transfer, we need to setup our stuff
* properly.
@@ -2437,46 +2298,55 @@ static CURLcode parse_proxy(struct Curl_easy *data,
struct connectdata *conn, char *proxy,
curl_proxytype proxytype)
{
- char *prox_portno;
- char *endofprot;
-
- /* We use 'proxyptr' to point to the proxy name from now on... */
- char *proxyptr;
char *portptr;
- char *atsign;
long port = -1;
char *proxyuser = NULL;
char *proxypasswd = NULL;
+ char *host;
bool sockstype;
+ CURLUcode uc;
+ struct proxy_info *proxyinfo;
+ CURLU *uhp = curl_url();
+ CURLcode result = CURLE_OK;
+ char *scheme = NULL;
- /* We do the proxy host string parsing here. We want the host name and the
- * port name. Accept a protocol:// prefix
- */
+ /* When parsing the proxy, allowing non-supported schemes since we have
+ these made up ones for proxies. Guess scheme for URLs without it. */
+ uc = curl_url_set(uhp, CURLUPART_URL, proxy,
+ CURLU_NON_SUPPORT_SCHEME|CURLU_GUESS_SCHEME);
+ if(!uc) {
+ /* parsed okay as a URL */
+ uc = curl_url_get(uhp, CURLUPART_SCHEME, &scheme, 0);
+ if(uc) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
- /* Parse the protocol part if present */
- endofprot = strstr(proxy, "://");
- if(endofprot) {
- proxyptr = endofprot + 3;
- if(checkprefix("https", proxy))
+ if(strcasecompare("https", scheme))
proxytype = CURLPROXY_HTTPS;
- else if(checkprefix("socks5h", proxy))
+ else if(strcasecompare("socks5h", scheme))
proxytype = CURLPROXY_SOCKS5_HOSTNAME;
- else if(checkprefix("socks5", proxy))
+ else if(strcasecompare("socks5", scheme))
proxytype = CURLPROXY_SOCKS5;
- else if(checkprefix("socks4a", proxy))
+ else if(strcasecompare("socks4a", scheme))
proxytype = CURLPROXY_SOCKS4A;
- else if(checkprefix("socks4", proxy) || checkprefix("socks", proxy))
+ else if(strcasecompare("socks4", scheme) ||
+ strcasecompare("socks", scheme))
proxytype = CURLPROXY_SOCKS4;
- else if(checkprefix("http:", proxy))
+ else if(strcasecompare("http", scheme))
; /* leave it as HTTP or HTTP/1.0 */
else {
/* Any other xxx:// reject! */
failf(data, "Unsupported proxy scheme for \'%s\'", proxy);
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto error;
}
}
- else
- proxyptr = proxy; /* No xxx:// head: It's a HTTP proxy */
+ else {
+ failf(data, "Unsupported proxy syntax in \'%s\'", proxy);
+ result = CURLE_COULDNT_RESOLVE_PROXY;
+ goto error;
+ }
#ifdef USE_SSL
if(!(Curl_ssl->supports & SSLSUPP_HTTPS_PROXY))
@@ -2484,93 +2354,44 @@ static CURLcode parse_proxy(struct Curl_easy *data,
if(proxytype == CURLPROXY_HTTPS) {
failf(data, "Unsupported proxy \'%s\', libcurl is built without the "
"HTTPS-proxy support.", proxy);
- return CURLE_NOT_BUILT_IN;
+ result = CURLE_NOT_BUILT_IN;
+ goto error;
}
- sockstype = proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
- proxytype == CURLPROXY_SOCKS5 ||
- proxytype == CURLPROXY_SOCKS4A ||
- proxytype == CURLPROXY_SOCKS4;
-
- /* Is there a username and password given in this proxy url? */
- atsign = strchr(proxyptr, '@');
- if(atsign) {
- CURLcode result =
- Curl_parse_login_details(proxyptr, atsign - proxyptr,
- &proxyuser, &proxypasswd, NULL);
- if(result)
- return result;
- proxyptr = atsign + 1;
- }
+ sockstype =
+ proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
+ proxytype == CURLPROXY_SOCKS5 ||
+ proxytype == CURLPROXY_SOCKS4A ||
+ proxytype == CURLPROXY_SOCKS4;
- /* start scanning for port number at this point */
- portptr = proxyptr;
+ proxyinfo = sockstype ? &conn->socks_proxy : &conn->http_proxy;
+ proxyinfo->proxytype = proxytype;
- /* detect and extract RFC6874-style IPv6-addresses */
- if(*proxyptr == '[') {
- char *ptr = ++proxyptr; /* advance beyond the initial bracket */
- while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '.')))
- ptr++;
- if(*ptr == '%') {
- /* There might be a zone identifier */
- if(strncmp("%25", ptr, 3))
- infof(data, "Please URL encode %% as %%25, see RFC 6874.\n");
- ptr++;
- /* Allow unreserved characters as defined in RFC 3986 */
- while(*ptr && (ISALPHA(*ptr) || ISXDIGIT(*ptr) || (*ptr == '-') ||
- (*ptr == '.') || (*ptr == '_') || (*ptr == '~')))
- ptr++;
+ /* Is there a username and password given in this proxy url? */
+ curl_url_get(uhp, CURLUPART_USER, &proxyuser, CURLU_URLDECODE);
+ curl_url_get(uhp, CURLUPART_PASSWORD, &proxypasswd, CURLU_URLDECODE);
+ if(proxyuser || proxypasswd) {
+ Curl_safefree(proxyinfo->user);
+ proxyinfo->user = proxyuser;
+ Curl_safefree(proxyinfo->passwd);
+ if(!proxypasswd) {
+ proxypasswd = strdup("");
+ if(!proxypasswd) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
}
- if(*ptr == ']')
- /* yeps, it ended nicely with a bracket as well */
- *ptr++ = 0;
- else
- infof(data, "Invalid IPv6 address format\n");
- portptr = ptr;
- /* Note that if this didn't end with a bracket, we still advanced the
- * proxyptr first, but I can't see anything wrong with that as no host
- * name nor a numeric can legally start with a bracket.
- */
+ proxyinfo->passwd = proxypasswd;
+ conn->bits.proxy_user_passwd = TRUE; /* enable it */
}
- /* Get port number off proxy.server.com:1080 */
- prox_portno = strchr(portptr, ':');
- if(prox_portno) {
- char *endp = NULL;
+ curl_url_get(uhp, CURLUPART_PORT, &portptr, 0);
- *prox_portno = 0x0; /* cut off number from host name */
- prox_portno ++;
- /* now set the local port number */
- port = strtol(prox_portno, &endp, 10);
- if((endp && *endp && (*endp != '/') && (*endp != ' ')) ||
- (port < 0) || (port > 65535)) {
- /* meant to detect for example invalid IPv6 numerical addresses without
- brackets: "2a00:fac0:a000::7:13". Accept a trailing slash only
- because we then allow "URL style" with the number followed by a
- slash, used in curl test cases already. Space is also an acceptable
- terminating symbol. */
- infof(data, "No valid port number in proxy string (%s)\n",
- prox_portno);
- }
- else
- conn->port = port;
+ if(portptr) {
+ port = strtol(portptr, NULL, 10);
+ free(portptr);
}
else {
- if(proxyptr[0]=='/') {
- /* If the first character in the proxy string is a slash, fail
- immediately. The following code will otherwise clear the string which
- will lead to code running as if no proxy was set! */
- Curl_safefree(proxyuser);
- Curl_safefree(proxypasswd);
- return CURLE_COULDNT_RESOLVE_PROXY;
- }
-
- /* without a port number after the host name, some people seem to use
- a slash so we strip everything from the first slash */
- atsign = strchr(proxyptr, '/');
- if(atsign)
- *atsign = '\0'; /* cut off path part from host name */
-
if(data->set.proxyport)
/* None given in the proxy string, then get the default one if it is
given */
@@ -2582,57 +2403,32 @@ static CURLcode parse_proxy(struct Curl_easy *data,
port = CURL_DEFAULT_PROXY_PORT;
}
}
-
- if(*proxyptr) {
- struct proxy_info *proxyinfo =
- sockstype ? &conn->socks_proxy : &conn->http_proxy;
- proxyinfo->proxytype = proxytype;
-
- if(proxyuser) {
- /* found user and password, rip them out. note that we are unescaping
- them, as there is otherwise no way to have a username or password
- with reserved characters like ':' in them. */
- Curl_safefree(proxyinfo->user);
- proxyinfo->user = curl_easy_unescape(data, proxyuser, 0, NULL);
- Curl_safefree(proxyuser);
-
- if(!proxyinfo->user) {
- Curl_safefree(proxypasswd);
- return CURLE_OUT_OF_MEMORY;
- }
-
- Curl_safefree(proxyinfo->passwd);
- if(proxypasswd && strlen(proxypasswd) < MAX_CURL_PASSWORD_LENGTH)
- proxyinfo->passwd = curl_easy_unescape(data, proxypasswd, 0, NULL);
- else
- proxyinfo->passwd = strdup("");
- Curl_safefree(proxypasswd);
-
- if(!proxyinfo->passwd)
- return CURLE_OUT_OF_MEMORY;
-
- conn->bits.proxy_user_passwd = TRUE; /* enable it */
- }
-
- if(port >= 0) {
- proxyinfo->port = port;
- if(conn->port < 0 || sockstype || !conn->socks_proxy.host.rawalloc)
- conn->port = port;
- }
-
- /* now, clone the cleaned proxy host name */
- Curl_safefree(proxyinfo->host.rawalloc);
- proxyinfo->host.rawalloc = strdup(proxyptr);
- proxyinfo->host.name = proxyinfo->host.rawalloc;
-
- if(!proxyinfo->host.rawalloc)
- return CURLE_OUT_OF_MEMORY;
+ if(port >= 0) {
+ proxyinfo->port = port;
+ if(conn->port < 0 || sockstype || !conn->socks_proxy.host.rawalloc)
+ conn->port = port;
}
- Curl_safefree(proxyuser);
- Curl_safefree(proxypasswd);
+ /* now, clone the proxy host name */
+ uc = curl_url_get(uhp, CURLUPART_HOST, &host, CURLU_URLDECODE);
+ if(uc) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ Curl_safefree(proxyinfo->host.rawalloc);
+ proxyinfo->host.rawalloc = host;
+ if(host[0] == '[') {
+ /* this is a numerical IPv6, strip off the brackets */
+ size_t len = strlen(host);
+ host[len-1] = 0; /* clear the trailing bracket */
+ host++;
+ }
+ proxyinfo->host.name = host;
- return CURLE_OK;
+ error:
+ free(scheme);
+ curl_url_cleanup(uhp);
+ return result;
}
/*
@@ -2979,7 +2775,7 @@ static CURLcode parse_remote_port(struct Curl_easy *data,
char portbuf[16];
CURLUcode uc;
conn->remote_port = (unsigned short)data->set.use_port;
- snprintf(portbuf, sizeof(portbuf), "%u", conn->remote_port);
+ msnprintf(portbuf, sizeof(portbuf), "%d", conn->remote_port);
uc = curl_url_set(data->state.uh, CURLUPART_PORT, portbuf, 0);
if(uc)
return CURLE_OUT_OF_MEMORY;
@@ -2999,6 +2795,20 @@ static CURLcode override_login(struct Curl_easy *data,
bool user_changed = FALSE;
bool passwd_changed = FALSE;
CURLUcode uc;
+
+ if(data->set.use_netrc == CURL_NETRC_REQUIRED && conn->bits.user_passwd) {
+ /* ignore user+password in the URL */
+ if(*userp) {
+ Curl_safefree(*userp);
+ user_changed = TRUE;
+ }
+ if(*passwdp) {
+ Curl_safefree(*passwdp);
+ passwd_changed = TRUE;
+ }
+ conn->bits.user_passwd = FALSE; /* disable user+password */
+ }
+
if(data->set.str[STRING_USERNAME]) {
free(*userp);
*userp = strdup(data->set.str[STRING_USERNAME]);
@@ -3025,16 +2835,15 @@ static CURLcode override_login(struct Curl_easy *data,
}
conn->bits.netrc = FALSE;
- if(data->set.use_netrc != CURL_NETRC_IGNORED) {
- char *nuser = NULL;
- char *npasswd = NULL;
+ if(data->set.use_netrc != CURL_NETRC_IGNORED &&
+ (!*userp || !**userp || !*passwdp || !**passwdp)) {
+ bool netrc_user_changed = FALSE;
+ bool netrc_passwd_changed = FALSE;
int ret;
- if(data->set.use_netrc == CURL_NETRC_OPTIONAL)
- nuser = *userp; /* to separate otherwise identical machines */
-
ret = Curl_parsenetrc(conn->host.name,
- &nuser, &npasswd,
+ userp, passwdp,
+ &netrc_user_changed, &netrc_passwd_changed,
data->set.str[STRING_NETRC_FILE]);
if(ret > 0) {
infof(data, "Couldn't find host %s in the "
@@ -3051,31 +2860,11 @@ static CURLcode override_login(struct Curl_easy *data,
conn->bits.netrc = TRUE;
conn->bits.user_passwd = TRUE; /* enable user+password */
- if(data->set.use_netrc == CURL_NETRC_OPTIONAL) {
- /* prefer credentials outside netrc */
- if(nuser && !*userp) {
- free(*userp);
- *userp = nuser;
- user_changed = TRUE;
- }
- if(npasswd && !*passwdp) {
- free(*passwdp);
- *passwdp = npasswd;
- passwd_changed = TRUE;
- }
+ if(netrc_user_changed) {
+ user_changed = TRUE;
}
- else {
- /* prefer netrc credentials */
- if(nuser) {
- free(*userp);
- *userp = nuser;
- user_changed = TRUE;
- }
- if(npasswd) {
- free(*passwdp);
- *passwdp = npasswd;
- passwd_changed = TRUE;
- }
+ if(netrc_passwd_changed) {
+ passwd_changed = TRUE;
}
}
}
@@ -3351,6 +3140,34 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data,
conn_to_host = conn_to_host->next;
}
+#ifdef USE_ALTSVC
+ if(data->asi && !host && (port == -1) &&
+ (conn->handler->protocol == CURLPROTO_HTTPS)) {
+ /* no connect_to match, try alt-svc! */
+ const char *nhost;
+ int nport;
+ enum alpnid nalpnid;
+ bool hit;
+ host = conn->host.rawalloc;
+ hit = Curl_altsvc_lookup(data->asi,
+ ALPN_h1, host, conn->remote_port, /* from */
+ &nalpnid, &nhost, &nport /* to */);
+ if(hit) {
+ char *hostd = strdup((char *)nhost);
+ if(!hostd)
+ return CURLE_OUT_OF_MEMORY;
+ conn->conn_to_host.rawalloc = hostd;
+ conn->conn_to_host.name = hostd;
+ conn->bits.conn_to_host = TRUE;
+ conn->conn_to_port = nport;
+ conn->bits.conn_to_port = TRUE;
+ infof(data, "Alt-svc connecting from [%s]%s:%d to [%s]%s:%d\n",
+ Curl_alpnid2str(ALPN_h1), host, conn->remote_port,
+ Curl_alpnid2str(nalpnid), hostd, nport);
+ }
+ }
+#endif
+
return result;
}
@@ -3364,12 +3181,14 @@ static CURLcode resolve_server(struct Curl_easy *data,
CURLcode result = CURLE_OK;
timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+ DEBUGASSERT(conn);
+ DEBUGASSERT(data);
/*************************************************************
* Resolve the name of the server or proxy
*************************************************************/
if(conn->bits.reuse)
/* We're reusing the connection - no need to resolve anything, and
- fix_hostname() was called already in create_conn() for the re-use
+ idnconvert_hostname() was called already in create_conn() for the re-use
case. */
*async = FALSE;
@@ -3424,7 +3243,10 @@ static CURLcode resolve_server(struct Curl_easy *data,
conn->port = conn->remote_port;
/* Resolve target host right on */
- rc = Curl_resolv_timeout(conn, connhost->name, (int)conn->port,
+ conn->hostname_resolve = strdup(connhost->name);
+ if(!conn->hostname_resolve)
+ return CURLE_OUT_OF_MEMORY;
+ rc = Curl_resolv_timeout(conn, conn->hostname_resolve, (int)conn->port,
&hostaddr, timeout_ms);
if(rc == CURLRESOLV_PENDING)
*async = TRUE;
@@ -3445,7 +3267,10 @@ static CURLcode resolve_server(struct Curl_easy *data,
&conn->socks_proxy.host : &conn->http_proxy.host;
/* resolve proxy */
- rc = Curl_resolv_timeout(conn, host->name, (int)conn->port,
+ conn->hostname_resolve = strdup(host->name);
+ if(!conn->hostname_resolve)
+ return CURLE_OUT_OF_MEMORY;
+ rc = Curl_resolv_timeout(conn, conn->hostname_resolve, (int)conn->port,
&hostaddr, timeout_ms);
if(rc == CURLRESOLV_PENDING)
@@ -3475,8 +3300,8 @@ static CURLcode resolve_server(struct Curl_easy *data,
static void reuse_conn(struct connectdata *old_conn,
struct connectdata *conn)
{
- free_fixed_hostname(&old_conn->http_proxy.host);
- free_fixed_hostname(&old_conn->socks_proxy.host);
+ free_idnconverted_hostname(&old_conn->http_proxy.host);
+ free_idnconverted_hostname(&old_conn->socks_proxy.host);
free(old_conn->http_proxy.host.rawalloc);
free(old_conn->socks_proxy.host.rawalloc);
@@ -3520,14 +3345,18 @@ static void reuse_conn(struct connectdata *old_conn,
/* host can change, when doing keepalive with a proxy or if the case is
different this time etc */
- free_fixed_hostname(&conn->host);
- free_fixed_hostname(&conn->conn_to_host);
+ free_idnconverted_hostname(&conn->host);
+ free_idnconverted_hostname(&conn->conn_to_host);
Curl_safefree(conn->host.rawalloc);
Curl_safefree(conn->conn_to_host.rawalloc);
conn->host = old_conn->host;
conn->conn_to_host = old_conn->conn_to_host;
conn->conn_to_port = old_conn->conn_to_port;
conn->remote_port = old_conn->remote_port;
+ Curl_safefree(conn->hostname_resolve);
+
+ conn->hostname_resolve = old_conn->hostname_resolve;
+ old_conn->hostname_resolve = NULL;
/* persist connection info in session handle */
Curl_persistconninfo(conn);
@@ -3545,11 +3374,7 @@ static void reuse_conn(struct connectdata *old_conn,
Curl_safefree(old_conn->http_proxy.passwd);
Curl_safefree(old_conn->socks_proxy.passwd);
Curl_safefree(old_conn->localdev);
-
- Curl_llist_destroy(&old_conn->send_pipe, NULL);
- Curl_llist_destroy(&old_conn->recv_pipe, NULL);
-
- Curl_safefree(old_conn->master_buffer);
+ Curl_llist_destroy(&old_conn->easyq, NULL);
#ifdef USE_UNIX_SOCKETS
Curl_safefree(old_conn->unix_domain_socket);
@@ -3587,6 +3412,7 @@ static CURLcode create_conn(struct Curl_easy *data,
size_t max_total_connections = Curl_multi_max_total_connections(data->multi);
*async = FALSE;
+ *in_connect = NULL;
/*************************************************************
* Check input data
@@ -3677,30 +3503,30 @@ static CURLcode create_conn(struct Curl_easy *data,
goto out;
/*************************************************************
- * IDN-fix the hostnames
+ * IDN-convert the hostnames
*************************************************************/
- result = fix_hostname(conn, &conn->host);
+ result = idnconvert_hostname(conn, &conn->host);
if(result)
goto out;
if(conn->bits.conn_to_host) {
- result = fix_hostname(conn, &conn->conn_to_host);
+ result = idnconvert_hostname(conn, &conn->conn_to_host);
if(result)
goto out;
}
if(conn->bits.httpproxy) {
- result = fix_hostname(conn, &conn->http_proxy.host);
+ result = idnconvert_hostname(conn, &conn->http_proxy.host);
if(result)
goto out;
}
if(conn->bits.socksproxy) {
- result = fix_hostname(conn, &conn->socks_proxy.host);
+ result = idnconvert_hostname(conn, &conn->socks_proxy.host);
if(result)
goto out;
}
/*************************************************************
* Check whether the host and the "connect to host" are equal.
- * Do this after the hostnames have been IDN-fixed.
+ * Do this after the hostnames have been IDN-converted.
*************************************************************/
if(conn->bits.conn_to_host &&
strcasecompare(conn->conn_to_host.name, conn->host.name)) {
@@ -3752,7 +3578,6 @@ static CURLcode create_conn(struct Curl_easy *data,
/* Setup a "faked" transfer that'll do nothing */
if(!result) {
- conn->data = data;
conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; /* we are "connected */
result = Curl_conncache_add_conn(data->state.conn_cache, conn);
@@ -3769,9 +3594,8 @@ static CURLcode create_conn(struct Curl_easy *data,
(void)conn->handler->done(conn, result, FALSE);
goto out;
}
-
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
- -1, NULL); /* no upload */
+ Curl_attach_connnection(data, conn);
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
}
/* since we skip do_init() */
@@ -3857,19 +3681,20 @@ static CURLcode create_conn(struct Curl_easy *data,
/* reuse_fresh is TRUE if we are told to use a new connection by force, but
we only acknowledge this option if this is not a re-used connection
already (which happens due to follow-location or during a HTTP
- authentication phase). */
- if(data->set.reuse_fresh && !data->state.this_is_a_follow)
+ authentication phase). CONNECT_ONLY transfers also refuse reuse. */
+ if((data->set.reuse_fresh && !data->state.this_is_a_follow) ||
+ data->set.connect_only)
reuse = FALSE;
else
reuse = ConnectionExists(data, conn, &conn_temp, &force_reuse, &waitpipe);
/* If we found a reusable connection that is now marked as in use, we may
- still want to open a new connection if we are pipelining. */
- if(reuse && !force_reuse && IsPipeliningPossible(data, conn_temp)) {
- size_t pipelen = conn_temp->send_pipe.size + conn_temp->recv_pipe.size;
- if(pipelen > 0) {
- infof(data, "Found connection %ld, with requests in the pipe (%zu)\n",
- conn_temp->connection_id, pipelen);
+ still want to open a new connection if we are multiplexing. */
+ if(reuse && !force_reuse && IsMultiplexingPossible(data, conn_temp)) {
+ size_t multiplexed = CONN_INUSE(conn_temp);
+ if(multiplexed > 0) {
+ infof(data, "Found connection %ld, with %zu requests on it\n",
+ conn_temp->connection_id, multiplexed);
if(Curl_conncache_bundle_size(conn_temp) < max_host_connections &&
Curl_conncache_size(data) < max_total_connections) {
@@ -3919,7 +3744,7 @@ static CURLcode create_conn(struct Curl_easy *data,
}
if(waitpipe)
- /* There is a connection that *might* become usable for pipelining
+ /* There is a connection that *might* become usable for multiplexing
"soon", and we wait for that */
connections_available = FALSE;
else {
@@ -3933,7 +3758,7 @@ static CURLcode create_conn(struct Curl_easy *data,
/* The bundle is full. Extract the oldest connection. */
conn_candidate = Curl_conncache_extract_bundle(data, bundle);
- Curl_conncache_unlock(conn);
+ Curl_conncache_unlock(data);
if(conn_candidate)
(void)Curl_disconnect(data, conn_candidate,
@@ -3945,7 +3770,7 @@ static CURLcode create_conn(struct Curl_easy *data,
}
}
else
- Curl_conncache_unlock(conn);
+ Curl_conncache_unlock(data);
}
@@ -4028,6 +3853,15 @@ static CURLcode create_conn(struct Curl_easy *data,
*************************************************************/
result = resolve_server(data, conn, async);
+ /* Strip trailing dots. resolve_server copied the name. */
+ strip_trailing_dot(&conn->host);
+ if(conn->bits.httpproxy)
+ strip_trailing_dot(&conn->http_proxy.host);
+ if(conn->bits.socksproxy)
+ strip_trailing_dot(&conn->socks_proxy.host);
+ if(conn->bits.conn_to_host)
+ strip_trailing_dot(&conn->conn_to_host);
+
out:
return result;
}
@@ -4105,11 +3939,11 @@ CURLcode Curl_setup_conn(struct connectdata *conn,
}
CURLcode Curl_connect(struct Curl_easy *data,
- struct connectdata **in_connect,
bool *asyncp,
bool *protocol_done)
{
CURLcode result;
+ struct connectdata *conn;
*asyncp = FALSE; /* assume synchronous resolves by default */
@@ -4119,30 +3953,31 @@ CURLcode Curl_connect(struct Curl_easy *data,
data->req.maxdownload = -1;
/* call the stuff that needs to be called */
- result = create_conn(data, in_connect, asyncp);
+ result = create_conn(data, &conn, asyncp);
if(!result) {
- if(CONN_INUSE(*in_connect))
- /* pipelining */
+ if(CONN_INUSE(conn))
+ /* multiplexed */
*protocol_done = TRUE;
else if(!*asyncp) {
/* DNS resolution is done: that's either because this is a reused
connection, in which case DNS was unnecessary, or because DNS
really did finish already (synch resolver/fast async resolve) */
- result = Curl_setup_conn(*in_connect, protocol_done);
+ result = Curl_setup_conn(conn, protocol_done);
}
}
if(result == CURLE_NO_CONNECTION_AVAILABLE) {
- *in_connect = NULL;
return result;
}
- else if(result && *in_connect) {
+ else if(result && conn) {
/* We're not allowed to return failure with memory left allocated in the
connectdata struct, free those here */
- Curl_disconnect(data, *in_connect, TRUE);
- *in_connect = NULL; /* return a NULL */
+ Curl_disconnect(data, conn, TRUE);
}
+ else if(!result && !data->conn)
+ /* FILE: transfers already have the connection attached */
+ Curl_attach_connnection(data, conn);
return result;
}
diff --git a/Utilities/cmcurl/lib/url.h b/Utilities/cmcurl/lib/url.h
index 095d63833..4db9e8653 100644
--- a/Utilities/cmcurl/lib/url.h
+++ b/Utilities/cmcurl/lib/url.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -52,8 +52,7 @@ void Curl_freeset(struct Curl_easy * data);
void Curl_up_free(struct Curl_easy *data);
CURLcode Curl_uc_to_curlcode(CURLUcode uc);
CURLcode Curl_close(struct Curl_easy *data); /* opposite of curl_open() */
-CURLcode Curl_connect(struct Curl_easy *, struct connectdata **,
- bool *async, bool *protocol_connect);
+CURLcode Curl_connect(struct Curl_easy *, bool *async, bool *protocol_connect);
CURLcode Curl_disconnect(struct Curl_easy *data,
struct connectdata *, bool dead_connection);
CURLcode Curl_protocol_connect(struct connectdata *conn, bool *done);
@@ -72,14 +71,7 @@ int Curl_doing_getsock(struct connectdata *conn,
CURLcode Curl_parse_login_details(const char *login, const size_t len,
char **userptr, char **passwdptr,
char **optionsptr);
-
-int Curl_removeHandleFromPipeline(struct Curl_easy *handle,
- struct curl_llist *pipeline);
-/* remove the specified connection from all (possible) pipelines and related
- queues */
-void Curl_getoff_all_pipelines(struct Curl_easy *data,
- struct connectdata *conn);
-
+void Curl_close_connections(struct Curl_easy *data);
CURLcode Curl_upkeep(struct conncache *conn_cache, void *data);
const struct Curl_handler *Curl_builtin_scheme(const char *scheme);
diff --git a/Utilities/cmcurl/lib/urlapi-int.h b/Utilities/cmcurl/lib/urlapi-int.h
index a57d2e22b..5f059c203 100644
--- a/Utilities/cmcurl/lib/urlapi-int.h
+++ b/Utilities/cmcurl/lib/urlapi-int.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -22,12 +22,16 @@
*
***************************************************************************/
#include "curl_setup.h"
-/* scheme is not URL encoded, the longest libcurl supported ones are 6
- letters */
-#define MAX_SCHEME_LEN 8
+/* scheme is not URL encoded, the longest libcurl supported ones are... */
+#define MAX_SCHEME_LEN 40
bool Curl_is_absolute_url(const char *url, char *scheme, size_t buflen);
char *Curl_concat_url(const char *base, const char *relurl);
size_t Curl_strlen_url(const char *url, bool relative);
void Curl_strcpy_url(char *output, const char *url, bool relative);
+
+#ifdef DEBUGBUILD
+CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname);
+#endif
+
#endif /* HEADER_CURL_URLAPI_INT_H */
diff --git a/Utilities/cmcurl/lib/urlapi.c b/Utilities/cmcurl/lib/urlapi.c
index c53e52343..d07e4f5df 100644
--- a/Utilities/cmcurl/lib/urlapi.c
+++ b/Utilities/cmcurl/lib/urlapi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -56,6 +56,7 @@ struct Curl_URL {
char *password;
char *options; /* IMAP only? */
char *host;
+ char *zoneid; /* for numerical IPv6 addresses */
char *port;
char *path;
char *query;
@@ -74,6 +75,7 @@ static void free_urlhandle(struct Curl_URL *u)
free(u->password);
free(u->options);
free(u->host);
+ free(u->zoneid);
free(u->port);
free(u->path);
free(u->query);
@@ -135,7 +137,7 @@ static bool urlchar_needs_escaping(int c)
* URL encoding should be skipped for host names, otherwise IDN resolution
* will fail.
*/
-size_t Curl_strlen_url(const char *url, bool relative)
+static size_t strlen_url(const char *url, bool relative)
{
const unsigned char *ptr;
size_t newlen = 0;
@@ -177,7 +179,7 @@ size_t Curl_strlen_url(const char *url, bool relative)
* URL encoding should be skipped for host names, otherwise IDN resolution
* will fail.
*/
-void Curl_strcpy_url(char *output, const char *url, bool relative)
+static void strcpy_url(char *output, const char *url, bool relative)
{
/* we must add this with whitespace-replacing */
bool left = TRUE;
@@ -203,7 +205,7 @@ void Curl_strcpy_url(char *output, const char *url, bool relative)
/* FALLTHROUGH */
default:
if(urlchar_needs_escaping(*iptr)) {
- snprintf(optr, 4, "%%%02x", *iptr);
+ msnprintf(optr, 4, "%%%02x", *iptr);
optr += 3;
}
else
@@ -238,7 +240,7 @@ bool Curl_is_absolute_url(const char *url, char *buf, size_t buflen)
#endif
for(i = 0; i < buflen && url[i]; ++i) {
char s = url[i];
- if(s == ':') {
+ if((s == ':') && (url[i + 1] == '/')) {
if(buf)
buf[i] = 0;
return TRUE;
@@ -262,7 +264,7 @@ bool Curl_is_absolute_url(const char *url, char *buf, size_t buflen)
* The returned pointer must be freed by the caller unless NULL
* (returns NULL on out of memory).
*/
-char *Curl_concat_url(const char *base, const char *relurl)
+static char *concat_url(const char *base, const char *relurl)
{
/***
TRY to append this new path to the old URL
@@ -386,7 +388,7 @@ char *Curl_concat_url(const char *base, const char *relurl)
letter we replace each space with %20 while it is replaced with '+'
on the right side of the '?' letter.
*/
- newlen = Curl_strlen_url(useurl, !host_changed);
+ newlen = strlen_url(useurl, !host_changed);
urllen = strlen(url_clone);
@@ -408,7 +410,7 @@ char *Curl_concat_url(const char *base, const char *relurl)
newest[urllen++]='/';
/* then append the new piece on the right side */
- Curl_strcpy_url(&newest[urllen], useurl, !host_changed);
+ strcpy_url(&newest[urllen], useurl, !host_changed);
free(url_clone);
@@ -488,19 +490,40 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u,
return result;
}
-static CURLUcode parse_port(struct Curl_URL *u, char *hostname)
+UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname)
{
- char *portptr;
+ char *portptr = NULL;
char endbracket;
int len;
- if((1 == sscanf(hostname, "[%*45[0123456789abcdefABCDEF:.%%]%c%n",
- &endbracket, &len)) &&
- (']' == endbracket)) {
- /* this is a RFC2732-style specified IP-address */
- portptr = &hostname[len];
- if (*portptr != ':')
+ /*
+ * Find the end of an IPv6 address, either on the ']' ending bracket or
+ * a percent-encoded zone index.
+ */
+ if(1 == sscanf(hostname, "[%*45[0123456789abcdefABCDEF:.]%c%n",
+ &endbracket, &len)) {
+ if(']' == endbracket)
+ portptr = &hostname[len];
+ else if('%' == endbracket) {
+ int zonelen = len;
+ if(1 == sscanf(hostname + zonelen, "%*[^]]%c%n", &endbracket, &len)) {
+ if(']' != endbracket)
+ return CURLUE_MALFORMED_INPUT;
+ portptr = &hostname[--zonelen + len + 1];
+ }
+ else
+ return CURLUE_MALFORMED_INPUT;
+ }
+ else
return CURLUE_MALFORMED_INPUT;
+
+ /* this is a RFC2732-style specified IP-address */
+ if(portptr && *portptr) {
+ if(*portptr != ':')
+ return CURLUE_MALFORMED_INPUT;
+ }
+ else
+ portptr = NULL;
}
else
portptr = strchr(hostname, ':');
@@ -510,6 +533,14 @@ static CURLUcode parse_port(struct Curl_URL *u, char *hostname)
long port;
char portbuf[7];
+ /* Browser behavior adaptation. If there's a colon with no digits after,
+ just cut off the name there which makes us ignore the colon and just
+ use the default port. Firefox, Chrome and Safari all do that. */
+ if(!portptr[1]) {
+ *portptr = '\0';
+ return CURLUE_OK;
+ }
+
if(!ISDIGIT(portptr[1]))
return CURLUE_BAD_PORT_NUMBER;
@@ -523,22 +554,14 @@ static CURLUcode parse_port(struct Curl_URL *u, char *hostname)
if(rest[0])
return CURLUE_BAD_PORT_NUMBER;
- if(rest != &portptr[1]) {
- *portptr++ = '\0'; /* cut off the name there */
- *rest = 0;
- /* generate a new to get rid of leading zeroes etc */
- snprintf(portbuf, sizeof(portbuf), "%ld", port);
- u->portnum = port;
- u->port = strdup(portbuf);
- if(!u->port)
- return CURLUE_OUT_OF_MEMORY;
- }
- else {
- /* Browser behavior adaptation. If there's a colon with no digits after,
- just cut off the name there which makes us ignore the colon and just
- use the default port. Firefox and Chrome both do that. */
- *portptr = '\0';
- }
+ *portptr++ = '\0'; /* cut off the name there */
+ *rest = 0;
+ /* generate a new port number string to get rid of leading zeroes etc */
+ msnprintf(portbuf, sizeof(portbuf), "%ld", port);
+ u->portnum = port;
+ u->port = strdup(portbuf);
+ if(!u->port)
+ return CURLUE_OUT_OF_MEMORY;
}
return CURLUE_OK;
@@ -547,15 +570,15 @@ static CURLUcode parse_port(struct Curl_URL *u, char *hostname)
/* scan for byte values < 31 or 127 */
static CURLUcode junkscan(char *part)
{
- char badbytes[]={
- /* */ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
- 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
- 0x7f,
- 0x00 /* zero terminate */
- };
if(part) {
+ static const char badbytes[]={
+ /* */ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x7f,
+ 0x00 /* zero terminate */
+ };
size_t n = strlen(part);
size_t nfine = strcspn(part, badbytes);
if(nfine != n)
@@ -566,25 +589,45 @@ static CURLUcode junkscan(char *part)
return CURLUE_OK;
}
-static CURLUcode hostname_check(char *hostname, unsigned int flags)
+static CURLUcode hostname_check(struct Curl_URL *u, char *hostname)
{
const char *l = NULL; /* accepted characters */
size_t len;
size_t hlen = strlen(hostname);
- (void)flags;
if(hostname[0] == '[') {
hostname++;
- l = "0123456789abcdefABCDEF::.%";
+ l = "0123456789abcdefABCDEF::.";
hlen -= 2;
}
if(l) {
/* only valid letters are ok */
len = strspn(hostname, l);
- if(hlen != len)
- /* hostname with bad content */
- return CURLUE_MALFORMED_INPUT;
+ if(hlen != len) {
+ if(hostname[len] == '%') {
+ /* this could now be '%[zone id]' */
+ char zoneid[16];
+ int i = 0;
+ char *h = &hostname[len + 1];
+ /* pass '25' if present and is a url encoded percent sign */
+ if(!strncmp(h, "25", 2) && h[2] && (h[2] != ']'))
+ h += 2;
+ while(*h && (*h != ']') && (i < 15))
+ zoneid[i++] = *h++;
+ if(!i || (']' != *h))
+ return CURLUE_MALFORMED_INPUT;
+ zoneid[i] = 0;
+ u->zoneid = strdup(zoneid);
+ if(!u->zoneid)
+ return CURLUE_OUT_OF_MEMORY;
+ hostname[len] = ']'; /* insert end bracket */
+ hostname[len + 1] = 0; /* terminate the hostname */
+ }
+ else
+ return CURLUE_MALFORMED_INPUT;
+ /* hostname is fine */
+ }
}
else {
/* letters from the second string is not ok */
@@ -593,6 +636,8 @@ static CURLUcode hostname_check(char *hostname, unsigned int flags)
/* hostname with bad content */
return CURLUE_MALFORMED_INPUT;
}
+ if(!hostname[0])
+ return CURLUE_NO_HOST;
return CURLUE_OK;
}
@@ -607,7 +652,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
char *fragment = NULL;
CURLUcode result;
bool url_has_scheme = FALSE;
- char schemebuf[MAX_SCHEME_LEN];
+ char schemebuf[MAX_SCHEME_LEN + 1];
char *schemep = NULL;
size_t schemelen = 0;
size_t urllen;
@@ -621,6 +666,10 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
************************************************************/
/* allocate scratch area */
urllen = strlen(url);
+ if(urllen > CURL_MAX_INPUT_LENGTH)
+ /* excessive input length */
+ return CURLUE_MALFORMED_INPUT;
+
path = u->scratch = malloc(urllen * 2 + 2);
if(!path)
return CURLUE_OUT_OF_MEMORY;
@@ -827,11 +876,11 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
if(result)
return result;
- result = parse_port(u, hostname);
+ result = Curl_parse_port(u, hostname);
if(result)
return result;
- result = hostname_check(hostname, flags);
+ result = hostname_check(u, hostname);
if(result)
return result;
@@ -840,7 +889,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
return CURLUE_OUT_OF_MEMORY;
}
- if(query && query[0]) {
+ if(query) {
u->query = strdup(query);
if(!u->query)
return CURLUE_OUT_OF_MEMORY;
@@ -950,6 +999,9 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
ptr = u->host;
ifmissing = CURLUE_NO_HOST;
break;
+ case CURLUPART_ZONEID:
+ ptr = u->zoneid;
+ break;
case CURLUPART_PORT:
ptr = u->port;
ifmissing = CURLUE_NO_PORT;
@@ -960,7 +1012,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
const struct Curl_handler *h =
Curl_builtin_scheme(u->scheme);
if(h) {
- snprintf(portbuf, sizeof(portbuf), "%ld", h->defport);
+ msnprintf(portbuf, sizeof(portbuf), "%ld", h->defport);
ptr = portbuf;
}
}
@@ -996,6 +1048,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
char *scheme;
char *options = u->options;
char *port = u->port;
+ char *allochost = NULL;
if(u->scheme && strcasecompare("file", u->scheme)) {
url = aprintf("file://%s%s%s",
u->path,
@@ -1019,7 +1072,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
/* there's no stored port number, but asked to deliver
a default one for the scheme */
if(h) {
- snprintf(portbuf, sizeof(portbuf), "%ld", h->defport);
+ msnprintf(portbuf, sizeof(portbuf), "%ld", h->defport);
port = portbuf;
}
}
@@ -1034,6 +1087,18 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
if(h && !(h->flags & PROTOPT_URLOPTIONS))
options = NULL;
+ if((u->host[0] == '[') && u->zoneid) {
+ /* make it '[ host %25 zoneid ]' */
+ size_t hostlen = strlen(u->host);
+ size_t alen = hostlen + 3 + strlen(u->zoneid) + 1;
+ allochost = malloc(alen);
+ if(!allochost)
+ return CURLUE_OUT_OF_MEMORY;
+ memcpy(allochost, u->host, hostlen - 1);
+ msnprintf(&allochost[hostlen - 1], alen - hostlen + 1,
+ "%%25%s]", u->zoneid);
+ }
+
url = aprintf("%s://%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
scheme,
u->user ? u->user : "",
@@ -1042,24 +1107,25 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
options ? ";" : "",
options ? options : "",
(u->user || u->password || options) ? "@": "",
- u->host,
+ allochost ? allochost : u->host,
port ? ":": "",
port ? port : "",
(u->path && (u->path[0] != '/')) ? "/": "",
u->path ? u->path : "/",
- u->query? "?": "",
- u->query? u->query : "",
+ (u->query && u->query[0]) ? "?": "",
+ (u->query && u->query[0]) ? u->query : "",
u->fragment? "#": "",
u->fragment? u->fragment : "");
+ free(allochost);
}
if(!url)
return CURLUE_OUT_OF_MEMORY;
*part = url;
return CURLUE_OK;
- break;
}
default:
ptr = NULL;
+ break;
}
if(ptr) {
*part = strdup(ptr);
@@ -1099,6 +1165,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
bool plusencode = FALSE;
bool urlskipslash = FALSE;
bool appendquery = FALSE;
+ bool equalsencode = FALSE;
if(!u)
return CURLUE_BAD_HANDLE;
@@ -1122,7 +1189,11 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
case CURLUPART_HOST:
storep = &u->host;
break;
+ case CURLUPART_ZONEID:
+ storep = &u->zoneid;
+ break;
case CURLUPART_PORT:
+ u->portnum = 0;
storep = &u->port;
break;
case CURLUPART_PATH:
@@ -1146,6 +1217,9 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
switch(what) {
case CURLUPART_SCHEME:
+ if(strlen(part) > MAX_SCHEME_LEN)
+ /* too long */
+ return CURLUE_MALFORMED_INPUT;
if(!(flags & CURLU_NON_SUPPORT_SCHEME) &&
/* verify that it is a fine scheme */
!Curl_builtin_scheme(part))
@@ -1164,14 +1238,25 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
break;
case CURLUPART_HOST:
storep = &u->host;
+ free(u->zoneid);
+ u->zoneid = NULL;
+ break;
+ case CURLUPART_ZONEID:
+ storep = &u->zoneid;
break;
case CURLUPART_PORT:
+ {
+ char *endp;
urlencode = FALSE; /* never */
- port = strtol(part, NULL, 10); /* Port number must be decimal */
+ port = strtol(part, &endp, 10); /* Port number must be decimal */
if((port <= 0) || (port > 0xffff))
return CURLUE_BAD_PORT_NUMBER;
+ if(*endp)
+ /* weirdly provided number, not good! */
+ return CURLUE_MALFORMED_INPUT;
storep = &u->port;
- break;
+ }
+ break;
case CURLUPART_PATH:
urlskipslash = TRUE;
storep = &u->path;
@@ -1179,6 +1264,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
case CURLUPART_QUERY:
plusencode = urlencode;
appendquery = (flags & CURLU_APPENDQUERY)?1:0;
+ equalsencode = appendquery;
storep = &u->query;
break;
case CURLUPART_FRAGMENT:
@@ -1196,7 +1282,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
char *redired_url;
CURLU *handle2;
- if(Curl_is_absolute_url(part, NULL, MAX_SCHEME_LEN)) {
+ if(Curl_is_absolute_url(part, NULL, MAX_SCHEME_LEN + 1)) {
handle2 = curl_url();
if(!handle2)
return CURLUE_OUT_OF_MEMORY;
@@ -1223,7 +1309,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
}
/* apply the relative part to create a new URL */
- redired_url = Curl_concat_url(oldurl, part);
+ redired_url = concat_url(oldurl, part);
free(oldurl);
if(!redired_url)
return CURLUE_OUT_OF_MEMORY;
@@ -1249,8 +1335,12 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
const char *newp = part;
size_t nalloc = strlen(part);
+ if(nalloc > CURL_MAX_INPUT_LENGTH)
+ /* excessive input length */
+ return CURLUE_MALFORMED_INPUT;
+
if(urlencode) {
- const char *i;
+ const unsigned char *i;
char *o;
bool free_part = FALSE;
char *enc = malloc(nalloc * 3 + 1); /* for worst case! */
@@ -1258,7 +1348,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
return CURLUE_OUT_OF_MEMORY;
if(plusencode) {
/* space to plus */
- i = part;
+ i = (const unsigned char *)part;
for(o = enc; *i; ++o, ++i)
*o = (*i == ' ') ? '+' : *i;
*o = 0; /* zero terminate */
@@ -1269,16 +1359,19 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
}
free_part = TRUE;
}
- for(i = part, o = enc; *i; i++) {
+ for(i = (const unsigned char *)part, o = enc; *i; i++) {
if(Curl_isunreserved(*i) ||
((*i == '/') && urlskipslash) ||
- ((*i == '=') && appendquery) ||
+ ((*i == '=') && equalsencode) ||
((*i == '+') && plusencode)) {
+ if((*i == '=') && equalsencode)
+ /* only skip the first equals sign */
+ equalsencode = FALSE;
*o = *i;
o++;
}
else {
- snprintf(o, 4, "%%%02x", *i);
+ msnprintf(o, 4, "%%%02x", *i);
o += 3;
}
}
@@ -1329,6 +1422,13 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
}
}
+ if(what == CURLUPART_HOST) {
+ if(hostname_check(u, (char *)newp)) {
+ free((char *)newp);
+ return CURLUE_MALFORMED_INPUT;
+ }
+ }
+
free(*storep);
*storep = (char *)newp;
}
diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h
index 11a6a22c6..d759592d9 100644
--- a/Utilities/cmcurl/lib/urldata.h
+++ b/Utilities/cmcurl/lib/urldata.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -77,7 +77,11 @@
/* Default FTP/IMAP etc response timeout in milliseconds.
Symbian OS panics when given a timeout much greater than 1/2 hour.
*/
-#define RESP_TIMEOUT (1800*1000)
+#define RESP_TIMEOUT (120*1000)
+
+/* Max string intput length is a precaution against abuse and to detect junk
+ input easier and better. */
+#define CURL_MAX_INPUT_LENGTH 8000000
#include "cookie.h"
#include "psl.h"
@@ -129,12 +133,14 @@ typedef ssize_t (Curl_recv)(struct connectdata *conn, /* connection data */
#ifdef HAVE_GSSAPI
# ifdef HAVE_GSSGNU
# include <gss.h>
-# elif defined HAVE_GSSMIT
+# elif defined HAVE_GSSAPI_GSSAPI_H
# include <gssapi/gssapi.h>
-# include <gssapi/gssapi_generic.h>
# else
# include <gssapi.h>
# endif
+# ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H
+# include <gssapi/gssapi_generic.h>
+# endif
#endif
#ifdef HAVE_LIBSSH2_H
@@ -142,10 +148,6 @@ typedef ssize_t (Curl_recv)(struct connectdata *conn, /* connection data */
#include <libssh2_sftp.h>
#endif /* HAVE_LIBSSH2_H */
-
-/* The "master buffer" is for HTTP pipelining */
-#define MASTERBUF_SIZE 16384
-
/* Initial size of the buffer to store headers in, it'll be enlarged in case
of need. */
#define HEADERSIZE 256
@@ -154,13 +156,16 @@ typedef ssize_t (Curl_recv)(struct connectdata *conn, /* connection data */
#define GOOD_EASY_HANDLE(x) \
((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER))
+/* the type we use for storing a single boolean bit */
+typedef unsigned int bit;
+
#ifdef HAVE_GSSAPI
/* Types needed for krb5-ftp connections */
struct krb5buffer {
void *data;
size_t size;
size_t index;
- int eof_flag;
+ bit eof_flag:1;
};
enum protection_level {
@@ -198,21 +203,17 @@ struct ssl_connect_data {
/* Use ssl encrypted communications TRUE/FALSE, not necessarily using it atm
but at least asked to or meaning to use it. See 'state' for the exact
current state of the connection. */
- bool use;
ssl_connection_state state;
ssl_connect_state connecting_state;
#if defined(USE_SSL)
struct ssl_backend_data *backend;
#endif
+ bit use:1;
};
struct ssl_primary_config {
long version; /* what version the client wants to use */
long version_max; /* max supported version the client wants to use*/
- bool verifypeer; /* set TRUE if this is desired */
- bool verifyhost; /* set TRUE if CN/SAN must match hostname */
- bool verifystatus; /* set TRUE if certificate status must be checked */
- bool sessionid; /* cache session IDs or not */
char *CApath; /* certificate dir (doesn't work on windows) */
char *CAfile; /* certificate to verify peer against */
char *clientcert;
@@ -220,32 +221,33 @@ struct ssl_primary_config {
char *egdsocket; /* path to file containing the EGD daemon socket */
char *cipher_list; /* list of ciphers to use */
char *cipher_list13; /* list of TLS 1.3 cipher suites to use */
+ bit verifypeer:1; /* set TRUE if this is desired */
+ bit verifyhost:1; /* set TRUE if CN/SAN must match hostname */
+ bit verifystatus:1; /* set TRUE if certificate status must be checked */
+ bit sessionid:1; /* cache session IDs or not */
};
struct ssl_config_data {
struct ssl_primary_config primary;
- bool enable_beast; /* especially allow this flaw for interoperability's
- sake*/
- bool no_revoke; /* disable SSL certificate revocation checks */
long certverifyresult; /* result from the certificate verification */
char *CRLfile; /* CRL to check certificate revocation */
char *issuercert;/* optional issuer certificate filename */
curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */
void *fsslctxp; /* parameter for call back */
- bool certinfo; /* gather lots of certificate info */
- bool falsestart;
-
char *cert; /* client certificate file name */
char *cert_type; /* format for certificate (default: PEM)*/
char *key; /* private key file name */
char *key_type; /* format for private key (default: PEM) */
char *key_passwd; /* plain text private key password */
-
#ifdef USE_TLS_SRP
char *username; /* TLS username (for, e.g., SRP) */
char *password; /* TLS password (for, e.g., SRP) */
enum CURL_TLSAUTH authtype; /* TLS authentication type (default SRP) */
#endif
+ bit certinfo:1; /* gather lots of certificate info */
+ bit falsestart:1;
+ bit enable_beast:1; /* allow this flaw for interoperability's sake*/
+ bit no_revoke:1; /* disable SSL certificate revocation checks */
};
struct ssl_general_config {
@@ -284,12 +286,12 @@ struct digestdata {
char *cnonce;
char *realm;
int algo;
- bool stale; /* set true for re-negotiation */
char *opaque;
char *qop;
char *algorithm;
int nc; /* nounce count */
- bool userhash;
+ bit stale:1; /* set true for re-negotiation */
+ bit userhash:1;
#endif
};
@@ -301,6 +303,14 @@ typedef enum {
NTLMSTATE_LAST
} curlntlm;
+typedef enum {
+ GSS_AUTHNONE,
+ GSS_AUTHRECV,
+ GSS_AUTHSENT,
+ GSS_AUTHDONE,
+ GSS_AUTHSUCC
+} curlnegotiate;
+
#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
#include <iconv.h>
#endif
@@ -326,8 +336,13 @@ struct kerberos5data {
/* Struct used for NTLM challenge-response authentication */
#if defined(USE_NTLM)
struct ntlmdata {
- curlntlm state;
#ifdef USE_WINDOWS_SSPI
+/* The sslContext is used for the Schannel bindings. The
+ * api is available on the Windows 7 SDK and later.
+ */
+#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
+ CtxtHandle *sslContext;
+#endif
CredHandle *credentials;
CtxtHandle *context;
SEC_WINNT_AUTH_IDENTITY identity;
@@ -346,11 +361,9 @@ struct ntlmdata {
};
#endif
+/* Struct used for Negotiate (SPNEGO) authentication */
#ifdef USE_SPNEGO
struct negotiatedata {
- /* When doing Negotiate (SPNEGO) auth, we first need to send a token
- and then validate the received one. */
- enum { GSS_AUTHNONE, GSS_AUTHRECV, GSS_AUTHSENT } state;
#ifdef HAVE_GSSAPI
OM_uint32 status;
gss_ctx_id_t context;
@@ -358,6 +371,9 @@ struct negotiatedata {
gss_buffer_desc output_token;
#else
#ifdef USE_WINDOWS_SSPI
+#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
+ CtxtHandle *sslContext;
+#endif
DWORD status;
CredHandle *credentials;
CtxtHandle *context;
@@ -369,6 +385,10 @@ struct negotiatedata {
size_t output_token_length;
#endif
#endif
+ bool noauthpersist;
+ bool havenoauthpersist;
+ bool havenegdata;
+ bool havemultiplerequests;
};
#endif
@@ -378,69 +398,67 @@ struct negotiatedata {
*/
struct ConnectBits {
/* always modify bits.close with the connclose() and connkeep() macros! */
- bool close; /* if set, we close the connection after this request */
- bool reuse; /* if set, this is a re-used connection */
- bool conn_to_host; /* if set, this connection has a "connect to host"
- that overrides the host in the URL */
- bool conn_to_port; /* if set, this connection has a "connect to port"
- that overrides the port in the URL (remote port) */
- bool proxy; /* if set, this transfer is done through a proxy - any type */
- bool httpproxy; /* if set, this transfer is done through a http proxy */
- bool socksproxy; /* if set, this transfer is done through a socks proxy */
- bool user_passwd; /* do we use user+password for this connection? */
- bool proxy_user_passwd; /* user+password for the proxy? */
- bool ipv6_ip; /* we communicate with a remote site specified with pure IPv6
- IP address */
- bool ipv6; /* we communicate with a site using an IPv6 address */
-
- bool do_more; /* this is set TRUE if the ->curl_do_more() function is
- supposed to be called, after ->curl_do() */
- bool tcpconnect[2]; /* the TCP layer (or similar) is connected, this is set
- the first time on the first connect function call */
- bool protoconnstart;/* the protocol layer has STARTED its operation after
- the TCP layer connect */
-
- bool retry; /* this connection is about to get closed and then
- re-attempted at another connection. */
- bool tunnel_proxy; /* if CONNECT is used to "tunnel" through the proxy.
- This is implicit when SSL-protocols are used through
- proxies, but can also be enabled explicitly by
- apps */
- bool authneg; /* TRUE when the auth phase has started, which means
- that we are creating a request with an auth header,
- but it is not the final request in the auth
- negotiation. */
- bool rewindaftersend;/* TRUE when the sending couldn't be stopped even
- though it will be discarded. When the whole send
- operation is done, we must call the data rewind
- callback. */
- bool ftp_use_epsv; /* As set with CURLOPT_FTP_USE_EPSV, but if we find out
- EPSV doesn't work we disable it for the forthcoming
- requests */
-
- bool ftp_use_eprt; /* As set with CURLOPT_FTP_USE_EPRT, but if we find out
- EPRT doesn't work we disable it for the forthcoming
- requests */
- bool ftp_use_data_ssl; /* Enabled SSL for the data connection */
- bool netrc; /* name+password provided by netrc */
- bool userpwd_in_url; /* name+password found in url */
- bool stream_was_rewound; /* Indicates that the stream was rewound after a
- request read past the end of its response byte
- boundary */
- bool proxy_connect_closed; /* set true if a proxy disconnected the
- connection in a CONNECT request with auth, so
- that libcurl should reconnect and continue. */
- bool bound; /* set true if bind() has already been done on this socket/
- connection */
- bool type_set; /* type= was used in the URL */
- bool multiplex; /* connection is multiplexed */
-
- bool tcp_fastopen; /* use TCP Fast Open */
- bool tls_enable_npn; /* TLS NPN extension? */
- bool tls_enable_alpn; /* TLS ALPN extension? */
bool proxy_ssl_connected[2]; /* TRUE when SSL initialization for HTTPS proxy
is complete */
- bool socksproxy_connecting; /* connecting through a socks proxy */
+ bool tcpconnect[2]; /* the TCP layer (or similar) is connected, this is set
+ the first time on the first connect function call */
+ bit close:1; /* if set, we close the connection after this request */
+ bit reuse:1; /* if set, this is a re-used connection */
+ bit conn_to_host:1; /* if set, this connection has a "connect to host"
+ that overrides the host in the URL */
+ bit conn_to_port:1; /* if set, this connection has a "connect to port"
+ that overrides the port in the URL (remote port) */
+ bit proxy:1; /* if set, this transfer is done through a proxy - any type */
+ bit httpproxy:1; /* if set, this transfer is done through a http proxy */
+ bit socksproxy:1; /* if set, this transfer is done through a socks proxy */
+ bit user_passwd:1; /* do we use user+password for this connection? */
+ bit proxy_user_passwd:1; /* user+password for the proxy? */
+ bit ipv6_ip:1; /* we communicate with a remote site specified with pure IPv6
+ IP address */
+ bit ipv6:1; /* we communicate with a site using an IPv6 address */
+ bit do_more:1; /* this is set TRUE if the ->curl_do_more() function is
+ supposed to be called, after ->curl_do() */
+ bit protoconnstart:1;/* the protocol layer has STARTED its operation after
+ the TCP layer connect */
+ bit retry:1; /* this connection is about to get closed and then
+ re-attempted at another connection. */
+ bit tunnel_proxy:1; /* if CONNECT is used to "tunnel" through the proxy.
+ This is implicit when SSL-protocols are used through
+ proxies, but can also be enabled explicitly by
+ apps */
+ bit authneg:1; /* TRUE when the auth phase has started, which means
+ that we are creating a request with an auth header,
+ but it is not the final request in the auth
+ negotiation. */
+ bit rewindaftersend:1;/* TRUE when the sending couldn't be stopped even
+ though it will be discarded. When the whole send
+ operation is done, we must call the data rewind
+ callback. */
+#ifndef CURL_DISABLE_FTP
+ bit ftp_use_epsv:1; /* As set with CURLOPT_FTP_USE_EPSV, but if we find out
+ EPSV doesn't work we disable it for the forthcoming
+ requests */
+ bit ftp_use_eprt:1; /* As set with CURLOPT_FTP_USE_EPRT, but if we find out
+ EPRT doesn't work we disable it for the forthcoming
+ requests */
+ bit ftp_use_data_ssl:1; /* Enabled SSL for the data connection */
+#endif
+ bit netrc:1; /* name+password provided by netrc */
+ bit userpwd_in_url:1; /* name+password found in url */
+ bit stream_was_rewound:1; /* The stream was rewound after a request read
+ past the end of its response byte boundary */
+ bit proxy_connect_closed:1; /* TRUE if a proxy disconnected the connection
+ in a CONNECT request with auth, so that
+ libcurl should reconnect and continue. */
+ bit bound:1; /* set true if bind() has already been done on this socket/
+ connection */
+ bit type_set:1; /* type= was used in the URL */
+ bit multiplex:1; /* connection is multiplexed */
+ bit tcp_fastopen:1; /* use TCP Fast Open */
+ bit tls_enable_npn:1; /* TLS NPN extension? */
+ bit tls_enable_alpn:1; /* TLS ALPN extension? */
+ bit socksproxy_connecting:1; /* connecting through a socks proxy */
+ bit connect_only:1;
};
struct hostname {
@@ -467,14 +485,13 @@ struct hostname {
#define KEEP_RECVBITS (KEEP_RECV | KEEP_RECV_HOLD | KEEP_RECV_PAUSE)
#define KEEP_SENDBITS (KEEP_SEND | KEEP_SEND_HOLD | KEEP_SEND_PAUSE)
-
struct Curl_async {
char *hostname;
int port;
struct Curl_dns_entry *dns;
- bool done; /* set TRUE when the lookup is complete */
int status; /* if done is TRUE, this is the status from the callback */
void *os_specific; /* 'struct thread_data' for Windows */
+ bit done:1; /* set TRUE when the lookup is complete */
};
#define FIRSTSOCKET 0
@@ -532,25 +549,21 @@ struct dohdata {
*/
struct SingleRequest {
curl_off_t size; /* -1 if unknown at this point */
- curl_off_t *bytecountp; /* return number of bytes read or NULL */
-
curl_off_t maxdownload; /* in bytes, the maximum amount of data to fetch,
-1 means unlimited */
- curl_off_t *writebytecountp; /* return number of bytes written or NULL */
-
curl_off_t bytecount; /* total number of bytes read */
curl_off_t writebytecount; /* number of bytes written */
- long headerbytecount; /* only count received headers */
- long deductheadercount; /* this amount of bytes doesn't count when we check
- if anything has been transferred at the end of a
- connection. We use this counter to make only a
- 100 reply (without a following second response
- code) result in a CURLE_GOT_NOTHING error code */
+ curl_off_t headerbytecount; /* only count received headers */
+ curl_off_t deductheadercount; /* this amount of bytes doesn't count when we
+ check if anything has been transferred at
+ the end of a connection. We use this
+ counter to make only a 100 reply (without a
+ following second response code) result in a
+ CURLE_GOT_NOTHING error code */
struct curltime start; /* transfer started at this time */
struct curltime now; /* current time */
- bool header; /* incoming data has HTTP header */
enum {
HEADER_NORMAL, /* no bad header at all */
HEADER_PARTHEADER, /* part of the chunk is a bad header, the rest
@@ -566,7 +579,6 @@ struct SingleRequest {
char *str_start; /* within buf */
char *end_ptr; /* within buf */
char *p; /* within headerbuff */
- bool content_range; /* set TRUE if Content-Range: was found */
curl_off_t offset; /* possible resume offset read from the
Content-Range: header */
int httpcode; /* error code from the 'HTTP/1.? XXX' or
@@ -579,19 +591,8 @@ struct SingleRequest {
/* See sec 3.5, RFC2616. */
time_t timeofdoc;
long bodywrites;
-
char *buf;
- curl_socket_t maxfd;
-
int keepon;
-
- bool upload_done; /* set to TRUE when doing chunked transfer-encoding upload
- and we're uploading the last chunk */
-
- bool ignorebody; /* we read a response-body but we ignore it! */
- bool ignorecl; /* This HTTP response has no body so we ignore the Content-
- Length: header */
-
char *location; /* This points to an allocated version of the Location:
header data */
char *newurl; /* Set to the new URL to use when a redirect or a retry is
@@ -601,24 +602,30 @@ struct SingleRequest {
still left in the buffer, aimed for upload. */
ssize_t upload_present;
- /* 'upload_fromhere' is used as a read-pointer when we uploaded parts of a
- buffer, so the next read should read from where this pointer points to,
- and the 'upload_present' contains the number of bytes available at this
- position */
+ /* 'upload_fromhere' is used as a read-pointer when we uploaded parts of a
+ buffer, so the next read should read from where this pointer points to,
+ and the 'upload_present' contains the number of bytes available at this
+ position */
char *upload_fromhere;
-
- bool chunk; /* if set, this is a chunked transfer-encoding */
- bool upload_chunky; /* set TRUE if we are doing chunked transfer-encoding
- on upload */
- bool getheader; /* TRUE if header parsing is wanted */
-
- bool forbidchunk; /* used only to explicitly forbid chunk-upload for
- specific upload buffers. See readmoredata() in
- http.c for details. */
-
void *protop; /* Allocated protocol-specific data. Each protocol
handler makes sure this points to data it needs. */
+#ifndef CURL_DISABLE_DOH
struct dohdata doh; /* DoH specific data for this request */
+#endif
+ bit header:1; /* incoming data has HTTP header */
+ bit content_range:1; /* set TRUE if Content-Range: was found */
+ bit upload_done:1; /* set to TRUE when doing chunked transfer-encoding
+ upload and we're uploading the last chunk */
+ bit ignorebody:1; /* we read a response-body but we ignore it! */
+ bit ignorecl:1; /* This HTTP response has no body so we ignore the
+ Content-Length: header */
+ bit chunk:1; /* if set, this is a chunked transfer-encoding */
+ bit upload_chunky:1; /* set TRUE if we are doing chunked transfer-encoding
+ on upload */
+ bit getheader:1; /* TRUE if header parsing is wanted */
+ bit forbidchunk:1; /* used only to explicitly forbid chunk-upload for
+ specific upload buffers. See readmoredata() in http.c
+ for details. */
};
/*
@@ -766,13 +773,13 @@ struct http_connect_state {
char *line_start;
char *ptr; /* where to store more data */
curl_off_t cl; /* size of content to read and ignore */
- bool chunked_encoding;
enum {
TUNNEL_INIT, /* init/default/no tunnel state */
TUNNEL_CONNECT, /* CONNECT has been sent off */
TUNNEL_COMPLETE /* CONNECT response received completely */
} tunnel_state;
- bool close_connection;
+ bit chunked_encoding:1;
+ bit close_connection:1;
};
/*
@@ -796,11 +803,10 @@ struct connectdata {
void *closesocket_client;
/* This is used by the connection cache logic. If this returns TRUE, this
- handle is being used by one or more easy handles and can only used by any
+ handle is still used by one or more easy handles and can only used by any
other easy handle without careful consideration (== only for
- pipelining/multiplexing) and it cannot be used by another multi
- handle! */
-#define CONN_INUSE(c) ((c)->send_pipe.size + (c)->recv_pipe.size)
+ multiplexing) and it cannot be used by another multi handle! */
+#define CONN_INUSE(c) ((c)->easyq.size)
/**** Fields set when inited and not modified again */
long connection_id; /* Contains a unique number to make it easier to
@@ -828,6 +834,7 @@ struct connectdata {
int socktype; /* SOCK_STREAM or SOCK_DGRAM */
struct hostname host;
+ char *hostname_resolve; /* host name to resolve to address, allocated */
char *secondaryhostname; /* secondary socket host name (ftp) */
struct hostname conn_to_host; /* the host to connect to. valid only if
bits.conn_to_host is set */
@@ -870,6 +877,7 @@ struct connectdata {
struct curltime now; /* "current" time */
struct curltime created; /* creation time */
+ struct curltime lastused; /* when returned to the connection cache */
curl_socket_t sock[2]; /* two sockets, the second is used for the data
transfer when doing FTP */
curl_socket_t tempsock[2]; /* temporary sockets for happy eyeballs */
@@ -888,8 +896,6 @@ struct connectdata {
#endif
struct ssl_primary_config ssl_config;
struct ssl_primary_config proxy_ssl_config;
- bool tls_upgraded;
-
struct ConnectBits bits; /* various state-flags for this connection */
/* connecttime: when connect() is called on the current IP address. Used to
@@ -936,7 +942,7 @@ struct connectdata {
} allocptr;
#ifdef HAVE_GSSAPI
- int sec_complete; /* if Kerberos is enabled for this connection */
+ bit sec_complete:1; /* if Kerberos is enabled for this connection */
enum protection_level command_prot;
enum protection_level data_prot;
enum protection_level request_data_prot;
@@ -951,30 +957,19 @@ struct connectdata {
struct kerberos5data krb5; /* variables into the structure definition, */
#endif /* however, some of them are ftp specific. */
- /* the two following *_inuse fields are only flags, not counters in any way.
- If TRUE it means the channel is in use, and if FALSE it means the channel
- is up for grabs by one. */
-
- bool readchannel_inuse; /* whether the read channel is in use by an easy
- handle */
- bool writechannel_inuse; /* whether the write channel is in use by an easy
- handle */
- struct curl_llist send_pipe; /* List of handles waiting to send on this
- pipeline */
- struct curl_llist recv_pipe; /* List of handles waiting to read their
- responses on this pipeline */
- char *master_buffer; /* The master buffer allocated on-demand;
- used for pipelining. */
- size_t read_pos; /* Current read position in the master buffer */
- size_t buf_len; /* Length of the buffer?? */
-
-
+ struct curl_llist easyq; /* List of easy handles using this connection */
curl_seek_callback seek_func; /* function that seeks the input */
void *seek_client; /* pointer to pass to the seek() above */
/*************** Request - specific items ************/
+#if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS)
+ CtxtHandle *sslContext;
+#endif
#if defined(USE_NTLM)
+ curlntlm http_ntlm_state;
+ curlntlm proxy_ntlm_state;
+
struct ntlmdata ntlm; /* NTLM differs from other authentication schemes
because it authenticates connections, not
single requests! */
@@ -989,7 +984,14 @@ struct connectdata {
#endif
#endif
- char syserr_buf [256]; /* buffer for Curl_strerror() */
+#ifdef USE_SPNEGO
+ curlnegotiate http_negotiate_state;
+ curlnegotiate proxy_negotiate_state;
+
+ struct negotiatedata negotiate; /* state data for host Negotiate auth */
+ struct negotiatedata proxyneg; /* state data for proxy Negotiate auth */
+#endif
+
/* data used for the asynch name resolve callback */
struct Curl_async async;
@@ -1032,8 +1034,16 @@ struct connectdata {
#ifdef USE_UNIX_SOCKETS
char *unix_domain_socket;
- bool abstract_unix_socket;
+ bit abstract_unix_socket:1;
#endif
+ bit tls_upgraded:1;
+ /* the two following *_inuse fields are only flags, not counters in any way.
+ If TRUE it means the channel is in use, and if FALSE it means the channel
+ is up for grabs by one. */
+ bit readchannel_inuse:1; /* whether the read channel is in use by an easy
+ handle */
+ bit writechannel_inuse:1; /* whether the write channel is in use by an easy
+ handle */
};
/* The end of connectdata. */
@@ -1048,10 +1058,8 @@ struct PureInfo {
int httpversion; /* the http version number X.Y = X*10+Y */
time_t filetime; /* If requested, this is might get set. Set to -1 if the
time was unretrievable. */
- bool timecond; /* set to TRUE if the time condition didn't match, which
- thus made the document NOT get fetched */
- long header_size; /* size of read header(s) in bytes */
- long request_size; /* the amount of bytes sent in the request(s) */
+ curl_off_t header_size; /* size of read header(s) in bytes */
+ curl_off_t request_size; /* the amount of bytes sent in the request(s) */
unsigned long proxyauthavail; /* what proxy auth types were announced */
unsigned long httpauthavail; /* what host auth types were announced */
long numconnects; /* how many new connection did libcurl created */
@@ -1068,16 +1076,16 @@ struct PureInfo {
char conn_primary_ip[MAX_IPADR_LEN];
long conn_primary_port;
-
char conn_local_ip[MAX_IPADR_LEN];
long conn_local_port;
-
const char *conn_scheme;
unsigned int conn_protocol;
-
struct curl_certinfo certs; /* info about the certs, only populated in
OpenSSL builds. Asked for with
CURLOPT_CERTINFO / CURLINFO_CERTINFO */
+
+ bit timecond:1; /* set to TRUE if the time condition didn't match, which
+ thus made the document NOT get fetched */
};
@@ -1091,7 +1099,6 @@ struct Progress {
curl_off_t current_speed; /* uses the currently fastest transfer */
- bool callback; /* set when progress callback is used */
int width; /* screen width at download start */
int flags; /* see progress.h */
@@ -1112,7 +1119,6 @@ struct Progress {
struct curltime t_startop;
struct curltime t_acceptdata;
- bool is_t_startransfer_set;
/* upload speed limit */
struct curltime ul_limit_start;
@@ -1126,6 +1132,8 @@ struct Progress {
curl_off_t speeder[ CURR_TIME ];
struct curltime speeder_time[ CURR_TIME ];
int speeder_c;
+ bit callback:1; /* set when progress callback is used */
+ bit is_t_startransfer_set:1;
};
typedef enum {
@@ -1137,7 +1145,6 @@ typedef enum {
HTTPREQ_PUT,
HTTPREQ_HEAD,
HTTPREQ_OPTIONS,
- HTTPREQ_CUSTOM,
HTTPREQ_LAST /* last in list */
} Curl_HttpReq;
@@ -1174,12 +1181,12 @@ struct auth {
unsigned long picked;
unsigned long avail; /* Bitmask for what the server reports to support for
this resource */
- bool done; /* TRUE when the auth phase is done and ready to do the *actual*
- request */
- bool multipass; /* TRUE if this is not yet authenticated but within the
- auth multipass negotiation */
- bool iestyle; /* TRUE if digest should be done IE-style or FALSE if it should
- be RFC compliant */
+ bit done:1; /* TRUE when the auth phase is done and ready to do the
+ *actual* request */
+ bit multipass:1; /* TRUE if this is not yet authenticated but within the
+ auth multipass negotiation */
+ bit iestyle:1; /* TRUE if digest should be done IE-style or FALSE if it
+ should be RFC compliant */
};
struct Curl_http2_dep {
@@ -1206,6 +1213,7 @@ typedef enum {
EXPIRE_ASYNC_NAME,
EXPIRE_CONNECTTIMEOUT,
EXPIRE_DNS_PER_NAME,
+ EXPIRE_HAPPY_EYEBALLS_DNS, /* See asyn-ares.c */
EXPIRE_HAPPY_EYEBALLS,
EXPIRE_MULTI_PENDING,
EXPIRE_RUN_NOW,
@@ -1215,6 +1223,15 @@ typedef enum {
EXPIRE_LAST /* not an actual timer, used as a marker only */
} expire_id;
+
+typedef enum {
+ TRAILERS_NONE,
+ TRAILERS_INITIALIZED,
+ TRAILERS_SENDING,
+ TRAILERS_DONE
+} trailers_state;
+
+
/*
* One instance for each timeout an easy handle can set.
*/
@@ -1241,11 +1258,6 @@ struct UrlState {
/* Points to the connection cache */
struct conncache *conn_cache;
- /* when curl_easy_perform() is called, the multi handle is "owned" by
- the easy handle so curl_easy_cleanup() on such an easy handle will
- also close the multi handle! */
- bool multi_owned_by_easy;
-
/* buffers to store authentication data in, as parsed from input options */
struct curltime keeps_speed; /* for the progress meter really */
@@ -1258,8 +1270,6 @@ struct UrlState {
char *ulbuf; /* allocated upload buffer or NULL */
curl_off_t current_speed; /* the ProgressShow() function sets this,
bytes / second */
- bool this_is_a_follow; /* this is a followed Location: request */
- bool refused_stream; /* this was refused, try again */
char *first_host; /* host name of the first (not followed) request.
if set, this should be the host name that we will
sent authorization to, no else. Used to make Location:
@@ -1272,29 +1282,16 @@ struct UrlState {
unsigned int tempcount; /* number of entries in use in tempwrite, 0 - 3 */
struct tempbuf tempwrite[3]; /* BOTH, HEADER, BODY */
char *scratch; /* huge buffer[set.buffer_size*2] for upload CRLF replacing */
- bool errorbuf; /* Set to TRUE if the error buffer is already filled in.
- This must be set to FALSE every time _easy_perform() is
- called. */
int os_errno; /* filled in with errno whenever an error occurs */
#ifdef HAVE_SIGNAL
/* storage for the previous bag^H^H^HSIGPIPE signal handler :-) */
void (*prev_signal)(int sig);
#endif
- bool allow_port; /* Is set.use_port allowed to take effect or not. This
- is always set TRUE when curl_easy_perform() is called. */
struct digestdata digest; /* state data for host Digest auth */
struct digestdata proxydigest; /* state data for proxy Digest auth */
-#ifdef USE_SPNEGO
- struct negotiatedata negotiate; /* state data for host Negotiate auth */
- struct negotiatedata proxyneg; /* state data for proxy Negotiate auth */
-#endif
-
struct auth authhost; /* auth details for host */
struct auth authproxy; /* auth details for proxy */
-
- bool authproblem; /* TRUE if there's some problem authenticating */
-
void *resolver; /* resolver state, if it is used in the URL state -
ares_channel f.e. */
@@ -1310,27 +1307,18 @@ struct UrlState {
/* a place to store the most recently set FTP entrypath */
char *most_recent_ftp_entrypath;
- /* set after initial USER failure, to prevent an authentication loop */
- bool ftp_trying_alternative;
- bool wildcardmatch; /* enable wildcard matching */
int httpversion; /* the lowest HTTP version*10 reported by any server
involved in this request */
- bool expect100header; /* TRUE if we added Expect: 100-continue */
#if !defined(WIN32) && !defined(MSDOS) && !defined(__EMX__) && \
!defined(__SYMBIAN32__)
/* do FTP line-end conversions on most platforms */
#define CURL_DO_LINEEND_CONV
/* for FTP downloads: track CRLF sequences that span blocks */
- bool prev_block_had_trailing_cr;
+ bit prev_block_had_trailing_cr:1;
/* for FTP downloads: how many CRLFs did we converted to LFs? */
curl_off_t crlf_conversions;
#endif
- bool slash_removed; /* set TRUE if the 'path' points to a path where the
- initial URL slash separator has been taken off */
- bool use_range;
- bool rangestringalloc; /* the range string is malloc()'ed */
-
char *range; /* range, if used. See README for detailed specification on
this syntax. */
curl_off_t resume_from; /* continue [ftp] transfer from here */
@@ -1346,21 +1334,48 @@ struct UrlState {
size_t drain; /* Increased when this stream has data to read, even if its
socket is not necessarily is readable. Decreased when
checked. */
- bool done; /* set to FALSE when Curl_init_do() is called and set to TRUE
- when multi_done() is called, to prevent multi_done() to get
- invoked twice when the multi interface is used. */
curl_read_callback fread_func; /* read callback/function */
void *in; /* CURLOPT_READDATA */
struct Curl_easy *stream_depends_on;
- bool stream_depends_e; /* set or don't set the Exclusive bit */
int stream_weight;
-#ifdef CURLDEBUG
- bool conncache_lock;
-#endif
CURLU *uh; /* URL handle for the current parsed URL */
struct urlpieces up;
+#ifndef CURL_DISABLE_HTTP
+ size_t trailers_bytes_sent;
+ Curl_send_buffer *trailers_buf; /* a buffer containing the compiled trailing
+ headers */
+#endif
+ trailers_state trailers_state; /* whether we are sending trailers
+ and what stage are we at */
+#ifdef CURLDEBUG
+ bit conncache_lock:1;
+#endif
+ /* when curl_easy_perform() is called, the multi handle is "owned" by
+ the easy handle so curl_easy_cleanup() on such an easy handle will
+ also close the multi handle! */
+ bit multi_owned_by_easy:1;
+
+ bit this_is_a_follow:1; /* this is a followed Location: request */
+ bit refused_stream:1; /* this was refused, try again */
+ bit errorbuf:1; /* Set to TRUE if the error buffer is already filled in.
+ This must be set to FALSE every time _easy_perform() is
+ called. */
+ bit allow_port:1; /* Is set.use_port allowed to take effect or not. This
+ is always set TRUE when curl_easy_perform() is called. */
+ bit authproblem:1; /* TRUE if there's some problem authenticating */
+ /* set after initial USER failure, to prevent an authentication loop */
+ bit ftp_trying_alternative:1;
+ bit wildcardmatch:1; /* enable wildcard matching */
+ bit expect100header:1; /* TRUE if we added Expect: 100-continue */
+ bit use_range:1;
+ bit rangestringalloc:1; /* the range string is malloc()'ed */
+ bit done:1; /* set to FALSE when Curl_init_do() is called and set to TRUE
+ when multi_done() is called, to prevent multi_done() to get
+ invoked twice when the multi interface is used. */
+ bit stream_depends_e:1; /* set or don't set the Exclusive bit */
+ bit previouslypending:1; /* this transfer WAS in the multi->pending queue */
};
@@ -1373,13 +1388,15 @@ struct UrlState {
struct DynamicStatic {
char *url; /* work URL, copied from UserDefined */
- bool url_alloc; /* URL string is malloc()'ed */
char *referer; /* referer string */
- bool referer_alloc; /* referer string is malloc()ed */
struct curl_slist *cookielist; /* list of cookie files set by
curl_easy_setopt(COOKIEFILE) calls */
struct curl_slist *resolve; /* set to point to the set.resolve list when
this should be dealt with in pretransfer */
+ bit url_alloc:1; /* URL string is malloc()'ed */
+ bit referer_alloc:1; /* referer string is malloc()ed */
+ bit wildcard_resolve:1; /* Set to true if any resolve change is a
+ wildcard */
};
/*
@@ -1449,7 +1466,7 @@ enum dupstring {
STRING_RTSP_SESSION_ID, /* Session ID to use */
STRING_RTSP_STREAM_URI, /* Stream URI for this request */
STRING_RTSP_TRANSPORT, /* Transport for this session */
-#if defined(USE_LIBSSH2) || defined(USE_LIBSSH)
+#ifdef USE_SSH
STRING_SSH_PRIVATE_KEY, /* path to the private key file for auth */
STRING_SSH_PUBLIC_KEY, /* path to the public key file for auth */
STRING_SSH_HOST_PUBLIC_KEY_MD5, /* md5 of host public key in ascii hex */
@@ -1472,6 +1489,9 @@ enum dupstring {
#endif
STRING_TARGET, /* CURLOPT_REQUEST_TARGET */
STRING_DOH, /* CURLOPT_DOH_URL */
+#ifdef USE_ALTSVC
+ STRING_ALTSVC, /* CURLOPT_ALTSVC */
+#endif
/* -- end of zero-terminated strings -- */
STRING_LASTZEROTERMINATED,
@@ -1509,8 +1529,6 @@ struct UserDefined {
int keep_post; /* keep POSTs as POSTs after a 30x request; each
bit represents a request, from 301 to 303 */
- bool free_referer; /* set TRUE if 'referer' points to a string we
- allocated */
void *postfields; /* if POST, set the fields' values here */
curl_seek_callback seek_func; /* function that seeks the input */
curl_off_t postfieldsize; /* if POST, this might have a size to use instead
@@ -1523,8 +1541,6 @@ struct UserDefined {
curl_write_callback fwrite_header; /* function that stores headers */
curl_write_callback fwrite_rtp; /* function that stores interleaved RTP */
curl_read_callback fread_func_set; /* function that reads the input */
- int is_fread_set; /* boolean, has read callback been set to non-NULL? */
- int is_fwrite_set; /* boolean, has write callback been set to non-NULL? */
curl_progress_callback fprogress; /* OLD and deprecated progress callback */
curl_xferinfo_callback fxferinfo; /* progress callback */
curl_debug_callback fdebug; /* function that write informational data */
@@ -1555,8 +1571,9 @@ struct UserDefined {
long accepttimeout; /* in milliseconds, 0 means no timeout */
long happy_eyeballs_timeout; /* in milliseconds, 0 is a valid value */
long server_response_timeout; /* in milliseconds, 0 means no timeout */
+ long maxage_conn; /* in seconds, max idle time to allow a connection that
+ is to be reused */
long tftp_blksize; /* in bytes, 0 means use default */
- bool tftp_no_options; /* do not send TFTP options requests */
curl_off_t filesize; /* size of file to upload, -1 means unknown */
long low_speed_limit; /* bytes/second */
long low_speed_time; /* number of seconds */
@@ -1568,9 +1585,6 @@ struct UserDefined {
struct curl_slist *proxyheaders; /* linked list of extra CONNECT headers */
struct curl_httppost *httppost; /* linked list of old POST data */
curl_mimepart mimepost; /* MIME/POST data. */
- bool sep_headers; /* handle host and proxy headers separately */
- bool cookiesession; /* new cookie session? */
- bool crlf; /* convert crlf on ftp upload(?) */
struct curl_slist *quote; /* after connection is established */
struct curl_slist *postquote; /* after the transfer */
struct curl_slist *prequote; /* before the transfer, after type */
@@ -1589,7 +1603,6 @@ struct UserDefined {
Curl_HttpReq httpreq; /* what kind of HTTP request (if any) is this */
long httpversion; /* when non-zero, a specific HTTP version requested to
be used in the library's request(s) */
- bool strip_path_slash; /* strip off initial slash from path */
struct ssl_config_data ssl; /* user defined SSL stuff */
struct ssl_config_data proxy_ssl; /* user defined SSL stuff for proxy */
struct ssl_general_config general_ssl; /* general user defined SSL stuff */
@@ -1599,87 +1612,35 @@ struct UserDefined {
size_t upload_buffer_size; /* size of upload buffer to use,
keep it >= CURL_MAX_WRITE_SIZE */
void *private_data; /* application-private data */
-
struct curl_slist *http200aliases; /* linked list of aliases for http200 */
-
long ipver; /* the CURL_IPRESOLVE_* defines in the public header file
0 - whatever, 1 - v2, 2 - v6 */
-
curl_off_t max_filesize; /* Maximum file size to download */
-
+#ifndef CURL_DISABLE_FTP
curl_ftpfile ftp_filemethod; /* how to get to a file when FTP is used */
-
+ curl_ftpauth ftpsslauth; /* what AUTH XXX to be attempted */
+ curl_ftpccc ftp_ccc; /* FTP CCC options */
+#endif
int ftp_create_missing_dirs; /* 1 - create directories that don't exist
2 - the same but also allow MKD to fail once
*/
-
curl_sshkeycallback ssh_keyfunc; /* key matching callback */
void *ssh_keyfunc_userp; /* custom pointer to callback */
- bool ssh_compression; /* enable SSH compression */
-
-/* Here follows boolean settings that define how to behave during
- this session. They are STATIC, set by libcurl users or at least initially
- and they don't change during operations. */
- bool get_filetime; /* get the time and get of the remote file */
- bool tunnel_thru_httpproxy; /* use CONNECT through a HTTP proxy */
- bool prefer_ascii; /* ASCII rather than binary */
- bool ftp_append; /* append, not overwrite, on upload */
- bool ftp_list_only; /* switch FTP command for listing directories */
- bool ftp_use_port; /* use the FTP PORT command */
- bool hide_progress; /* don't use the progress meter */
- bool http_fail_on_error; /* fail on HTTP error codes >= 400 */
- bool http_keep_sending_on_error; /* for HTTP status codes >= 300 */
- bool http_follow_location; /* follow HTTP redirects */
- bool http_transfer_encoding; /* request compressed HTTP transfer-encoding */
- bool allow_auth_to_other_hosts;
- bool include_header; /* include received protocol headers in data output */
- bool http_set_referer; /* is a custom referer used */
- bool http_auto_referer; /* set "correct" referer when following location: */
- bool opt_no_body; /* as set with CURLOPT_NOBODY */
- bool upload; /* upload request */
enum CURL_NETRC_OPTION
use_netrc; /* defined in include/curl.h */
- bool verbose; /* output verbosity */
- bool krb; /* Kerberos connection requested */
- bool reuse_forbid; /* forbidden to be reused, close after use */
- bool reuse_fresh; /* do not re-use an existing connection */
- bool ftp_use_epsv; /* if EPSV is to be attempted or not */
- bool ftp_use_eprt; /* if EPRT is to be attempted or not */
- bool ftp_use_pret; /* if PRET is to be used before PASV or not */
-
curl_usessl use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or
IMAP or POP3 or others! */
- curl_ftpauth ftpsslauth; /* what AUTH XXX to be attempted */
- curl_ftpccc ftp_ccc; /* FTP CCC options */
- bool no_signal; /* do not use any signal/alarm handler */
- bool global_dns_cache; /* subject for future removal */
- bool tcp_nodelay; /* whether to enable TCP_NODELAY or not */
- bool ignorecl; /* ignore content length */
- bool ftp_skip_ip; /* skip the IP address the FTP server passes on to
- us */
- bool connect_only; /* make connection, let application use the socket */
- long ssh_auth_types; /* allowed SSH auth types */
- bool http_te_skip; /* pass the raw body data to the user, even when
- transfer-encoded (chunked, compressed) */
- bool http_ce_skip; /* pass the raw body data to the user, even when
- content-encoded (chunked, compressed) */
long new_file_perms; /* Permissions to use when creating remote files */
long new_directory_perms; /* Permissions to use when creating remote dirs */
- bool proxy_transfer_mode; /* set transfer mode (;type=<a|i>) when doing FTP
- via an HTTP proxy */
+ long ssh_auth_types; /* allowed SSH auth types */
char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */
unsigned int scope_id; /* Scope id for IPv6 */
long allowed_protocols;
long redir_protocols;
-#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
- bool socks5_gssapi_nec; /* Flag to support NEC SOCKS5 server */
-#endif
struct curl_slist *mail_rcpt; /* linked list of mail recipients */
- bool sasl_ir; /* Enable/disable SASL initial response */
/* Common RTSP header options */
Curl_RtspReq rtspreq; /* RTSP request type */
long rtspversion; /* like httpversion, for RTSP */
- bool wildcard_enabled; /* enable wildcard matching */
curl_chunk_bgn_callback chunk_bgn; /* called before part of transfer
starts */
curl_chunk_end_callback chunk_end; /* called after part transferring
@@ -1691,50 +1652,109 @@ struct UserDefined {
long gssapi_delegation; /* GSS-API credential delegation, see the
documentation of CURLOPT_GSSAPI_DELEGATION */
- bool tcp_keepalive; /* use TCP keepalives */
long tcp_keepidle; /* seconds in idle before sending keepalive probe */
long tcp_keepintvl; /* seconds between TCP keepalive probes */
- bool tcp_fastopen; /* use TCP Fast Open */
- size_t maxconnects; /* Max idle connections in the connection cache */
+ size_t maxconnects; /* Max idle connections in the connection cache */
- bool ssl_enable_npn; /* TLS NPN extension? */
- bool ssl_enable_alpn; /* TLS ALPN extension? */
- bool path_as_is; /* allow dotdots? */
- bool pipewait; /* wait for pipe/multiplex status before starting a
- new connection */
long expect_100_timeout; /* in milliseconds */
- bool suppress_connect_headers; /* suppress proxy CONNECT response headers
- from user callbacks */
-
- bool dns_shuffle_addresses; /* whether to shuffle addresses before use */
-
struct Curl_easy *stream_depends_on;
- bool stream_depends_e; /* set or don't set the Exclusive bit */
int stream_weight;
-
- bool haproxyprotocol; /* whether to send HAProxy PROXY protocol v1 header */
-
struct Curl_http2_dep *stream_dependents;
- bool abstract_unix_socket;
-
curl_resolver_start_callback resolver_start; /* optional callback called
before resolver start */
void *resolver_start_client; /* pointer to pass to resolver start callback */
- bool disallow_username_in_url; /* disallow username in url */
long upkeep_interval_ms; /* Time between calls for connection upkeep. */
- bool doh; /* DNS-over-HTTPS enabled */
- bool doh_get; /* use GET for DoH requests, instead of POST */
multidone_func fmultidone;
struct Curl_easy *dohfor; /* this is a DoH request for that transfer */
+ CURLU *uh; /* URL handle for the current parsed URL */
+ void *trailer_data; /* pointer to pass to trailer data callback */
+ curl_trailer_callback trailer_callback; /* trailing data callback */
+ bit is_fread_set:1; /* has read callback been set to non-NULL? */
+ bit is_fwrite_set:1; /* has write callback been set to non-NULL? */
+ bit free_referer:1; /* set TRUE if 'referer' points to a string we
+ allocated */
+ bit tftp_no_options:1; /* do not send TFTP options requests */
+ bit sep_headers:1; /* handle host and proxy headers separately */
+ bit cookiesession:1; /* new cookie session? */
+ bit crlf:1; /* convert crlf on ftp upload(?) */
+ bit strip_path_slash:1; /* strip off initial slash from path */
+ bit ssh_compression:1; /* enable SSH compression */
+
+/* Here follows boolean settings that define how to behave during
+ this session. They are STATIC, set by libcurl users or at least initially
+ and they don't change during operations. */
+ bit get_filetime:1; /* get the time and get of the remote file */
+ bit tunnel_thru_httpproxy:1; /* use CONNECT through a HTTP proxy */
+ bit prefer_ascii:1; /* ASCII rather than binary */
+ bit ftp_append:1; /* append, not overwrite, on upload */
+ bit ftp_list_only:1; /* switch FTP command for listing directories */
+#ifndef CURL_DISABLE_FTP
+ bit ftp_use_port:1; /* use the FTP PORT command */
+ bit ftp_use_epsv:1; /* if EPSV is to be attempted or not */
+ bit ftp_use_eprt:1; /* if EPRT is to be attempted or not */
+ bit ftp_use_pret:1; /* if PRET is to be used before PASV or not */
+ bit ftp_skip_ip:1; /* skip the IP address the FTP server passes on to
+ us */
+#endif
+ bit hide_progress:1; /* don't use the progress meter */
+ bit http_fail_on_error:1; /* fail on HTTP error codes >= 400 */
+ bit http_keep_sending_on_error:1; /* for HTTP status codes >= 300 */
+ bit http_follow_location:1; /* follow HTTP redirects */
+ bit http_transfer_encoding:1; /* request compressed HTTP
+ transfer-encoding */
+ bit allow_auth_to_other_hosts:1;
+ bit include_header:1; /* include received protocol headers in data output */
+ bit http_set_referer:1; /* is a custom referer used */
+ bit http_auto_referer:1; /* set "correct" referer when following
+ location: */
+ bit opt_no_body:1; /* as set with CURLOPT_NOBODY */
+ bit upload:1; /* upload request */
+ bit verbose:1; /* output verbosity */
+ bit krb:1; /* Kerberos connection requested */
+ bit reuse_forbid:1; /* forbidden to be reused, close after use */
+ bit reuse_fresh:1; /* do not re-use an existing connection */
+
+ bit no_signal:1; /* do not use any signal/alarm handler */
+ bit tcp_nodelay:1; /* whether to enable TCP_NODELAY or not */
+ bit ignorecl:1; /* ignore content length */
+ bit connect_only:1; /* make connection, let application use the socket */
+ bit http_te_skip:1; /* pass the raw body data to the user, even when
+ transfer-encoded (chunked, compressed) */
+ bit http_ce_skip:1; /* pass the raw body data to the user, even when
+ content-encoded (chunked, compressed) */
+ bit proxy_transfer_mode:1; /* set transfer mode (;type=<a|i>) when doing
+ FTP via an HTTP proxy */
+#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+ bit socks5_gssapi_nec:1; /* Flag to support NEC SOCKS5 server */
+#endif
+ bit sasl_ir:1; /* Enable/disable SASL initial response */
+ bit wildcard_enabled:1; /* enable wildcard matching */
+ bit tcp_keepalive:1; /* use TCP keepalives */
+ bit tcp_fastopen:1; /* use TCP Fast Open */
+ bit ssl_enable_npn:1; /* TLS NPN extension? */
+ bit ssl_enable_alpn:1;/* TLS ALPN extension? */
+ bit path_as_is:1; /* allow dotdots? */
+ bit pipewait:1; /* wait for multiplex status before starting a new
+ connection */
+ bit suppress_connect_headers:1; /* suppress proxy CONNECT response headers
+ from user callbacks */
+ bit dns_shuffle_addresses:1; /* whether to shuffle addresses before use */
+ bit stream_depends_e:1; /* set or don't set the Exclusive bit */
+ bit haproxyprotocol:1; /* whether to send HAProxy PROXY protocol v1
+ header */
+ bit abstract_unix_socket:1;
+ bit disallow_username_in_url:1; /* disallow username in url */
+ bit doh:1; /* DNS-over-HTTPS enabled */
+ bit doh_get:1; /* use GET for DoH requests, instead of POST */
+ bit http09_allowed:1; /* allow HTTP/0.9 responses */
};
struct Names {
struct curl_hash *hostcache;
enum {
HCACHE_NONE, /* not pointing to anything */
- HCACHE_GLOBAL, /* points to the (shrug) global one */
HCACHE_MULTI, /* points to a shared one in the multi handle */
HCACHE_SHARED /* points to a shared one in a shared object */
} hostcachetype;
@@ -1755,9 +1775,10 @@ struct Curl_easy {
struct Curl_easy *next;
struct Curl_easy *prev;
- struct connectdata *easy_conn; /* the "unit's" connection */
+ struct connectdata *conn;
struct curl_llist_element connect_queue;
- struct curl_llist_element pipeline_queue;
+ struct curl_llist_element sh_queue; /* list per Curl_sh_entry */
+ struct curl_llist_element conn_queue; /* list per connectdata */
CURLMstate mstate; /* the handle's state */
CURLcode result; /* previous result */
@@ -1769,6 +1790,8 @@ struct Curl_easy {
the state etc are also kept. This array is mostly used to detect when a
socket is to be removed from the hash. See singlesocket(). */
curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE];
+ int actions[MAX_SOCKSPEREASYHANDLE]; /* action for each socket in
+ sockets[] */
int numsocks;
struct Names dns;
@@ -1789,10 +1812,15 @@ struct Curl_easy {
NOTE that the 'cookie' field in the
UserDefined struct defines if the "engine"
is to be used or not. */
+#ifdef USE_ALTSVC
+ struct altsvcinfo *asi; /* the alt-svc cache */
+#endif
struct Progress progress; /* for all the progress meter data */
struct UrlState state; /* struct for fields used for state info and
other dynamic purposes */
+#ifndef CURL_DISABLE_FTP
struct WildcardData wildcard; /* wildcard download state info */
+#endif
struct PureInfo info; /* stats, reports and info data */
struct curl_tlssessioninfo tsi; /* Information about the TLS session, only
valid after a client has asked for it */
diff --git a/Utilities/cmcurl/lib/vauth/cleartext.c b/Utilities/cmcurl/lib/vauth/cleartext.c
index be6d6111e..6f452c169 100644
--- a/Utilities/cmcurl/lib/vauth/cleartext.c
+++ b/Utilities/cmcurl/lib/vauth/cleartext.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -25,6 +25,9 @@
#include "curl_setup.h"
+#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \
+ !defined(CURL_DISABLE_POP3)
+
#include <curl/curl.h>
#include "urldata.h"
@@ -49,8 +52,9 @@
* Parameters:
*
* data [in] - The session handle.
- * userp [in] - The user name.
- * passwdp [in] - The user's password.
+ * authzid [in] - The authorization identity.
+ * authcid [in] - The authentication identity.
+ * passwd [in] - The password.
* outptr [in/out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
* outlen [out] - The length of the output message.
@@ -58,36 +62,40 @@
* Returns CURLE_OK on success.
*/
CURLcode Curl_auth_create_plain_message(struct Curl_easy *data,
- const char *userp,
- const char *passwdp,
+ const char *authzid,
+ const char *authcid,
+ const char *passwd,
char **outptr, size_t *outlen)
{
CURLcode result;
char *plainauth;
- size_t ulen;
+ size_t zlen;
+ size_t clen;
size_t plen;
size_t plainlen;
*outlen = 0;
*outptr = NULL;
- ulen = strlen(userp);
- plen = strlen(passwdp);
+ zlen = (authzid == NULL ? 0 : strlen(authzid));
+ clen = strlen(authcid);
+ plen = strlen(passwd);
/* Compute binary message length. Check for overflows. */
- if((ulen > SIZE_T_MAX/4) || (plen > (SIZE_T_MAX/2 - 2)))
+ if(((zlen + clen) > SIZE_T_MAX/4) || (plen > (SIZE_T_MAX/2 - 2)))
return CURLE_OUT_OF_MEMORY;
- plainlen = 2 * ulen + plen + 2;
+ plainlen = zlen + clen + plen + 2;
plainauth = malloc(plainlen);
if(!plainauth)
return CURLE_OUT_OF_MEMORY;
/* Calculate the reply */
- memcpy(plainauth, userp, ulen);
- plainauth[ulen] = '\0';
- memcpy(plainauth + ulen + 1, userp, ulen);
- plainauth[2 * ulen + 1] = '\0';
- memcpy(plainauth + 2 * ulen + 2, passwdp, plen);
+ if(zlen != 0)
+ memcpy(plainauth, authzid, zlen);
+ plainauth[zlen] = '\0';
+ memcpy(plainauth + zlen + 1, authcid, clen);
+ plainauth[zlen + clen + 1] = '\0';
+ memcpy(plainauth + zlen + clen + 2, passwd, plen);
/* Base64 encode the reply */
result = Curl_base64_encode(data, plainauth, plainlen, outptr, outlen);
@@ -157,3 +165,5 @@ CURLcode Curl_auth_create_external_message(struct Curl_easy *data,
/* This is the same formatting as the login message */
return Curl_auth_create_login_message(data, user, outptr, outlen);
}
+
+#endif /* if no users */
diff --git a/Utilities/cmcurl/lib/vauth/digest.c b/Utilities/cmcurl/lib/vauth/digest.c
index ab5156eb7..f9cdc9dd0 100644
--- a/Utilities/cmcurl/lib/vauth/digest.c
+++ b/Utilities/cmcurl/lib/vauth/digest.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -143,7 +143,7 @@ static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */
{
int i;
for(i = 0; i < 16; i++)
- snprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
+ msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
}
/* Convert sha256 chunk to RFC7616 -suitable ascii string*/
@@ -152,7 +152,7 @@ static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */
{
int i;
for(i = 0; i < 32; i++)
- snprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
+ msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
}
/* Perform quoted-string escaping as described in RFC2616 and its errata */
@@ -432,7 +432,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
/* Convert calculated 16 octet hex into 32 bytes string */
for(i = 0; i < MD5_DIGEST_LEN; i++)
- snprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
+ msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
/* Generate our SPN */
spn = Curl_auth_build_spn(service, realm, NULL);
@@ -455,7 +455,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
Curl_MD5_final(ctxt, digest);
for(i = 0; i < MD5_DIGEST_LEN; i++)
- snprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]);
+ msnprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]);
/* Now calculate the response hash */
ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
@@ -485,7 +485,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
Curl_MD5_final(ctxt, digest);
for(i = 0; i < MD5_DIGEST_LEN; i++)
- snprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
+ msnprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
/* Generate the response */
response = aprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\","
@@ -785,8 +785,7 @@ static CURLcode _Curl_auth_create_digest_http_message(
return CURLE_OUT_OF_MEMORY;
if(digest->qop && strcasecompare(digest->qop, "auth-int")) {
- /* We don't support auth-int for PUT or POST at the moment.
- TODO: replace hash of empty string with entity-body for PUT/POST */
+ /* We don't support auth-int for PUT or POST */
char hashed[65];
unsigned char *hashthis2;
diff --git a/Utilities/cmcurl/lib/vauth/digest_sspi.c b/Utilities/cmcurl/lib/vauth/digest_sspi.c
index 928755735..fe8093e8b 100644
--- a/Utilities/cmcurl/lib/vauth/digest_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/digest_sspi.c
@@ -6,7 +6,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2014 - 2016, Steve Holme, <steve_holme@hotmail.com>.
- * Copyright (C) 2015 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2015 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -146,7 +146,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
}
/* Generate our SPN */
- spn = Curl_auth_build_spn(service, data->easy_conn->host.name, NULL);
+ spn = Curl_auth_build_spn(service, data->conn->host.name, NULL);
if(!spn) {
free(output_token);
free(input_token);
diff --git a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c
index 55daec1ff..ea0a5f189 100644
--- a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c
+++ b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2014 - 2017, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) 2014 - 2019, Steve Holme, <steve_holme@hotmail.com>.
* Copyright (C) 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
@@ -372,7 +372,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
}
/*
- * Curl_auth_gssapi_cleanup()
+ * Curl_auth_cleanup_gssapi()
*
* This is used to clean up the GSSAPI (Kerberos V5) specific data.
*
@@ -381,7 +381,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
* krb5 [in/out] - The Kerberos 5 data struct being cleaned up.
*
*/
-void Curl_auth_gssapi_cleanup(struct kerberos5data *krb5)
+void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5)
{
OM_uint32 minor_status;
diff --git a/Utilities/cmcurl/lib/vauth/krb5_sspi.c b/Utilities/cmcurl/lib/vauth/krb5_sspi.c
index cb11ed901..1f6e462bf 100644
--- a/Utilities/cmcurl/lib/vauth/krb5_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/krb5_sspi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2014 - 2017, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) 2014 - 2019, Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -474,7 +474,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
}
/*
- * Curl_auth_gssapi_cleanup()
+ * Curl_auth_cleanup_gssapi()
*
* This is used to clean up the GSSAPI (Kerberos V5) specific data.
*
@@ -483,7 +483,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
* krb5 [in/out] - The Kerberos 5 data struct being cleaned up.
*
*/
-void Curl_auth_gssapi_cleanup(struct kerberos5data *krb5)
+void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5)
{
/* Free our security context */
if(krb5->context) {
diff --git a/Utilities/cmcurl/lib/vauth/ntlm.c b/Utilities/cmcurl/lib/vauth/ntlm.c
index 11f42f504..047c2b5a3 100644
--- a/Utilities/cmcurl/lib/vauth/ntlm.c
+++ b/Utilities/cmcurl/lib/vauth/ntlm.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -182,10 +182,11 @@ static CURLcode ntlm_decode_type2_target(struct Curl_easy *data,
target_info_len = Curl_read16_le(&buffer[40]);
target_info_offset = Curl_read32_le(&buffer[44]);
if(target_info_len > 0) {
- if(((target_info_offset + target_info_len) > size) ||
+ if((target_info_offset >= size) ||
+ ((target_info_offset + target_info_len) > size) ||
(target_info_offset < 48)) {
infof(data, "NTLM handshake failure (bad type-2 message). "
- "Target Info Offset Len is set incorrect by the peer\n");
+ "Target Info Offset Len is set incorrect by the peer\n");
return CURLE_BAD_CONTENT_ENCODING;
}
@@ -402,45 +403,45 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
(void)hostname,
/* Clean up any former leftovers and initialise to defaults */
- Curl_auth_ntlm_cleanup(ntlm);
+ Curl_auth_cleanup_ntlm(ntlm);
#if defined(USE_NTRESPONSES) && defined(USE_NTLM2SESSION)
#define NTLM2FLAG NTLMFLAG_NEGOTIATE_NTLM2_KEY
#else
#define NTLM2FLAG 0
#endif
- snprintf((char *)ntlmbuf, NTLM_BUFSIZE,
- NTLMSSP_SIGNATURE "%c"
- "\x01%c%c%c" /* 32-bit type = 1 */
- "%c%c%c%c" /* 32-bit NTLM flag field */
- "%c%c" /* domain length */
- "%c%c" /* domain allocated space */
- "%c%c" /* domain name offset */
- "%c%c" /* 2 zeroes */
- "%c%c" /* host length */
- "%c%c" /* host allocated space */
- "%c%c" /* host name offset */
- "%c%c" /* 2 zeroes */
- "%s" /* host name */
- "%s", /* domain string */
- 0, /* trailing zero */
- 0, 0, 0, /* part of type-1 long */
-
- LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM |
- NTLMFLAG_REQUEST_TARGET |
- NTLMFLAG_NEGOTIATE_NTLM_KEY |
- NTLM2FLAG |
- NTLMFLAG_NEGOTIATE_ALWAYS_SIGN),
- SHORTPAIR(domlen),
- SHORTPAIR(domlen),
- SHORTPAIR(domoff),
- 0, 0,
- SHORTPAIR(hostlen),
- SHORTPAIR(hostlen),
- SHORTPAIR(hostoff),
- 0, 0,
- host, /* this is empty */
- domain /* this is empty */);
+ msnprintf((char *)ntlmbuf, NTLM_BUFSIZE,
+ NTLMSSP_SIGNATURE "%c"
+ "\x01%c%c%c" /* 32-bit type = 1 */
+ "%c%c%c%c" /* 32-bit NTLM flag field */
+ "%c%c" /* domain length */
+ "%c%c" /* domain allocated space */
+ "%c%c" /* domain name offset */
+ "%c%c" /* 2 zeroes */
+ "%c%c" /* host length */
+ "%c%c" /* host allocated space */
+ "%c%c" /* host name offset */
+ "%c%c" /* 2 zeroes */
+ "%s" /* host name */
+ "%s", /* domain string */
+ 0, /* trailing zero */
+ 0, 0, 0, /* part of type-1 long */
+
+ LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM |
+ NTLMFLAG_REQUEST_TARGET |
+ NTLMFLAG_NEGOTIATE_NTLM_KEY |
+ NTLM2FLAG |
+ NTLMFLAG_NEGOTIATE_ALWAYS_SIGN),
+ SHORTPAIR(domlen),
+ SHORTPAIR(domlen),
+ SHORTPAIR(domoff),
+ 0, 0,
+ SHORTPAIR(hostlen),
+ SHORTPAIR(hostlen),
+ SHORTPAIR(hostoff),
+ 0, 0,
+ host, /* this is empty */
+ domain /* this is empty */);
/* Initial packet length */
size = 32 + hostlen + domlen;
@@ -562,7 +563,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
}
#if defined(USE_NTRESPONSES) && defined(USE_NTLM_V2)
- if(ntlm->target_info_len) {
+ if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) {
unsigned char ntbuffer[0x18];
unsigned char entropy[8];
unsigned char ntlmv2hash[0x18];
@@ -599,7 +600,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
#if defined(USE_NTRESPONSES) && defined(USE_NTLM2SESSION)
/* We don't support NTLM2 if we don't have USE_NTRESPONSES */
- if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) {
+ if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM_KEY) {
unsigned char ntbuffer[0x18];
unsigned char tmp[0x18];
unsigned char md5sum[MD5_DIGEST_LENGTH];
@@ -631,7 +632,9 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
Curl_ntlm_core_lm_resp(ntbuffer, md5sum, ntresp);
/* End of NTLM2 Session code */
-
+ /* NTLM v2 session security is a misnomer because it is not NTLM v2.
+ It is NTLM v1 using the extended session security that is also
+ in NTLM v2 */
}
else
#endif
@@ -678,88 +681,88 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
hostoff = useroff + userlen;
/* Create the big type-3 message binary blob */
- size = snprintf((char *)ntlmbuf, NTLM_BUFSIZE,
- NTLMSSP_SIGNATURE "%c"
- "\x03%c%c%c" /* 32-bit type = 3 */
-
- "%c%c" /* LanManager length */
- "%c%c" /* LanManager allocated space */
- "%c%c" /* LanManager offset */
- "%c%c" /* 2 zeroes */
-
- "%c%c" /* NT-response length */
- "%c%c" /* NT-response allocated space */
- "%c%c" /* NT-response offset */
- "%c%c" /* 2 zeroes */
-
- "%c%c" /* domain length */
- "%c%c" /* domain allocated space */
- "%c%c" /* domain name offset */
- "%c%c" /* 2 zeroes */
-
- "%c%c" /* user length */
- "%c%c" /* user allocated space */
- "%c%c" /* user offset */
- "%c%c" /* 2 zeroes */
-
- "%c%c" /* host length */
- "%c%c" /* host allocated space */
- "%c%c" /* host offset */
- "%c%c" /* 2 zeroes */
-
- "%c%c" /* session key length (unknown purpose) */
- "%c%c" /* session key allocated space (unknown purpose) */
- "%c%c" /* session key offset (unknown purpose) */
- "%c%c" /* 2 zeroes */
-
- "%c%c%c%c", /* flags */
-
- /* domain string */
- /* user string */
- /* host string */
- /* LanManager response */
- /* NT response */
-
- 0, /* zero termination */
- 0, 0, 0, /* type-3 long, the 24 upper bits */
-
- SHORTPAIR(0x18), /* LanManager response length, twice */
- SHORTPAIR(0x18),
- SHORTPAIR(lmrespoff),
- 0x0, 0x0,
+ size = msnprintf((char *)ntlmbuf, NTLM_BUFSIZE,
+ NTLMSSP_SIGNATURE "%c"
+ "\x03%c%c%c" /* 32-bit type = 3 */
+
+ "%c%c" /* LanManager length */
+ "%c%c" /* LanManager allocated space */
+ "%c%c" /* LanManager offset */
+ "%c%c" /* 2 zeroes */
+
+ "%c%c" /* NT-response length */
+ "%c%c" /* NT-response allocated space */
+ "%c%c" /* NT-response offset */
+ "%c%c" /* 2 zeroes */
+
+ "%c%c" /* domain length */
+ "%c%c" /* domain allocated space */
+ "%c%c" /* domain name offset */
+ "%c%c" /* 2 zeroes */
+
+ "%c%c" /* user length */
+ "%c%c" /* user allocated space */
+ "%c%c" /* user offset */
+ "%c%c" /* 2 zeroes */
+
+ "%c%c" /* host length */
+ "%c%c" /* host allocated space */
+ "%c%c" /* host offset */
+ "%c%c" /* 2 zeroes */
+
+ "%c%c" /* session key length (unknown purpose) */
+ "%c%c" /* session key allocated space (unknown purpose) */
+ "%c%c" /* session key offset (unknown purpose) */
+ "%c%c" /* 2 zeroes */
+
+ "%c%c%c%c", /* flags */
+
+ /* domain string */
+ /* user string */
+ /* host string */
+ /* LanManager response */
+ /* NT response */
+
+ 0, /* zero termination */
+ 0, 0, 0, /* type-3 long, the 24 upper bits */
+
+ SHORTPAIR(0x18), /* LanManager response length, twice */
+ SHORTPAIR(0x18),
+ SHORTPAIR(lmrespoff),
+ 0x0, 0x0,
#ifdef USE_NTRESPONSES
- SHORTPAIR(ntresplen), /* NT-response length, twice */
- SHORTPAIR(ntresplen),
- SHORTPAIR(ntrespoff),
- 0x0, 0x0,
+ SHORTPAIR(ntresplen), /* NT-response length, twice */
+ SHORTPAIR(ntresplen),
+ SHORTPAIR(ntrespoff),
+ 0x0, 0x0,
#else
- 0x0, 0x0,
- 0x0, 0x0,
- 0x0, 0x0,
- 0x0, 0x0,
+ 0x0, 0x0,
+ 0x0, 0x0,
+ 0x0, 0x0,
+ 0x0, 0x0,
#endif
- SHORTPAIR(domlen),
- SHORTPAIR(domlen),
- SHORTPAIR(domoff),
- 0x0, 0x0,
+ SHORTPAIR(domlen),
+ SHORTPAIR(domlen),
+ SHORTPAIR(domoff),
+ 0x0, 0x0,
- SHORTPAIR(userlen),
- SHORTPAIR(userlen),
- SHORTPAIR(useroff),
- 0x0, 0x0,
+ SHORTPAIR(userlen),
+ SHORTPAIR(userlen),
+ SHORTPAIR(useroff),
+ 0x0, 0x0,
- SHORTPAIR(hostlen),
- SHORTPAIR(hostlen),
- SHORTPAIR(hostoff),
- 0x0, 0x0,
+ SHORTPAIR(hostlen),
+ SHORTPAIR(hostlen),
+ SHORTPAIR(hostoff),
+ 0x0, 0x0,
- 0x0, 0x0,
- 0x0, 0x0,
- 0x0, 0x0,
- 0x0, 0x0,
+ 0x0, 0x0,
+ 0x0, 0x0,
+ 0x0, 0x0,
+ 0x0, 0x0,
- LONGQUARTET(ntlm->flags));
+ LONGQUARTET(ntlm->flags));
DEBUGASSERT(size == 64);
DEBUGASSERT(size == (size_t)lmrespoff);
@@ -776,11 +779,14 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
});
#ifdef USE_NTRESPONSES
- if(size < (NTLM_BUFSIZE - ntresplen)) {
- DEBUGASSERT(size == (size_t)ntrespoff);
- memcpy(&ntlmbuf[size], ptr_ntresp, ntresplen);
- size += ntresplen;
+ /* ntresplen + size should not be risking an integer overflow here */
+ if(ntresplen + size > sizeof(ntlmbuf)) {
+ failf(data, "incoming NTLM message too big");
+ return CURLE_OUT_OF_MEMORY;
}
+ DEBUGASSERT(size == (size_t)ntrespoff);
+ memcpy(&ntlmbuf[size], ptr_ntresp, ntresplen);
+ size += ntresplen;
DEBUG_OUT({
fprintf(stderr, "\n ntresp=");
@@ -838,22 +844,22 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
/* Return with binary blob encoded into base64 */
result = Curl_base64_encode(data, (char *)ntlmbuf, size, outptr, outlen);
- Curl_auth_ntlm_cleanup(ntlm);
+ Curl_auth_cleanup_ntlm(ntlm);
return result;
}
/*
-* Curl_auth_ntlm_cleanup()
-*
-* This is used to clean up the NTLM specific data.
-*
-* Parameters:
-*
-* ntlm [in/out] - The NTLM data struct being cleaned up.
-*
-*/
-void Curl_auth_ntlm_cleanup(struct ntlmdata *ntlm)
+ * Curl_auth_cleanup_ntlm()
+ *
+ * This is used to clean up the NTLM specific data.
+ *
+ * Parameters:
+ *
+ * ntlm [in/out] - The NTLM data struct being cleaned up.
+ *
+ */
+void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm)
{
/* Free the target info */
Curl_safefree(ntlm->target_info);
diff --git a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c
index b66cfe737..589cca16c 100644
--- a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -95,7 +95,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
/* Clean up any former leftovers and initialise to defaults */
- Curl_auth_ntlm_cleanup(ntlm);
+ Curl_auth_cleanup_ntlm(ntlm);
/* Query the security package for NTLM */
status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
@@ -249,7 +249,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
char **outptr, size_t *outlen)
{
CURLcode result = CURLE_OK;
- SecBuffer type_2_buf;
+ SecBuffer type_2_bufs[2];
SecBuffer type_3_buf;
SecBufferDesc type_2_desc;
SecBufferDesc type_3_desc;
@@ -261,12 +261,39 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
(void) userp;
/* Setup the type-2 "input" security buffer */
- type_2_desc.ulVersion = SECBUFFER_VERSION;
- type_2_desc.cBuffers = 1;
- type_2_desc.pBuffers = &type_2_buf;
- type_2_buf.BufferType = SECBUFFER_TOKEN;
- type_2_buf.pvBuffer = ntlm->input_token;
- type_2_buf.cbBuffer = curlx_uztoul(ntlm->input_token_len);
+ type_2_desc.ulVersion = SECBUFFER_VERSION;
+ type_2_desc.cBuffers = 1;
+ type_2_desc.pBuffers = &type_2_bufs[0];
+ type_2_bufs[0].BufferType = SECBUFFER_TOKEN;
+ type_2_bufs[0].pvBuffer = ntlm->input_token;
+ type_2_bufs[0].cbBuffer = curlx_uztoul(ntlm->input_token_len);
+
+#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
+ /* ssl context comes from schannel.
+ * When extended protection is used in IIS server,
+ * we have to pass a second SecBuffer to the SecBufferDesc
+ * otherwise IIS will not pass the authentication (401 response).
+ * Minimum supported version is Windows 7.
+ * https://docs.microsoft.com/en-us/security-updates
+ * /SecurityAdvisories/2009/973811
+ */
+ if(ntlm->sslContext) {
+ SEC_CHANNEL_BINDINGS channelBindings;
+ SecPkgContext_Bindings pkgBindings;
+ pkgBindings.Bindings = &channelBindings;
+ status = s_pSecFn->QueryContextAttributes(
+ ntlm->sslContext,
+ SECPKG_ATTR_ENDPOINT_BINDINGS,
+ &pkgBindings
+ );
+ if(status == SEC_E_OK) {
+ type_2_desc.cBuffers++;
+ type_2_bufs[1].BufferType = SECBUFFER_CHANNEL_BINDINGS;
+ type_2_bufs[1].cbBuffer = pkgBindings.BindingsLength;
+ type_2_bufs[1].pvBuffer = pkgBindings.Bindings;
+ }
+ }
+#endif
/* Setup the type-3 "output" security buffer */
type_3_desc.ulVersion = SECBUFFER_VERSION;
@@ -296,13 +323,13 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
result = Curl_base64_encode(data, (char *) ntlm->output_token,
type_3_buf.cbBuffer, outptr, outlen);
- Curl_auth_ntlm_cleanup(ntlm);
+ Curl_auth_cleanup_ntlm(ntlm);
return result;
}
/*
- * Curl_auth_ntlm_cleanup()
+ * Curl_auth_cleanup_ntlm()
*
* This is used to clean up the NTLM specific data.
*
@@ -311,7 +338,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
* ntlm [in/out] - The NTLM data struct being cleaned up.
*
*/
-void Curl_auth_ntlm_cleanup(struct ntlmdata *ntlm)
+void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm)
{
/* Free our security context */
if(ntlm->context) {
diff --git a/Utilities/cmcurl/lib/vauth/oauth2.c b/Utilities/cmcurl/lib/vauth/oauth2.c
index 6288f89a3..b4e9f8e70 100644
--- a/Utilities/cmcurl/lib/vauth/oauth2.c
+++ b/Utilities/cmcurl/lib/vauth/oauth2.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -24,6 +24,9 @@
#include "curl_setup.h"
+#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \
+ !defined(CURL_DISABLE_POP3)
+
#include <curl/curl.h>
#include "urldata.h"
@@ -46,8 +49,8 @@
*
* data[in] - The session handle.
* user[in] - The user name.
- * host[in] - The host name(for OAUTHBEARER).
- * port[in] - The port(for OAUTHBEARER when not Port 80).
+ * host[in] - The host name.
+ * port[in] - The port(when not Port 80).
* bearer[in] - The bearer token.
* outptr[in / out] - The address where a pointer to newly allocated memory
* holding the result will be stored upon completion.
@@ -66,13 +69,11 @@ CURLcode Curl_auth_create_oauth_bearer_message(struct Curl_easy *data,
char *oauth = NULL;
/* Generate the message */
- if(host == NULL && (port == 0 || port == 80))
- oauth = aprintf("user=%s\1auth=Bearer %s\1\1", user, bearer);
- else if(port == 0 || port == 80)
- oauth = aprintf("user=%s\1host=%s\1auth=Bearer %s\1\1", user, host,
+ if(port == 0 || port == 80)
+ oauth = aprintf("n,a=%s,\1host=%s\1auth=Bearer %s\1\1", user, host,
bearer);
else
- oauth = aprintf("user=%s\1host=%s\1port=%ld\1auth=Bearer %s\1\1", user,
+ oauth = aprintf("n,a=%s,\1host=%s\1port=%ld\1auth=Bearer %s\1\1", user,
host, port, bearer);
if(!oauth)
return CURLE_OUT_OF_MEMORY;
@@ -84,3 +85,42 @@ CURLcode Curl_auth_create_oauth_bearer_message(struct Curl_easy *data,
return result;
}
+
+/*
+ * Curl_auth_create_xoauth_bearer_message()
+ *
+ * This is used to generate an already encoded XOAuth 2.0 message ready for
+ * sending to the recipient.
+ *
+ * Parameters:
+ *
+ * data[in] - The session handle.
+ * user[in] - The user name.
+ * bearer[in] - The bearer token.
+ * outptr[in / out] - The address where a pointer to newly allocated memory
+ * holding the result will be stored upon completion.
+ * outlen[out] - The length of the output message.
+ *
+ * Returns CURLE_OK on success.
+ */
+CURLcode Curl_auth_create_xoauth_bearer_message(struct Curl_easy *data,
+ const char *user,
+ const char *bearer,
+ char **outptr, size_t *outlen)
+{
+ CURLcode result = CURLE_OK;
+
+ /* Generate the message */
+ char *xoauth = aprintf("user=%s\1auth=Bearer %s\1\1", user, bearer);
+ if(!xoauth)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Base64 encode the reply */
+ result = Curl_base64_encode(data, xoauth, strlen(xoauth), outptr, outlen);
+
+ free(xoauth);
+
+ return result;
+}
+#endif /* disabled, no users */
+
diff --git a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c
index 4a48bdd20..5d43e1100 100644
--- a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c
+++ b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -97,7 +97,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
/* We finished successfully our part of authentication, but server
* rejected it (since we're again here). Exit with an error since we
* can't invent anything better */
- Curl_auth_spnego_cleanup(nego);
+ Curl_auth_cleanup_spnego(nego);
return CURLE_LOGIN_DENIED;
}
@@ -170,7 +170,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
Curl_gss_log_error(data, "gss_init_sec_context() failed: ",
major_status, minor_status);
- return CURLE_OUT_OF_MEMORY;
+ return CURLE_LOGIN_DENIED;
}
if(!output_token.value || !output_token.length) {
@@ -238,7 +238,7 @@ CURLcode Curl_auth_create_spnego_message(struct Curl_easy *data,
}
/*
- * Curl_auth_spnego_cleanup()
+ * Curl_auth_cleanup_spnego()
*
* This is used to clean up the SPNEGO (Negotiate) specific data.
*
@@ -247,7 +247,7 @@ CURLcode Curl_auth_create_spnego_message(struct Curl_easy *data,
* nego [in/out] - The Negotiate data struct being cleaned up.
*
*/
-void Curl_auth_spnego_cleanup(struct negotiatedata *nego)
+void Curl_auth_cleanup_spnego(struct negotiatedata *nego)
{
OM_uint32 minor_status;
@@ -273,6 +273,10 @@ void Curl_auth_spnego_cleanup(struct negotiatedata *nego)
/* Reset any variables */
nego->status = 0;
+ nego->noauthpersist = FALSE;
+ nego->havenoauthpersist = FALSE;
+ nego->havenegdata = FALSE;
+ nego->havemultiplerequests = FALSE;
}
#endif /* HAVE_GSSAPI && USE_SPNEGO */
diff --git a/Utilities/cmcurl/lib/vauth/spnego_sspi.c b/Utilities/cmcurl/lib/vauth/spnego_sspi.c
index 77d1895a5..4b21cc769 100644
--- a/Utilities/cmcurl/lib/vauth/spnego_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/spnego_sspi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -92,7 +92,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
size_t chlglen = 0;
unsigned char *chlg = NULL;
PSecPkgInfo SecurityPackage;
- SecBuffer chlg_buf;
+ SecBuffer chlg_buf[2];
SecBuffer resp_buf;
SecBufferDesc chlg_desc;
SecBufferDesc resp_desc;
@@ -107,7 +107,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
/* We finished successfully our part of authentication, but server
* rejected it (since we're again here). Exit with an error since we
* can't invent anything better */
- Curl_auth_spnego_cleanup(nego);
+ Curl_auth_cleanup_spnego(nego);
return CURLE_LOGIN_DENIED;
}
@@ -189,12 +189,39 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
}
/* Setup the challenge "input" security buffer */
- chlg_desc.ulVersion = SECBUFFER_VERSION;
- chlg_desc.cBuffers = 1;
- chlg_desc.pBuffers = &chlg_buf;
- chlg_buf.BufferType = SECBUFFER_TOKEN;
- chlg_buf.pvBuffer = chlg;
- chlg_buf.cbBuffer = curlx_uztoul(chlglen);
+ chlg_desc.ulVersion = SECBUFFER_VERSION;
+ chlg_desc.cBuffers = 1;
+ chlg_desc.pBuffers = &chlg_buf[0];
+ chlg_buf[0].BufferType = SECBUFFER_TOKEN;
+ chlg_buf[0].pvBuffer = chlg;
+ chlg_buf[0].cbBuffer = curlx_uztoul(chlglen);
+
+#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
+ /* ssl context comes from Schannel.
+ * When extended protection is used in IIS server,
+ * we have to pass a second SecBuffer to the SecBufferDesc
+ * otherwise IIS will not pass the authentication (401 response).
+ * Minimum supported version is Windows 7.
+ * https://docs.microsoft.com/en-us/security-updates
+ * /SecurityAdvisories/2009/973811
+ */
+ if(nego->sslContext) {
+ SEC_CHANNEL_BINDINGS channelBindings;
+ SecPkgContext_Bindings pkgBindings;
+ pkgBindings.Bindings = &channelBindings;
+ nego->status = s_pSecFn->QueryContextAttributes(
+ nego->sslContext,
+ SECPKG_ATTR_ENDPOINT_BINDINGS,
+ &pkgBindings
+ );
+ if(nego->status == SEC_E_OK) {
+ chlg_desc.cBuffers++;
+ chlg_buf[1].BufferType = SECBUFFER_CHANNEL_BINDINGS;
+ chlg_buf[1].cbBuffer = pkgBindings.BindingsLength;
+ chlg_buf[1].pvBuffer = pkgBindings.Bindings;
+ }
+ }
+#endif
}
/* Setup the response "output" security buffer */
@@ -221,8 +248,9 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
free(chlg);
if(GSS_ERROR(nego->status)) {
+ char buffer[STRERROR_LEN];
failf(data, "InitializeSecurityContext failed: %s",
- Curl_sspi_strerror(data->easy_conn, nego->status));
+ Curl_sspi_strerror(nego->status, buffer, sizeof(buffer)));
return CURLE_OUT_OF_MEMORY;
}
@@ -279,7 +307,7 @@ CURLcode Curl_auth_create_spnego_message(struct Curl_easy *data,
}
/*
- * Curl_auth_spnego_cleanup()
+ * Curl_auth_cleanup_spnego()
*
* This is used to clean up the SPNEGO (Negotiate) specific data.
*
@@ -288,7 +316,7 @@ CURLcode Curl_auth_create_spnego_message(struct Curl_easy *data,
* nego [in/out] - The Negotiate data struct being cleaned up.
*
*/
-void Curl_auth_spnego_cleanup(struct negotiatedata *nego)
+void Curl_auth_cleanup_spnego(struct negotiatedata *nego)
{
/* Free our security context */
if(nego->context) {
@@ -315,6 +343,10 @@ void Curl_auth_spnego_cleanup(struct negotiatedata *nego)
/* Reset any variables */
nego->status = 0;
nego->token_max = 0;
+ nego->noauthpersist = FALSE;
+ nego->havenoauthpersist = FALSE;
+ nego->havenegdata = FALSE;
+ nego->havemultiplerequests = FALSE;
}
#endif /* USE_WINDOWS_SSPI && USE_SPNEGO */
diff --git a/Utilities/cmcurl/lib/vauth/vauth.c b/Utilities/cmcurl/lib/vauth/vauth.c
index 502d443ab..a9c5c9c4f 100644
--- a/Utilities/cmcurl/lib/vauth/vauth.c
+++ b/Utilities/cmcurl/lib/vauth/vauth.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2014 - 2016, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) 2014 - 2019, Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -105,26 +105,26 @@ TCHAR *Curl_auth_build_spn(const char *service, const char *host,
#endif /* USE_WINDOWS_SSPI */
/*
-* Curl_auth_user_contains_domain()
-*
-* This is used to test if the specified user contains a Windows domain name as
-* follows:
-*
-* User\Domain (Down-level Logon Name)
-* User/Domain (curl Down-level format - for compatibility with existing code)
-* User@Domain (User Principal Name)
-*
-* Note: The user name may be empty when using a GSS-API library or Windows SSPI
-* as the user and domain are either obtained from the credentials cache when
-* using GSS-API or via the currently logged in user's credentials when using
-* Windows SSPI.
-*
-* Parameters:
-*
-* user [in] - The user name.
-*
-* Returns TRUE on success; otherwise FALSE.
-*/
+ * Curl_auth_user_contains_domain()
+ *
+ * This is used to test if the specified user contains a Windows domain name as
+ * follows:
+ *
+ * Domain\User (Down-level Logon Name)
+ * Domain/User (curl Down-level format - for compatibility with existing code)
+ * User@Domain (User Principal Name)
+ *
+ * Note: The user name may be empty when using a GSS-API library or Windows
+ * SSPI as the user and domain are either obtained from the credentials cache
+ * when using GSS-API or via the currently logged in user's credentials when
+ * using Windows SSPI.
+ *
+ * Parameters:
+ *
+ * user [in] - The user name.
+ *
+ * Returns TRUE on success; otherwise FALSE.
+ */
bool Curl_auth_user_contains_domain(const char *user)
{
bool valid = FALSE;
diff --git a/Utilities/cmcurl/lib/vauth/vauth.h b/Utilities/cmcurl/lib/vauth/vauth.h
index f43064211..73bd25ed5 100644
--- a/Utilities/cmcurl/lib/vauth/vauth.h
+++ b/Utilities/cmcurl/lib/vauth/vauth.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2014 - 2017, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) 2014 - 2019, Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -60,8 +60,9 @@ bool Curl_auth_user_contains_domain(const char *user);
/* This is used to generate a base64 encoded PLAIN cleartext message */
CURLcode Curl_auth_create_plain_message(struct Curl_easy *data,
- const char *userp,
- const char *passwdp,
+ const char *authzid,
+ const char *authcid,
+ const char *passwd,
char **outptr, size_t *outlen);
/* This is used to generate a base64 encoded LOGIN cleartext message */
@@ -141,7 +142,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
char **outptr, size_t *outlen);
/* This is used to clean up the NTLM specific data */
-void Curl_auth_ntlm_cleanup(struct ntlmdata *ntlm);
+void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm);
#endif /* USE_NTLM */
/* This is used to generate a base64 encoded OAuth 2.0 message */
@@ -151,6 +152,13 @@ CURLcode Curl_auth_create_oauth_bearer_message(struct Curl_easy *data,
const long port,
const char *bearer,
char **outptr, size_t *outlen);
+
+/* This is used to generate a base64 encoded XOAuth 2.0 message */
+CURLcode Curl_auth_create_xoauth_bearer_message(struct Curl_easy *data,
+ const char *user,
+ const char *bearer,
+ char **outptr, size_t *outlen);
+
#if defined(USE_KERBEROS5)
/* This is used to evaluate if GSSAPI (Kerberos V5) is supported */
bool Curl_auth_is_gssapi_supported(void);
@@ -176,7 +184,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
size_t *outlen);
/* This is used to clean up the GSSAPI specific data */
-void Curl_auth_gssapi_cleanup(struct kerberos5data *krb5);
+void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5);
#endif /* USE_KERBEROS5 */
#if defined(USE_SPNEGO)
@@ -200,7 +208,7 @@ CURLcode Curl_auth_create_spnego_message(struct Curl_easy *data,
char **outptr, size_t *outlen);
/* This is used to clean up the SPNEGO specifiec data */
-void Curl_auth_spnego_cleanup(struct negotiatedata *nego);
+void Curl_auth_cleanup_spnego(struct negotiatedata *nego);
#endif /* USE_SPNEGO */
diff --git a/Utilities/cmcurl/lib/version.c b/Utilities/cmcurl/lib/version.c
index 05c2cd8b0..14b0531d3 100644
--- a/Utilities/cmcurl/lib/version.c
+++ b/Utilities/cmcurl/lib/version.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -31,7 +31,7 @@
#ifdef USE_ARES
# if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
- (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__))
+ (defined(WIN32) || defined(__SYMBIAN32__))
# define CARES_STATICLIB
# endif
# include <ares.h>
@@ -95,7 +95,7 @@ static size_t brotli_version(char *buf, size_t bufsz)
unsigned int minor = (brotli_version & 0x00FFFFFF) >> 12;
unsigned int patch = brotli_version & 0x00000FFF;
- return snprintf(buf, bufsz, "%u.%u.%u", major, minor, patch);
+ return msnprintf(buf, bufsz, "%u.%u.%u", major, minor, patch);
}
#endif
@@ -126,12 +126,12 @@ char *curl_version(void)
}
#ifdef HAVE_LIBZ
- len = snprintf(ptr, left, " zlib/%s", zlibVersion());
+ len = msnprintf(ptr, left, " zlib/%s", zlibVersion());
left -= len;
ptr += len;
#endif
#ifdef HAVE_BROTLI
- len = snprintf(ptr, left, "%s", " brotli/");
+ len = msnprintf(ptr, left, "%s", " brotli/");
left -= len;
ptr += len;
len = brotli_version(ptr, left);
@@ -140,45 +140,45 @@ char *curl_version(void)
#endif
#ifdef USE_ARES
/* this function is only present in c-ares, not in the original ares */
- len = snprintf(ptr, left, " c-ares/%s", ares_version(NULL));
+ len = msnprintf(ptr, left, " c-ares/%s", ares_version(NULL));
left -= len;
ptr += len;
#endif
#ifdef USE_LIBIDN2
if(idn2_check_version(IDN2_VERSION)) {
- len = snprintf(ptr, left, " libidn2/%s", idn2_check_version(NULL));
+ len = msnprintf(ptr, left, " libidn2/%s", idn2_check_version(NULL));
left -= len;
ptr += len;
}
#endif
#ifdef USE_LIBPSL
- len = snprintf(ptr, left, " libpsl/%s", psl_get_version());
+ len = msnprintf(ptr, left, " libpsl/%s", psl_get_version());
left -= len;
ptr += len;
#endif
#ifdef USE_WIN32_IDN
- len = snprintf(ptr, left, " WinIDN");
+ len = msnprintf(ptr, left, " WinIDN");
left -= len;
ptr += len;
#endif
#if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS)
#ifdef _LIBICONV_VERSION
- len = snprintf(ptr, left, " iconv/%d.%d",
- _LIBICONV_VERSION >> 8, _LIBICONV_VERSION & 255);
+ len = msnprintf(ptr, left, " iconv/%d.%d",
+ _LIBICONV_VERSION >> 8, _LIBICONV_VERSION & 255);
#else
/* version unknown */
- len = snprintf(ptr, left, " iconv");
+ len = msnprintf(ptr, left, " iconv");
#endif /* _LIBICONV_VERSION */
left -= len;
ptr += len;
#endif
#ifdef USE_LIBSSH2
- len = snprintf(ptr, left, " libssh2/%s", CURL_LIBSSH2_VERSION);
+ len = msnprintf(ptr, left, " libssh2/%s", CURL_LIBSSH2_VERSION);
left -= len;
ptr += len;
#endif
#ifdef USE_LIBSSH
- len = snprintf(ptr, left, " libssh/%s", CURL_LIBSSH_VERSION);
+ len = msnprintf(ptr, left, " libssh/%s", CURL_LIBSSH_VERSION);
left -= len;
ptr += len;
#endif
@@ -197,14 +197,14 @@ char *curl_version(void)
else
suff[0] = '\0';
- snprintf(ptr, left, " librtmp/%d.%d%s",
- RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff,
- suff);
+ msnprintf(ptr, left, " librtmp/%d.%d%s",
+ RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff,
+ suff);
/*
If another lib version is added below this one, this code would
also have to do:
- len = what snprintf() returned
+ len = what msnprintf() returned
left -= len;
ptr += len;
@@ -212,6 +212,10 @@ char *curl_version(void)
}
#endif
+ /* Silent scan-build even if librtmp is not enabled. */
+ (void) left;
+ (void) ptr;
+
initialized = true;
return version;
}
@@ -270,7 +274,7 @@ static const char * const protocols[] = {
#ifndef CURL_DISABLE_RTSP
"rtsp",
#endif
-#if defined(USE_LIBSSH) || defined(USE_LIBSSH2)
+#if defined(USE_SSH)
"scp",
"sftp",
#endif
@@ -366,6 +370,9 @@ static curl_version_info_data version_info = {
#if defined(HAVE_BROTLI)
| CURL_VERSION_BROTLI
#endif
+#if defined(USE_ALTSVC)
+ | CURL_VERSION_ALTSVC
+#endif
,
NULL, /* ssl_version */
0, /* ssl_version_num, this is kept at zero */
@@ -383,12 +390,16 @@ static curl_version_info_data version_info = {
curl_version_info_data *curl_version_info(CURLversion stamp)
{
static bool initialized;
-#if defined(USE_LIBSSH) || defined(USE_LIBSSH2)
+#if defined(USE_SSH)
static char ssh_buffer[80];
#endif
#ifdef USE_SSL
+#ifdef CURL_WITH_MULTI_SSL
+ static char ssl_buffer[200];
+#else
static char ssl_buffer[80];
#endif
+#endif
#ifdef HAVE_BROTLI
static char brotli_buffer[80];
#endif
@@ -436,10 +447,10 @@ curl_version_info_data *curl_version_info(CURLversion stamp)
#endif
#if defined(USE_LIBSSH2)
- snprintf(ssh_buffer, sizeof(ssh_buffer), "libssh2/%s", LIBSSH2_VERSION);
+ msnprintf(ssh_buffer, sizeof(ssh_buffer), "libssh2/%s", LIBSSH2_VERSION);
version_info.libssh_version = ssh_buffer;
#elif defined(USE_LIBSSH)
- snprintf(ssh_buffer, sizeof(ssh_buffer), "libssh/%s", CURL_LIBSSH_VERSION);
+ msnprintf(ssh_buffer, sizeof(ssh_buffer), "libssh/%s", CURL_LIBSSH_VERSION);
version_info.libssh_version = ssh_buffer;
#endif
diff --git a/Utilities/cmcurl/lib/vtls/axtls.c b/Utilities/cmcurl/lib/vtls/axtls.c
deleted file mode 100644
index b262392a0..000000000
--- a/Utilities/cmcurl/lib/vtls/axtls.c
+++ /dev/null
@@ -1,741 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) 2010, DirecTV, Contact: Eric Hu, <ehu@directv.com>.
- * Copyright (C) 2010 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ***************************************************************************/
-
-/*
- * Source file for all axTLS-specific code for the TLS/SSL layer. No code
- * but vtls.c should ever call or use these functions.
- */
-
-#include "curl_setup.h"
-
-#ifdef USE_AXTLS
-
-#error axTLS support has been disabled in curl due to doubts about quality,
-#error user dedication and a lack of use/testing. We urge users to consider
-#error using a more established TLS backend instead.
-
-#include <axTLS/config.h>
-#include <axTLS/ssl.h>
-#include "axtls.h"
-
-#include "sendf.h"
-#include "inet_pton.h"
-#include "vtls.h"
-#include "parsedate.h"
-#include "connect.h" /* for the connect timeout */
-#include "select.h"
-#include "curl_printf.h"
-#include "hostcheck.h"
-#include <unistd.h>
-
-/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
-
-struct ssl_backend_data {
- SSL_CTX* ssl_ctx;
- SSL* ssl;
-};
-
-#define BACKEND connssl->backend
-
-static CURLcode map_error_to_curl(int axtls_err)
-{
- switch(axtls_err) {
- case SSL_ERROR_NOT_SUPPORTED:
- case SSL_ERROR_INVALID_VERSION:
- case -70: /* protocol version alert from server */
- return CURLE_UNSUPPORTED_PROTOCOL;
- break;
- case SSL_ERROR_NO_CIPHER:
- return CURLE_SSL_CIPHER;
- break;
- case SSL_ERROR_BAD_CERTIFICATE: /* this may be bad server cert too */
- case SSL_ERROR_NO_CERT_DEFINED:
- case -42: /* bad certificate alert from server */
- case -43: /* unsupported cert alert from server */
- case -44: /* cert revoked alert from server */
- case -45: /* cert expired alert from server */
- case -46: /* cert unknown alert from server */
- return CURLE_SSL_CERTPROBLEM;
- break;
- case SSL_X509_ERROR(X509_NOT_OK):
- case SSL_X509_ERROR(X509_VFY_ERROR_NO_TRUSTED_CERT):
- case SSL_X509_ERROR(X509_VFY_ERROR_BAD_SIGNATURE):
- case SSL_X509_ERROR(X509_VFY_ERROR_NOT_YET_VALID):
- case SSL_X509_ERROR(X509_VFY_ERROR_EXPIRED):
- case SSL_X509_ERROR(X509_VFY_ERROR_SELF_SIGNED):
- case SSL_X509_ERROR(X509_VFY_ERROR_INVALID_CHAIN):
- case SSL_X509_ERROR(X509_VFY_ERROR_UNSUPPORTED_DIGEST):
- case SSL_X509_ERROR(X509_INVALID_PRIV_KEY):
- return CURLE_PEER_FAILED_VERIFICATION;
- break;
- case -48: /* unknown ca alert from server */
- return CURLE_SSL_CACERT;
- break;
- case -49: /* access denied alert from server */
- return CURLE_REMOTE_ACCESS_DENIED;
- break;
- case SSL_ERROR_CONN_LOST:
- case SSL_ERROR_SOCK_SETUP_FAILURE:
- case SSL_ERROR_INVALID_HANDSHAKE:
- case SSL_ERROR_INVALID_PROT_MSG:
- case SSL_ERROR_INVALID_HMAC:
- case SSL_ERROR_INVALID_SESSION:
- case SSL_ERROR_INVALID_KEY: /* it's too bad this doesn't map better */
- case SSL_ERROR_FINISHED_INVALID:
- case SSL_ERROR_NO_CLIENT_RENOG:
- default:
- return CURLE_SSL_CONNECT_ERROR;
- break;
- }
-}
-
-static Curl_recv axtls_recv;
-static Curl_send axtls_send;
-
-static void free_ssl_structs(struct ssl_connect_data *connssl)
-{
- if(BACKEND->ssl) {
- ssl_free(BACKEND->ssl);
- BACKEND->ssl = NULL;
- }
- if(BACKEND->ssl_ctx) {
- ssl_ctx_free(BACKEND->ssl_ctx);
- BACKEND->ssl_ctx = NULL;
- }
-}
-
-/*
- * For both blocking and non-blocking connects, this function sets up the
- * ssl context and state. This function is called after the TCP connect
- * has completed.
- */
-static CURLcode connect_prep(struct connectdata *conn, int sockindex)
-{
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- struct Curl_easy *data = conn->data;
- SSL_CTX *ssl_ctx;
- SSL *ssl = NULL;
- int cert_types[] = {SSL_OBJ_X509_CERT, SSL_OBJ_PKCS12, 0};
- int key_types[] = {SSL_OBJ_RSA_KEY, SSL_OBJ_PKCS8, SSL_OBJ_PKCS12, 0};
- int i, ssl_fcn_return;
-
- /* Assuming users will not compile in custom key/cert to axTLS.
- * Also, even for blocking connects, use axTLS non-blocking feature.
- */
- uint32_t client_option = SSL_NO_DEFAULT_KEY |
- SSL_SERVER_VERIFY_LATER |
- SSL_CONNECT_IN_PARTS;
-
- if(connssl->state == ssl_connection_complete)
- /* to make us tolerant against being called more than once for the
- same connection */
- return CURLE_OK;
-
- if(SSL_CONN_CONFIG(version_max) != CURL_SSLVERSION_MAX_NONE) {
- failf(data, "axtls does not support CURL_SSLVERSION_MAX");
- return CURLE_SSL_CONNECT_ERROR;
- }
-
-
- /* axTLS only supports TLSv1 */
- /* check to see if we've been told to use an explicit SSL/TLS version */
- switch(SSL_CONN_CONFIG(version)) {
- case CURL_SSLVERSION_DEFAULT:
- case CURL_SSLVERSION_TLSv1:
- break;
- default:
- failf(data, "axTLS only supports TLS 1.0 and 1.1, "
- "and it cannot be specified which one to use");
- return CURLE_SSL_CONNECT_ERROR;
- }
-
-#ifdef AXTLSDEBUG
- client_option |= SSL_DISPLAY_STATES | SSL_DISPLAY_RSA | SSL_DISPLAY_CERTS;
-#endif /* AXTLSDEBUG */
-
- /* Allocate an SSL_CTX struct */
- ssl_ctx = ssl_ctx_new(client_option, SSL_DEFAULT_CLNT_SESS);
- if(ssl_ctx == NULL) {
- failf(data, "unable to create client SSL context");
- return CURLE_SSL_CONNECT_ERROR;
- }
-
- BACKEND->ssl_ctx = ssl_ctx;
- BACKEND->ssl = NULL;
-
- /* Load the trusted CA cert bundle file */
- if(SSL_CONN_CONFIG(CAfile)) {
- if(ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CACERT,
- SSL_CONN_CONFIG(CAfile), NULL) != SSL_OK) {
- infof(data, "error reading ca cert file %s \n",
- SSL_CONN_CONFIG(CAfile));
- if(SSL_CONN_CONFIG(verifypeer)) {
- return CURLE_SSL_CACERT_BADFILE;
- }
- }
- else
- infof(data, "found certificates in %s\n", SSL_CONN_CONFIG(CAfile));
- }
-
- /* gtls.c tasks we're skipping for now:
- * 1) certificate revocation list checking
- * 2) dns name assignment to host
- * 3) set protocol priority. axTLS is TLSv1 only, so can probably ignore
- * 4) set certificate priority. axTLS ignores type and sends certs in
- * order added. can probably ignore this.
- */
-
- /* Load client certificate */
- if(SSL_SET_OPTION(cert)) {
- i = 0;
- /* Instead of trying to analyze cert type here, let axTLS try them all. */
- while(cert_types[i] != 0) {
- ssl_fcn_return = ssl_obj_load(ssl_ctx, cert_types[i],
- SSL_SET_OPTION(cert), NULL);
- if(ssl_fcn_return == SSL_OK) {
- infof(data, "successfully read cert file %s \n",
- SSL_SET_OPTION(cert));
- break;
- }
- i++;
- }
- /* Tried all cert types, none worked. */
- if(cert_types[i] == 0) {
- failf(data, "%s is not x509 or pkcs12 format",
- SSL_SET_OPTION(cert));
- return CURLE_SSL_CERTPROBLEM;
- }
- }
-
- /* Load client key.
- If a pkcs12 file successfully loaded a cert, then there's nothing to do
- because the key has already been loaded. */
- if(SSL_SET_OPTION(key) && cert_types[i] != SSL_OBJ_PKCS12) {
- i = 0;
- /* Instead of trying to analyze key type here, let axTLS try them all. */
- while(key_types[i] != 0) {
- ssl_fcn_return = ssl_obj_load(ssl_ctx, key_types[i],
- SSL_SET_OPTION(key), NULL);
- if(ssl_fcn_return == SSL_OK) {
- infof(data, "successfully read key file %s \n",
- SSL_SET_OPTION(key));
- break;
- }
- i++;
- }
- /* Tried all key types, none worked. */
- if(key_types[i] == 0) {
- failf(data, "Failure: %s is not a supported key file",
- SSL_SET_OPTION(key));
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
-
- /* gtls.c does more here that is being left out for now
- * 1) set session credentials. can probably ignore since axtls puts this
- * info in the ssl_ctx struct
- * 2) setting up callbacks. these seem gnutls specific
- */
-
- if(SSL_SET_OPTION(primary.sessionid)) {
- const uint8_t *ssl_sessionid;
- size_t ssl_idsize;
-
- /* In axTLS, handshaking happens inside ssl_client_new. */
- Curl_ssl_sessionid_lock(conn);
- if(!Curl_ssl_getsessionid(conn, (void **) &ssl_sessionid, &ssl_idsize,
- sockindex)) {
- /* we got a session id, use it! */
- infof(data, "SSL re-using session ID\n");
- ssl = ssl_client_new(ssl_ctx, conn->sock[sockindex],
- ssl_sessionid, (uint8_t)ssl_idsize, NULL);
- }
- Curl_ssl_sessionid_unlock(conn);
- }
-
- if(!ssl)
- ssl = ssl_client_new(ssl_ctx, conn->sock[sockindex], NULL, 0, NULL);
-
- BACKEND->ssl = ssl;
- return CURLE_OK;
-}
-
-static void Curl_axtls_close(struct connectdata *conn, int sockindex)
-{
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-
- infof(conn->data, " Curl_axtls_close\n");
-
- /* line from openssl.c: (void)SSL_shutdown(BACKEND->ssl);
- axTLS compat layer does nothing for SSL_shutdown */
-
- /* The following line is from openssl.c. There seems to be no axTLS
- equivalent. ssl_free and ssl_ctx_free close things.
- SSL_set_connect_state(connssl->handle); */
-
- free_ssl_structs(connssl);
-}
-
-/*
- * For both blocking and non-blocking connects, this function finalizes the
- * SSL connection.
- */
-static CURLcode connect_finish(struct connectdata *conn, int sockindex)
-{
- struct Curl_easy *data = conn->data;
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- SSL *ssl = BACKEND->ssl;
- const char *peer_CN;
- uint32_t dns_altname_index;
- const char *dns_altname;
- int8_t found_subject_alt_names = 0;
- int8_t found_subject_alt_name_matching_conn = 0;
- const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
- conn->host.name;
- const char * const dispname = SSL_IS_PROXY() ?
- conn->http_proxy.host.dispname : conn->host.dispname;
-
- /* Here, gtls.c gets the peer certificates and fails out depending on
- * settings in "data." axTLS api doesn't have get cert chain fcn, so omit?
- */
-
- /* Verify server's certificate */
- if(SSL_CONN_CONFIG(verifypeer)) {
- if(ssl_verify_cert(ssl) != SSL_OK) {
- Curl_axtls_close(conn, sockindex);
- failf(data, "server cert verify failed");
- return CURLE_PEER_FAILED_VERIFICATION;
- }
- }
- else
- infof(data, "\t server certificate verification SKIPPED\n");
-
- /* Here, gtls.c does issuer verification. axTLS has no straightforward
- * equivalent, so omitting for now.*/
-
- /* Here, gtls.c does the following
- * 1) x509 hostname checking per RFC2818. axTLS doesn't support this, but
- * it seems useful. This is now implemented, by Oscar Koeroo
- * 2) checks cert validity based on time. axTLS does this in ssl_verify_cert
- * 3) displays a bunch of cert information. axTLS doesn't support most of
- * this, but a couple fields are available.
- */
-
- /* There is no (DNS) Altnames count in the version 1.4.8 API. There is a
- risk of an inifite loop */
- for(dns_altname_index = 0; ; dns_altname_index++) {
- dns_altname = ssl_get_cert_subject_alt_dnsname(ssl, dns_altname_index);
- if(dns_altname == NULL) {
- break;
- }
- found_subject_alt_names = 1;
-
- infof(data, "\tComparing subject alt name DNS with hostname: %s <-> %s\n",
- dns_altname, hostname);
- if(Curl_cert_hostcheck(dns_altname, hostname)) {
- found_subject_alt_name_matching_conn = 1;
- break;
- }
- }
-
- /* RFC2818 checks */
- if(found_subject_alt_names && !found_subject_alt_name_matching_conn) {
- if(SSL_CONN_CONFIG(verifyhost)) {
- /* Break connection ! */
- Curl_axtls_close(conn, sockindex);
- failf(data, "\tsubjectAltName(s) do not match %s\n", dispname);
- return CURLE_PEER_FAILED_VERIFICATION;
- }
- else
- infof(data, "\tsubjectAltName(s) do not match %s\n", dispname);
- }
- else if(found_subject_alt_names == 0) {
- /* Per RFC2818, when no Subject Alt Names were available, examine the peer
- CN as a legacy fallback */
- peer_CN = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME);
- if(peer_CN == NULL) {
- if(SSL_CONN_CONFIG(verifyhost)) {
- Curl_axtls_close(conn, sockindex);
- failf(data, "unable to obtain common name from peer certificate");
- return CURLE_PEER_FAILED_VERIFICATION;
- }
- else
- infof(data, "unable to obtain common name from peer certificate");
- }
- else {
- if(!Curl_cert_hostcheck((const char *)peer_CN, hostname)) {
- if(SSL_CONN_CONFIG(verifyhost)) {
- /* Break connection ! */
- Curl_axtls_close(conn, sockindex);
- failf(data, "\tcommon name \"%s\" does not match \"%s\"\n",
- peer_CN, dispname);
- return CURLE_PEER_FAILED_VERIFICATION;
- }
- else
- infof(data, "\tcommon name \"%s\" does not match \"%s\"\n",
- peer_CN, dispname);
- }
- }
- }
-
- /* General housekeeping */
- connssl->state = ssl_connection_complete;
- conn->recv[sockindex] = axtls_recv;
- conn->send[sockindex] = axtls_send;
-
- /* Put our freshly minted SSL session in cache */
- if(SSL_SET_OPTION(primary.sessionid)) {
- const uint8_t *ssl_sessionid = ssl_get_session_id(ssl);
- size_t ssl_idsize = ssl_get_session_id_size(ssl);
- Curl_ssl_sessionid_lock(conn);
- if(Curl_ssl_addsessionid(conn, (void *) ssl_sessionid, ssl_idsize,
- sockindex) != CURLE_OK)
- infof(data, "failed to add session to cache\n");
- Curl_ssl_sessionid_unlock(conn);
- }
-
- return CURLE_OK;
-}
-
-/*
- * Use axTLS's non-blocking connection feature to open an SSL connection.
- * This is called after a TCP connection is already established.
- */
-static CURLcode Curl_axtls_connect_nonblocking(struct connectdata *conn,
- int sockindex, bool *done)
-{
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- CURLcode conn_step;
- int ssl_fcn_return;
- int i;
-
- *done = FALSE;
- /* connectdata is calloc'd and connecting_state is only changed in this
- function, so this is safe, as the state is effectively initialized. */
- if(connssl->connecting_state == ssl_connect_1) {
- conn_step = connect_prep(conn, sockindex);
- if(conn_step != CURLE_OK) {
- Curl_axtls_close(conn, sockindex);
- return conn_step;
- }
- connssl->connecting_state = ssl_connect_2;
- }
-
- if(connssl->connecting_state == ssl_connect_2) {
- /* Check to make sure handshake was ok. */
- if(ssl_handshake_status(BACKEND->ssl) != SSL_OK) {
- /* Loop to perform more work in between sleeps. This is work around the
- fact that axtls does not expose any knowledge about when work needs
- to be performed. This can save ~25% of time on SSL handshakes. */
- for(i = 0; i<5; i++) {
- ssl_fcn_return = ssl_read(BACKEND->ssl, NULL);
- if(ssl_fcn_return < 0) {
- Curl_axtls_close(conn, sockindex);
- ssl_display_error(ssl_fcn_return); /* goes to stdout. */
- return map_error_to_curl(ssl_fcn_return);
- }
- return CURLE_OK;
- }
- }
- infof(conn->data, "handshake completed successfully\n");
- connssl->connecting_state = ssl_connect_3;
- }
-
- if(connssl->connecting_state == ssl_connect_3) {
- conn_step = connect_finish(conn, sockindex);
- if(conn_step != CURLE_OK) {
- Curl_axtls_close(conn, sockindex);
- return conn_step;
- }
-
- /* Reset connect state */
- connssl->connecting_state = ssl_connect_1;
-
- *done = TRUE;
- return CURLE_OK;
- }
-
- /* Unrecognized state. Things are very bad. */
- connssl->state = ssl_connection_none;
- connssl->connecting_state = ssl_connect_1;
- /* Return value perhaps not strictly correct, but distinguishes the issue.*/
- return CURLE_BAD_FUNCTION_ARGUMENT;
-}
-
-
-/*
- * This function is called after the TCP connect has completed. Setup the TLS
- * layer and do all necessary magic for a blocking connect.
- */
-static CURLcode Curl_axtls_connect(struct connectdata *conn, int sockindex)
-{
- struct Curl_easy *data = conn->data;
- CURLcode conn_step = connect_prep(conn, sockindex);
- int ssl_fcn_return;
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- SSL *ssl = BACKEND->ssl;
- long timeout_ms;
-
- if(conn_step != CURLE_OK) {
- Curl_axtls_close(conn, sockindex);
- return conn_step;
- }
-
- /* Check to make sure handshake was ok. */
- while(ssl_handshake_status(ssl) != SSL_OK) {
- /* check allowed time left */
- timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
-
- ssl_fcn_return = ssl_read(ssl, NULL);
- if(ssl_fcn_return < 0) {
- Curl_axtls_close(conn, sockindex);
- ssl_display_error(ssl_fcn_return); /* goes to stdout. */
- return map_error_to_curl(ssl_fcn_return);
- }
- /* TODO: avoid polling */
- Curl_wait_ms(10);
- }
- infof(conn->data, "handshake completed successfully\n");
-
- conn_step = connect_finish(conn, sockindex);
- if(conn_step != CURLE_OK) {
- Curl_axtls_close(conn, sockindex);
- return conn_step;
- }
-
- return CURLE_OK;
-}
-
-/* return number of sent (non-SSL) bytes */
-static ssize_t axtls_send(struct connectdata *conn,
- int sockindex,
- const void *mem,
- size_t len,
- CURLcode *err)
-{
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- /* ssl_write() returns 'int' while write() and send() returns 'size_t' */
- int rc = ssl_write(BACKEND->ssl, mem, (int)len);
-
- infof(conn->data, " axtls_send\n");
-
- if(rc < 0) {
- *err = map_error_to_curl(rc);
- rc = -1; /* generic error code for send failure */
- }
-
- *err = CURLE_OK;
- return rc;
-}
-
-/*
- * This function is called to shut down the SSL layer but keep the
- * socket open (CCC - Clear Command Channel)
- */
-static int Curl_axtls_shutdown(struct connectdata *conn, int sockindex)
-{
- /* Outline taken from openssl.c since functions are in axTLS compat layer.
- axTLS's error set is much smaller, so a lot of error-handling was removed.
- */
- int retval = 0;
- struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- struct Curl_easy *data = conn->data;
- uint8_t *buf;
- ssize_t nread;
-
- infof(conn->data, " Curl_axtls_shutdown\n");
-
- /* This has only been tested on the proftpd server, and the mod_tls code
- sends a close notify alert without waiting for a close notify alert in
- response. Thus we wait for a close notify alert from the server, but
- we do not send one. Let's hope other servers do the same... */
-
- /* axTLS compat layer does nothing for SSL_shutdown, so we do nothing too
- if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)
- (void)SSL_shutdown(BACKEND->ssl);
- */
-
- if(BACKEND->ssl) {
- int what = SOCKET_READABLE(conn->sock[sockindex], SSL_SHUTDOWN_TIMEOUT);
- if(what > 0) {
- /* Something to read, let's do it and hope that it is the close
- notify alert from the server. buf is managed internally by
- axTLS and will be released upon calling ssl_free via
- free_ssl_structs. */
- nread = (ssize_t)ssl_read(BACKEND->ssl, &buf);
-
- if(nread < SSL_OK) {
- failf(data, "close notify alert not received during shutdown");
- retval = -1;
- }
- }
- else if(0 == what) {
- /* timeout */
- failf(data, "SSL shutdown timeout");
- }
- else {
- /* anything that gets here is fatally bad */
- failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
- retval = -1;
- }
-
- free_ssl_structs(connssl);
- }
- return retval;
-}
-
-static ssize_t axtls_recv(struct connectdata *conn, /* connection data */
- int num, /* socketindex */
- char *buf, /* store read data here */
- size_t buffersize, /* max amount to read */
- CURLcode *err)
-{
- struct ssl_connect_data *connssl = &conn->ssl[num];
- ssize_t ret = 0;
- uint8_t *read_buf;
-
- infof(conn->data, " axtls_recv\n");
-
- *err = CURLE_OK;
- if(connssl) {
- ret = ssl_read(BACKEND->ssl, &read_buf);
- if(ret > SSL_OK) {
- /* ssl_read returns SSL_OK if there is more data to read, so if it is
- larger, then all data has been read already. */
- memcpy(buf, read_buf,
- (size_t)ret > buffersize ? buffersize : (size_t)ret);
- }
- else if(ret == SSL_OK) {
- /* more data to be read, signal caller to call again */
- *err = CURLE_AGAIN;
- ret = -1;
- }
- else if(ret == -3) {
- /* With patched axTLS, SSL_CLOSE_NOTIFY=-3. Hard-coding until axTLS
- team approves proposed fix. */
- Curl_axtls_close(conn, num);
- }
- else {
- failf(conn->data, "axTLS recv error (%d)", ret);
- *err = map_error_to_curl((int) ret);
- ret = -1;
- }
- }
-
- return ret;
-}
-
-/*
- * Return codes:
- * 1 means the connection is still in place
- * 0 means the connection has been closed
- * -1 means the connection status is unknown
- */
-static int Curl_axtls_check_cxn(struct connectdata *conn)
-{
- /* openssl.c line:
- rc = SSL_peek(conn->ssl[FIRSTSOCKET].backend->ssl, (void*)&buf, 1);
- axTLS compat layer always returns the last argument, so connection is
- always alive? */
-
- infof(conn->data, " Curl_axtls_check_cxn\n");
- return 1; /* connection still in place */
-}
-
-static void Curl_axtls_session_free(void *ptr)
-{
- (void)ptr;
- /* free the ID */
- /* both openssl.c and gtls.c do something here, but axTLS's OpenSSL
- compatibility layer does nothing, so we do nothing too. */
-}
-
-static size_t Curl_axtls_version(char *buffer, size_t size)
-{
- return snprintf(buffer, size, "axTLS/%s", ssl_version());
-}
-
-static CURLcode Curl_axtls_random(struct Curl_easy *data,
- unsigned char *entropy, size_t length)
-{
- static bool ssl_seeded = FALSE;
- (void)data;
- if(!ssl_seeded) {
- ssl_seeded = TRUE;
- /* Initialize the seed if not already done. This call is not exactly thread
- * safe (and neither is the ssl_seeded check), but the worst effect of a
- * race condition is that some global resources will leak. */
- RNG_initialize();
- }
- get_random((int)length, entropy);
- return CURLE_OK;
-}
-
-static void *Curl_axtls_get_internals(struct ssl_connect_data *connssl,
- CURLINFO info UNUSED_PARAM)
-{
- (void)info;
- return BACKEND->ssl;
-}
-
-const struct Curl_ssl Curl_ssl_axtls = {
- { CURLSSLBACKEND_AXTLS, "axtls" }, /* info */
- 0, /* no fancy stuff */
- sizeof(struct ssl_backend_data),
-
- /*
- * axTLS has no global init. Everything is done through SSL and SSL_CTX
- * structs stored in connectdata structure.
- */
- Curl_none_init, /* init */
- /* axTLS has no global cleanup. */
- Curl_none_cleanup, /* cleanup */
- Curl_axtls_version, /* version */
- Curl_axtls_check_cxn, /* check_cxn */
- Curl_axtls_shutdown, /* shutdown */
- Curl_none_data_pending, /* data_pending */
- Curl_axtls_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
- Curl_axtls_connect, /* connect */
- Curl_axtls_connect_nonblocking, /* connect_nonblocking */
- Curl_axtls_get_internals, /* get_internals */
- Curl_axtls_close, /* close_one */
- Curl_none_close_all, /* close_all */
- Curl_axtls_session_free, /* session_free */
- Curl_none_set_engine, /* set_engine */
- Curl_none_set_engine_default, /* set_engine_default */
- Curl_none_engines_list, /* engines_list */
- Curl_none_false_start, /* false_start */
- Curl_none_md5sum, /* md5sum */
- NULL /* sha256sum */
-};
-
-#endif /* USE_AXTLS */
diff --git a/Utilities/cmcurl/lib/vtls/cyassl.c b/Utilities/cmcurl/lib/vtls/cyassl.c
index e10398ac3..44a2bdda6 100644
--- a/Utilities/cmcurl/lib/vtls/cyassl.c
+++ b/Utilities/cmcurl/lib/vtls/cyassl.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -67,15 +67,6 @@ and that's a problem since options.h hasn't been included yet. */
#endif
#endif
-/* HAVE_SUPPORTED_CURVES is wolfSSL's build time symbol for enabling the ECC
- supported curve extension in options.h. Note ECC is enabled separately. */
-#ifndef HAVE_SUPPORTED_CURVES
-#if defined(HAVE_CYASSL_CTX_USESUPPORTEDCURVE) || \
- defined(HAVE_WOLFSSL_CTX_USESUPPORTEDCURVE)
-#define HAVE_SUPPORTED_CURVES
-#endif
-#endif
-
#include <limits.h>
#include "urldata.h"
@@ -88,6 +79,7 @@ and that's a problem since options.h hasn't been included yet. */
#include "strcase.h"
#include "x509asn1.h"
#include "curl_printf.h"
+#include "multiif.h"
#include <cyassl/openssl/ssl.h>
#include <cyassl/ssl.h>
@@ -151,7 +143,6 @@ static CURLcode
cyassl_connect_step1(struct connectdata *conn,
int sockindex)
{
- char error_buffer[CYASSL_MAX_ERROR_SZ];
char *ciphers;
struct Curl_easy *data = conn->data;
struct ssl_connect_data* connssl = &conn->ssl[sockindex];
@@ -364,16 +355,6 @@ cyassl_connect_step1(struct connectdata *conn,
}
#endif
-#ifdef HAVE_SUPPORTED_CURVES
- /* CyaSSL/wolfSSL does not send the supported ECC curves ext automatically:
- https://github.com/wolfSSL/wolfssl/issues/366
- The supported curves below are those also supported by OpenSSL 1.0.2 and
- in the same order. */
- CyaSSL_CTX_UseSupportedCurve(BACKEND->ctx, 0x17); /* secp256r1 */
- CyaSSL_CTX_UseSupportedCurve(BACKEND->ctx, 0x19); /* secp521r1 */
- CyaSSL_CTX_UseSupportedCurve(BACKEND->ctx, 0x18); /* secp384r1 */
-#endif
-
/* give application a chance to interfere with SSL set up. */
if(data->set.ssl.fsslctx) {
CURLcode result = CURLE_OK;
@@ -438,6 +419,7 @@ cyassl_connect_step1(struct connectdata *conn,
if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL, sockindex)) {
/* we got a session id, use it! */
if(!SSL_set_session(BACKEND->handle, ssl_sessionid)) {
+ char error_buffer[CYASSL_MAX_ERROR_SZ];
Curl_ssl_sessionid_unlock(conn);
failf(data, "SSL: SSL_set_session failed: %s",
ERR_error_string(SSL_get_error(BACKEND->handle, 0),
@@ -618,6 +600,8 @@ cyassl_connect_step2(struct connectdata *conn,
else
infof(data, "ALPN, unrecognized protocol %.*s\n", protocol_len,
protocol);
+ Curl_multiuse_state(conn, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
}
else if(rc == SSL_ALPN_NOT_FOUND)
infof(data, "ALPN, server did not agree to a protocol\n");
@@ -777,13 +761,13 @@ static void Curl_cyassl_session_free(void *ptr)
static size_t Curl_cyassl_version(char *buffer, size_t size)
{
#if LIBCYASSL_VERSION_HEX >= 0x03006000
- return snprintf(buffer, size, "wolfSSL/%s", wolfSSL_lib_version());
+ return msnprintf(buffer, size, "wolfSSL/%s", wolfSSL_lib_version());
#elif defined(WOLFSSL_VERSION)
- return snprintf(buffer, size, "wolfSSL/%s", WOLFSSL_VERSION);
+ return msnprintf(buffer, size, "wolfSSL/%s", WOLFSSL_VERSION);
#elif defined(CYASSL_VERSION)
- return snprintf(buffer, size, "CyaSSL/%s", CYASSL_VERSION);
+ return msnprintf(buffer, size, "CyaSSL/%s", CYASSL_VERSION);
#else
- return snprintf(buffer, size, "CyaSSL/%s", "<1.8.8");
+ return msnprintf(buffer, size, "CyaSSL/%s", "<1.8.8");
#endif
}
@@ -794,6 +778,12 @@ static int Curl_cyassl_init(void)
}
+static void Curl_cyassl_cleanup(void)
+{
+ CyaSSL_Cleanup();
+}
+
+
static bool Curl_cyassl_data_pending(const struct connectdata* conn,
int connindex)
{
@@ -1004,7 +994,7 @@ const struct Curl_ssl Curl_ssl_cyassl = {
sizeof(struct ssl_backend_data),
Curl_cyassl_init, /* init */
- Curl_none_cleanup, /* cleanup */
+ Curl_cyassl_cleanup, /* cleanup */
Curl_cyassl_version, /* version */
Curl_none_check_cxn, /* check_cxn */
Curl_cyassl_shutdown, /* shutdown */
diff --git a/Utilities/cmcurl/lib/vtls/gskit.c b/Utilities/cmcurl/lib/vtls/gskit.c
index 8d1b3d6af..b93ff5d4f 100644
--- a/Utilities/cmcurl/lib/vtls/gskit.c
+++ b/Utilities/cmcurl/lib/vtls/gskit.c
@@ -734,12 +734,11 @@ static ssize_t gskit_recv(struct connectdata *conn, int num, char *buf,
{
struct ssl_connect_data *connssl = &conn->ssl[num];
struct Curl_easy *data = conn->data;
- int buffsize;
int nread;
CURLcode cc = CURLE_RECV_ERROR;
if(pipe_ssloverssl(conn, num, SOS_READ) >= 0) {
- buffsize = buffersize > (size_t) INT_MAX? INT_MAX: (int) buffersize;
+ int buffsize = buffersize > (size_t) INT_MAX? INT_MAX: (int) buffersize;
cc = gskit_status(data, gsk_secure_soc_read(BACKEND->handle,
buf, buffsize, &nread),
"gsk_secure_soc_read()", CURLE_RECV_ERROR);
@@ -806,7 +805,6 @@ static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex)
conn->host.name;
const char *sni;
unsigned int protoflags = 0;
- long timeout;
Qso_OverlappedIO_t commarea;
int sockpair[2];
static const int sobufsize = CURL_MAX_WRITE_SIZE;
@@ -914,7 +912,7 @@ static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex)
if(!result) {
/* Compute the handshake timeout. Since GSKit granularity is 1 second,
we round up the required value. */
- timeout = Curl_timeleft(data, NULL, TRUE);
+ long timeout = Curl_timeleft(data, NULL, TRUE);
if(timeout < 0)
result = CURLE_OPERATION_TIMEDOUT;
else
@@ -1021,14 +1019,13 @@ static CURLcode gskit_connect_step2(struct connectdata *conn, int sockindex,
struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
Qso_OverlappedIO_t cstat;
- long timeout_ms;
struct timeval stmv;
CURLcode result;
/* Poll or wait for end of SSL asynchronous handshake. */
for(;;) {
- timeout_ms = nonblocking? 0: Curl_timeleft(data, NULL, TRUE);
+ long timeout_ms = nonblocking? 0: Curl_timeleft(data, NULL, TRUE);
if(timeout_ms < 0)
timeout_ms = 0;
stmv.tv_sec = timeout_ms / 1000;
@@ -1077,7 +1074,6 @@ static CURLcode gskit_connect_step3(struct connectdata *conn, int sockindex)
const char *cert = (const char *) NULL;
const char *certend;
const char *ptr;
- int i;
CURLcode result;
/* SSL handshake done: gather certificate info and verify host. */
@@ -1087,6 +1083,8 @@ static CURLcode gskit_connect_step3(struct connectdata *conn, int sockindex)
&cdev, &cdec),
"gsk_attribute_get_cert_info()", CURLE_SSL_CONNECT_ERROR) ==
CURLE_OK) {
+ int i;
+
infof(data, "Server certificate:\n");
p = cdev;
for(i = 0; i++ < cdec; p++)
@@ -1160,7 +1158,6 @@ static CURLcode gskit_connect_common(struct connectdata *conn, int sockindex,
struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
long timeout_ms;
- Qso_OverlappedIO_t cstat;
CURLcode result = CURLE_OK;
*done = connssl->state == ssl_connection_complete;
@@ -1262,7 +1259,6 @@ static int Curl_gskit_shutdown(struct connectdata *conn, int sockindex)
{
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct Curl_easy *data = conn->data;
- ssize_t nread;
int what;
int rc;
char buf[120];
@@ -1270,8 +1266,10 @@ static int Curl_gskit_shutdown(struct connectdata *conn, int sockindex)
if(!BACKEND->handle)
return 0;
+#ifndef CURL_DISABLE_FTP
if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE)
return 0;
+#endif
close_one(connssl, conn, sockindex);
rc = 0;
@@ -1279,6 +1277,8 @@ static int Curl_gskit_shutdown(struct connectdata *conn, int sockindex)
SSL_SHUTDOWN_TIMEOUT);
for(;;) {
+ ssize_t nread;
+
if(what < 0) {
/* anything that gets here is fatally bad */
failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
@@ -1314,7 +1314,7 @@ static int Curl_gskit_shutdown(struct connectdata *conn, int sockindex)
static size_t Curl_gskit_version(char *buffer, size_t size)
{
- return snprintf(buffer, size, "GSKit");
+ return msnprintf(buffer, size, "GSKit");
}
diff --git a/Utilities/cmcurl/lib/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c
index 37662a748..8693cdce3 100644
--- a/Utilities/cmcurl/lib/vtls/gtls.c
+++ b/Utilities/cmcurl/lib/vtls/gtls.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -55,6 +55,7 @@
#include "strcase.h"
#include "warnless.h"
#include "x509asn1.h"
+#include "multiif.h"
#include "curl_printf.h"
#include "curl_memory.h"
/* The last #include file should be: */
@@ -227,17 +228,17 @@ static void showtime(struct Curl_easy *data,
if(result)
return;
- snprintf(str,
- sizeof(str),
- "\t %s: %s, %02d %s %4d %02d:%02d:%02d GMT",
- text,
- Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
- tm->tm_mday,
- Curl_month[tm->tm_mon],
- tm->tm_year + 1900,
- tm->tm_hour,
- tm->tm_min,
- tm->tm_sec);
+ msnprintf(str,
+ sizeof(str),
+ "\t %s: %s, %02d %s %4d %02d:%02d:%02d GMT",
+ text,
+ Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
+ tm->tm_mday,
+ Curl_month[tm->tm_mon],
+ tm->tm_year + 1900,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec);
infof(data, "%s\n", str);
}
#endif
@@ -285,11 +286,11 @@ static CURLcode handshake(struct connectdata *conn,
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
gnutls_session_t session = BACKEND->session;
curl_socket_t sockfd = conn->sock[sockindex];
- time_t timeout_ms;
- int rc;
- int what;
for(;;) {
+ time_t timeout_ms;
+ int rc;
+
/* check allowed time left */
timeout_ms = Curl_timeleft(data, NULL, duringconnect);
@@ -302,7 +303,7 @@ static CURLcode handshake(struct connectdata *conn,
/* if ssl is expecting something, check if it's available. */
if(connssl->connecting_state == ssl_connect_2_reading
|| connssl->connecting_state == ssl_connect_2_writing) {
-
+ int what;
curl_socket_t writefd = ssl_connect_2_writing ==
connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
curl_socket_t readfd = ssl_connect_2_reading ==
@@ -956,7 +957,6 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
gnutls_pubkey_t key = NULL;
/* Result is returned to caller */
- int ret = 0;
CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
/* if a path wasn't specified, don't pin */
@@ -967,6 +967,8 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
return result;
do {
+ int ret;
+
/* Begin Gyrations to get the public key */
gnutls_pubkey_init(&key);
@@ -1110,7 +1112,7 @@ gtls_connect_step3(struct connectdata *conn,
"CRLfile: %s", SSL_CONN_CONFIG(CAfile) ? SSL_CONN_CONFIG(CAfile):
"none",
SSL_SET_OPTION(CRLfile)?SSL_SET_OPTION(CRLfile):"none");
- return CURLE_SSL_CACERT;
+ return CURLE_PEER_FAILED_VERIFICATION;
}
else
infof(data, "\t server certificate verification FAILED\n");
@@ -1278,10 +1280,7 @@ gtls_connect_step3(struct connectdata *conn,
#define use_addr in_addr
#endif
unsigned char addrbuf[sizeof(struct use_addr)];
- unsigned char certaddr[sizeof(struct use_addr)];
- size_t addrlen = 0, certaddrlen;
- int i;
- int ret = 0;
+ size_t addrlen = 0;
if(Curl_inet_pton(AF_INET, hostname, addrbuf) > 0)
addrlen = 4;
@@ -1291,10 +1290,13 @@ gtls_connect_step3(struct connectdata *conn,
#endif
if(addrlen) {
+ unsigned char certaddr[sizeof(struct use_addr)];
+ int i;
+
for(i = 0; ; i++) {
- certaddrlen = sizeof(certaddr);
- ret = gnutls_x509_crt_get_subject_alt_name(x509_cert, i, certaddr,
- &certaddrlen, NULL);
+ size_t certaddrlen = sizeof(certaddr);
+ int ret = gnutls_x509_crt_get_subject_alt_name(x509_cert, i, certaddr,
+ &certaddrlen, NULL);
/* If this happens, it wasn't an IP address. */
if(ret == GNUTLS_E_SHORT_MEMORY_BUFFER)
continue;
@@ -1423,11 +1425,6 @@ gtls_connect_step3(struct connectdata *conn,
size = sizeof(certbuf);
gnutls_x509_crt_get_issuer_dn(x509_cert, certbuf, &size);
infof(data, "\t issuer: %s\n", certbuf);
-
- /* compression algorithm (if any) */
- ptr = gnutls_compression_get_name(gnutls_compression_get(session));
- /* the *_get_name() says "NULL" if GNUTLS_COMP_NULL is returned */
- infof(data, "\t compression: %s\n", ptr);
#endif
gnutls_x509_crt_deinit(x509_cert);
@@ -1454,6 +1451,9 @@ gtls_connect_step3(struct connectdata *conn,
}
else
infof(data, "ALPN, server did not agree to a protocol\n");
+
+ Curl_multiuse_state(conn, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
}
#endif
@@ -1466,8 +1466,6 @@ gtls_connect_step3(struct connectdata *conn,
already got it from the cache and asked to use it in the connection, it
might've been rejected and then a new one is in use now and we need to
detect that. */
- bool incache;
- void *ssl_sessionid;
void *connect_sessionid;
size_t connect_idsize = 0;
@@ -1476,6 +1474,9 @@ gtls_connect_step3(struct connectdata *conn,
connect_sessionid = malloc(connect_idsize); /* get a buffer for it */
if(connect_sessionid) {
+ bool incache;
+ void *ssl_sessionid;
+
/* extract session ID to the allocated buffer */
gnutls_session_get_data(session, connect_sessionid, &connect_idsize);
@@ -1636,12 +1637,10 @@ static void Curl_gtls_close(struct connectdata *conn, int sockindex)
static int Curl_gtls_shutdown(struct connectdata *conn, int sockindex)
{
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- ssize_t result;
int retval = 0;
struct Curl_easy *data = conn->data;
- bool done = FALSE;
- char buf[120];
+#ifndef CURL_DISABLE_FTP
/* This has only been tested on the proftpd server, and the mod_tls code
sends a close notify alert without waiting for a close notify alert in
response. Thus we wait for a close notify alert from the server, but
@@ -1649,8 +1648,13 @@ static int Curl_gtls_shutdown(struct connectdata *conn, int sockindex)
if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)
gnutls_bye(BACKEND->session, GNUTLS_SHUT_WR);
+#endif
if(BACKEND->session) {
+ ssize_t result;
+ bool done = FALSE;
+ char buf[120];
+
while(!done) {
int what = SOCKET_READABLE(conn->sock[sockindex],
SSL_SHUTDOWN_TIMEOUT);
@@ -1748,7 +1752,7 @@ static void Curl_gtls_session_free(void *ptr)
static size_t Curl_gtls_version(char *buffer, size_t size)
{
- return snprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL));
+ return msnprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL));
}
#ifndef USE_GNUTLS_NETTLE
@@ -1763,12 +1767,6 @@ static int Curl_gtls_seed(struct Curl_easy *data)
if(!ssl_seeded || data->set.str[STRING_SSL_RANDOM_FILE] ||
data->set.str[STRING_SSL_EGDSOCKET]) {
-
- /* TODO: to a good job seeding the RNG
- This may involve the gcry_control function and these options:
- GCRYCTL_SET_RANDOM_SEED_FILE
- GCRYCTL_SET_RNDEGD_SOCKET
- */
ssl_seeded = TRUE;
}
return 0;
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c
index c5ed8872e..63d1f4c81 100644
--- a/Utilities/cmcurl/lib/vtls/mbedtls.c
+++ b/Utilities/cmcurl/lib/vtls/mbedtls.c
@@ -6,7 +6,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2010 - 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
- * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -54,6 +54,7 @@
#include "parsedate.h"
#include "connect.h" /* for the connect timeout */
#include "select.h"
+#include "multiif.h"
#include "polarssl_threadlock.h"
/* The last 3 #include files should be in this order */
@@ -342,7 +343,8 @@ mbed_connect_step1(struct connectdata *conn,
if(SSL_SET_OPTION(key)) {
ret = mbedtls_pk_parse_keyfile(&BACKEND->pk, SSL_SET_OPTION(key),
SSL_SET_OPTION(key_passwd));
- if(ret == 0 && !mbedtls_pk_can_do(&BACKEND->pk, MBEDTLS_PK_RSA))
+ if(ret == 0 && !(mbedtls_pk_can_do(&BACKEND->pk, MBEDTLS_PK_RSA) ||
+ mbedtls_pk_can_do(&BACKEND->pk, MBEDTLS_PK_ECKEY)))
ret = MBEDTLS_ERR_PK_TYPE_MISMATCH;
if(ret) {
@@ -373,7 +375,7 @@ mbed_connect_step1(struct connectdata *conn,
}
}
- infof(data, "mbedTLS: Connecting to %s:%d\n", hostname, port);
+ infof(data, "mbedTLS: Connecting to %s:%ld\n", hostname, port);
mbedtls_ssl_config_init(&BACKEND->config);
@@ -539,13 +541,6 @@ mbed_connect_step2(struct connectdata *conn,
data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
-#ifdef HAS_ALPN
- const char *next_protocol;
-#endif
-
- char errorbuf[128];
- errorbuf[0] = 0;
-
conn->recv[sockindex] = mbed_recv;
conn->send[sockindex] = mbed_send;
@@ -560,6 +555,8 @@ mbed_connect_step2(struct connectdata *conn,
return CURLE_OK;
}
else if(ret) {
+ char errorbuf[128];
+ errorbuf[0] = 0;
#ifdef MBEDTLS_ERROR_C
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
#endif /* MBEDTLS_ERROR_C */
@@ -574,19 +571,21 @@ mbed_connect_step2(struct connectdata *conn,
ret = mbedtls_ssl_get_verify_result(&BACKEND->ssl);
+ if(!SSL_CONN_CONFIG(verifyhost))
+ /* Ignore hostname errors if verifyhost is disabled */
+ ret &= ~MBEDTLS_X509_BADCERT_CN_MISMATCH;
+
if(ret && SSL_CONN_CONFIG(verifypeer)) {
if(ret & MBEDTLS_X509_BADCERT_EXPIRED)
failf(data, "Cert verify failed: BADCERT_EXPIRED");
- if(ret & MBEDTLS_X509_BADCERT_REVOKED) {
+ else if(ret & MBEDTLS_X509_BADCERT_REVOKED)
failf(data, "Cert verify failed: BADCERT_REVOKED");
- return CURLE_SSL_CACERT;
- }
- if(ret & MBEDTLS_X509_BADCERT_CN_MISMATCH)
+ else if(ret & MBEDTLS_X509_BADCERT_CN_MISMATCH)
failf(data, "Cert verify failed: BADCERT_CN_MISMATCH");
- if(ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED)
+ else if(ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED)
failf(data, "Cert verify failed: BADCERT_NOT_TRUSTED");
return CURLE_PEER_FAILED_VERIFICATION;
@@ -662,7 +661,7 @@ mbed_connect_step2(struct connectdata *conn,
#ifdef HAS_ALPN
if(conn->bits.tls_enable_alpn) {
- next_protocol = mbedtls_ssl_get_alpn_protocol(&BACKEND->ssl);
+ const char *next_protocol = mbedtls_ssl_get_alpn_protocol(&BACKEND->ssl);
if(next_protocol) {
infof(data, "ALPN, server accepted to use %s\n", next_protocol);
@@ -682,6 +681,8 @@ mbed_connect_step2(struct connectdata *conn,
else {
infof(data, "ALPN, server did not agree to a protocol\n");
}
+ Curl_multiuse_state(conn, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
}
#endif
@@ -714,6 +715,8 @@ mbed_connect_step3(struct connectdata *conn,
ret = mbedtls_ssl_get_session(&BACKEND->ssl, our_ssl_sessionid);
if(ret) {
+ if(ret != MBEDTLS_ERR_SSL_ALLOC_FAILED)
+ mbedtls_ssl_session_free(our_ssl_sessionid);
free(our_ssl_sessionid);
failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret);
return CURLE_SSL_CONNECT_ERROR;
@@ -727,6 +730,7 @@ mbed_connect_step3(struct connectdata *conn,
retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid, 0, sockindex);
Curl_ssl_sessionid_unlock(conn);
if(retcode) {
+ mbedtls_ssl_session_free(our_ssl_sessionid);
free(our_ssl_sessionid);
failf(data, "failed to store ssl session");
return retcode;
@@ -811,9 +815,14 @@ static void Curl_mbedtls_session_free(void *ptr)
static size_t Curl_mbedtls_version(char *buffer, size_t size)
{
+#ifdef MBEDTLS_VERSION_C
+ /* if mbedtls_version_get_number() is available it is better */
unsigned int version = mbedtls_version_get_number();
- return snprintf(buffer, size, "mbedTLS/%u.%u.%u", version>>24,
- (version>>16)&0xff, (version>>8)&0xff);
+ return msnprintf(buffer, size, "mbedTLS/%u.%u.%u", version>>24,
+ (version>>16)&0xff, (version>>8)&0xff);
+#else
+ return msnprintf(buffer, size, "mbedTLS/%s", MBEDTLS_VERSION_STRING);
+#endif
}
static CURLcode Curl_mbedtls_random(struct Curl_easy *data,
diff --git a/Utilities/cmcurl/lib/vtls/mesalink.c b/Utilities/cmcurl/lib/vtls/mesalink.c
index 6a2b67e63..718c282ee 100644
--- a/Utilities/cmcurl/lib/vtls/mesalink.c
+++ b/Utilities/cmcurl/lib/vtls/mesalink.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2017-2018, Yiming Jing, <jingyiming@baidu.com>
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2017 - 2018, Yiming Jing, <jingyiming@baidu.com>
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -268,7 +268,7 @@ mesalink_connect_step2(struct connectdata *conn, int sockindex)
char error_buffer[MESALINK_MAX_ERROR_SZ];
int detail = SSL_get_error(BACKEND->handle, ret);
- if(SSL_ERROR_WANT_CONNECT == detail) {
+ if(SSL_ERROR_WANT_CONNECT == detail || SSL_ERROR_WANT_READ == detail) {
connssl->connecting_state = ssl_connect_2_reading;
return CURLE_OK;
}
@@ -424,7 +424,7 @@ mesalink_recv(struct connectdata *conn, int num, char *buf, size_t buffersize,
static size_t
Curl_mesalink_version(char *buffer, size_t size)
{
- return snprintf(buffer, size, "MesaLink/%s", MESALINK_VERSION_STRING);
+ return msnprintf(buffer, size, "MesaLink/%s", MESALINK_VERSION_STRING);
}
static int
diff --git a/Utilities/cmcurl/lib/vtls/nss.c b/Utilities/cmcurl/lib/vtls/nss.c
index a3d3e58bb..491def106 100644
--- a/Utilities/cmcurl/lib/vtls/nss.c
+++ b/Utilities/cmcurl/lib/vtls/nss.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -38,6 +38,7 @@
#include "select.h"
#include "vtls.h"
#include "llist.h"
+#include "multiif.h"
#include "curl_printf.h"
#include "nssg.h"
#include <nspr.h>
@@ -246,6 +247,32 @@ static void nss_print_error_message(struct Curl_easy *data, PRUint32 err)
failf(data, "%s", PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT));
}
+static char *nss_sslver_to_name(PRUint16 nssver)
+{
+ switch(nssver) {
+ case SSL_LIBRARY_VERSION_2:
+ return strdup("SSLv2");
+ case SSL_LIBRARY_VERSION_3_0:
+ return strdup("SSLv3");
+ case SSL_LIBRARY_VERSION_TLS_1_0:
+ return strdup("TLSv1.0");
+#ifdef SSL_LIBRARY_VERSION_TLS_1_1
+ case SSL_LIBRARY_VERSION_TLS_1_1:
+ return strdup("TLSv1.1");
+#endif
+#ifdef SSL_LIBRARY_VERSION_TLS_1_2
+ case SSL_LIBRARY_VERSION_TLS_1_2:
+ return strdup("TLSv1.2");
+#endif
+#ifdef SSL_LIBRARY_VERSION_TLS_1_3
+ case SSL_LIBRARY_VERSION_TLS_1_3:
+ return strdup("TLSv1.3");
+#endif
+ default:
+ return curl_maprintf("0x%04x", nssver);
+ }
+}
+
static SECStatus set_ciphers(struct Curl_easy *data, PRFileDesc * model,
char *cipher_list)
{
@@ -351,7 +378,7 @@ static int is_file(const char *filename)
return 0;
if(stat(filename, &st) == 0)
- if(S_ISREG(st.st_mode))
+ if(S_ISREG(st.st_mode) || S_ISFIFO(st.st_mode) || S_ISCHR(st.st_mode))
return 1;
return 0;
@@ -817,6 +844,8 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg)
!memcmp(ALPN_HTTP_1_1, buf, ALPN_HTTP_1_1_LENGTH)) {
conn->negnpn = CURL_HTTP_VERSION_1_1;
}
+ Curl_multiuse_state(conn, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
}
}
@@ -1279,6 +1308,8 @@ static void nss_unload_module(SECMODModule **pmod)
static CURLcode nss_init_core(struct Curl_easy *data, const char *cert_dir)
{
NSSInitParameters initparams;
+ PRErrorCode err;
+ const char *err_name;
if(nss_context != NULL)
return CURLE_OK;
@@ -1299,7 +1330,9 @@ static CURLcode nss_init_core(struct Curl_easy *data, const char *cert_dir)
if(nss_context != NULL)
return CURLE_OK;
- infof(data, "Unable to initialize NSS database\n");
+ err = PR_GetError();
+ err_name = nss_error_to_name(err);
+ infof(data, "Unable to initialize NSS database: %d (%s)\n", err, err_name);
}
infof(data, "Initializing NSS with certpath: none\n");
@@ -1309,7 +1342,9 @@ static CURLcode nss_init_core(struct Curl_easy *data, const char *cert_dir)
if(nss_context != NULL)
return CURLE_OK;
- infof(data, "Unable to initialize NSS\n");
+ err = PR_GetError();
+ err_name = nss_error_to_name(err);
+ failf(data, "Unable to initialize NSS: %d (%s)", err, err_name);
return CURLE_SSL_CACERT_BADFILE;
}
@@ -1638,17 +1673,6 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn,
static CURLcode nss_sslver_from_curl(PRUint16 *nssver, long version)
{
switch(version) {
- case CURL_SSLVERSION_TLSv1:
- /* TODO: set sslver->max to SSL_LIBRARY_VERSION_TLS_1_3 once stable */
-#ifdef SSL_LIBRARY_VERSION_TLS_1_2
- *nssver = SSL_LIBRARY_VERSION_TLS_1_2;
-#elif defined SSL_LIBRARY_VERSION_TLS_1_1
- *nssver = SSL_LIBRARY_VERSION_TLS_1_1;
-#else
- *nssver = SSL_LIBRARY_VERSION_TLS_1_0;
-#endif
- return CURLE_OK;
-
case CURL_SSLVERSION_SSLv2:
*nssver = SSL_LIBRARY_VERSION_2;
return CURLE_OK;
@@ -1709,10 +1733,8 @@ static CURLcode nss_init_sslver(SSLVersionRange *sslver,
}
switch(min) {
- case CURL_SSLVERSION_DEFAULT:
- break;
case CURL_SSLVERSION_TLSv1:
- sslver->min = SSL_LIBRARY_VERSION_TLS_1_0;
+ case CURL_SSLVERSION_DEFAULT:
break;
default:
result = nss_sslver_from_curl(&sslver->min, min);
@@ -1789,10 +1811,19 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
CURLcode result;
bool second_layer = FALSE;
+ SSLVersionRange sslver_supported;
SSLVersionRange sslver = {
SSL_LIBRARY_VERSION_TLS_1_0, /* min */
- SSL_LIBRARY_VERSION_TLS_1_0 /* max */
+#ifdef SSL_LIBRARY_VERSION_TLS_1_3
+ SSL_LIBRARY_VERSION_TLS_1_3 /* max */
+#elif defined SSL_LIBRARY_VERSION_TLS_1_2
+ SSL_LIBRARY_VERSION_TLS_1_2
+#elif defined SSL_LIBRARY_VERSION_TLS_1_1
+ SSL_LIBRARY_VERSION_TLS_1_1
+#else
+ SSL_LIBRARY_VERSION_TLS_1_0
+#endif
};
BACKEND->data = data;
@@ -1800,7 +1831,6 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
/* list of all NSS objects we need to destroy in Curl_nss_close() */
Curl_llist_init(&BACKEND->obj_list, nss_destroy_object);
- /* FIXME. NSS doesn't support multiple databases open at the same time. */
PR_Lock(nss_initlock);
result = nss_init(conn->data);
if(result) {
@@ -1841,6 +1871,20 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
/* enable/disable the requested SSL version(s) */
if(nss_init_sslver(&sslver, data, conn) != CURLE_OK)
goto error;
+ if(SSL_VersionRangeGetSupported(ssl_variant_stream,
+ &sslver_supported) != SECSuccess)
+ goto error;
+ if(sslver_supported.max < sslver.max && sslver_supported.max >= sslver.min) {
+ char *sslver_req_str, *sslver_supp_str;
+ sslver_req_str = nss_sslver_to_name(sslver.max);
+ sslver_supp_str = nss_sslver_to_name(sslver_supported.max);
+ if(sslver_req_str && sslver_supp_str)
+ infof(data, "Falling back from %s to max supported SSL version (%s)\n",
+ sslver_req_str, sslver_supp_str);
+ free(sslver_req_str);
+ free(sslver_supp_str);
+ sslver.max = sslver_supported.max;
+ }
if(SSL_VersionRangeSet(model, &sslver) != SECSuccess)
goto error;
@@ -2090,7 +2134,7 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex)
else if(*certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN)
result = CURLE_PEER_FAILED_VERIFICATION;
else if(*certverifyresult != 0)
- result = CURLE_SSL_CACERT;
+ result = CURLE_PEER_FAILED_VERIFICATION;
goto error;
}
@@ -2164,7 +2208,7 @@ static CURLcode nss_connect_common(struct connectdata *conn, int sockindex,
if(!blocking)
/* CURLE_AGAIN in non-blocking mode is not an error */
return CURLE_OK;
- /* fall through */
+ /* FALLTHROUGH */
default:
return result;
}
@@ -2279,7 +2323,7 @@ static ssize_t nss_recv(struct connectdata *conn, /* connection data */
static size_t Curl_nss_version(char *buffer, size_t size)
{
- return snprintf(buffer, size, "NSS/%s", NSS_VERSION);
+ return msnprintf(buffer, size, "NSS/%s", NSS_VERSION);
}
/* data might be NULL */
diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c
index 4c5e8c19c..85e9be616 100644
--- a/Utilities/cmcurl/lib/vtls/openssl.c
+++ b/Utilities/cmcurl/lib/vtls/openssl.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -48,6 +48,7 @@
#include "vtls.h"
#include "strcase.h"
#include "hostcheck.h"
+#include "multiif.h"
#include "curl_printf.h"
#include <openssl/ssl.h>
#include <openssl/rand.h>
@@ -65,11 +66,15 @@
#include <openssl/buffer.h>
#include <openssl/pkcs12.h>
+#ifdef USE_AMISSL
+#include "amigaos.h"
+#endif
+
#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP)
#include <openssl/ocsp.h>
#endif
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && /* 0.9.8 or later */ \
+#if (OPENSSL_VERSION_NUMBER >= 0x0090700fL) && /* 0.9.7 or later */ \
!defined(OPENSSL_NO_ENGINE)
#define USE_OPENSSL_ENGINE
#include <openssl/engine.h>
@@ -82,6 +87,13 @@
#include "curl_memory.h"
#include "memdebug.h"
+/* Uncomment the ALLOW_RENEG line to a real #define if you want to allow TLS
+ renegotiations when built with BoringSSL. Renegotiating is non-compliant
+ with HTTP/2 and "an extremely dangerous protocol feature". Beware.
+
+#define ALLOW_RENEG 1
+ */
+
#ifndef OPENSSL_VERSION_NUMBER
#error "OPENSSL_VERSION_NUMBER not defined"
#endif
@@ -384,6 +396,31 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size)
return buf;
}
+/* Return an extra data index for the connection data.
+ * This index can be used with SSL_get_ex_data() and SSL_set_ex_data().
+ */
+static int ossl_get_ssl_conn_index(void)
+{
+ static int ssl_ex_data_conn_index = -1;
+ if(ssl_ex_data_conn_index < 0) {
+ ssl_ex_data_conn_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+ }
+ return ssl_ex_data_conn_index;
+}
+
+/* Return an extra data index for the sockindex.
+ * This index can be used with SSL_get_ex_data() and SSL_set_ex_data().
+ */
+static int ossl_get_ssl_sockindex_index(void)
+{
+ static int ssl_ex_data_sockindex_index = -1;
+ if(ssl_ex_data_sockindex_index < 0) {
+ ssl_ex_data_sockindex_index = SSL_get_ex_new_index(0, NULL, NULL, NULL,
+ NULL);
+ }
+ return ssl_ex_data_sockindex_index;
+}
+
static int passwd_callback(char *buf, int num, int encrypting,
void *global_passwd)
{
@@ -788,8 +825,11 @@ int cert_stuff(struct connectdata *conn,
fail:
EVP_PKEY_free(pri);
X509_free(x509);
+#ifdef USE_AMISSL
+ sk_X509_pop_free(ca, Curl_amiga_X509_free);
+#else
sk_X509_pop_free(ca, X509_free);
-
+#endif
if(!cert_done)
return 0; /* failure! */
break;
@@ -799,15 +839,15 @@ int cert_stuff(struct connectdata *conn,
return 0;
}
- file_type = do_file_type(key_type);
+ if(!key_file)
+ key_file = cert_file;
+ else
+ file_type = do_file_type(key_type);
switch(file_type) {
case SSL_FILETYPE_PEM:
if(cert_done)
break;
- if(!key_file)
- /* cert & key can only be in PEM case in the same file */
- key_file = cert_file;
/* FALLTHROUGH */
case SSL_FILETYPE_ASN1:
if(SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type) != 1) {
@@ -1035,6 +1075,10 @@ static int Curl_ossl_init(void)
}
#endif
+ /* Initialize the extra data indexes */
+ if(ossl_get_ssl_conn_index() < 0 || ossl_get_ssl_sockindex_index() < 0)
+ return 0;
+
return 1;
}
@@ -1049,7 +1093,7 @@ static void Curl_ossl_cleanup(void)
/* Free ciphers and digests lists */
EVP_cleanup();
-#ifdef HAVE_ENGINE_CLEANUP
+#ifdef USE_OPENSSL_ENGINE
/* Free engine list */
ENGINE_cleanup();
#endif
@@ -1264,6 +1308,7 @@ static int Curl_ossl_shutdown(struct connectdata *conn, int sockindex)
int err;
bool done = FALSE;
+#ifndef CURL_DISABLE_FTP
/* This has only been tested on the proftpd server, and the mod_tls code
sends a close notify alert without waiting for a close notify alert in
response. Thus we wait for a close notify alert from the server, but
@@ -1271,6 +1316,7 @@ static int Curl_ossl_shutdown(struct connectdata *conn, int sockindex)
if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)
(void)SSL_shutdown(BACKEND->handle);
+#endif
if(BACKEND->handle) {
buffsize = (int)sizeof(buf);
@@ -1656,6 +1702,7 @@ static CURLcode verifystatus(struct connectdata *conn,
struct ssl_connect_data *connssl)
{
int i, ocsp_status;
+ unsigned char *status;
const unsigned char *p;
CURLcode result = CURLE_OK;
struct Curl_easy *data = conn->data;
@@ -1665,14 +1712,14 @@ static CURLcode verifystatus(struct connectdata *conn,
X509_STORE *st = NULL;
STACK_OF(X509) *ch = NULL;
- long len = SSL_get_tlsext_status_ocsp_resp(BACKEND->handle, &p);
+ long len = SSL_get_tlsext_status_ocsp_resp(BACKEND->handle, &status);
- if(!p) {
+ if(!status) {
failf(data, "No OCSP response received");
result = CURLE_SSL_INVALIDCERTSTATUS;
goto end;
}
-
+ p = status;
rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
if(!rsp) {
failf(data, "Invalid OCSP response");
@@ -1867,15 +1914,8 @@ static const char *ssl_msg_type(int ssl_ver, int msg)
return "Unknown";
}
-static const char *tls_rt_type(int type, const void *buf, size_t buflen)
+static const char *tls_rt_type(int type)
{
- (void)buf;
- (void)buflen;
-#ifdef SSL3_RT_INNER_CONTENT_TYPE
- if(type == SSL3_RT_INNER_CONTENT_TYPE && buf && buflen >= 1)
- type = *(unsigned char *)buf;
-#endif
-
switch(type) {
#ifdef SSL3_RT_HEADER
case SSL3_RT_HEADER:
@@ -1945,12 +1985,20 @@ static void ssl_tls_trace(int direction, int ssl_ver, int content_type,
case 0:
break;
default:
- snprintf(unknown, sizeof(unknown), "(%x)", ssl_ver);
+ msnprintf(unknown, sizeof(unknown), "(%x)", ssl_ver);
verstr = unknown;
break;
}
- if(ssl_ver) {
+ /* Log progress for interesting records only (like Handshake or Alert), skip
+ * all raw record headers (content_type == SSL3_RT_HEADER or ssl_ver == 0).
+ * For TLS 1.3, skip notification of the decrypted inner Content Type.
+ */
+ if(ssl_ver
+#ifdef SSL3_RT_INNER_CONTENT_TYPE
+ && content_type != SSL3_RT_INNER_CONTENT_TYPE
+#endif
+ ) {
const char *msg_name, *tls_rt_name;
char ssl_buf[1024];
int msg_type, txt_len;
@@ -1964,17 +2012,10 @@ static void ssl_tls_trace(int direction, int ssl_ver, int content_type,
* is at 'buf[0]'.
*/
if(ssl_ver == SSL3_VERSION_MAJOR && content_type)
- tls_rt_name = tls_rt_type(content_type, buf, len);
+ tls_rt_name = tls_rt_type(content_type);
else
tls_rt_name = "";
-#ifdef SSL3_RT_INNER_CONTENT_TYPE
- if(content_type == SSL3_RT_INNER_CONTENT_TYPE) {
- msg_type = 0;
- msg_name = "[no content]";
- }
- else
-#endif
if(content_type == SSL3_RT_CHANGE_CIPHER_SPEC) {
msg_type = *(char *)buf;
msg_name = "Change cipher spec";
@@ -1988,9 +2029,9 @@ static void ssl_tls_trace(int direction, int ssl_ver, int content_type,
msg_name = ssl_msg_type(ssl_ver, msg_type);
}
- txt_len = snprintf(ssl_buf, sizeof(ssl_buf), "%s (%s), %s, %s (%d):\n",
- verstr, direction?"OUT":"IN",
- tls_rt_name, msg_name, msg_type);
+ txt_len = msnprintf(ssl_buf, sizeof(ssl_buf), "%s (%s), %s, %s (%d):\n",
+ verstr, direction?"OUT":"IN",
+ tls_rt_name, msg_name, msg_type);
if(0 <= txt_len && (unsigned)txt_len < sizeof(ssl_buf)) {
Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len);
}
@@ -2137,6 +2178,7 @@ set_ssl_version_min_max(long *ctx_options, struct connectdata *conn,
}
#else
(void)sockindex;
+ (void)ctx_options;
failf(data, OSSL_PACKAGE " was built without TLS 1.3 support");
return CURLE_NOT_BUILT_IN;
#endif
@@ -2189,6 +2231,62 @@ set_ssl_version_min_max(long *ctx_options, struct connectdata *conn,
return CURLE_OK;
}
+/* The "new session" callback must return zero if the session can be removed
+ * or non-zero if the session has been put into the session cache.
+ */
+static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
+{
+ int res = 0;
+ struct connectdata *conn;
+ struct Curl_easy *data;
+ int sockindex;
+ curl_socket_t *sockindex_ptr;
+ int connectdata_idx = ossl_get_ssl_conn_index();
+ int sockindex_idx = ossl_get_ssl_sockindex_index();
+
+ if(connectdata_idx < 0 || sockindex_idx < 0)
+ return 0;
+
+ conn = (struct connectdata*) SSL_get_ex_data(ssl, connectdata_idx);
+ if(!conn)
+ return 0;
+
+ data = conn->data;
+
+ /* The sockindex has been stored as a pointer to an array element */
+ sockindex_ptr = (curl_socket_t*) SSL_get_ex_data(ssl, sockindex_idx);
+ sockindex = (int)(sockindex_ptr - conn->sock);
+
+ if(SSL_SET_OPTION(primary.sessionid)) {
+ bool incache;
+ void *old_ssl_sessionid = NULL;
+
+ Curl_ssl_sessionid_lock(conn);
+ incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL,
+ sockindex));
+ if(incache) {
+ if(old_ssl_sessionid != ssl_sessionid) {
+ infof(data, "old SSL session ID is stale, removing\n");
+ Curl_ssl_delsessionid(conn, old_ssl_sessionid);
+ incache = FALSE;
+ }
+ }
+
+ if(!incache) {
+ if(!Curl_ssl_addsessionid(conn, ssl_sessionid,
+ 0 /* unknown size */, sockindex)) {
+ /* the session has been put into the session cache */
+ res = 1;
+ }
+ else
+ failf(data, "failed to store ssl session");
+ }
+ Curl_ssl_sessionid_unlock(conn);
+ }
+
+ return res;
+}
+
static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
{
CURLcode result = CURLE_OK;
@@ -2585,6 +2683,14 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
}
#endif
+ /* Enable the session cache because it's a prerequisite for the "new session"
+ * callback. Use the "external storage" mode to avoid that OpenSSL creates
+ * an internal session cache.
+ */
+ SSL_CTX_set_session_cache_mode(BACKEND->ctx,
+ SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL);
+ SSL_CTX_sess_set_new_cb(BACKEND->ctx, ossl_new_session_cb);
+
/* give application a chance to interfere with SSL set up. */
if(data->set.ssl.fsslctx) {
result = (*data->set.ssl.fsslctx)(data, BACKEND->ctx,
@@ -2610,6 +2716,10 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
SSL_set_tlsext_status_type(BACKEND->handle, TLSEXT_STATUSTYPE_ocsp);
#endif
+#if defined(OPENSSL_IS_BORINGSSL) && defined(ALLOW_RENEG)
+ SSL_set_renegotiate_mode(BACKEND->handle, ssl_renegotiate_freely);
+#endif
+
SSL_set_connect_state(BACKEND->handle);
BACKEND->server_cert = 0x0;
@@ -2627,6 +2737,15 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
/* Check if there's a cached ID we can/should use here! */
if(SSL_SET_OPTION(primary.sessionid)) {
void *ssl_sessionid = NULL;
+ int connectdata_idx = ossl_get_ssl_conn_index();
+ int sockindex_idx = ossl_get_ssl_sockindex_index();
+
+ if(connectdata_idx >= 0 && sockindex_idx >= 0) {
+ /* Store the data needed for the "new session" callback.
+ * The sockindex is stored as a pointer to an array element. */
+ SSL_set_ex_data(BACKEND->handle, connectdata_idx, conn);
+ SSL_set_ex_data(BACKEND->handle, sockindex_idx, conn->sock + sockindex);
+ }
Curl_ssl_sessionid_lock(conn);
if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL, sockindex)) {
@@ -2699,6 +2818,12 @@ static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex)
connssl->connecting_state = ssl_connect_2_writing;
return CURLE_OK;
}
+#ifdef SSL_ERROR_WANT_ASYNC
+ if(SSL_ERROR_WANT_ASYNC == detail) {
+ connssl->connecting_state = ssl_connect_2;
+ return CURLE_OK;
+ }
+#endif
else {
/* untreated error */
unsigned long errdetail;
@@ -2721,14 +2846,14 @@ static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex)
if((lib == ERR_LIB_SSL) &&
(reason == SSL_R_CERTIFICATE_VERIFY_FAILED)) {
- result = CURLE_SSL_CACERT;
+ result = CURLE_PEER_FAILED_VERIFICATION;
lerr = SSL_get_verify_result(BACKEND->handle);
if(lerr != X509_V_OK) {
*certverifyresult = lerr;
- snprintf(error_buffer, sizeof(error_buffer),
- "SSL certificate problem: %s",
- X509_verify_cert_error_string(lerr));
+ msnprintf(error_buffer, sizeof(error_buffer),
+ "SSL certificate problem: %s",
+ X509_verify_cert_error_string(lerr));
}
else
/* strcpy() is fine here as long as the string fits within
@@ -2795,6 +2920,9 @@ static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex)
}
else
infof(data, "ALPN, server did not agree to a protocol\n");
+
+ Curl_multiuse_state(conn, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
}
#endif
@@ -2839,7 +2967,7 @@ static void pubkey_show(struct Curl_easy *data,
char *ptr;
char namebuf[32];
- snprintf(namebuf, sizeof(namebuf), "%s(%s)", type, name);
+ msnprintf(namebuf, sizeof(namebuf), "%s(%s)", type, name);
if(bn)
BN_print(mem, bn);
@@ -2900,8 +3028,8 @@ static int X509V3_ext(struct Curl_easy *data,
while((j<(size_t)biomem->length) && (biomem->data[j] == ' '))
j++;
if(j<(size_t)biomem->length)
- ptr += snprintf(ptr, sizeof(buf)-(ptr-buf), "%s%c", sep,
- biomem->data[j]);
+ ptr += msnprintf(ptr, sizeof(buf)-(ptr-buf), "%s%c", sep,
+ biomem->data[j]);
}
Curl_ssl_push_certinfo(data, certnum, namebuf, buf);
@@ -3101,11 +3229,6 @@ static CURLcode get_cert_chain(struct connectdata *conn,
#endif
break;
}
-#if 0
- case EVP_PKEY_EC: /* symbol not present in OpenSSL 0.9.6 */
- /* left TODO */
- break;
-#endif
}
EVP_PKEY_free(pubkey);
}
@@ -3214,20 +3337,8 @@ static CURLcode servercert(struct connectdata *conn,
/* we've been asked to gather certificate info! */
(void)get_cert_chain(conn, connssl);
- fp = BIO_new(BIO_s_file());
- if(fp == NULL) {
- failf(data,
- "BIO_new return NULL, " OSSL_PACKAGE
- " error %s",
- ossl_strerror(ERR_get_error(), error_buffer,
- sizeof(error_buffer)) );
- BIO_free(mem);
- return CURLE_OUT_OF_MEMORY;
- }
-
BACKEND->server_cert = SSL_get_peer_certificate(BACKEND->handle);
if(!BACKEND->server_cert) {
- BIO_free(fp);
BIO_free(mem);
if(!strict)
return CURLE_OK;
@@ -3262,7 +3373,6 @@ static CURLcode servercert(struct connectdata *conn,
if(SSL_CONN_CONFIG(verifyhost)) {
result = verifyhost(conn, BACKEND->server_cert);
if(result) {
- BIO_free(fp);
X509_free(BACKEND->server_cert);
BACKEND->server_cert = NULL;
return result;
@@ -3284,6 +3394,18 @@ static CURLcode servercert(struct connectdata *conn,
/* e.g. match issuer name with provided issuer certificate */
if(SSL_SET_OPTION(issuercert)) {
+ fp = BIO_new(BIO_s_file());
+ if(fp == NULL) {
+ failf(data,
+ "BIO_new return NULL, " OSSL_PACKAGE
+ " error %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)) );
+ X509_free(BACKEND->server_cert);
+ BACKEND->server_cert = NULL;
+ return CURLE_OUT_OF_MEMORY;
+ }
+
if(BIO_read_filename(fp, SSL_SET_OPTION(issuercert)) <= 0) {
if(strict)
failf(data, "SSL: Unable to open issuer cert (%s)",
@@ -3319,6 +3441,7 @@ static CURLcode servercert(struct connectdata *conn,
infof(data, " SSL certificate issuer check ok (%s)\n",
SSL_SET_OPTION(issuercert));
+ BIO_free(fp);
X509_free(issuer);
}
@@ -3347,7 +3470,6 @@ static CURLcode servercert(struct connectdata *conn,
if(SSL_CONN_CONFIG(verifystatus)) {
result = verifystatus(conn, connssl);
if(result) {
- BIO_free(fp);
X509_free(BACKEND->server_cert);
BACKEND->server_cert = NULL;
return result;
@@ -3367,7 +3489,6 @@ static CURLcode servercert(struct connectdata *conn,
failf(data, "SSL: public key does not match pinned public key!");
}
- BIO_free(fp);
X509_free(BACKEND->server_cert);
BACKEND->server_cert = NULL;
connssl->connecting_state = ssl_connect_done;
@@ -3378,52 +3499,10 @@ static CURLcode servercert(struct connectdata *conn,
static CURLcode ossl_connect_step3(struct connectdata *conn, int sockindex)
{
CURLcode result = CURLE_OK;
- struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
- if(SSL_SET_OPTION(primary.sessionid)) {
- bool incache;
- SSL_SESSION *our_ssl_sessionid;
- void *old_ssl_sessionid = NULL;
-
- our_ssl_sessionid = SSL_get1_session(BACKEND->handle);
-
- /* SSL_get1_session() will increment the reference count and the session
- will stay in memory until explicitly freed with SSL_SESSION_free(3),
- regardless of its state. */
-
- Curl_ssl_sessionid_lock(conn);
- incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL,
- sockindex));
- if(incache) {
- if(old_ssl_sessionid != our_ssl_sessionid) {
- infof(data, "old SSL session ID is stale, removing\n");
- Curl_ssl_delsessionid(conn, old_ssl_sessionid);
- incache = FALSE;
- }
- }
-
- if(!incache) {
- result = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
- 0 /* unknown size */, sockindex);
- if(result) {
- Curl_ssl_sessionid_unlock(conn);
- failf(data, "failed to store ssl session");
- return result;
- }
- }
- else {
- /* Session was incache, so refcount already incremented earlier.
- * Avoid further increments with each SSL_get1_session() call.
- * This does not free the session as refcount remains > 0
- */
- SSL_SESSION_free(our_ssl_sessionid);
- }
- Curl_ssl_sessionid_unlock(conn);
- }
-
/*
* We check certificates to authenticate the server; otherwise we risk
* man-in-the-middle attack; NEVERTHELESS, if we're told explicitly not to
@@ -3678,7 +3757,10 @@ static ssize_t ossl_recv(struct connectdata *conn, /* connection data */
switch(err) {
case SSL_ERROR_NONE: /* this is not an error */
+ break;
case SSL_ERROR_ZERO_RETURN: /* no more data */
+ /* close_notify alert */
+ connclose(conn, "TLS close_notify");
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
@@ -3709,8 +3791,13 @@ static ssize_t ossl_recv(struct connectdata *conn, /* connection data */
static size_t Curl_ossl_version(char *buffer, size_t size)
{
#ifdef OPENSSL_IS_BORINGSSL
- return snprintf(buffer, size, OSSL_PACKAGE);
-#else /* OPENSSL_IS_BORINGSSL */
+ return msnprintf(buffer, size, OSSL_PACKAGE);
+#elif defined(HAVE_OPENSSL_VERSION) && defined(OPENSSL_VERSION_STRING)
+ return msnprintf(buffer, size, "%s/%s",
+ OSSL_PACKAGE, OpenSSL_version(OPENSSL_VERSION_STRING));
+#else
+ /* not BoringSSL and not using OpenSSL_version */
+
char sub[3];
unsigned long ssleay_value;
sub[2]='\0';
@@ -3736,12 +3823,16 @@ static size_t Curl_ossl_version(char *buffer, size_t size)
sub[0]='\0';
}
- return snprintf(buffer, size, "%s/%lx.%lx.%lx%s",
- OSSL_PACKAGE,
- (ssleay_value>>28)&0xf,
- (ssleay_value>>20)&0xff,
- (ssleay_value>>12)&0xff,
- sub);
+ return msnprintf(buffer, size, "%s/%lx.%lx.%lx%s"
+#ifdef OPENSSL_FIPS
+ "-fips"
+#endif
+ ,
+ OSSL_PACKAGE,
+ (ssleay_value>>28)&0xf,
+ (ssleay_value>>20)&0xff,
+ (ssleay_value>>12)&0xff,
+ sub);
#endif /* OPENSSL_IS_BORINGSSL */
}
diff --git a/Utilities/cmcurl/lib/vtls/polarssl.c b/Utilities/cmcurl/lib/vtls/polarssl.c
index 27af0ccf3..7ea26b442 100644
--- a/Utilities/cmcurl/lib/vtls/polarssl.c
+++ b/Utilities/cmcurl/lib/vtls/polarssl.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 2010 - 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
*
* This software is licensed as described in the file COPYING, which
@@ -55,6 +55,7 @@
#include "select.h"
#include "strcase.h"
#include "polarssl_threadlock.h"
+#include "multiif.h"
#include "curl_printf.h"
#include "curl_memory.h"
/* The last #include file should be: */
@@ -497,7 +498,7 @@ polarssl_connect_step2(struct connectdata *conn,
if(ret & BADCERT_REVOKED) {
failf(data, "Cert verify failed: BADCERT_REVOKED");
- return CURLE_SSL_CACERT;
+ return CURLE_PEER_FAILED_VERIFICATION;
}
if(ret & BADCERT_CN_MISMATCH)
@@ -593,6 +594,8 @@ polarssl_connect_step2(struct connectdata *conn,
}
else
infof(data, "ALPN, server did not agree to a protocol\n");
+ Curl_multiuse_state(conn, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
}
#endif
@@ -716,9 +719,9 @@ static void Curl_polarssl_session_free(void *ptr)
static size_t Curl_polarssl_version(char *buffer, size_t size)
{
unsigned int version = version_get_number();
- return snprintf(buffer, size, "%s/%d.%d.%d",
- version >= 0x01030A00?"mbedTLS":"PolarSSL",
- version>>24, (version>>16)&0xff, (version>>8)&0xff);
+ return msnprintf(buffer, size, "%s/%d.%d.%d",
+ version >= 0x01030A00?"mbedTLS":"PolarSSL",
+ version>>24, (version>>16)&0xff, (version>>8)&0xff);
}
static CURLcode
@@ -908,9 +911,7 @@ const struct Curl_ssl Curl_ssl_polarssl = {
Curl_none_check_cxn, /* check_cxn */
Curl_none_shutdown, /* shutdown */
Curl_polarssl_data_pending, /* data_pending */
- /* This might cause libcurl to use a weeker random!
- * TODO: use Polarssl's CTR-DRBG or HMAC-DRBG
- */
+ /* This might cause libcurl to use a weeker random! */
Curl_none_random, /* random */
Curl_none_cert_status_request, /* cert_status_request */
Curl_polarssl_connect, /* connect */
diff --git a/Utilities/cmcurl/lib/vtls/polarssl_threadlock.c b/Utilities/cmcurl/lib/vtls/polarssl_threadlock.c
index dd5fbd7ec..27c94b11e 100644
--- a/Utilities/cmcurl/lib/vtls/polarssl_threadlock.c
+++ b/Utilities/cmcurl/lib/vtls/polarssl_threadlock.c
@@ -23,16 +23,15 @@
#include "curl_setup.h"
#if (defined(USE_POLARSSL) || defined(USE_MBEDTLS)) && \
- (defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32))
-
-#if defined(USE_THREADS_POSIX)
-# ifdef HAVE_PTHREAD_H
-# include <pthread.h>
-# endif
-#elif defined(USE_THREADS_WIN32)
-# ifdef HAVE_PROCESS_H
-# include <process.h>
-# endif
+ ((defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \
+ (defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H)))
+
+#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
+# include <pthread.h>
+# define POLARSSL_MUTEX_T pthread_mutex_t
+#elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H)
+# include <process.h>
+# define POLARSSL_MUTEX_T HANDLE
#endif
#include "polarssl_threadlock.h"
@@ -50,25 +49,23 @@ static POLARSSL_MUTEX_T *mutex_buf = NULL;
int Curl_polarsslthreadlock_thread_setup(void)
{
int i;
- int ret;
mutex_buf = calloc(NUMT * sizeof(POLARSSL_MUTEX_T), 1);
if(!mutex_buf)
return 0; /* error, no number of threads defined */
-#ifdef HAVE_PTHREAD_H
for(i = 0; i < NUMT; i++) {
+ int ret;
+#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
ret = pthread_mutex_init(&mutex_buf[i], NULL);
if(ret)
return 0; /* pthread_mutex_init failed */
- }
-#elif defined(HAVE_PROCESS_H)
- for(i = 0; i < NUMT; i++) {
+#elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H)
mutex_buf[i] = CreateMutex(0, FALSE, 0);
if(mutex_buf[i] == 0)
return 0; /* CreateMutex failed */
+#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */
}
-#endif /* HAVE_PTHREAD_H */
return 1; /* OK */
}
@@ -76,24 +73,22 @@ int Curl_polarsslthreadlock_thread_setup(void)
int Curl_polarsslthreadlock_thread_cleanup(void)
{
int i;
- int ret;
if(!mutex_buf)
return 0; /* error, no threads locks defined */
-#ifdef HAVE_PTHREAD_H
for(i = 0; i < NUMT; i++) {
+ int ret;
+#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
ret = pthread_mutex_destroy(&mutex_buf[i]);
if(ret)
return 0; /* pthread_mutex_destroy failed */
- }
-#elif defined(HAVE_PROCESS_H)
- for(i = 0; i < NUMT; i++) {
+#elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H)
ret = CloseHandle(mutex_buf[i]);
if(!ret)
return 0; /* CloseHandle failed */
+#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */
}
-#endif /* HAVE_PTHREAD_H */
free(mutex_buf);
mutex_buf = NULL;
@@ -102,51 +97,47 @@ int Curl_polarsslthreadlock_thread_cleanup(void)
int Curl_polarsslthreadlock_lock_function(int n)
{
- int ret;
-#ifdef HAVE_PTHREAD_H
if(n < NUMT) {
+ int ret;
+#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
ret = pthread_mutex_lock(&mutex_buf[n]);
if(ret) {
DEBUGF(fprintf(stderr,
"Error: polarsslthreadlock_lock_function failed\n"));
return 0; /* pthread_mutex_lock failed */
}
- }
-#elif defined(HAVE_PROCESS_H)
- if(n < NUMT) {
+#elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H)
ret = (WaitForSingleObject(mutex_buf[n], INFINITE) == WAIT_FAILED?1:0);
if(ret) {
DEBUGF(fprintf(stderr,
"Error: polarsslthreadlock_lock_function failed\n"));
return 0; /* pthread_mutex_lock failed */
}
+#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */
}
-#endif /* HAVE_PTHREAD_H */
return 1; /* OK */
}
int Curl_polarsslthreadlock_unlock_function(int n)
{
- int ret;
-#ifdef HAVE_PTHREAD_H
if(n < NUMT) {
+ int ret;
+#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
ret = pthread_mutex_unlock(&mutex_buf[n]);
if(ret) {
DEBUGF(fprintf(stderr,
"Error: polarsslthreadlock_unlock_function failed\n"));
return 0; /* pthread_mutex_unlock failed */
}
- }
-#elif defined(HAVE_PROCESS_H)
- if(n < NUMT) {
+#elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H)
ret = ReleaseMutex(mutex_buf[n]);
if(!ret) {
DEBUGF(fprintf(stderr,
"Error: polarsslthreadlock_unlock_function failed\n"));
return 0; /* pthread_mutex_lock failed */
}
+#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */
}
-#endif /* HAVE_PTHREAD_H */
return 1; /* OK */
}
diff --git a/Utilities/cmcurl/lib/vtls/polarssl_threadlock.h b/Utilities/cmcurl/lib/vtls/polarssl_threadlock.h
index dda5359b8..122647528 100644
--- a/Utilities/cmcurl/lib/vtls/polarssl_threadlock.h
+++ b/Utilities/cmcurl/lib/vtls/polarssl_threadlock.h
@@ -26,13 +26,8 @@
#if (defined USE_POLARSSL) || (defined USE_MBEDTLS)
-#if defined(USE_THREADS_POSIX)
-# define POLARSSL_MUTEX_T pthread_mutex_t
-#elif defined(USE_THREADS_WIN32)
-# define POLARSSL_MUTEX_T HANDLE
-#endif
-
-#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
+#if (defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \
+ (defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H))
int Curl_polarsslthreadlock_thread_setup(void);
int Curl_polarsslthreadlock_thread_cleanup(void);
diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c
index e4426924b..0f6f734fd 100644
--- a/Utilities/cmcurl/lib/vtls/schannel.c
+++ b/Utilities/cmcurl/lib/vtls/schannel.c
@@ -7,7 +7,7 @@
*
* Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de>
* Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
- * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -23,9 +23,8 @@
***************************************************************************/
/*
- * Source file for all SChannel-specific code for the TLS/SSL layer. No code
+ * Source file for all Schannel-specific code for the TLS/SSL layer. No code
* but vtls.c should ever call or use these functions.
- *
*/
/*
@@ -59,6 +58,7 @@
#include "warnless.h"
#include "x509asn1.h"
#include "curl_printf.h"
+#include "multiif.h"
#include "system_win32.h"
/* The last #include file should be: */
@@ -196,7 +196,7 @@ set_ssl_version_min_max(SCHANNEL_CRED *schannel_cred, struct connectdata *conn)
schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_2_CLIENT;
break;
case CURL_SSLVERSION_TLSv1_3:
- failf(data, "Schannel: TLS 1.3 is not yet supported");
+ failf(data, "schannel: TLS 1.3 is not yet supported");
return CURLE_SSL_CONNECT_ERROR;
}
}
@@ -325,6 +325,9 @@ get_alg_id_by_name(char *name)
#ifdef CALG_ECDSA
CIPHEROPTION(CALG_ECDSA);
#endif
+#ifdef CALG_ECDH_EPHEM
+ CIPHEROPTION(CALG_ECDH_EPHEM);
+#endif
return 0;
}
@@ -357,6 +360,7 @@ get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path,
TCHAR **thumbprint)
{
TCHAR *sep;
+ TCHAR *store_path_start;
size_t store_name_len;
sep = _tcschr(path, TEXT('\\'));
@@ -387,13 +391,17 @@ get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path,
else
return CURLE_SSL_CERTPROBLEM;
- *store_path = sep + 1;
+ store_path_start = sep + 1;
- sep = _tcschr(*store_path, TEXT('\\'));
+ sep = _tcschr(store_path_start, TEXT('\\'));
if(sep == NULL)
return CURLE_SSL_CERTPROBLEM;
- *sep = 0;
+ *sep = TEXT('\0');
+ *store_path = _tcsdup(store_path_start);
+ *sep = TEXT('\\');
+ if(*store_path == NULL)
+ return CURLE_OUT_OF_MEMORY;
*thumbprint = sep + 1;
if(_tcslen(*thumbprint) != CERT_THUMBPRINT_STR_LEN)
@@ -429,14 +437,15 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
conn->host.name;
- infof(data, "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n",
- hostname, conn->remote_port);
+ DEBUGF(infof(data,
+ "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n",
+ hostname, conn->remote_port));
if(Curl_verify_windows_version(5, 1, PLATFORM_WINNT,
VERSION_LESS_THAN_EQUAL)) {
- /* SChannel in Windows XP (OS version 5.1) uses legacy handshakes and
+ /* Schannel in Windows XP (OS version 5.1) uses legacy handshakes and
algorithms that may not be supported by all servers. */
- infof(data, "schannel: WinSSL version is old and may not be able to "
+ infof(data, "schannel: Windows version is old and may not be able to "
"connect to some servers due to lack of SNI, algorithms, etc.\n");
}
@@ -490,12 +499,13 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
Curl_ssl_sessionid_lock(conn);
if(!Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL, sockindex)) {
BACKEND->cred = old_cred;
- infof(data, "schannel: re-using existing credential handle\n");
+ DEBUGF(infof(data, "schannel: re-using existing credential handle\n"));
/* increment the reference counter of the credential/session handle */
BACKEND->cred->refcount++;
- infof(data, "schannel: incremented credential handle refcount = %d\n",
- BACKEND->cred->refcount);
+ DEBUGF(infof(data,
+ "schannel: incremented credential handle refcount = %d\n",
+ BACKEND->cred->refcount));
}
Curl_ssl_sessionid_unlock(conn);
}
@@ -513,31 +523,32 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
#endif
schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION;
- /* TODO s/data->set.ssl.no_revoke/SSL_SET_OPTION(no_revoke)/g */
if(data->set.ssl.no_revoke) {
schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
SCH_CRED_IGNORE_REVOCATION_OFFLINE;
- infof(data, "schannel: disabled server certificate revocation "
- "checks\n");
+ DEBUGF(infof(data, "schannel: disabled server certificate revocation "
+ "checks\n"));
}
else {
schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
- infof(data, "schannel: checking server certificate revocation\n");
+ DEBUGF(infof(data,
+ "schannel: checking server certificate revocation\n"));
}
}
else {
schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
SCH_CRED_IGNORE_REVOCATION_OFFLINE;
- infof(data, "schannel: disabled server certificate revocation checks\n");
+ DEBUGF(infof(data,
+ "schannel: disabled server cert revocation checks\n"));
}
if(!conn->ssl_config.verifyhost) {
schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
- infof(data, "schannel: verifyhost setting prevents Schannel from "
- "comparing the supplied target name with the subject "
- "names in server certificates.\n");
+ DEBUGF(infof(data, "schannel: verifyhost setting prevents Schannel from "
+ "comparing the supplied target name with the subject "
+ "names in server certificates.\n"));
}
switch(conn->ssl_config.version) {
@@ -609,9 +620,11 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
failf(data, "schannel: Failed to open cert store %x %s, "
"last error is %x",
cert_store_name, cert_store_path, GetLastError());
+ free(cert_store_path);
Curl_unicodefree(cert_path);
return CURLE_SSL_CERTPROBLEM;
}
+ free(cert_store_path);
cert_thumbprint.pbData = cert_thumbprint_data;
cert_thumbprint.cbData = CERT_THUMBPRINT_DATA_LEN;
@@ -674,8 +687,9 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
CertFreeCertificateContext(client_certs[0]);
if(sspi_status != SEC_E_OK) {
+ char buffer[STRERROR_LEN];
failf(data, "schannel: AcquireCredentialsHandle failed: %s",
- Curl_sspi_strerror(conn, sspi_status));
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
Curl_safefree(BACKEND->cred);
switch(sspi_status) {
case SEC_E_INSUFFICIENT_MEMORY:
@@ -790,15 +804,16 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
Curl_unicodefree(host_name);
if(sspi_status != SEC_I_CONTINUE_NEEDED) {
+ char buffer[STRERROR_LEN];
Curl_safefree(BACKEND->ctxt);
switch(sspi_status) {
case SEC_E_INSUFFICIENT_MEMORY:
failf(data, "schannel: initial InitializeSecurityContext failed: %s",
- Curl_sspi_strerror(conn, sspi_status));
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
return CURLE_OUT_OF_MEMORY;
case SEC_E_WRONG_PRINCIPAL:
failf(data, "schannel: SNI or certificate check failed: %s",
- Curl_sspi_strerror(conn, sspi_status));
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
return CURLE_PEER_FAILED_VERIFICATION;
/*
case SEC_E_INVALID_HANDLE:
@@ -813,13 +828,13 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
*/
default:
failf(data, "schannel: initial InitializeSecurityContext failed: %s",
- Curl_sspi_strerror(conn, sspi_status));
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
return CURLE_SSL_CONNECT_ERROR;
}
}
- infof(data, "schannel: sending initial handshake data: "
- "sending %lu bytes...\n", outbuf.cbBuffer);
+ DEBUGF(infof(data, "schannel: sending initial handshake data: "
+ "sending %lu bytes...\n", outbuf.cbBuffer));
/* send initial handshake data which is now stored in output buffer */
result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
@@ -831,8 +846,8 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
return CURLE_SSL_CONNECT_ERROR;
}
- infof(data, "schannel: sent initial handshake data: "
- "sent %zd bytes\n", written);
+ DEBUGF(infof(data, "schannel: sent initial handshake data: "
+ "sent %zd bytes\n", written));
BACKEND->recv_unrecoverable_err = CURLE_OK;
BACKEND->recv_sspi_close_notify = false;
@@ -853,13 +868,11 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
unsigned char *reallocated_buffer;
- size_t reallocated_length;
SecBuffer outbuf[3];
SecBufferDesc outbuf_desc;
SecBuffer inbuf[2];
SecBufferDesc inbuf_desc;
SECURITY_STATUS sspi_status = SEC_E_OK;
- TCHAR *host_name;
CURLcode result;
bool doread;
char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
@@ -868,8 +881,9 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE;
- infof(data, "schannel: SSL/TLS connection with %s port %hu (step 2/3)\n",
- hostname, conn->remote_port);
+ DEBUGF(infof(data,
+ "schannel: SSL/TLS connection with %s port %hu (step 2/3)\n",
+ hostname, conn->remote_port));
if(!BACKEND->cred || !BACKEND->ctxt)
return CURLE_SSL_CONNECT_ERROR;
@@ -901,7 +915,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
if(BACKEND->encdata_length - BACKEND->encdata_offset <
CURL_SCHANNEL_BUFFER_FREE_SIZE) {
/* increase internal encrypted data buffer */
- reallocated_length = BACKEND->encdata_offset +
+ size_t reallocated_length = BACKEND->encdata_offset +
CURL_SCHANNEL_BUFFER_FREE_SIZE;
reallocated_buffer = realloc(BACKEND->encdata_buffer,
reallocated_length);
@@ -917,6 +931,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
}
for(;;) {
+ TCHAR *host_name;
if(doread) {
/* read encrypted handshake data from socket */
result = Curl_read_plain(conn->sock[sockindex],
@@ -928,8 +943,8 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
if(result == CURLE_AGAIN) {
if(connssl->connecting_state != ssl_connect_2_writing)
connssl->connecting_state = ssl_connect_2_reading;
- infof(data, "schannel: failed to receive handshake, "
- "need more data\n");
+ DEBUGF(infof(data, "schannel: failed to receive handshake, "
+ "need more data\n"));
return CURLE_OK;
}
else if((result != CURLE_OK) || (nread == 0)) {
@@ -941,11 +956,12 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
/* increase encrypted data buffer offset */
BACKEND->encdata_offset += nread;
BACKEND->encdata_is_incomplete = false;
- infof(data, "schannel: encrypted data got %zd\n", nread);
+ DEBUGF(infof(data, "schannel: encrypted data got %zd\n", nread));
}
- infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
- BACKEND->encdata_offset, BACKEND->encdata_length);
+ DEBUGF(infof(data,
+ "schannel: encrypted data buffer: offset %zu length %zu\n",
+ BACKEND->encdata_offset, BACKEND->encdata_length));
/* setup input buffers */
InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(BACKEND->encdata_offset),
@@ -988,7 +1004,8 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
BACKEND->encdata_is_incomplete = true;
connssl->connecting_state = ssl_connect_2_reading;
- infof(data, "schannel: received incomplete message, need more data\n");
+ DEBUGF(infof(data,
+ "schannel: received incomplete message, need more data\n"));
return CURLE_OK;
}
@@ -999,7 +1016,8 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
!(BACKEND->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
BACKEND->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
connssl->connecting_state = ssl_connect_2_writing;
- infof(data, "schannel: a client certificate has been requested\n");
+ DEBUGF(infof(data,
+ "schannel: a client certificate has been requested\n"));
return CURLE_OK;
}
@@ -1008,8 +1026,8 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
for(i = 0; i < 3; i++) {
/* search for handshake tokens that need to be send */
if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) {
- infof(data, "schannel: sending next handshake data: "
- "sending %lu bytes...\n", outbuf[i].cbBuffer);
+ DEBUGF(infof(data, "schannel: sending next handshake data: "
+ "sending %lu bytes...\n", outbuf[i].cbBuffer));
/* send handshake token to server */
result = Curl_write_plain(conn, conn->sock[sockindex],
@@ -1030,14 +1048,15 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
}
}
else {
+ char buffer[STRERROR_LEN];
switch(sspi_status) {
case SEC_E_INSUFFICIENT_MEMORY:
failf(data, "schannel: next InitializeSecurityContext failed: %s",
- Curl_sspi_strerror(conn, sspi_status));
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
return CURLE_OUT_OF_MEMORY;
case SEC_E_WRONG_PRINCIPAL:
failf(data, "schannel: SNI or certificate check failed: %s",
- Curl_sspi_strerror(conn, sspi_status));
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
return CURLE_PEER_FAILED_VERIFICATION;
/*
case SEC_E_INVALID_HANDLE:
@@ -1052,14 +1071,15 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
*/
default:
failf(data, "schannel: next InitializeSecurityContext failed: %s",
- Curl_sspi_strerror(conn, sspi_status));
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
return CURLE_SSL_CONNECT_ERROR;
}
}
/* check if there was additional remaining encrypted data */
if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) {
- infof(data, "schannel: encrypted data length: %lu\n", inbuf[1].cbBuffer);
+ DEBUGF(infof(data, "schannel: encrypted data length: %lu\n",
+ inbuf[1].cbBuffer));
/*
There are two cases where we could be getting extra data here:
1) If we're renegotiating a connection and the handshake is already
@@ -1098,7 +1118,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
/* check if the handshake is complete */
if(sspi_status == SEC_E_OK) {
connssl->connecting_state = ssl_connect_3;
- infof(data, "schannel: SSL/TLS handshake complete\n");
+ DEBUGF(infof(data, "schannel: SSL/TLS handshake complete\n"));
}
pubkey_ptr = SSL_IS_PROXY() ?
@@ -1114,13 +1134,68 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
#ifdef HAS_MANUAL_VERIFY_API
if(conn->ssl_config.verifypeer && BACKEND->use_manual_cred_validation) {
- return verify_certificate(conn, sockindex);
+ return Curl_verify_certificate(conn, sockindex);
}
#endif
return CURLE_OK;
}
+static bool
+valid_cert_encoding(const CERT_CONTEXT *cert_context)
+{
+ return (cert_context != NULL) &&
+ ((cert_context->dwCertEncodingType & X509_ASN_ENCODING) != 0) &&
+ (cert_context->pbCertEncoded != NULL) &&
+ (cert_context->cbCertEncoded > 0);
+}
+
+typedef bool(*Read_crt_func)(const CERT_CONTEXT *ccert_context, void *arg);
+
+static void
+traverse_cert_store(const CERT_CONTEXT *context, Read_crt_func func,
+ void *arg)
+{
+ const CERT_CONTEXT *current_context = NULL;
+ bool should_continue = true;
+ while(should_continue &&
+ (current_context = CertEnumCertificatesInStore(
+ context->hCertStore,
+ current_context)) != NULL)
+ should_continue = func(current_context, arg);
+
+ if(current_context)
+ CertFreeCertificateContext(current_context);
+}
+
+static bool
+cert_counter_callback(const CERT_CONTEXT *ccert_context, void *certs_count)
+{
+ if(valid_cert_encoding(ccert_context))
+ (*(int *)certs_count)++;
+ return true;
+}
+
+struct Adder_args
+{
+ struct connectdata *conn;
+ CURLcode result;
+ int idx;
+};
+
+static bool
+add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, void *raw_arg)
+{
+ struct Adder_args *args = (struct Adder_args*)raw_arg;
+ args->result = CURLE_OK;
+ if(valid_cert_encoding(ccert_context)) {
+ const char *beg = (const char *) ccert_context->pbCertEncoded;
+ const char *end = beg + ccert_context->cbCertEncoded;
+ args->result = Curl_extract_certinfo(args->conn, (args->idx)++, beg, end);
+ }
+ return args->result == CURLE_OK;
+}
+
static CURLcode
schannel_connect_step3(struct connectdata *conn, int sockindex)
{
@@ -1129,7 +1204,7 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
SECURITY_STATUS sspi_status = SEC_E_OK;
CERT_CONTEXT *ccert_context = NULL;
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
+#ifdef DEBUGBUILD
const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
conn->host.name;
#endif
@@ -1139,8 +1214,9 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
- infof(data, "schannel: SSL/TLS connection with %s port %hu (step 3/3)\n",
- hostname, conn->remote_port);
+ DEBUGF(infof(data,
+ "schannel: SSL/TLS connection with %s port %hu (step 3/3)\n",
+ hostname, conn->remote_port));
if(!BACKEND->cred)
return CURLE_SSL_CONNECT_ERROR;
@@ -1192,6 +1268,8 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
}
else
infof(data, "ALPN, server did not agree to a protocol\n");
+ Curl_multiuse_state(conn, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
}
#endif
@@ -1205,7 +1283,8 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
sockindex));
if(incache) {
if(old_cred != BACKEND->cred) {
- infof(data, "schannel: old credential handle is stale, removing\n");
+ DEBUGF(infof(data,
+ "schannel: old credential handle is stale, removing\n"));
/* we're not taking old_cred ownership here, no refcount++ is needed */
Curl_ssl_delsessionid(conn, (void *)old_cred);
incache = FALSE;
@@ -1223,13 +1302,15 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
else {
/* this cred session is now also referenced by sessionid cache */
BACKEND->cred->refcount++;
- infof(data, "schannel: stored credential handle in session cache\n");
+ DEBUGF(infof(data,
+ "schannel: stored credential handle in session cache\n"));
}
}
Curl_ssl_sessionid_unlock(conn);
}
if(data->set.ssl.certinfo) {
+ int certs_count = 0;
sspi_status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
SECPKG_ATTR_REMOTE_CERT_CONTEXT, &ccert_context);
@@ -1238,15 +1319,15 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
return CURLE_PEER_FAILED_VERIFICATION;
}
- result = Curl_ssl_init_certinfo(data, 1);
- if(!result) {
- if(((ccert_context->dwCertEncodingType & X509_ASN_ENCODING) != 0) &&
- (ccert_context->cbCertEncoded > 0)) {
+ traverse_cert_store(ccert_context, cert_counter_callback, &certs_count);
- const char *beg = (const char *) ccert_context->pbCertEncoded;
- const char *end = beg + ccert_context->cbCertEncoded;
- result = Curl_extract_certinfo(conn, 0, beg, end);
- }
+ result = Curl_ssl_init_certinfo(data, certs_count);
+ if(!result) {
+ struct Adder_args args;
+ args.conn = conn;
+ args.idx = 0;
+ traverse_cert_store(ccert_context, add_cert_to_certinfo, &args);
+ result = args.result;
}
CertFreeCertificateContext(ccert_context);
if(result)
@@ -1359,6 +1440,16 @@ schannel_connect_common(struct connectdata *conn, int sockindex,
connssl->state = ssl_connection_complete;
conn->recv[sockindex] = schannel_recv;
conn->send[sockindex] = schannel_send;
+
+#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
+ /* When SSPI is used in combination with Schannel
+ * we need the Schannel context to create the Schannel
+ * binding to pass the IIS extended protection checks.
+ * Available on Windows 7 or later.
+ */
+ conn->sslContext = &BACKEND->ctxt->ctxt_handle;
+#endif
+
*done = TRUE;
}
else
@@ -1543,7 +1634,7 @@ schannel_recv(struct connectdata *conn, int sockindex,
* handled in the cleanup.
*/
- infof(data, "schannel: client wants to read %zu bytes\n", len);
+ DEBUGF(infof(data, "schannel: client wants to read %zu bytes\n", len));
*err = CURLE_OK;
if(len && len <= BACKEND->decdata_offset) {
@@ -1588,12 +1679,13 @@ schannel_recv(struct connectdata *conn, int sockindex,
BACKEND->encdata_buffer = reallocated_buffer;
BACKEND->encdata_length = reallocated_length;
size = BACKEND->encdata_length - BACKEND->encdata_offset;
- infof(data, "schannel: encdata_buffer resized %zu\n",
- BACKEND->encdata_length);
+ DEBUGF(infof(data, "schannel: encdata_buffer resized %zu\n",
+ BACKEND->encdata_length));
}
- infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
- BACKEND->encdata_offset, BACKEND->encdata_length);
+ DEBUGF(infof(data,
+ "schannel: encrypted data buffer: offset %zu length %zu\n",
+ BACKEND->encdata_offset, BACKEND->encdata_length));
/* read encrypted data from socket */
*err = Curl_read_plain(conn->sock[sockindex],
@@ -1603,7 +1695,8 @@ schannel_recv(struct connectdata *conn, int sockindex,
if(*err) {
nread = -1;
if(*err == CURLE_AGAIN)
- infof(data, "schannel: Curl_read_plain returned CURLE_AGAIN\n");
+ DEBUGF(infof(data,
+ "schannel: Curl_read_plain returned CURLE_AGAIN\n"));
else if(*err == CURLE_RECV_ERROR)
infof(data, "schannel: Curl_read_plain returned CURLE_RECV_ERROR\n");
else
@@ -1611,17 +1704,18 @@ schannel_recv(struct connectdata *conn, int sockindex,
}
else if(nread == 0) {
BACKEND->recv_connection_closed = true;
- infof(data, "schannel: server closed the connection\n");
+ DEBUGF(infof(data, "schannel: server closed the connection\n"));
}
else if(nread > 0) {
BACKEND->encdata_offset += (size_t)nread;
BACKEND->encdata_is_incomplete = false;
- infof(data, "schannel: encrypted data got %zd\n", nread);
+ DEBUGF(infof(data, "schannel: encrypted data got %zd\n", nread));
}
}
- infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
- BACKEND->encdata_offset, BACKEND->encdata_length);
+ DEBUGF(infof(data,
+ "schannel: encrypted data buffer: offset %zu length %zu\n",
+ BACKEND->encdata_offset, BACKEND->encdata_length));
/* decrypt loop */
while(BACKEND->encdata_offset > 0 && sspi_status == SEC_E_OK &&
@@ -1649,8 +1743,8 @@ schannel_recv(struct connectdata *conn, int sockindex,
/* check for successfully decrypted data, even before actual
renegotiation or shutdown of the connection context */
if(inbuf[1].BufferType == SECBUFFER_DATA) {
- infof(data, "schannel: decrypted data length: %lu\n",
- inbuf[1].cbBuffer);
+ DEBUGF(infof(data, "schannel: decrypted data length: %lu\n",
+ inbuf[1].cbBuffer));
/* increase buffer in order to fit the received amount of data */
size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ?
@@ -1682,15 +1776,16 @@ schannel_recv(struct connectdata *conn, int sockindex,
BACKEND->decdata_offset += size;
}
- infof(data, "schannel: decrypted data added: %zu\n", size);
- infof(data, "schannel: decrypted data cached: offset %zu length %zu\n",
- BACKEND->decdata_offset, BACKEND->decdata_length);
+ DEBUGF(infof(data, "schannel: decrypted data added: %zu\n", size));
+ DEBUGF(infof(data,
+ "schannel: decrypted cached: offset %zu length %zu\n",
+ BACKEND->decdata_offset, BACKEND->decdata_length));
}
/* check for remaining encrypted data */
if(inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) {
- infof(data, "schannel: encrypted data length: %lu\n",
- inbuf[3].cbBuffer);
+ DEBUGF(infof(data, "schannel: encrypted data length: %lu\n",
+ inbuf[3].cbBuffer));
/* check if the remaining data is less than the total amount
* and therefore begins after the already processed data
@@ -1704,8 +1799,9 @@ schannel_recv(struct connectdata *conn, int sockindex,
BACKEND->encdata_offset = inbuf[3].cbBuffer;
}
- infof(data, "schannel: encrypted data cached: offset %zu length %zu\n",
- BACKEND->encdata_offset, BACKEND->encdata_length);
+ DEBUGF(infof(data,
+ "schannel: encrypted cached: offset %zu length %zu\n",
+ BACKEND->encdata_offset, BACKEND->encdata_length));
}
else {
/* reset encrypted buffer offset, because there is no data remaining */
@@ -1759,22 +1855,25 @@ schannel_recv(struct connectdata *conn, int sockindex,
goto cleanup;
}
else {
+ char buffer[STRERROR_LEN];
*err = CURLE_RECV_ERROR;
infof(data, "schannel: failed to read data from server: %s\n",
- Curl_sspi_strerror(conn, sspi_status));
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
goto cleanup;
}
}
- infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
- BACKEND->encdata_offset, BACKEND->encdata_length);
+ DEBUGF(infof(data,
+ "schannel: encrypted data buffer: offset %zu length %zu\n",
+ BACKEND->encdata_offset, BACKEND->encdata_length));
- infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
- BACKEND->decdata_offset, BACKEND->decdata_length);
+ DEBUGF(infof(data,
+ "schannel: decrypted data buffer: offset %zu length %zu\n",
+ BACKEND->decdata_offset, BACKEND->decdata_length));
cleanup:
/* Warning- there is no guarantee the encdata state is valid at this point */
- infof(data, "schannel: schannel_recv cleanup\n");
+ DEBUGF(infof(data, "schannel: schannel_recv cleanup\n"));
/* Error if the connection has closed without a close_notify.
Behavior here is a matter of debate. We don't want to be vulnerable to a
@@ -1807,10 +1906,10 @@ cleanup:
memmove(BACKEND->decdata_buffer, BACKEND->decdata_buffer + size,
BACKEND->decdata_offset - size);
BACKEND->decdata_offset -= size;
-
- infof(data, "schannel: decrypted data returned %zu\n", size);
- infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
- BACKEND->decdata_offset, BACKEND->decdata_length);
+ DEBUGF(infof(data, "schannel: decrypted data returned %zu\n", size));
+ DEBUGF(infof(data,
+ "schannel: decrypted data buffer: offset %zu length %zu\n",
+ BACKEND->decdata_offset, BACKEND->decdata_length));
*err = CURLE_OK;
return (ssize_t)size;
}
@@ -1888,6 +1987,8 @@ static int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
conn->host.name;
+ DEBUGASSERT(data);
+
infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu\n",
hostname, conn->remote_port);
@@ -1907,9 +2008,11 @@ static int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
sspi_status = s_pSecFn->ApplyControlToken(&BACKEND->ctxt->ctxt_handle,
&BuffDesc);
- if(sspi_status != SEC_E_OK)
+ if(sspi_status != SEC_E_OK) {
+ char buffer[STRERROR_LEN];
failf(data, "schannel: ApplyControlToken failure: %s",
- Curl_sspi_strerror(conn, sspi_status));
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+ }
host_name = Curl_convert_UTF8_to_tchar(hostname);
if(!host_name)
@@ -1951,13 +2054,18 @@ static int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
/* free SSPI Schannel API security context handle */
if(BACKEND->ctxt) {
- infof(data, "schannel: clear security context handle\n");
+ DEBUGF(infof(data, "schannel: clear security context handle\n"));
s_pSecFn->DeleteSecurityContext(&BACKEND->ctxt->ctxt_handle);
Curl_safefree(BACKEND->ctxt);
}
/* free SSPI Schannel API credential handle */
if(BACKEND->cred) {
+ /*
+ * When this function is called from Curl_schannel_close() the connection
+ * might not have an associated transfer so the check for conn->data is
+ * necessary.
+ */
Curl_ssl_sessionid_lock(conn);
Curl_schannel_session_free(BACKEND->cred);
Curl_ssl_sessionid_unlock(conn);
@@ -1994,7 +2102,7 @@ static void Curl_schannel_cleanup(void)
static size_t Curl_schannel_version(char *buffer, size_t size)
{
- size = snprintf(buffer, size, "WinSSL");
+ size = msnprintf(buffer, size, "Schannel");
return size;
}
@@ -2022,14 +2130,9 @@ static CURLcode Curl_schannel_random(struct Curl_easy *data UNUSED_PARAM,
static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex,
const char *pinnedpubkey)
{
- SECURITY_STATUS status;
struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
CERT_CONTEXT *pCertContextServer = NULL;
- const char *x509_der;
- DWORD x509_der_len;
- curl_X509certificate x509_parsed;
- curl_asn1Element *pubkey;
/* Result is returned to caller */
CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
@@ -2039,13 +2142,21 @@ static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex,
return CURLE_OK;
do {
- status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
- SECPKG_ATTR_REMOTE_CERT_CONTEXT,
- &pCertContextServer);
+ SECURITY_STATUS sspi_status;
+ const char *x509_der;
+ DWORD x509_der_len;
+ curl_X509certificate x509_parsed;
+ curl_asn1Element *pubkey;
- if((status != SEC_E_OK) || (pCertContextServer == NULL)) {
+ sspi_status =
+ s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
+ SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+ &pCertContextServer);
+
+ if((sspi_status != SEC_E_OK) || (pCertContextServer == NULL)) {
+ char buffer[STRERROR_LEN];
failf(data, "schannel: Failed to read remote certificate context: %s",
- Curl_sspi_strerror(conn, status));
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
break; /* failed */
}
@@ -2082,11 +2193,11 @@ static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex,
}
static void Curl_schannel_checksum(const unsigned char *input,
- size_t inputlen,
- unsigned char *checksum,
- size_t checksumlen,
- DWORD provType,
- const unsigned int algId)
+ size_t inputlen,
+ unsigned char *checksum,
+ size_t checksumlen,
+ DWORD provType,
+ const unsigned int algId)
{
HCRYPTPROV hProv = 0;
HCRYPTHASH hHash = 0;
@@ -2136,9 +2247,9 @@ static CURLcode Curl_schannel_md5sum(unsigned char *input,
unsigned char *md5sum,
size_t md5len)
{
- Curl_schannel_checksum(input, inputlen, md5sum, md5len,
- PROV_RSA_FULL, CALG_MD5);
- return CURLE_OK;
+ Curl_schannel_checksum(input, inputlen, md5sum, md5len,
+ PROV_RSA_FULL, CALG_MD5);
+ return CURLE_OK;
}
static CURLcode Curl_schannel_sha256sum(const unsigned char *input,
@@ -2146,9 +2257,9 @@ static CURLcode Curl_schannel_sha256sum(const unsigned char *input,
unsigned char *sha256sum,
size_t sha256len)
{
- Curl_schannel_checksum(input, inputlen, sha256sum, sha256len,
- PROV_RSA_AES, CALG_SHA_256);
- return CURLE_OK;
+ Curl_schannel_checksum(input, inputlen, sha256sum, sha256len,
+ PROV_RSA_AES, CALG_SHA_256);
+ return CURLE_OK;
}
static void *Curl_schannel_get_internals(struct ssl_connect_data *connssl,
diff --git a/Utilities/cmcurl/lib/vtls/schannel.h b/Utilities/cmcurl/lib/vtls/schannel.h
index e491bd431..ee8d7d47a 100644
--- a/Utilities/cmcurl/lib/vtls/schannel.h
+++ b/Utilities/cmcurl/lib/vtls/schannel.h
@@ -8,7 +8,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2012, Marc Hoersken, <info@marc-hoersken.de>, et al.
- * Copyright (C) 2012 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -53,7 +53,7 @@
extern const struct Curl_ssl Curl_ssl_schannel;
-CURLcode verify_certificate(struct connectdata *conn, int sockindex);
+CURLcode Curl_verify_certificate(struct connectdata *conn, int sockindex);
/* structs to expose only in schannel.c and schannel_verify.c */
#ifdef EXPOSE_SCHANNEL_INTERNAL_STRUCTS
diff --git a/Utilities/cmcurl/lib/vtls/schannel_verify.c b/Utilities/cmcurl/lib/vtls/schannel_verify.c
index 2516f5665..5a09e969e 100644
--- a/Utilities/cmcurl/lib/vtls/schannel_verify.c
+++ b/Utilities/cmcurl/lib/vtls/schannel_verify.c
@@ -7,7 +7,7 @@
*
* Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de>
* Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
- * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -23,7 +23,7 @@
***************************************************************************/
/*
- * Source file for SChannel-specific certificate verification. This code should
+ * Source file for Schannel-specific certificate verification. This code should
* only be invoked by code in schannel.c.
*/
@@ -87,18 +87,19 @@ static CURLcode add_certs_to_store(HCERTSTORE trust_store,
LARGE_INTEGER file_size;
char *ca_file_buffer = NULL;
char *current_ca_file_ptr = NULL;
- const TCHAR *ca_file_tstr = NULL;
+ TCHAR *ca_file_tstr = NULL;
size_t ca_file_bufsize = 0;
DWORD total_bytes_read = 0;
bool more_certs = 0;
int num_certs = 0;
size_t END_CERT_LEN;
- ca_file_tstr = Curl_convert_UTF8_to_tchar(ca_file);
+ ca_file_tstr = Curl_convert_UTF8_to_tchar((char *)ca_file);
if(!ca_file_tstr) {
+ char buffer[STRERROR_LEN];
failf(data,
"schannel: invalid path name for CA file '%s': %s",
- ca_file, Curl_strerror(conn, GetLastError()));
+ ca_file, Curl_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
goto cleanup;
}
@@ -116,17 +117,19 @@ static CURLcode add_certs_to_store(HCERTSTORE trust_store,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(ca_file_handle == INVALID_HANDLE_VALUE) {
+ char buffer[STRERROR_LEN];
failf(data,
"schannel: failed to open CA file '%s': %s",
- ca_file, Curl_strerror(conn, GetLastError()));
+ ca_file, Curl_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
goto cleanup;
}
if(!GetFileSizeEx(ca_file_handle, &file_size)) {
+ char buffer[STRERROR_LEN];
failf(data,
"schannel: failed to determine size of CA file '%s': %s",
- ca_file, Curl_strerror(conn, GetLastError()));
+ ca_file, Curl_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
goto cleanup;
}
@@ -153,10 +156,10 @@ static CURLcode add_certs_to_store(HCERTSTORE trust_store,
if(!ReadFile(ca_file_handle, ca_file_buffer + total_bytes_read,
bytes_to_read, &bytes_read, NULL)) {
-
+ char buffer[STRERROR_LEN];
failf(data,
"schannel: failed to read from CA file '%s': %s",
- ca_file, Curl_strerror(conn, GetLastError()));
+ ca_file, Curl_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
goto cleanup;
}
@@ -215,11 +218,12 @@ static CURLcode add_certs_to_store(HCERTSTORE trust_store,
NULL,
NULL,
(const void **)&cert_context)) {
-
+ char buffer[STRERROR_LEN];
failf(data,
"schannel: failed to extract certificate from CA file "
"'%s': %s",
- ca_file, Curl_strerror(conn, GetLastError()));
+ ca_file,
+ Curl_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
more_certs = 0;
}
@@ -243,10 +247,12 @@ static CURLcode add_certs_to_store(HCERTSTORE trust_store,
NULL);
CertFreeCertificateContext(cert_context);
if(!add_cert_result) {
+ char buffer[STRERROR_LEN];
failf(data,
"schannel: failed to add certificate from CA file '%s' "
"to certificate store: %s",
- ca_file, Curl_strerror(conn, GetLastError()));
+ ca_file,
+ Curl_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
more_certs = 0;
}
@@ -406,9 +412,9 @@ cleanup:
return result;
}
-CURLcode verify_certificate(struct connectdata *conn, int sockindex)
+CURLcode Curl_verify_certificate(struct connectdata *conn, int sockindex)
{
- SECURITY_STATUS status;
+ SECURITY_STATUS sspi_status;
struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
CURLcode result = CURLE_OK;
@@ -420,13 +426,15 @@ CURLcode verify_certificate(struct connectdata *conn, int sockindex)
conn->http_proxy.host.name :
conn->host.name;
- status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
- SECPKG_ATTR_REMOTE_CERT_CONTEXT,
- &pCertContextServer);
+ sspi_status =
+ s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
+ SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+ &pCertContextServer);
- if((status != SEC_E_OK) || (pCertContextServer == NULL)) {
+ if((sspi_status != SEC_E_OK) || (pCertContextServer == NULL)) {
+ char buffer[STRERROR_LEN];
failf(data, "schannel: Failed to read remote certificate context: %s",
- Curl_sspi_strerror(conn, status));
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
result = CURLE_PEER_FAILED_VERIFICATION;
}
@@ -450,8 +458,9 @@ CURLcode verify_certificate(struct connectdata *conn, int sockindex)
CERT_STORE_CREATE_NEW_FLAG,
NULL);
if(!trust_store) {
+ char buffer[STRERROR_LEN];
failf(data, "schannel: failed to create certificate store: %s",
- Curl_strerror(conn, GetLastError()));
+ Curl_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
}
else {
@@ -477,9 +486,10 @@ CURLcode verify_certificate(struct connectdata *conn, int sockindex)
CertCreateCertificateChainEngine(
(CERT_CHAIN_ENGINE_CONFIG *)&engine_config, &cert_chain_engine);
if(!create_engine_result) {
+ char buffer[STRERROR_LEN];
failf(data,
"schannel: failed to create certificate chain engine: %s",
- Curl_strerror(conn, GetLastError()));
+ Curl_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
}
}
@@ -500,8 +510,9 @@ CURLcode verify_certificate(struct connectdata *conn, int sockindex)
CERT_CHAIN_REVOCATION_CHECK_CHAIN),
NULL,
&pChainContext)) {
+ char buffer[STRERROR_LEN];
failf(data, "schannel: CertGetCertificateChain failed: %s",
- Curl_sspi_strerror(conn, GetLastError()));
+ Curl_strerror(GetLastError(), buffer, sizeof(buffer)));
pChainContext = NULL;
result = CURLE_PEER_FAILED_VERIFICATION;
}
diff --git a/Utilities/cmcurl/lib/vtls/darwinssl.c b/Utilities/cmcurl/lib/vtls/sectransp.c
index e8116b8a1..2fdf662a1 100644
--- a/Utilities/cmcurl/lib/vtls/darwinssl.c
+++ b/Utilities/cmcurl/lib/vtls/sectransp.c
@@ -6,7 +6,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2012 - 2017, Nick Zitzmann, <nickzman@gmail.com>.
- * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -31,8 +31,9 @@
#include "urldata.h" /* for the Curl_easy definition */
#include "curl_base64.h"
#include "strtok.h"
+#include "multiif.h"
-#ifdef USE_DARWINSSL
+#ifdef USE_SECTRANSP
#ifdef __clang__
#pragma clang diagnostic push
@@ -59,7 +60,7 @@
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050
-#error "The darwinssl back-end requires Leopard or later."
+#error "The Secure Transport back-end requires Leopard or later."
#endif /* MAC_OS_X_VERSION_MAX_ALLOWED < 1050 */
#define CURL_BUILD_IOS 0
@@ -105,7 +106,7 @@
#define CURL_SUPPORT_MAC_10_9 0
#else
-#error "The darwinssl back-end requires iOS or OS X."
+#error "The Secure Transport back-end requires iOS or macOS."
#endif /* (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */
#if CURL_BUILD_MAC
@@ -118,7 +119,7 @@
#include "connect.h"
#include "select.h"
#include "vtls.h"
-#include "darwinssl.h"
+#include "sectransp.h"
#include "curl_printf.h"
#include "strdup.h"
@@ -144,20 +145,20 @@ struct ssl_backend_data {
/* version 1 supports macOS 10.12+ and iOS 10+ */
#if ((TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000) || \
(!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200))
-#define DARWIN_SSL_PINNEDPUBKEY_V1 1
+#define SECTRANSP_PINNEDPUBKEY_V1 1
#endif
/* version 2 supports MacOSX 10.7+ */
#if (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
-#define DARWIN_SSL_PINNEDPUBKEY_V2 1
+#define SECTRANSP_PINNEDPUBKEY_V2 1
#endif
-#if defined(DARWIN_SSL_PINNEDPUBKEY_V1) || defined(DARWIN_SSL_PINNEDPUBKEY_V2)
+#if defined(SECTRANSP_PINNEDPUBKEY_V1) || defined(SECTRANSP_PINNEDPUBKEY_V2)
/* this backend supports CURLOPT_PINNEDPUBLICKEY */
-#define DARWIN_SSL_PINNEDPUBKEY 1
-#endif /* DARWIN_SSL_PINNEDPUBKEY */
+#define SECTRANSP_PINNEDPUBKEY 1
+#endif /* SECTRANSP_PINNEDPUBKEY */
-#ifdef DARWIN_SSL_PINNEDPUBKEY
+#ifdef SECTRANSP_PINNEDPUBKEY
/* both new and old APIs return rsa keys missing the spki header (not DER) */
static const unsigned char rsa4096SpkiHeader[] = {
0x30, 0x82, 0x02, 0x22, 0x30, 0x0d,
@@ -170,7 +171,7 @@ static const unsigned char rsa2048SpkiHeader[] = {
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
0x00, 0x03, 0x82, 0x01, 0x0f, 0x00};
-#ifdef DARWIN_SSL_PINNEDPUBKEY_V1
+#ifdef SECTRANSP_PINNEDPUBKEY_V1
/* the *new* version doesn't return DER encoded ecdsa certs like the old... */
static const unsigned char ecDsaSecp256r1SpkiHeader[] = {
0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
@@ -184,8 +185,8 @@ static const unsigned char ecDsaSecp384r1SpkiHeader[] = {
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
0x01, 0x06, 0x05, 0x2b, 0x81, 0x04,
0x00, 0x22, 0x03, 0x62, 0x00};
-#endif /* DARWIN_SSL_PINNEDPUBKEY_V1 */
-#endif /* DARWIN_SSL_PINNEDPUBKEY */
+#endif /* SECTRANSP_PINNEDPUBKEY_V1 */
+#endif /* SECTRANSP_PINNEDPUBKEY */
/* The following two functions were ripped from Apple sample code,
* with some modifications: */
@@ -950,7 +951,7 @@ static CURLcode CopyCertSubject(struct Curl_easy *data,
if(!c) {
failf(data, "SSL: invalid CA certificate subject");
- return CURLE_SSL_CACERT;
+ return CURLE_PEER_FAILED_VERIFICATION;
}
/* If the subject is already available as UTF-8 encoded (ie 'direct') then
@@ -970,7 +971,7 @@ static CURLcode CopyCertSubject(struct Curl_easy *data,
if(!CFStringGetCString(c, cbuf, cbuf_size,
kCFStringEncodingUTF8)) {
failf(data, "SSL: invalid CA certificate subject");
- result = CURLE_SSL_CACERT;
+ result = CURLE_PEER_FAILED_VERIFICATION;
}
else
/* pass back the buffer */
@@ -1242,7 +1243,7 @@ CF_INLINE bool is_file(const char *filename)
}
#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
-static CURLcode darwinssl_version_from_curl(SSLProtocol *darwinver,
+static CURLcode sectransp_version_from_curl(SSLProtocol *darwinver,
long ssl_version)
{
switch(ssl_version) {
@@ -1298,7 +1299,6 @@ set_ssl_version_min_max(struct connectdata *conn, int sockindex)
case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
ssl_version = CURL_SSLVERSION_TLSv1_0;
- ssl_version_max = max_supported_version_by_os;
break;
}
@@ -1313,13 +1313,13 @@ set_ssl_version_min_max(struct connectdata *conn, int sockindex)
if(SSLSetProtocolVersionMax != NULL) {
SSLProtocol darwin_ver_min = kTLSProtocol1;
SSLProtocol darwin_ver_max = kTLSProtocol1;
- CURLcode result = darwinssl_version_from_curl(&darwin_ver_min,
+ CURLcode result = sectransp_version_from_curl(&darwin_ver_min,
ssl_version);
if(result) {
failf(data, "unsupported min version passed via CURLOPT_SSLVERSION");
return result;
}
- result = darwinssl_version_from_curl(&darwin_ver_max,
+ result = sectransp_version_from_curl(&darwin_ver_max,
ssl_version_max >> 16);
if(result) {
failf(data, "unsupported max version passed via CURLOPT_SSLVERSION");
@@ -1362,12 +1362,12 @@ set_ssl_version_min_max(struct connectdata *conn, int sockindex)
#endif /* CURL_SUPPORT_MAC_10_8 */
}
#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
- failf(data, "DarwinSSL: cannot set SSL protocol");
+ failf(data, "Secure Transport: cannot set SSL protocol");
return CURLE_SSL_CONNECT_ERROR;
}
-static CURLcode darwinssl_connect_step1(struct connectdata *conn,
+static CURLcode sectransp_connect_step1(struct connectdata *conn,
int sockindex)
{
struct Curl_easy *data = conn->data;
@@ -1430,7 +1430,6 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
if(SSLSetProtocolVersionMax != NULL) {
switch(conn->ssl_config.version) {
- case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
(void)SSLSetProtocolVersionMin(BACKEND->ssl_ctx, kTLSProtocol1);
#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1
@@ -1445,6 +1444,7 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
#endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) &&
HAVE_BUILTIN_AVAILABLE == 1 */
break;
+ case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1_0:
case CURL_SSLVERSION_TLSv1_1:
case CURL_SSLVERSION_TLSv1_2:
@@ -1578,7 +1578,7 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1
if(conn->bits.tls_enable_alpn) {
- if(__builtin_available(macOS 10.13.4, iOS 11, *)) {
+ if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) {
CFMutableArrayRef alpnArr = CFArrayCreateMutable(NULL, 0,
&kCFTypeArrayCallBacks);
@@ -1649,7 +1649,7 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
}
CFRelease(cert);
- if(result == CURLE_SSL_CACERT)
+ if(result == CURLE_PEER_FAILED_VERIFICATION)
return CURLE_SSL_CERTPROBLEM;
if(result)
return result;
@@ -1903,7 +1903,6 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
/* We want to enable 1/n-1 when using a CBC cipher unless the user
specifically doesn't want us doing that: */
if(SSLSetSessionOption != NULL) {
- /* TODO s/data->set.ssl.enable_beast/SSL_SET_OPTION(enable_beast)/g */
SSLSetSessionOption(BACKEND->ssl_ctx, kSSLSessionOptionSendOneByteRecord,
!data->set.ssl.enable_beast);
SSLSetSessionOption(BACKEND->ssl_ctx, kSSLSessionOptionFalseStart,
@@ -2235,7 +2234,7 @@ static int verify_cert(const char *cafile, struct Curl_easy *data,
}
}
-#ifdef DARWIN_SSL_PINNEDPUBKEY
+#ifdef SECTRANSP_PINNEDPUBKEY
static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
SSLContextRef ctx,
const char *pinnedpubkey)
@@ -2267,14 +2266,14 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
if(keyRef == NULL)
break;
-#ifdef DARWIN_SSL_PINNEDPUBKEY_V1
+#ifdef SECTRANSP_PINNEDPUBKEY_V1
publicKeyBits = SecKeyCopyExternalRepresentation(keyRef, NULL);
CFRelease(keyRef);
if(publicKeyBits == NULL)
break;
-#elif DARWIN_SSL_PINNEDPUBKEY_V2
+#elif SECTRANSP_PINNEDPUBKEY_V2
OSStatus success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL,
&publicKeyBits);
@@ -2282,7 +2281,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
if(success != errSecSuccess || publicKeyBits == NULL)
break;
-#endif /* DARWIN_SSL_PINNEDPUBKEY_V2 */
+#endif /* SECTRANSP_PINNEDPUBKEY_V2 */
pubkeylen = CFDataGetLength(publicKeyBits);
pubkey = (unsigned char *)CFDataGetBytePtr(publicKeyBits);
@@ -2296,7 +2295,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
/* 2048 bit RSA pubkeylen == 270 */
spkiHeader = rsa2048SpkiHeader;
break;
-#ifdef DARWIN_SSL_PINNEDPUBKEY_V1
+#ifdef SECTRANSP_PINNEDPUBKEY_V1
case 65:
/* ecDSA secp256r1 pubkeylen == 65 */
spkiHeader = ecDsaSecp256r1SpkiHeader;
@@ -2309,7 +2308,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
break;
default:
infof(data, "SSL: unhandled public key length: %d\n", pubkeylen);
-#elif DARWIN_SSL_PINNEDPUBKEY_V2
+#elif SECTRANSP_PINNEDPUBKEY_V2
default:
/* ecDSA secp256r1 pubkeylen == 91 header already included?
* ecDSA secp384r1 header already included too
@@ -2317,7 +2316,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
*/
result = Curl_pin_peer_pubkey(data, pinnedpubkey, pubkey,
pubkeylen);
-#endif /* DARWIN_SSL_PINNEDPUBKEY_V2 */
+#endif /* SECTRANSP_PINNEDPUBKEY_V2 */
continue; /* break from loop */
}
@@ -2340,10 +2339,10 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
return result;
}
-#endif /* DARWIN_SSL_PINNEDPUBKEY */
+#endif /* SECTRANSP_PINNEDPUBKEY */
static CURLcode
-darwinssl_connect_step2(struct connectdata *conn, int sockindex)
+sectransp_connect_step2(struct connectdata *conn, int sockindex)
{
struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
@@ -2377,7 +2376,7 @@ darwinssl_connect_step2(struct connectdata *conn, int sockindex)
return res;
}
/* the documentation says we need to call SSLHandshake() again */
- return darwinssl_connect_step2(conn, sockindex);
+ return sectransp_connect_step2(conn, sockindex);
/* Problem with encrypt / decrypt */
case errSSLPeerDecodeError:
@@ -2429,37 +2428,37 @@ darwinssl_connect_step2(struct connectdata *conn, int sockindex)
/* These are all certificate problems with the server: */
case errSSLXCertChainInvalid:
failf(data, "SSL certificate problem: Invalid certificate chain");
- return CURLE_SSL_CACERT;
+ return CURLE_PEER_FAILED_VERIFICATION;
case errSSLUnknownRootCert:
failf(data, "SSL certificate problem: Untrusted root certificate");
- return CURLE_SSL_CACERT;
+ return CURLE_PEER_FAILED_VERIFICATION;
case errSSLNoRootCert:
failf(data, "SSL certificate problem: No root certificate");
- return CURLE_SSL_CACERT;
+ return CURLE_PEER_FAILED_VERIFICATION;
case errSSLCertNotYetValid:
failf(data, "SSL certificate problem: The certificate chain had a "
"certificate that is not yet valid");
- return CURLE_SSL_CACERT;
+ return CURLE_PEER_FAILED_VERIFICATION;
case errSSLCertExpired:
case errSSLPeerCertExpired:
failf(data, "SSL certificate problem: Certificate chain had an "
"expired certificate");
- return CURLE_SSL_CACERT;
+ return CURLE_PEER_FAILED_VERIFICATION;
case errSSLBadCert:
case errSSLPeerBadCert:
failf(data, "SSL certificate problem: Couldn't understand the server "
"certificate format");
- return CURLE_SSL_CACERT;
+ return CURLE_PEER_FAILED_VERIFICATION;
case errSSLPeerUnsupportedCert:
failf(data, "SSL certificate problem: An unsupported certificate "
"format was encountered");
- return CURLE_SSL_CACERT;
+ return CURLE_PEER_FAILED_VERIFICATION;
case errSSLPeerCertRevoked:
failf(data, "SSL certificate problem: The certificate was revoked");
- return CURLE_SSL_CACERT;
+ return CURLE_PEER_FAILED_VERIFICATION;
case errSSLPeerCertUnknown:
failf(data, "SSL certificate problem: The certificate is unknown");
- return CURLE_SSL_CACERT;
+ return CURLE_PEER_FAILED_VERIFICATION;
/* These are all certificate problems with the client: */
case errSecAuthFailed:
@@ -2579,7 +2578,7 @@ darwinssl_connect_step2(struct connectdata *conn, int sockindex)
/* we have been connected fine, we're not waiting for anything else. */
connssl->connecting_state = ssl_connect_3;
-#ifdef DARWIN_SSL_PINNEDPUBKEY
+#ifdef SECTRANSP_PINNEDPUBKEY
if(data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]) {
CURLcode result = pkp_pin_peer_pubkey(data, BACKEND->ssl_ctx,
data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]);
@@ -2588,7 +2587,7 @@ darwinssl_connect_step2(struct connectdata *conn, int sockindex)
return result;
}
}
-#endif /* DARWIN_SSL_PINNEDPUBKEY */
+#endif /* SECTRANSP_PINNEDPUBKEY */
/* Informational message */
(void)SSLGetNegotiatedCipher(BACKEND->ssl_ctx, &cipher);
@@ -2629,7 +2628,7 @@ darwinssl_connect_step2(struct connectdata *conn, int sockindex)
#if(CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1
if(conn->bits.tls_enable_alpn) {
- if(__builtin_available(macOS 10.13.4, iOS 11, *)) {
+ if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) {
CFArrayRef alpnArr = NULL;
CFStringRef chosenProtocol = NULL;
err = SSLCopyALPNProtocols(BACKEND->ssl_ctx, &alpnArr);
@@ -2652,6 +2651,9 @@ darwinssl_connect_step2(struct connectdata *conn, int sockindex)
else
infof(data, "ALPN, server did not agree to a protocol\n");
+ Curl_multiuse_state(conn, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+
/* chosenProtocol is a reference to the string within alpnArr
and doesn't need to be freed separately */
if(alpnArr)
@@ -2772,7 +2774,7 @@ show_verbose_server_cert(struct connectdata *conn,
#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
static CURLcode
-darwinssl_connect_step3(struct connectdata *conn,
+sectransp_connect_step3(struct connectdata *conn,
int sockindex)
{
struct Curl_easy *data = conn->data;
@@ -2790,11 +2792,11 @@ darwinssl_connect_step3(struct connectdata *conn,
return CURLE_OK;
}
-static Curl_recv darwinssl_recv;
-static Curl_send darwinssl_send;
+static Curl_recv sectransp_recv;
+static Curl_send sectransp_send;
static CURLcode
-darwinssl_connect_common(struct connectdata *conn,
+sectransp_connect_common(struct connectdata *conn,
int sockindex,
bool nonblocking,
bool *done)
@@ -2822,7 +2824,7 @@ darwinssl_connect_common(struct connectdata *conn,
return CURLE_OPERATION_TIMEDOUT;
}
- result = darwinssl_connect_step1(conn, sockindex);
+ result = sectransp_connect_step1(conn, sockindex);
if(result)
return result;
}
@@ -2876,7 +2878,7 @@ darwinssl_connect_common(struct connectdata *conn,
* before step2 has completed while ensuring that a client using select()
* or epoll() will always have a valid fdset to wait on.
*/
- result = darwinssl_connect_step2(conn, sockindex);
+ result = sectransp_connect_step2(conn, sockindex);
if(result || (nonblocking &&
(ssl_connect_2 == connssl->connecting_state ||
ssl_connect_2_reading == connssl->connecting_state ||
@@ -2887,15 +2889,15 @@ darwinssl_connect_common(struct connectdata *conn,
if(ssl_connect_3 == connssl->connecting_state) {
- result = darwinssl_connect_step3(conn, sockindex);
+ result = sectransp_connect_step3(conn, sockindex);
if(result)
return result;
}
if(ssl_connect_done == connssl->connecting_state) {
connssl->state = ssl_connection_complete;
- conn->recv[sockindex] = darwinssl_recv;
- conn->send[sockindex] = darwinssl_send;
+ conn->recv[sockindex] = sectransp_recv;
+ conn->send[sockindex] = sectransp_send;
*done = TRUE;
}
else
@@ -2907,18 +2909,18 @@ darwinssl_connect_common(struct connectdata *conn,
return CURLE_OK;
}
-static CURLcode Curl_darwinssl_connect_nonblocking(struct connectdata *conn,
+static CURLcode Curl_sectransp_connect_nonblocking(struct connectdata *conn,
int sockindex, bool *done)
{
- return darwinssl_connect_common(conn, sockindex, TRUE, done);
+ return sectransp_connect_common(conn, sockindex, TRUE, done);
}
-static CURLcode Curl_darwinssl_connect(struct connectdata *conn, int sockindex)
+static CURLcode Curl_sectransp_connect(struct connectdata *conn, int sockindex)
{
CURLcode result;
bool done = FALSE;
- result = darwinssl_connect_common(conn, sockindex, FALSE, &done);
+ result = sectransp_connect_common(conn, sockindex, FALSE, &done);
if(result)
return result;
@@ -2928,7 +2930,7 @@ static CURLcode Curl_darwinssl_connect(struct connectdata *conn, int sockindex)
return CURLE_OK;
}
-static void Curl_darwinssl_close(struct connectdata *conn, int sockindex)
+static void Curl_sectransp_close(struct connectdata *conn, int sockindex)
{
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
@@ -2949,7 +2951,7 @@ static void Curl_darwinssl_close(struct connectdata *conn, int sockindex)
BACKEND->ssl_sockfd = 0;
}
-static int Curl_darwinssl_shutdown(struct connectdata *conn, int sockindex)
+static int Curl_sectransp_shutdown(struct connectdata *conn, int sockindex)
{
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct Curl_easy *data = conn->data;
@@ -2961,10 +2963,12 @@ static int Curl_darwinssl_shutdown(struct connectdata *conn, int sockindex)
if(!BACKEND->ssl_ctx)
return 0;
+#ifndef CURL_DISABLE_FTP
if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE)
return 0;
+#endif
- Curl_darwinssl_close(conn, sockindex);
+ Curl_sectransp_close(conn, sockindex);
rc = 0;
@@ -3002,20 +3006,20 @@ static int Curl_darwinssl_shutdown(struct connectdata *conn, int sockindex)
return rc;
}
-static void Curl_darwinssl_session_free(void *ptr)
+static void Curl_sectransp_session_free(void *ptr)
{
/* ST, as of iOS 5 and Mountain Lion, has no public method of deleting a
cached session ID inside the Security framework. There is a private
function that does this, but I don't want to have to explain to you why I
got your application rejected from the App Store due to the use of a
private API, so the best we can do is free up our own char array that we
- created way back in darwinssl_connect_step1... */
+ created way back in sectransp_connect_step1... */
Curl_safefree(ptr);
}
-static size_t Curl_darwinssl_version(char *buffer, size_t size)
+static size_t Curl_sectransp_version(char *buffer, size_t size)
{
- return snprintf(buffer, size, "SecureTransport");
+ return msnprintf(buffer, size, "SecureTransport");
}
/*
@@ -3026,7 +3030,7 @@ static size_t Curl_darwinssl_version(char *buffer, size_t size)
* 0 means the connection has been closed
* -1 means the connection status is unknown
*/
-static int Curl_darwinssl_check_cxn(struct connectdata *conn)
+static int Curl_sectransp_check_cxn(struct connectdata *conn)
{
struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET];
OSStatus err;
@@ -3041,7 +3045,7 @@ static int Curl_darwinssl_check_cxn(struct connectdata *conn)
return 0;
}
-static bool Curl_darwinssl_data_pending(const struct connectdata *conn,
+static bool Curl_sectransp_data_pending(const struct connectdata *conn,
int connindex)
{
const struct ssl_connect_data *connssl = &conn->ssl[connindex];
@@ -3058,7 +3062,7 @@ static bool Curl_darwinssl_data_pending(const struct connectdata *conn,
return false;
}
-static CURLcode Curl_darwinssl_random(struct Curl_easy *data UNUSED_PARAM,
+static CURLcode Curl_sectransp_random(struct Curl_easy *data UNUSED_PARAM,
unsigned char *entropy, size_t length)
{
/* arc4random_buf() isn't available on cats older than Lion, so let's
@@ -3078,7 +3082,7 @@ static CURLcode Curl_darwinssl_random(struct Curl_easy *data UNUSED_PARAM,
return CURLE_OK;
}
-static CURLcode Curl_darwinssl_md5sum(unsigned char *tmp, /* input */
+static CURLcode Curl_sectransp_md5sum(unsigned char *tmp, /* input */
size_t tmplen,
unsigned char *md5sum, /* output */
size_t md5len)
@@ -3088,7 +3092,7 @@ static CURLcode Curl_darwinssl_md5sum(unsigned char *tmp, /* input */
return CURLE_OK;
}
-static CURLcode Curl_darwinssl_sha256sum(const unsigned char *tmp, /* input */
+static CURLcode Curl_sectransp_sha256sum(const unsigned char *tmp, /* input */
size_t tmplen,
unsigned char *sha256sum, /* output */
size_t sha256len)
@@ -3098,7 +3102,7 @@ static CURLcode Curl_darwinssl_sha256sum(const unsigned char *tmp, /* input */
return CURLE_OK;
}
-static bool Curl_darwinssl_false_start(void)
+static bool Curl_sectransp_false_start(void)
{
#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7
if(SSLSetSessionOption != NULL)
@@ -3107,7 +3111,7 @@ static bool Curl_darwinssl_false_start(void)
return FALSE;
}
-static ssize_t darwinssl_send(struct connectdata *conn,
+static ssize_t sectransp_send(struct connectdata *conn,
int sockindex,
const void *mem,
size_t len,
@@ -3173,7 +3177,7 @@ static ssize_t darwinssl_send(struct connectdata *conn,
return (ssize_t)processed;
}
-static ssize_t darwinssl_recv(struct connectdata *conn,
+static ssize_t sectransp_recv(struct connectdata *conn,
int num,
char *buf,
size_t buffersize,
@@ -3213,48 +3217,48 @@ static ssize_t darwinssl_recv(struct connectdata *conn,
return (ssize_t)processed;
}
-static void *Curl_darwinssl_get_internals(struct ssl_connect_data *connssl,
+static void *Curl_sectransp_get_internals(struct ssl_connect_data *connssl,
CURLINFO info UNUSED_PARAM)
{
(void)info;
return BACKEND->ssl_ctx;
}
-const struct Curl_ssl Curl_ssl_darwinssl = {
- { CURLSSLBACKEND_DARWINSSL, "darwinssl" }, /* info */
+const struct Curl_ssl Curl_ssl_sectransp = {
+ { CURLSSLBACKEND_SECURETRANSPORT, "secure-transport" }, /* info */
-#ifdef DARWIN_SSL_PINNEDPUBKEY
+#ifdef SECTRANSP_PINNEDPUBKEY
SSLSUPP_PINNEDPUBKEY,
#else
0,
-#endif /* DARWIN_SSL_PINNEDPUBKEY */
+#endif /* SECTRANSP_PINNEDPUBKEY */
sizeof(struct ssl_backend_data),
Curl_none_init, /* init */
Curl_none_cleanup, /* cleanup */
- Curl_darwinssl_version, /* version */
- Curl_darwinssl_check_cxn, /* check_cxn */
- Curl_darwinssl_shutdown, /* shutdown */
- Curl_darwinssl_data_pending, /* data_pending */
- Curl_darwinssl_random, /* random */
+ Curl_sectransp_version, /* version */
+ Curl_sectransp_check_cxn, /* check_cxn */
+ Curl_sectransp_shutdown, /* shutdown */
+ Curl_sectransp_data_pending, /* data_pending */
+ Curl_sectransp_random, /* random */
Curl_none_cert_status_request, /* cert_status_request */
- Curl_darwinssl_connect, /* connect */
- Curl_darwinssl_connect_nonblocking, /* connect_nonblocking */
- Curl_darwinssl_get_internals, /* get_internals */
- Curl_darwinssl_close, /* close_one */
+ Curl_sectransp_connect, /* connect */
+ Curl_sectransp_connect_nonblocking, /* connect_nonblocking */
+ Curl_sectransp_get_internals, /* get_internals */
+ Curl_sectransp_close, /* close_one */
Curl_none_close_all, /* close_all */
- Curl_darwinssl_session_free, /* session_free */
+ Curl_sectransp_session_free, /* session_free */
Curl_none_set_engine, /* set_engine */
Curl_none_set_engine_default, /* set_engine_default */
Curl_none_engines_list, /* engines_list */
- Curl_darwinssl_false_start, /* false_start */
- Curl_darwinssl_md5sum, /* md5sum */
- Curl_darwinssl_sha256sum /* sha256sum */
+ Curl_sectransp_false_start, /* false_start */
+ Curl_sectransp_md5sum, /* md5sum */
+ Curl_sectransp_sha256sum /* sha256sum */
};
#ifdef __clang__
#pragma clang diagnostic pop
#endif
-#endif /* USE_DARWINSSL */
+#endif /* USE_SECTRANSP */
diff --git a/Utilities/cmcurl/lib/vtls/darwinssl.h b/Utilities/cmcurl/lib/vtls/sectransp.h
index 23c7f705c..5cec797b3 100644
--- a/Utilities/cmcurl/lib/vtls/darwinssl.h
+++ b/Utilities/cmcurl/lib/vtls/sectransp.h
@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_DARWINSSL_H
-#define HEADER_CURL_DARWINSSL_H
+#ifndef HEADER_CURL_SECTRANSP_H
+#define HEADER_CURL_SECTRANSP_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -8,7 +8,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2012 - 2014, Nick Zitzmann, <nickzman@gmail.com>.
- * Copyright (C) 2012 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -24,9 +24,9 @@
***************************************************************************/
#include "curl_setup.h"
-#ifdef USE_DARWINSSL
+#ifdef USE_SECTRANSP
-extern const struct Curl_ssl Curl_ssl_darwinssl;
+extern const struct Curl_ssl Curl_ssl_sectransp;
-#endif /* USE_DARWINSSL */
-#endif /* HEADER_CURL_DARWINSSL_H */
+#endif /* USE_SECTRANSP */
+#endif /* HEADER_CURL_SECTRANSP_H */
diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c
index 6af39feab..a7452dcd5 100644
--- a/Utilities/cmcurl/lib/vtls/vtls.c
+++ b/Utilities/cmcurl/lib/vtls/vtls.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -248,7 +248,7 @@ Curl_ssl_connect(struct connectdata *conn, int sockindex)
conn->ssl[sockindex].use = TRUE;
conn->ssl[sockindex].state = ssl_connection_negotiating;
- result = Curl_ssl->connect(conn, sockindex);
+ result = Curl_ssl->connect_blocking(conn, sockindex);
if(!result)
Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */
@@ -498,9 +498,9 @@ CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
void Curl_ssl_close_all(struct Curl_easy *data)
{
- size_t i;
/* kill the session ID cache if not shared */
if(data->state.session && !SSLSESSION_SHARED(data)) {
+ size_t i;
for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++)
/* the single-killer function handles empty table slots */
Curl_ssl_kill_session(&data->state.session[i]);
@@ -513,7 +513,7 @@ void Curl_ssl_close_all(struct Curl_easy *data)
}
#if defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_SCHANNEL) || \
- defined(USE_DARWINSSL) || defined(USE_POLARSSL) || defined(USE_NSS) || \
+ defined(USE_SECTRANSP) || defined(USE_POLARSSL) || defined(USE_NSS) || \
defined(USE_MBEDTLS) || defined(USE_CYASSL)
int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks,
int numsocks)
@@ -546,7 +546,7 @@ int Curl_ssl_getsock(struct connectdata *conn,
(void)numsocks;
return GETSOCK_BLANK;
}
-/* USE_OPENSSL || USE_GNUTLS || USE_SCHANNEL || USE_DARWINSSL || USE_NSS */
+/* USE_OPENSSL || USE_GNUTLS || USE_SCHANNEL || USE_SECTRANSP || USE_NSS */
#endif
void Curl_ssl_close(struct connectdata *conn, int sockindex)
@@ -557,7 +557,7 @@ void Curl_ssl_close(struct connectdata *conn, int sockindex)
CURLcode Curl_ssl_shutdown(struct connectdata *conn, int sockindex)
{
- if(Curl_ssl->shutdown(conn, sockindex))
+ if(Curl_ssl->shut_down(conn, sockindex))
return CURLE_SSL_SHUTDOWN_FAILED;
conn->ssl[sockindex].use = FALSE; /* get back to ordinary socket usage */
@@ -644,11 +644,11 @@ bool Curl_ssl_data_pending(const struct connectdata *conn,
void Curl_ssl_free_certinfo(struct Curl_easy *data)
{
- int i;
struct curl_certinfo *ci = &data->info.certs;
if(ci->num_of_certs) {
/* free all individual lists used */
+ int i;
for(i = 0; i<ci->num_of_certs; i++) {
curl_slist_free_all(ci->certinfo[i]);
ci->certinfo[i] = NULL;
@@ -700,7 +700,7 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data,
return CURLE_OUT_OF_MEMORY;
/* sprintf the label and colon */
- snprintf(output, outlen, "%s:", label);
+ msnprintf(output, outlen, "%s:", label);
/* memcpy the value (it might not be zero terminated) */
memcpy(&output[labellen + 1], value, valuelen);
@@ -808,14 +808,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
{
FILE *fp;
unsigned char *buf = NULL, *pem_ptr = NULL;
- long filesize;
- size_t size, pem_len;
- CURLcode pem_read;
CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
- CURLcode encode;
- size_t encodedlen, pinkeylen;
- char *encoded, *pinkeycopy, *begin_pos, *end_pos;
- unsigned char *sha256sumdigest = NULL;
/* if a path wasn't specified, don't pin */
if(!pinnedpubkey)
@@ -825,6 +818,11 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
/* only do this if pinnedpubkey starts with "sha256//", length 8 */
if(strncmp(pinnedpubkey, "sha256//", 8) == 0) {
+ CURLcode encode;
+ size_t encodedlen, pinkeylen;
+ char *encoded, *pinkeycopy, *begin_pos, *end_pos;
+ unsigned char *sha256sumdigest;
+
if(!Curl_ssl->sha256sum) {
/* without sha256 support, this cannot match */
return result;
@@ -895,6 +893,10 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
return result;
do {
+ long filesize;
+ size_t size, pem_len;
+ CURLcode pem_read;
+
/* Determine the file's size */
if(fseek(fp, 0, SEEK_END))
break;
@@ -1114,7 +1116,7 @@ static CURLcode Curl_multissl_connect(struct connectdata *conn, int sockindex)
{
if(multissl_init(NULL))
return CURLE_FAILED_INIT;
- return Curl_ssl->connect(conn, sockindex);
+ return Curl_ssl->connect_blocking(conn, sockindex);
}
static CURLcode Curl_multissl_connect_nonblocking(struct connectdata *conn,
@@ -1170,12 +1172,10 @@ static const struct Curl_ssl Curl_ssl_multi = {
const struct Curl_ssl *Curl_ssl =
#if defined(CURL_WITH_MULTI_SSL)
&Curl_ssl_multi;
-#elif defined(USE_AXTLS)
- &Curl_ssl_axtls;
#elif defined(USE_CYASSL)
&Curl_ssl_cyassl;
-#elif defined(USE_DARWINSSL)
- &Curl_ssl_darwinssl;
+#elif defined(USE_SECTRANSP)
+ &Curl_ssl_sectransp;
#elif defined(USE_GNUTLS)
&Curl_ssl_gnutls;
#elif defined(USE_GSKIT)
@@ -1197,14 +1197,11 @@ const struct Curl_ssl *Curl_ssl =
#endif
static const struct Curl_ssl *available_backends[] = {
-#if defined(USE_AXTLS)
- &Curl_ssl_axtls,
-#endif
#if defined(USE_CYASSL)
&Curl_ssl_cyassl,
#endif
-#if defined(USE_DARWINSSL)
- &Curl_ssl_darwinssl,
+#if defined(USE_SECTRANSP)
+ &Curl_ssl_sectransp,
#endif
#if defined(USE_GNUTLS)
&Curl_ssl_gnutls,
@@ -1244,16 +1241,17 @@ static size_t Curl_multissl_version(char *buffer, size_t size)
if(current != selected) {
char *p = backends;
+ char *end = backends + sizeof(backends);
int i;
selected = current;
- for(i = 0; available_backends[i]; i++) {
+ for(i = 0; available_backends[i] && p < (end - 4); i++) {
if(i)
*(p++) = ' ';
if(selected != available_backends[i])
*(p++) = '(';
- p += available_backends[i]->version(p, backends + sizeof(backends) - p);
+ p += available_backends[i]->version(p, end - p - 2);
if(selected != available_backends[i])
*(p++) = ')';
}
@@ -1261,21 +1259,20 @@ static size_t Curl_multissl_version(char *buffer, size_t size)
total = p - backends;
}
- if(size < total)
+ if(size > total)
memcpy(buffer, backends, total + 1);
else {
memcpy(buffer, backends, size - 1);
buffer[size - 1] = '\0';
}
- return total;
+ return CURLMIN(size - 1, total);
}
static int multissl_init(const struct Curl_ssl *backend)
{
const char *env;
char *env_tmp;
- int i;
if(Curl_ssl != &Curl_ssl_multi)
return 1;
@@ -1294,6 +1291,7 @@ static int multissl_init(const struct Curl_ssl *backend)
env = CURL_DEFAULT_SSL_BACKEND;
#endif
if(env) {
+ int i;
for(i = 0; available_backends[i]; i++) {
if(strcasecompare(env, available_backends[i]->info.name)) {
Curl_ssl = available_backends[i];
@@ -1318,7 +1316,14 @@ CURLsslset curl_global_sslset(curl_sslbackend id, const char *name,
*avail = (const curl_ssl_backend **)&available_backends;
if(Curl_ssl != &Curl_ssl_multi)
- return id == Curl_ssl->info.id ? CURLSSLSET_OK : CURLSSLSET_TOO_LATE;
+ return id == Curl_ssl->info.id ||
+ (name && strcasecompare(name, Curl_ssl->info.name)) ?
+ CURLSSLSET_OK :
+#if defined(CURL_WITH_MULTI_SSL)
+ CURLSSLSET_TOO_LATE;
+#else
+ CURLSSLSET_UNKNOWN_BACKEND;
+#endif
for(i = 0; available_backends[i]; i++) {
if(available_backends[i]->info.id == id ||
diff --git a/Utilities/cmcurl/lib/vtls/vtls.h b/Utilities/cmcurl/lib/vtls/vtls.h
index 5cd11602e..2a87ca1f7 100644
--- a/Utilities/cmcurl/lib/vtls/vtls.h
+++ b/Utilities/cmcurl/lib/vtls/vtls.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -47,7 +47,7 @@ struct Curl_ssl {
size_t (*version)(char *buffer, size_t size);
int (*check_cxn)(struct connectdata *cxn);
- int (*shutdown)(struct connectdata *conn, int sockindex);
+ int (*shut_down)(struct connectdata *conn, int sockindex);
bool (*data_pending)(const struct connectdata *conn,
int connindex);
@@ -56,7 +56,7 @@ struct Curl_ssl {
size_t length);
bool (*cert_status_request)(void);
- CURLcode (*connect)(struct connectdata *conn, int sockindex);
+ CURLcode (*connect_blocking)(struct connectdata *conn, int sockindex);
CURLcode (*connect_nonblocking)(struct connectdata *conn, int sockindex,
bool *done);
void *(*get_internals)(struct ssl_connect_data *connssl, CURLINFO info);
@@ -103,10 +103,9 @@ CURLcode Curl_none_md5sum(unsigned char *input, size_t inputlen,
#include "nssg.h" /* NSS versions */
#include "gskit.h" /* Global Secure ToolKit versions */
#include "polarssl.h" /* PolarSSL versions */
-#include "axtls.h" /* axTLS versions */
#include "cyassl.h" /* CyaSSL versions */
#include "schannel.h" /* Schannel SSPI version */
-#include "darwinssl.h" /* SecureTransport (Darwin) version */
+#include "sectransp.h" /* SecureTransport (Darwin) version */
#include "mbedtls.h" /* mbedTLS versions */
#include "mesalink.h" /* MesaLink versions */
diff --git a/Utilities/cmcurl/lib/warnless.c b/Utilities/cmcurl/lib/warnless.c
index 05d9038dc..cfd5e8e14 100644
--- a/Utilities/cmcurl/lib/warnless.c
+++ b/Utilities/cmcurl/lib/warnless.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -401,44 +401,6 @@ unsigned short curlx_uitous(unsigned int uinum)
}
/*
-** unsigned int to unsigned char
-*/
-
-unsigned char curlx_uitouc(unsigned int uinum)
-{
-#ifdef __INTEL_COMPILER
-# pragma warning(push)
-# pragma warning(disable:810) /* conversion may lose significant bits */
-#endif
-
- DEBUGASSERT(uinum <= (unsigned int) CURL_MASK_UCHAR);
- return (unsigned char) (uinum & (unsigned int) CURL_MASK_UCHAR);
-
-#ifdef __INTEL_COMPILER
-# pragma warning(pop)
-#endif
-}
-
-/*
-** unsigned int to signed int
-*/
-
-int curlx_uitosi(unsigned int uinum)
-{
-#ifdef __INTEL_COMPILER
-# pragma warning(push)
-# pragma warning(disable:810) /* conversion may lose significant bits */
-#endif
-
- DEBUGASSERT(uinum <= (unsigned int) CURL_MASK_SINT);
- return (int) (uinum & (unsigned int) CURL_MASK_SINT);
-
-#ifdef __INTEL_COMPILER
-# pragma warning(pop)
-#endif
-}
-
-/*
** signed int to unsigned size_t
*/
diff --git a/Utilities/cmcurl/lib/warnless.h b/Utilities/cmcurl/lib/warnless.h
index 284ea1e75..ea4c4395d 100644
--- a/Utilities/cmcurl/lib/warnless.h
+++ b/Utilities/cmcurl/lib/warnless.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -57,10 +57,6 @@ int curlx_sztosi(ssize_t sznum);
unsigned short curlx_uitous(unsigned int uinum);
-unsigned char curlx_uitouc(unsigned int uinum);
-
-int curlx_uitosi(unsigned int uinum);
-
size_t curlx_sitouz(int sinum);
#ifdef USE_WINSOCK
diff --git a/Utilities/cmcurl/lib/wildcard.c b/Utilities/cmcurl/lib/wildcard.c
index 8ba0989b4..e94d3c544 100644
--- a/Utilities/cmcurl/lib/wildcard.c
+++ b/Utilities/cmcurl/lib/wildcard.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -22,6 +22,8 @@
#include "curl_setup.h"
+#ifndef CURL_DISABLE_FTP
+
#include "wildcard.h"
#include "llist.h"
#include "fileinfo.h"
@@ -67,3 +69,5 @@ void Curl_wildcard_dtor(struct WildcardData *wc)
wc->customptr = NULL;
wc->state = CURLWC_INIT;
}
+
+#endif /* if disabled */
diff --git a/Utilities/cmcurl/lib/wildcard.h b/Utilities/cmcurl/lib/wildcard.h
index b7826123a..306c8c99f 100644
--- a/Utilities/cmcurl/lib/wildcard.h
+++ b/Utilities/cmcurl/lib/wildcard.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2010 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2010 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -22,8 +22,9 @@
*
***************************************************************************/
-#include <curl/curl.h>
+#include "curl_setup.h"
+#ifndef CURL_DISABLE_FTP
#include "llist.h"
/* list of wildcard process states */
@@ -58,4 +59,9 @@ void Curl_wildcard_dtor(struct WildcardData *wc);
struct Curl_easy;
+#else
+/* FTP is disabled */
+#define Curl_wildcard_dtor(x)
+#endif
+
#endif /* HEADER_CURL_WILDCARD_H */
diff --git a/Utilities/cmcurl/lib/x509asn1.c b/Utilities/cmcurl/lib/x509asn1.c
index a576fc703..0c1256ba8 100644
--- a/Utilities/cmcurl/lib/x509asn1.c
+++ b/Utilities/cmcurl/lib/x509asn1.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -120,7 +120,7 @@ static const char *getASN1Element(curl_asn1Element *elem,
if an error occurs. */
if(!beg || !end || beg >= end || !*beg ||
(size_t)(end - beg) > CURL_ASN1_MAX)
- return (const char *) NULL;
+ return NULL;
/* Process header byte. */
elem->header = beg;
@@ -129,12 +129,12 @@ static const char *getASN1Element(curl_asn1Element *elem,
elem->class = (b >> 6) & 3;
b &= 0x1F;
if(b == 0x1F)
- return (const char *) NULL; /* Long tag values not supported here. */
+ return NULL; /* Long tag values not supported here. */
elem->tag = b;
/* Process length. */
if(beg >= end)
- return (const char *) NULL;
+ return NULL;
b = (unsigned char) *beg++;
if(!(b & 0x80))
len = b;
@@ -142,74 +142,77 @@ static const char *getASN1Element(curl_asn1Element *elem,
/* Unspecified length. Since we have all the data, we can determine the
effective length by skipping element until an end element is found. */
if(!elem->constructed)
- return (const char *) NULL;
+ return NULL;
elem->beg = beg;
while(beg < end && *beg) {
beg = getASN1Element(&lelem, beg, end);
if(!beg)
- return (const char *) NULL;
+ return NULL;
}
if(beg >= end)
- return (const char *) NULL;
+ return NULL;
elem->end = beg;
return beg + 1;
}
else if((unsigned)b > (size_t)(end - beg))
- return (const char *) NULL; /* Does not fit in source. */
+ return NULL; /* Does not fit in source. */
else {
/* Get long length. */
len = 0;
do {
if(len & 0xFF000000L)
- return (const char *) NULL; /* Lengths > 32 bits are not supported. */
+ return NULL; /* Lengths > 32 bits are not supported. */
len = (len << 8) | (unsigned char) *beg++;
} while(--b);
}
if(len > (size_t)(end - beg))
- return (const char *) NULL; /* Element data does not fit in source. */
+ return NULL; /* Element data does not fit in source. */
elem->beg = beg;
elem->end = beg + len;
return elem->end;
}
+/*
+ * Search the null terminated OID or OID identifier in local table.
+ * Return the table entry pointer or NULL if not found.
+ */
static const curl_OID * searchOID(const char *oid)
{
const curl_OID *op;
-
- /* Search the null terminated OID or OID identifier in local table.
- Return the table entry pointer or NULL if not found. */
-
for(op = OIDtable; op->numoid; op++)
if(!strcmp(op->numoid, oid) || strcasecompare(op->textoid, oid))
return op;
- return (const curl_OID *) NULL;
+ return NULL;
}
+/*
+ * Convert an ASN.1 Boolean value into its string representation. Return the
+ * dynamically allocated string, or NULL if source is not an ASN.1 Boolean
+ * value.
+ */
+
static const char *bool2str(const char *beg, const char *end)
{
- /* Convert an ASN.1 Boolean value into its string representation.
- Return the dynamically allocated string, or NULL if source is not an
- ASN.1 Boolean value. */
-
if(end - beg != 1)
- return (const char *) NULL;
+ return NULL;
return strdup(*beg? "TRUE": "FALSE");
}
+/*
+ * Convert an ASN.1 octet string to a printable string.
+ * Return the dynamically allocated string, or NULL if an error occurs.
+ */
static const char *octet2str(const char *beg, const char *end)
{
size_t n = end - beg;
char *buf = NULL;
- /* Convert an ASN.1 octet string to a printable string.
- Return the dynamically allocated string, or NULL if an error occurs. */
-
if(n <= (SIZE_T_MAX - 1) / 3) {
buf = malloc(3 * n + 1);
if(buf)
for(n = 0; beg < end; n += 3)
- snprintf(buf + n, 4, "%02x:", *(const unsigned char *) beg++);
+ msnprintf(buf + n, 4, "%02x:", *(const unsigned char *) beg++);
}
return buf;
}
@@ -220,21 +223,22 @@ static const char *bit2str(const char *beg, const char *end)
Return the dynamically allocated string, or NULL if an error occurs. */
if(++beg > end)
- return (const char *) NULL;
+ return NULL;
return octet2str(beg, end);
}
+/*
+ * Convert an ASN.1 integer value into its string representation.
+ * Return the dynamically allocated string, or NULL if source is not an
+ * ASN.1 integer value.
+ */
static const char *int2str(const char *beg, const char *end)
{
unsigned long val = 0;
size_t n = end - beg;
- /* Convert an ASN.1 integer value into its string representation.
- Return the dynamically allocated string, or NULL if source is not an
- ASN.1 integer value. */
-
if(!n)
- return (const char *) NULL;
+ return NULL;
if(n > 4)
return octet2str(beg, end);
@@ -249,23 +253,22 @@ static const char *int2str(const char *beg, const char *end)
return curl_maprintf("%s%lx", val >= 10? "0x": "", val);
}
+/*
+ * Perform a lazy conversion from an ASN.1 typed string to UTF8. Allocate the
+ * destination buffer dynamically. The allocation size will normally be too
+ * large: this is to avoid buffer overflows.
+ * Terminate the string with a nul byte and return the converted
+ * string length.
+ */
static ssize_t
utf8asn1str(char **to, int type, const char *from, const char *end)
{
size_t inlength = end - from;
int size = 1;
size_t outlength;
- int charsize;
- unsigned int wc;
char *buf;
- /* Perform a lazy conversion from an ASN.1 typed string to UTF8. Allocate the
- destination buffer dynamically. The allocation size will normally be too
- large: this is to avoid buffer overflows.
- Terminate the string with a nul byte and return the converted
- string length. */
-
- *to = (char *) NULL;
+ *to = NULL;
switch(type) {
case CURL_ASN1_BMP_STRING:
size = 2;
@@ -300,6 +303,9 @@ utf8asn1str(char **to, int type, const char *from, const char *end)
}
else {
for(outlength = 0; from < end;) {
+ int charsize;
+ unsigned int wc;
+
wc = 0;
switch(size) {
case 4:
@@ -341,96 +347,105 @@ utf8asn1str(char **to, int type, const char *from, const char *end)
return outlength;
}
+/*
+ * Convert an ASN.1 String into its UTF-8 string representation.
+ * Return the dynamically allocated string, or NULL if an error occurs.
+ */
static const char *string2str(int type, const char *beg, const char *end)
{
char *buf;
-
- /* Convert an ASN.1 String into its UTF-8 string representation.
- Return the dynamically allocated string, or NULL if an error occurs. */
-
if(utf8asn1str(&buf, type, beg, end) < 0)
- return (const char *) NULL;
+ return NULL;
return buf;
}
-static int encodeUint(char *buf, int n, unsigned int x)
+/*
+ * Decimal ASCII encode unsigned integer `x' into the buflen sized buffer at
+ * buf. Return the total number of encoded digits, even if larger than
+ * `buflen'.
+ */
+static size_t encodeUint(char *buf, size_t buflen, unsigned int x)
{
- int i = 0;
+ size_t i = 0;
unsigned int y = x / 10;
- /* Decimal ASCII encode unsigned integer `x' in the `n'-byte buffer at `buf'.
- Return the total number of encoded digits, even if larger than `n'. */
-
if(y) {
- i += encodeUint(buf, n, y);
+ i = encodeUint(buf, buflen, y);
x -= y * 10;
}
- if(i < n)
+ if(i < buflen)
buf[i] = (char) ('0' + x);
i++;
- if(i < n)
+ if(i < buflen)
buf[i] = '\0'; /* Store a terminator if possible. */
return i;
}
-static int encodeOID(char *buf, int n, const char *beg, const char *end)
+/*
+ * Convert an ASN.1 OID into its dotted string representation.
+ * Store the result in th `n'-byte buffer at `buf'.
+ * Return the converted string length, or 0 on errors.
+ */
+static size_t encodeOID(char *buf, size_t buflen,
+ const char *beg, const char *end)
{
- int i = 0;
+ size_t i;
unsigned int x;
unsigned int y;
- /* Convert an ASN.1 OID into its dotted string representation.
- Store the result in th `n'-byte buffer at `buf'.
- Return the converted string length, or -1 if an error occurs. */
-
/* Process the first two numbers. */
y = *(const unsigned char *) beg++;
x = y / 40;
y -= x * 40;
- i += encodeUint(buf + i, n - i, x);
- if(i < n)
+ i = encodeUint(buf, buflen, x);
+ if(i < buflen)
buf[i] = '.';
i++;
- i += encodeUint(buf + i, n - i, y);
+ if(i >= buflen)
+ i += encodeUint(NULL, 0, y);
+ else
+ i += encodeUint(buf + i, buflen - i, y);
/* Process the trailing numbers. */
while(beg < end) {
- if(i < n)
+ if(i < buflen)
buf[i] = '.';
i++;
x = 0;
do {
if(x & 0xFF000000)
- return -1;
+ return 0;
y = *(const unsigned char *) beg++;
x = (x << 7) | (y & 0x7F);
} while(y & 0x80);
- i += encodeUint(buf + i, n - i, x);
+ if(i >= buflen)
+ i += encodeUint(NULL, 0, x);
+ else
+ i += encodeUint(buf + i, buflen - i, x);
}
- if(i < n)
+ if(i < buflen)
buf[i] = '\0';
return i;
}
+/*
+ * Convert an ASN.1 OID into its dotted or symbolic string representation.
+ * Return the dynamically allocated string, or NULL if an error occurs.
+ */
+
static const char *OID2str(const char *beg, const char *end, bool symbolic)
{
- char *buf = (char *) NULL;
- const curl_OID * op;
- int n;
-
- /* Convert an ASN.1 OID into its dotted or symbolic string representation.
- Return the dynamically allocated string, or NULL if an error occurs. */
-
+ char *buf = NULL;
if(beg < end) {
- n = encodeOID((char *) NULL, -1, beg, end);
- if(n >= 0) {
- buf = malloc(n + 1);
+ size_t buflen = encodeOID(NULL, 0, beg, end);
+ if(buflen) {
+ buf = malloc(buflen + 1); /* one extra for the zero byte */
if(buf) {
- encodeOID(buf, n, beg, end);
- buf[n] = '\0';
+ encodeOID(buf, buflen, beg, end);
+ buf[buflen] = '\0';
if(symbolic) {
- op = searchOID(buf);
+ const curl_OID *op = searchOID(buf);
if(op) {
free(buf);
buf = strdup(op->textoid);
@@ -470,7 +485,7 @@ static const char *GTime2str(const char *beg, const char *end)
sec2 = fracp[-1];
break;
default:
- return (const char *) NULL;
+ return NULL;
}
/* Scan for timezone, measure fractional seconds. */
@@ -506,15 +521,16 @@ static const char *GTime2str(const char *beg, const char *end)
sep, tzl, tzp);
}
+/*
+ * Convert an ASN.1 UTC time to a printable string.
+ * Return the dynamically allocated string, or NULL if an error occurs.
+ */
static const char *UTime2str(const char *beg, const char *end)
{
const char *tzp;
size_t tzl;
const char *sec;
- /* Convert an ASN.1 UTC time to a printable string.
- Return the dynamically allocated string, or NULL if an error occurs. */
-
for(tzp = beg; tzp < end && *tzp >= '0' && *tzp <= '9'; tzp++)
;
/* Get the seconds. */
@@ -525,12 +541,12 @@ static const char *UTime2str(const char *beg, const char *end)
case 2:
break;
default:
- return (const char *) NULL;
+ return NULL;
}
/* Process timezone. */
if(tzp >= end)
- return (const char *) NULL;
+ return NULL;
if(*tzp == 'Z') {
tzp = "GMT";
end = tzp + 3;
@@ -545,13 +561,14 @@ static const char *UTime2str(const char *beg, const char *end)
tzl, tzp);
}
+/*
+ * Convert an ASN.1 element to a printable string.
+ * Return the dynamically allocated string, or NULL if an error occurs.
+ */
static const char *ASN1tostr(curl_asn1Element *elem, int type)
{
- /* Convert an ASN.1 element to a printable string.
- Return the dynamically allocated string, or NULL if an error occurs. */
-
if(elem->constructed)
- return (const char *) NULL; /* No conversion of structured elements. */
+ return NULL; /* No conversion of structured elements. */
if(!type)
type = elem->tag; /* Type not forced: use element tag as type. */
@@ -585,10 +602,14 @@ static const char *ASN1tostr(curl_asn1Element *elem, int type)
return string2str(type, elem->beg, elem->end);
}
- return (const char *) NULL; /* Unsupported. */
+ return NULL; /* Unsupported. */
}
-static ssize_t encodeDN(char *buf, size_t n, curl_asn1Element *dn)
+/*
+ * ASCII encode distinguished name at `dn' into the `buflen'-sized buffer at
+ * `buf'. Return the total string length, even if larger than `buflen'.
+ */
+static ssize_t encodeDN(char *buf, size_t buflen, curl_asn1Element *dn)
{
curl_asn1Element rdn;
curl_asn1Element atv;
@@ -600,9 +621,6 @@ static ssize_t encodeDN(char *buf, size_t n, curl_asn1Element *dn)
const char *p3;
const char *str;
- /* ASCII encode distinguished name at `dn' into the `n'-byte buffer at `buf'.
- Return the total string length, even if larger than `n'. */
-
for(p1 = dn->beg; p1 < dn->end;) {
p1 = getASN1Element(&rdn, p1, dn->end);
if(!p1)
@@ -626,7 +644,7 @@ static ssize_t encodeDN(char *buf, size_t n, curl_asn1Element *dn)
for(p3 = str; isupper(*p3); p3++)
;
for(p3 = (*p3 || p3 - str > 2)? "/": ", "; *p3; p3++) {
- if(l < n)
+ if(l < buflen)
buf[l] = *p3;
l++;
}
@@ -634,14 +652,14 @@ static ssize_t encodeDN(char *buf, size_t n, curl_asn1Element *dn)
/* Encode attribute name. */
for(p3 = str; *p3; p3++) {
- if(l < n)
+ if(l < buflen)
buf[l] = *p3;
l++;
}
free((char *) str);
/* Generate equal sign. */
- if(l < n)
+ if(l < buflen)
buf[l] = '=';
l++;
@@ -650,7 +668,7 @@ static ssize_t encodeDN(char *buf, size_t n, curl_asn1Element *dn)
if(!str)
return -1;
for(p3 = str; *p3; p3++) {
- if(l < n)
+ if(l < buflen)
buf[l] = *p3;
l++;
}
@@ -661,28 +679,30 @@ static ssize_t encodeDN(char *buf, size_t n, curl_asn1Element *dn)
return l;
}
+/*
+ * Convert an ASN.1 distinguished name into a printable string.
+ * Return the dynamically allocated string, or NULL if an error occurs.
+ */
static const char *DNtostr(curl_asn1Element *dn)
{
- char *buf = (char *) NULL;
- ssize_t n = encodeDN(buf, 0, dn);
-
- /* Convert an ASN.1 distinguished name into a printable string.
- Return the dynamically allocated string, or NULL if an error occurs. */
+ char *buf = NULL;
+ ssize_t buflen = encodeDN(NULL, 0, dn);
- if(n >= 0) {
- buf = malloc(n + 1);
+ if(buflen >= 0) {
+ buf = malloc(buflen + 1);
if(buf) {
- encodeDN(buf, n + 1, dn);
- buf[n] = '\0';
+ encodeDN(buf, buflen + 1, dn);
+ buf[buflen] = '\0';
}
}
- return (const char *) buf;
+ return buf;
}
/*
- * X509 parser.
+ * ASN.1 parse an X509 certificate into structure subfields.
+ * Syntax is assumed to have already been checked by the SSL backend.
+ * See RFC 5280.
*/
-
int Curl_parseX509(curl_X509certificate *cert,
const char *beg, const char *end)
{
@@ -691,10 +711,6 @@ int Curl_parseX509(curl_X509certificate *cert,
const char *ccp;
static const char defaultVersion = 0; /* v1. */
- /* ASN.1 parse an X509 certificate into structure subfields.
- Syntax is assumed to have already been checked by the SSL backend.
- See RFC 5280. */
-
cert->certificate.header = NULL;
cert->certificate.beg = beg;
cert->certificate.end = end;
@@ -801,13 +817,14 @@ int Curl_parseX509(curl_X509certificate *cert,
return 0;
}
+
+/*
+ * Copy at most 64-characters, terminate with a newline and returns the
+ * effective number of stored characters.
+ */
static size_t copySubstring(char *to, const char *from)
{
size_t i;
-
- /* Copy at most 64-characters, terminate with a newline and returns the
- effective number of stored characters. */
-
for(i = 0; i < 64; i++) {
to[i] = *from;
if(!*from++)
@@ -861,9 +878,6 @@ static void do_pubkey(struct Curl_easy *data, int certnum,
curl_asn1Element elem;
curl_asn1Element pk;
const char *p;
- const char *q;
- unsigned long len;
- unsigned int i;
/* Generate all information records for the public key. */
@@ -872,6 +886,9 @@ static void do_pubkey(struct Curl_easy *data, int certnum,
return;
if(strcasecompare(algo, "rsaEncryption")) {
+ const char *q;
+ unsigned long len;
+
p = getASN1Element(&elem, pk.beg, pk.end);
if(!p)
return;
@@ -880,9 +897,11 @@ static void do_pubkey(struct Curl_easy *data, int certnum,
for(q = elem.beg; !*q && q < elem.end; q++)
;
len = (unsigned long)((elem.end - q) * 8);
- if(len)
+ if(len) {
+ unsigned int i;
for(i = *(unsigned char *) q; !(i & 0x80); i <<= 1)
len--;
+ }
if(len > 32)
elem.beg = q; /* Strip leading zero bytes. */
if(!certnum)
@@ -1040,8 +1059,6 @@ CURLcode Curl_extract_certinfo(struct connectdata *conn,
do_pubkey(data, certnum, ccp, &param, &cert.subjectPublicKey);
free((char *) ccp);
-/* TODO: extensions. */
-
/* Signature. */
ccp = ASN1tostr(&cert.signature, 0);
if(!ccp)
@@ -1104,15 +1121,15 @@ static const char *checkOID(const char *beg, const char *end,
ccp = getASN1Element(&e, beg, end);
if(!ccp || e.tag != CURL_ASN1_OBJECT_IDENTIFIER)
- return (const char *) NULL;
+ return NULL;
p = OID2str(e.beg, e.end, FALSE);
if(!p)
- return (const char *) NULL;
+ return NULL;
matched = !strcmp(p, oid);
free((char *) p);
- return matched? ccp: (const char *) NULL;
+ return matched? ccp: NULL;
}
CURLcode Curl_verifyhost(struct connectdata *conn,
diff --git a/Utilities/cmexpat/CMakeLists.txt b/Utilities/cmexpat/CMakeLists.txt
index 470fcba99..13eb56dd4 100644
--- a/Utilities/cmexpat/CMakeLists.txt
+++ b/Utilities/cmexpat/CMakeLists.txt
@@ -1,6 +1,6 @@
# Disable warnings to avoid changing 3rd party code.
IF(CMAKE_C_COMPILER_ID MATCHES
- "^(GNU|Clang|AppleClang|XL|VisualAge|SunPro|MIPSpro|HP|Intel)$")
+ "^(GNU|Clang|AppleClang|XLClang|XL|VisualAge|SunPro|HP|Intel)$")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w")
ELSEIF(CMAKE_C_COMPILER_ID STREQUAL "PathScale")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -woffall")
diff --git a/Utilities/cmexpat/ConfigureChecks.cmake b/Utilities/cmexpat/ConfigureChecks.cmake
index 057cfa58d..b2edc3e0a 100644
--- a/Utilities/cmexpat/ConfigureChecks.cmake
+++ b/Utilities/cmexpat/ConfigureChecks.cmake
@@ -1,6 +1,7 @@
+include(CheckCCompilerFlag)
+include(CheckCSourceCompiles)
include(CheckIncludeFile)
include(CheckIncludeFiles)
-include(CheckFunctionExists)
include(CheckSymbolExists)
include(TestBigEndian)
@@ -16,10 +17,21 @@ check_include_file("sys/stat.h" HAVE_SYS_STAT_H)
check_include_file("sys/types.h" HAVE_SYS_TYPES_H)
check_include_file("unistd.h" HAVE_UNISTD_H)
-check_function_exists("getpagesize" HAVE_GETPAGESIZE)
-check_function_exists("bcopy" HAVE_BCOPY)
-check_symbol_exists("memmove" "string.h" HAVE_MEMMOVE)
-check_function_exists("mmap" HAVE_MMAP)
+check_symbol_exists("getpagesize" "unistd.h" HAVE_GETPAGESIZE)
+check_symbol_exists("mmap" "sys/mman.h" HAVE_MMAP)
+check_symbol_exists("getrandom" "sys/random.h" HAVE_GETRANDOM)
+
+if(USE_libbsd)
+ set(CMAKE_REQUIRED_LIBRARIES "${LIB_BSD}")
+ set(_bsd "bsd/")
+else()
+ set(_bsd "")
+endif()
+check_symbol_exists("arc4random_buf" "${_bsd}stdlib.h" HAVE_ARC4RANDOM_BUF)
+if(NOT HAVE_ARC4RANDOM_BUF)
+ check_symbol_exists("arc4random" "${_bsd}stdlib.h" HAVE_ARC4RANDOM)
+endif()
+set(CMAKE_REQUIRED_LIBRARIES)
#/* Define to 1 if you have the ANSI C header files. */
check_include_files("stdlib.h;stdarg.h;string.h;float.h" STDC_HEADERS)
@@ -40,5 +52,18 @@ else(HAVE_SYS_TYPES_H)
set(SIZE_T "unsigned")
endif(HAVE_SYS_TYPES_H)
+check_c_source_compiles("
+ #include <stdlib.h> /* for NULL */
+ #include <unistd.h> /* for syscall */
+ #include <sys/syscall.h> /* for SYS_getrandom */
+ int main() {
+ syscall(SYS_getrandom, NULL, 0, 0);
+ return 0;
+ }"
+ HAVE_SYSCALL_GETRANDOM)
+
configure_file(expat_config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/expat_config.h")
add_definitions(-DHAVE_EXPAT_CONFIG_H)
+
+check_c_compiler_flag("-fno-strict-aliasing" FLAG_NO_STRICT_ALIASING)
+check_c_compiler_flag("-fvisibility=hidden" FLAG_VISIBILITY)
diff --git a/Utilities/cmexpat/README.md b/Utilities/cmexpat/README.md
index 0a1777e7e..fd3911ebc 100644
--- a/Utilities/cmexpat/README.md
+++ b/Utilities/cmexpat/README.md
@@ -1,4 +1,9 @@
-# Expat, Release 2.2.3
+[![Travis CI Build Status](https://travis-ci.org/libexpat/libexpat.svg?branch=master)](https://travis-ci.org/libexpat/libexpat)
+[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/libexpat/libexpat?svg=true)](https://ci.appveyor.com/project/libexpat/libexpat)
+[![Packaging status](https://repology.org/badge/tiny-repos/expat.svg)](https://repology.org/metapackage/expat/versions)
+
+
+# Expat, Release 2.2.7
This is Expat, a C library for parsing XML, started by
[James Clark](https://en.wikipedia.org/wiki/James_Clark_(programmer)) in 1997.
@@ -72,47 +77,43 @@ the directories into which things will be installed.
If you are interested in building Expat to provide document
information in UTF-16 encoding rather than the default UTF-8, follow
-these instructions (after having run `make distclean`):
+these instructions (after having run `make distclean`).
+Please note that we configure with `--without-xmlwf` as xmlwf does not
+support this mode of compilation (yet):
+
+1. Mass-patch `Makefile.am` files to use `libexpatw.la` for a library name:
+ <br/>
+ `find -name Makefile.am -exec sed
+ -e 's,libexpat\.la,libexpatw.la,'
+ -e 's,libexpat_la,libexpatw_la,'
+ -i {} +`
+
+1. Run `automake` to re-write `Makefile.in` files:<br/>
+ `automake`
1. For UTF-16 output as unsigned short (and version/error strings as char),
run:<br/>
- `./configure CPPFLAGS=-DXML_UNICODE`<br/>
+ `./configure CPPFLAGS=-DXML_UNICODE --without-xmlwf`<br/>
For UTF-16 output as `wchar_t` (incl. version/error strings), run:<br/>
- `./configure CFLAGS="-g -O2 -fshort-wchar" CPPFLAGS=-DXML_UNICODE_WCHAR_T`
+ `./configure CFLAGS="-g -O2 -fshort-wchar" CPPFLAGS=-DXML_UNICODE_WCHAR_T
+ --without-xmlwf`
<br/>Note: The latter requires libc compiled with `-fshort-wchar`, as well.
-1. Edit `Makefile`, changing:<br/>
- `LIBRARY = libexpat.la`<br/>
- to:<br/>
- `LIBRARY = libexpatw.la`<br/>
- (Note the additional "w" in the library name.)
+1. Run `make` (which excludes xmlwf).
-1. Run `make buildlib` (which builds the library only).
- Or, to save step 2, run `make buildlib LIBRARY=libexpatw.la`.
+1. Run `make install` (again, excludes xmlwf).
-1. Run `make installlib` (which installs the library only).
- Or, if step 2 was omitted, run `make installlib LIBRARY=libexpatw.la`.
-
-Using `DESTDIR` or `INSTALL_ROOT` is enabled, with `INSTALL_ROOT` being the
-default value for `DESTDIR`, and the rest of the make file using only
-`DESTDIR`. It works as follows:
+Using `DESTDIR` is supported. It works as follows:
```console
make install DESTDIR=/path/to/image
```
-overrides the in-makefile set `DESTDIR`, while both
-
-```console
-INSTALL_ROOT=/path/to/image make install
-make install INSTALL_ROOT=/path/to/image
-```
+overrides the in-makefile set `DESTDIR`, because variable-setting priority is
-use `DESTDIR=$(INSTALL_ROOT)`, even if `DESTDIR` eventually is defined in the
-environment, because variable-setting priority is
1. commandline
-2. in-makefile
-3. environment
+1. in-makefile
+1. environment
Note: This only applies to the Expat library itself, building UTF-16 versions
of xmlwf and the tests is currently not supported.
diff --git a/Utilities/cmexpat/expat_config.h.cmake b/Utilities/cmexpat/expat_config.h.cmake
index ad540d604..899d3a615 100644
--- a/Utilities/cmexpat/expat_config.h.cmake
+++ b/Utilities/cmexpat/expat_config.h.cmake
@@ -1,10 +1,13 @@
-/* expat_config.h.in. Generated from configure.in by autoheader. */
+/* expat_config.h.cmake. Based upon generated expat_config.h.in. */
/* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */
#cmakedefine BYTEORDER @BYTEORDER@
-/* Define to 1 if you have the `bcopy' function. */
-#cmakedefine HAVE_BCOPY
+/* Define to 1 if you have the `arc4random' function. */
+#cmakedefine HAVE_ARC4RANDOM
+
+/* Define to 1 if you have the `arc4random_buf' function. */
+#cmakedefine HAVE_ARC4RANDOM_BUF
/* Define to 1 if you have the <dlfcn.h> header file. */
#cmakedefine HAVE_DLFCN_H
@@ -15,11 +18,14 @@
/* Define to 1 if you have the `getpagesize' function. */
#cmakedefine HAVE_GETPAGESIZE
+/* Define to 1 if you have the `getrandom' function. */
+#cmakedefine HAVE_GETRANDOM
+
/* Define to 1 if you have the <inttypes.h> header file. */
#cmakedefine HAVE_INTTYPES_H
-/* Define to 1 if you have the `memmove' function. */
-#cmakedefine HAVE_MEMMOVE
+/* Define to 1 if you have the `bsd' library (-lbsd). */
+#cmakedefine HAVE_LIBBSD
/* Define to 1 if you have the <memory.h> header file. */
#cmakedefine HAVE_MEMORY_H
@@ -39,6 +45,9 @@
/* Define to 1 if you have the <string.h> header file. */
#cmakedefine HAVE_STRING_H
+/* Define to 1 if you have `syscall' and `SYS_getrandom'. */
+#cmakedefine HAVE_SYSCALL_GETRANDOM
+
/* Define to 1 if you have the <sys/stat.h> header file. */
#cmakedefine HAVE_SYS_STAT_H
@@ -64,6 +73,17 @@
/* Define to make XML Namespaces functionality available. */
/* #undef XML_NS */
+#if ! defined(_WIN32)
+/* Define to extract entropy from /dev/urandom. */
+#cmakedefine XML_DEV_URANDOM
+#endif
+
+/* Define to use UTF-16 chars (two bytes). */
+#cmakedefine XML_UNICODE
+
+/* Define to use wchar_t as UTF-16 char type instead of unsigned short. */
+#cmakedefine XML_UNICODE_WCHAR_T
+
/* Define to __FUNCTION__ or "" if `__func__' does not conform to ANSI C. */
#ifdef _MSC_VER
# define __func__ __FUNCTION__
diff --git a/Utilities/cmexpat/lib/ascii.h b/Utilities/cmexpat/lib/ascii.h
index d10530b09..c3587e573 100644
--- a/Utilities/cmexpat/lib/ascii.h
+++ b/Utilities/cmexpat/lib/ascii.h
@@ -1,5 +1,33 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
- See the file COPYING for copying permission.
+/*
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+ Copyright (c) 2000-2017 Expat development team
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#define ASCII_A 0x41
diff --git a/Utilities/cmexpat/lib/asciitab.h b/Utilities/cmexpat/lib/asciitab.h
index 79a15c28c..2f59fd929 100644
--- a/Utilities/cmexpat/lib/asciitab.h
+++ b/Utilities/cmexpat/lib/asciitab.h
@@ -1,5 +1,33 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
- See the file COPYING for copying permission.
+/*
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+ Copyright (c) 2000-2017 Expat development team
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
diff --git a/Utilities/cmexpat/lib/expat.h b/Utilities/cmexpat/lib/expat.h
index 7e5bbb7e3..c050f1d91 100644
--- a/Utilities/cmexpat/lib/expat.h
+++ b/Utilities/cmexpat/lib/expat.h
@@ -1,5 +1,33 @@
-/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
- See the file COPYING for copying permission.
+/*
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+ Copyright (c) 2000-2017 Expat development team
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef Expat_INCLUDED
@@ -236,7 +264,7 @@ XML_ParserCreate_MM(const XML_Char *encoding,
const XML_Char *namespaceSeparator);
/* Prepare a parser object to be re-used. This is particularly
- valuable when memory allocation overhead is disproportionatly high,
+ valuable when memory allocation overhead is disproportionately high,
such as when a large number of small documnents need to be parsed.
All handlers are cleared from the parser, except for the
unknownEncodingHandler. The parser's external state is re-initialized
@@ -1048,7 +1076,7 @@ XML_GetFeatureList(void);
*/
#define XML_MAJOR_VERSION 2
#define XML_MINOR_VERSION 2
-#define XML_MICRO_VERSION 3
+#define XML_MICRO_VERSION 7
#ifdef __cplusplus
}
diff --git a/Utilities/cmexpat/lib/expat_external.h b/Utilities/cmexpat/lib/expat_external.h
index d60eeccfb..4e14470e8 100644
--- a/Utilities/cmexpat/lib/expat_external.h
+++ b/Utilities/cmexpat/lib/expat_external.h
@@ -1,5 +1,33 @@
-/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
- See the file COPYING for copying permission.
+/*
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+ Copyright (c) 2000-2017 Expat development team
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef Expat_External_INCLUDED
@@ -8,7 +36,7 @@
/* External API definitions */
#if defined(_MSC_EXTENSIONS) && !defined(__BEOS__) && !defined(__CYGWIN__)
-#define XML_USE_MSC_EXTENSIONS 1
+# define XML_USE_MSC_EXTENSIONS 1
#endif
/* Expat tries very hard to make the API boundary very specifically
@@ -34,11 +62,11 @@
system headers may assume the cdecl convention.
*/
#ifndef XMLCALL
-#if defined(_MSC_VER)
-#define XMLCALL __cdecl
-#elif defined(__GNUC__) && defined(__i386) && !defined(__INTEL_COMPILER)
-#define XMLCALL __attribute__((cdecl))
-#else
+# if defined(_MSC_VER)
+# define XMLCALL __cdecl
+# elif defined(__GNUC__) && defined(__i386) && !defined(__INTEL_COMPILER)
+# define XMLCALL __attribute__((cdecl))
+# else
/* For any platform which uses this definition and supports more than
one calling convention, we need to extend this definition to
declare the convention used on that platform, if it's possible to
@@ -49,43 +77,47 @@
pre-processor and how to specify the same calling convention as the
platform's malloc() implementation.
*/
-#define XMLCALL
-#endif
+# define XMLCALL
+# endif
#endif /* not defined XMLCALL */
/* Build within CMake hard-codes use of a static library. */
#define XML_STATIC
#if !defined(XML_STATIC) && !defined(XMLIMPORT)
-#ifndef XML_BUILDING_EXPAT
+# ifndef XML_BUILDING_EXPAT
/* using Expat from an application */
-#ifdef XML_USE_MSC_EXTENSIONS
-#define XMLIMPORT __declspec(dllimport)
-#endif
+# ifdef XML_USE_MSC_EXTENSIONS
+# define XMLIMPORT __declspec(dllimport)
+# endif
-#endif
+# endif
#endif /* not defined XML_STATIC */
-#if !defined(XMLIMPORT) && defined(__GNUC__) && (__GNUC__ >= 4)
-#define XMLIMPORT __attribute__ ((visibility ("default")))
+#ifndef XML_ENABLE_VISIBILITY
+# define XML_ENABLE_VISIBILITY 0
+#endif
+
+#if !defined(XMLIMPORT) && XML_ENABLE_VISIBILITY
+# define XMLIMPORT __attribute__ ((visibility ("default")))
#endif
/* If we didn't define it above, define it away: */
#ifndef XMLIMPORT
-#define XMLIMPORT
+# define XMLIMPORT
#endif
#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96))
-#define XML_ATTR_MALLOC __attribute__((__malloc__))
+# define XML_ATTR_MALLOC __attribute__((__malloc__))
#else
-#define XML_ATTR_MALLOC
+# define XML_ATTR_MALLOC
#endif
#if defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
-#define XML_ATTR_ALLOC_SIZE(x) __attribute__((__alloc_size__(x)))
+# define XML_ATTR_ALLOC_SIZE(x) __attribute__((__alloc_size__(x)))
#else
-#define XML_ATTR_ALLOC_SIZE(x)
+# define XML_ATTR_ALLOC_SIZE(x)
#endif
#define XMLPARSEAPI(type) XMLIMPORT type XMLCALL
@@ -95,33 +127,35 @@ extern "C" {
#endif
#ifdef XML_UNICODE_WCHAR_T
-# define XML_UNICODE
+# ifndef XML_UNICODE
+# define XML_UNICODE
+# endif
# if defined(__SIZEOF_WCHAR_T__) && (__SIZEOF_WCHAR_T__ != 2)
# error "sizeof(wchar_t) != 2; Need -fshort-wchar for both Expat and libc"
# endif
#endif
#ifdef XML_UNICODE /* Information is UTF-16 encoded. */
-#ifdef XML_UNICODE_WCHAR_T
+# ifdef XML_UNICODE_WCHAR_T
typedef wchar_t XML_Char;
typedef wchar_t XML_LChar;
-#else
+# else
typedef unsigned short XML_Char;
typedef char XML_LChar;
-#endif /* XML_UNICODE_WCHAR_T */
+# endif /* XML_UNICODE_WCHAR_T */
#else /* Information is UTF-8 encoded. */
typedef char XML_Char;
typedef char XML_LChar;
#endif /* XML_UNICODE */
#ifdef XML_LARGE_SIZE /* Use large integers for file/stream positions. */
-#if defined(XML_USE_MSC_EXTENSIONS) && _MSC_VER < 1400
+# if defined(XML_USE_MSC_EXTENSIONS) && _MSC_VER < 1400
typedef __int64 XML_Index;
typedef unsigned __int64 XML_Size;
-#else
+# else
typedef long long XML_Index;
typedef unsigned long long XML_Size;
-#endif
+# endif
#else
typedef long XML_Index;
typedef unsigned long XML_Size;
diff --git a/Utilities/cmexpat/lib/iasciitab.h b/Utilities/cmexpat/lib/iasciitab.h
index 24a1d5ccc..ce4a4bf7e 100644
--- a/Utilities/cmexpat/lib/iasciitab.h
+++ b/Utilities/cmexpat/lib/iasciitab.h
@@ -1,5 +1,33 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
- See the file COPYING for copying permission.
+/*
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+ Copyright (c) 2000-2017 Expat development team
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */
diff --git a/Utilities/cmexpat/lib/internal.h b/Utilities/cmexpat/lib/internal.h
index 94cb98e15..dc4ef0c7e 100644
--- a/Utilities/cmexpat/lib/internal.h
+++ b/Utilities/cmexpat/lib/internal.h
@@ -18,6 +18,35 @@
Note: Use of these macros is based on judgement, not hard rules,
and therefore subject to change.
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+ Copyright (c) 2000-2017 Expat development team
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#if defined(__GNUC__) && defined(__i386__) && !defined(__MINGW32__)
@@ -86,8 +115,13 @@ extern "C" {
#endif
+#ifdef XML_ENABLE_VISIBILITY
+#if XML_ENABLE_VISIBILITY
+__attribute__ ((visibility ("default")))
+#endif
+#endif
void
-align_limit_to_full_utf8_characters(const char * from, const char ** fromLimRef);
+_INTERNAL_trim_to_complete_utf8_characters(const char * from, const char ** fromLimRef);
#ifdef __cplusplus
diff --git a/Utilities/cmexpat/lib/latin1tab.h b/Utilities/cmexpat/lib/latin1tab.h
index 53c25d76b..95dfa52b1 100644
--- a/Utilities/cmexpat/lib/latin1tab.h
+++ b/Utilities/cmexpat/lib/latin1tab.h
@@ -1,5 +1,33 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
- See the file COPYING for copying permission.
+/*
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+ Copyright (c) 2000-2017 Expat development team
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
diff --git a/Utilities/cmexpat/lib/loadlibrary.c b/Utilities/cmexpat/lib/loadlibrary.c
index ffce86839..35fdf98bc 100644
--- a/Utilities/cmexpat/lib/loadlibrary.c
+++ b/Utilities/cmexpat/lib/loadlibrary.c
@@ -6,8 +6,10 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2016 - 2017, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) 2017, Expat development team
*
* All rights reserved.
+ * Licensed under the MIT license:
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -82,7 +84,7 @@ HMODULE _Expat_LoadLibrary(LPCTSTR filename)
/* Get a handle to kernel32 so we can access it's functions at runtime */
HMODULE hKernel32 = GetModuleHandle(TEXT("kernel32"));
if(!hKernel32)
- return NULL;
+ return NULL; /* LCOV_EXCL_LINE */
/* Attempt to find LoadLibraryEx() which is only available on Windows 2000
and above */
diff --git a/Utilities/cmexpat/lib/nametab.h b/Utilities/cmexpat/lib/nametab.h
index b05e62c77..bfa2bd38c 100644
--- a/Utilities/cmexpat/lib/nametab.h
+++ b/Utilities/cmexpat/lib/nametab.h
@@ -1,3 +1,35 @@
+/*
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+ Copyright (c) 2000-2017 Expat development team
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
static const unsigned namingBitmap[] = {
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
diff --git a/Utilities/cmexpat/lib/siphash.h b/Utilities/cmexpat/lib/siphash.h
index 1684b251d..3caabebe2 100644
--- a/Utilities/cmexpat/lib/siphash.h
+++ b/Utilities/cmexpat/lib/siphash.h
@@ -11,6 +11,12 @@
* --------------------------------------------------------------------------
* HISTORY:
*
+ * 2018-07-08 (Anton Maklakov)
+ * - Add "fall through" markers for GCC's -Wimplicit-fallthrough
+ *
+ * 2017-11-03 (Sebastian Pipping)
+ * - Hide sip_tobin and sip_binof unless SIPHASH_TOBIN macro is defined
+ *
* 2017-07-25 (Vadim Zeitlin)
* - Fix use of SIPHASH_MAIN macro
*
@@ -154,6 +160,8 @@ static struct sipkey *sip_tokey(struct sipkey *key, const void *src) {
} /* sip_tokey() */
+#ifdef SIPHASH_TOBIN
+
#define sip_binof(v) sip_tobin((unsigned char[8]){ 0 }, (v))
static void *sip_tobin(void *dst, uint64_t u64) {
@@ -161,6 +169,8 @@ static void *sip_tobin(void *dst, uint64_t u64) {
return dst;
} /* sip_tobin() */
+#endif /* SIPHASH_TOBIN */
+
static void sip_round(struct siphash *H, const int rounds) {
int i;
@@ -234,12 +244,19 @@ static uint64_t sip24_final(struct siphash *H) {
switch (left) {
case 7: b |= (uint64_t)H->buf[6] << 48;
+ /* fall through */
case 6: b |= (uint64_t)H->buf[5] << 40;
+ /* fall through */
case 5: b |= (uint64_t)H->buf[4] << 32;
+ /* fall through */
case 4: b |= (uint64_t)H->buf[3] << 24;
+ /* fall through */
case 3: b |= (uint64_t)H->buf[2] << 16;
+ /* fall through */
case 2: b |= (uint64_t)H->buf[1] << 8;
+ /* fall through */
case 1: b |= (uint64_t)H->buf[0] << 0;
+ /* fall through */
case 0: break;
}
diff --git a/Utilities/cmexpat/lib/utf8tab.h b/Utilities/cmexpat/lib/utf8tab.h
index 7bb3e7760..fa0bed6f5 100644
--- a/Utilities/cmexpat/lib/utf8tab.h
+++ b/Utilities/cmexpat/lib/utf8tab.h
@@ -1,7 +1,34 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
- See the file COPYING for copying permission.
-*/
+/*
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+ Copyright (c) 2000-2017 Expat development team
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
/* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
diff --git a/Utilities/cmexpat/lib/winconfig.h b/Utilities/cmexpat/lib/winconfig.h
index c9dbfea85..1af28822c 100644
--- a/Utilities/cmexpat/lib/winconfig.h
+++ b/Utilities/cmexpat/lib/winconfig.h
@@ -1,10 +1,33 @@
-/*================================================================
-** Copyright 2000, Clark Cooper
-** All rights reserved.
-**
-** This is free software. You are permitted to copy, distribute, or modify
-** it under the terms of the MIT/X license (contained in the COPYING file
-** with this distribution.)
+/*
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+ Copyright (c) 2000-2017 Expat development team
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef WINCONFIG_H
@@ -30,10 +53,6 @@
/* we will assume all Windows platforms are little endian */
#define BYTEORDER 1234
-/* Windows has memmove() available. */
-#define HAVE_MEMMOVE
-
-
#endif /* !defined(HAVE_EXPAT_CONFIG_H) */
#if defined(_MSC_VER)
diff --git a/Utilities/cmexpat/lib/xmlparse.c b/Utilities/cmexpat/lib/xmlparse.c
index b703e61a0..9c0987f4f 100644
--- a/Utilities/cmexpat/lib/xmlparse.c
+++ b/Utilities/cmexpat/lib/xmlparse.c
@@ -1,7 +1,33 @@
-/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
- See the file COPYING for copying permission.
-
- 101bfd65d1ff3d1511cf6671e6aae65f82cd97df6f4da137d46d510731830ad9 (2.2.3+)
+/* 69df5be70289a11fb834869ce4a91c23c1d9dd04baffcbd10e86742d149a080c (2.2.7+)
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+ Copyright (c) 2000-2017 Expat development team
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#if !defined(_GNU_SOURCE)
@@ -80,9 +106,6 @@
If insist on not using any of these, bypass this error by defining \
XML_POOR_ENTROPY; you have been warned. \
\
- For CMake, one way to pass the define is: \
- cmake -DCMAKE_C_FLAGS="-pipe -O2 -DHAVE_SYSCALL_GETRANDOM" . \
- \
If you have reasons to patch this detection code away or need changes \
to the build system, please open a bug. Thank you!
#endif
@@ -138,14 +161,8 @@ typedef char ICHAR;
/* Round up n to be a multiple of sz, where sz is a power of 2. */
#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1))
-/* Handle the case where memmove() doesn't exist. */
-#ifndef HAVE_MEMMOVE
-#ifdef HAVE_BCOPY
-#define memmove(d,s,l) bcopy((s),(d),(l))
-#else
-#error memmove does not exist on this platform, nor is a substitute available
-#endif /* HAVE_BCOPY */
-#endif /* HAVE_MEMMOVE */
+/* Do safe (NULL-aware) pointer arithmetic */
+#define EXPAT_SAFE_PTR_DIFF(p, q) (((p) && (q)) ? ((p) - (q)) : 0)
#include "internal.h"
#include "xmltok.h"
@@ -447,7 +464,7 @@ setContext(XML_Parser parser, const XML_Char *context);
static void FASTCALL normalizePublicId(XML_Char *s);
static DTD * dtdCreate(const XML_Memory_Handling_Suite *ms);
-/* do not call if parentParser != NULL */
+/* do not call if m_parentParser != NULL */
static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms);
static void
dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms);
@@ -519,7 +536,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName);
: ((*((pool)->ptr)++ = c), 1))
struct XML_ParserStruct {
- /* The first member must be userData so that the XML_GetUserData
+ /* The first member must be m_userData so that the XML_GetUserData
macro works. */
void *m_userData;
void *m_handlerArg;
@@ -529,7 +546,7 @@ struct XML_ParserStruct {
const char *m_bufferPtr;
/* past last character to be parsed */
char *m_bufferEnd;
- /* allocated end of buffer */
+ /* allocated end of m_buffer */
const char *m_bufferLim;
XML_Index m_parseEndByteIndex;
const char *m_parseEndPtr;
@@ -621,113 +638,10 @@ struct XML_ParserStruct {
unsigned long m_hash_secret_salt;
};
-#define MALLOC(s) (parser->m_mem.malloc_fcn((s)))
-#define REALLOC(p,s) (parser->m_mem.realloc_fcn((p),(s)))
-#define FREE(p) (parser->m_mem.free_fcn((p)))
-
-#define userData (parser->m_userData)
-#define handlerArg (parser->m_handlerArg)
-#define startElementHandler (parser->m_startElementHandler)
-#define endElementHandler (parser->m_endElementHandler)
-#define characterDataHandler (parser->m_characterDataHandler)
-#define processingInstructionHandler \
- (parser->m_processingInstructionHandler)
-#define commentHandler (parser->m_commentHandler)
-#define startCdataSectionHandler \
- (parser->m_startCdataSectionHandler)
-#define endCdataSectionHandler (parser->m_endCdataSectionHandler)
-#define defaultHandler (parser->m_defaultHandler)
-#define startDoctypeDeclHandler (parser->m_startDoctypeDeclHandler)
-#define endDoctypeDeclHandler (parser->m_endDoctypeDeclHandler)
-#define unparsedEntityDeclHandler \
- (parser->m_unparsedEntityDeclHandler)
-#define notationDeclHandler (parser->m_notationDeclHandler)
-#define startNamespaceDeclHandler \
- (parser->m_startNamespaceDeclHandler)
-#define endNamespaceDeclHandler (parser->m_endNamespaceDeclHandler)
-#define notStandaloneHandler (parser->m_notStandaloneHandler)
-#define externalEntityRefHandler \
- (parser->m_externalEntityRefHandler)
-#define externalEntityRefHandlerArg \
- (parser->m_externalEntityRefHandlerArg)
-#define internalEntityRefHandler \
- (parser->m_internalEntityRefHandler)
-#define skippedEntityHandler (parser->m_skippedEntityHandler)
-#define unknownEncodingHandler (parser->m_unknownEncodingHandler)
-#define elementDeclHandler (parser->m_elementDeclHandler)
-#define attlistDeclHandler (parser->m_attlistDeclHandler)
-#define entityDeclHandler (parser->m_entityDeclHandler)
-#define xmlDeclHandler (parser->m_xmlDeclHandler)
-#define encoding (parser->m_encoding)
-#define initEncoding (parser->m_initEncoding)
-#define internalEncoding (parser->m_internalEncoding)
-#define unknownEncodingMem (parser->m_unknownEncodingMem)
-#define unknownEncodingData (parser->m_unknownEncodingData)
-#define unknownEncodingHandlerData \
- (parser->m_unknownEncodingHandlerData)
-#define unknownEncodingRelease (parser->m_unknownEncodingRelease)
-#define protocolEncodingName (parser->m_protocolEncodingName)
-#define ns (parser->m_ns)
-#define ns_triplets (parser->m_ns_triplets)
-#define prologState (parser->m_prologState)
-#define processor (parser->m_processor)
-#define errorCode (parser->m_errorCode)
-#define eventPtr (parser->m_eventPtr)
-#define eventEndPtr (parser->m_eventEndPtr)
-#define positionPtr (parser->m_positionPtr)
-#define position (parser->m_position)
-#define openInternalEntities (parser->m_openInternalEntities)
-#define freeInternalEntities (parser->m_freeInternalEntities)
-#define defaultExpandInternalEntities \
- (parser->m_defaultExpandInternalEntities)
-#define tagLevel (parser->m_tagLevel)
-#define buffer (parser->m_buffer)
-#define bufferPtr (parser->m_bufferPtr)
-#define bufferEnd (parser->m_bufferEnd)
-#define parseEndByteIndex (parser->m_parseEndByteIndex)
-#define parseEndPtr (parser->m_parseEndPtr)
-#define bufferLim (parser->m_bufferLim)
-#define dataBuf (parser->m_dataBuf)
-#define dataBufEnd (parser->m_dataBufEnd)
-#define _dtd (parser->m_dtd)
-#define curBase (parser->m_curBase)
-#define declEntity (parser->m_declEntity)
-#define doctypeName (parser->m_doctypeName)
-#define doctypeSysid (parser->m_doctypeSysid)
-#define doctypePubid (parser->m_doctypePubid)
-#define declAttributeType (parser->m_declAttributeType)
-#define declNotationName (parser->m_declNotationName)
-#define declNotationPublicId (parser->m_declNotationPublicId)
-#define declElementType (parser->m_declElementType)
-#define declAttributeId (parser->m_declAttributeId)
-#define declAttributeIsCdata (parser->m_declAttributeIsCdata)
-#define declAttributeIsId (parser->m_declAttributeIsId)
-#define freeTagList (parser->m_freeTagList)
-#define freeBindingList (parser->m_freeBindingList)
-#define inheritedBindings (parser->m_inheritedBindings)
-#define tagStack (parser->m_tagStack)
-#define atts (parser->m_atts)
-#define attsSize (parser->m_attsSize)
-#define nSpecifiedAtts (parser->m_nSpecifiedAtts)
-#define idAttIndex (parser->m_idAttIndex)
-#define nsAtts (parser->m_nsAtts)
-#define nsAttsVersion (parser->m_nsAttsVersion)
-#define nsAttsPower (parser->m_nsAttsPower)
-#define attInfo (parser->m_attInfo)
-#define tempPool (parser->m_tempPool)
-#define temp2Pool (parser->m_temp2Pool)
-#define groupConnector (parser->m_groupConnector)
-#define groupSize (parser->m_groupSize)
-#define namespaceSeparator (parser->m_namespaceSeparator)
-#define parentParser (parser->m_parentParser)
-#define ps_parsing (parser->m_parsingStatus.parsing)
-#define ps_finalBuffer (parser->m_parsingStatus.finalBuffer)
-#ifdef XML_DTD
-#define isParamEntity (parser->m_isParamEntity)
-#define useForeignDTD (parser->m_useForeignDTD)
-#define paramEntityParsing (parser->m_paramEntityParsing)
-#endif /* XML_DTD */
-#define hash_secret_salt (parser->m_hash_secret_salt)
+#define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s)))
+#define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p),(s)))
+#define FREE(parser, p) (parser->m_mem.free_fcn((p)))
+
XML_Parser XMLCALL
XML_ParserCreate(const XML_Char *encodingName)
@@ -753,6 +667,9 @@ static const XML_Char implicitContext[] = {
};
+/* To avoid warnings about unused functions: */
+#if ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM)
+
#if defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM)
/* Obtain entropy on Linux 3.17+ */
@@ -818,8 +735,10 @@ writeRandomBytes_dev_urandom(void * target, size_t count) {
#endif /* ! defined(_WIN32) && defined(XML_DEV_URANDOM) */
+#endif /* ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) */
+
-#if defined(HAVE_ARC4RANDOM)
+#if defined(HAVE_ARC4RANDOM) && ! defined(HAVE_ARC4RANDOM_BUF)
static void
writeRandomBytes_arc4random(void * target, size_t count) {
@@ -837,7 +756,7 @@ writeRandomBytes_arc4random(void * target, size_t count) {
}
}
-#endif /* defined(HAVE_ARC4RANDOM) */
+#endif /* defined(HAVE_ARC4RANDOM) && ! defined(HAVE_ARC4RANDOM_BUF) */
#ifdef _WIN32
@@ -919,6 +838,8 @@ generate_hash_secret_salt(XML_Parser parser)
{
unsigned long entropy;
(void)parser;
+
+ /* "Failproof" high quality providers: */
#if defined(HAVE_ARC4RANDOM_BUF)
arc4random_buf(&entropy, sizeof(entropy));
return ENTROPY_DEBUG("arc4random_buf", entropy);
@@ -967,9 +888,9 @@ static XML_Bool /* only valid for root parser */
startParsing(XML_Parser parser)
{
/* hash functions must be initialized before setContext() is called */
- if (hash_secret_salt == 0)
- hash_secret_salt = generate_hash_secret_salt(parser);
- if (ns) {
+ if (parser->m_hash_secret_salt == 0)
+ parser->m_hash_secret_salt = generate_hash_secret_salt(parser);
+ if (parser->m_ns) {
/* implicit context only set for root parser, since child
parsers (i.e. external entity parsers) will inherit it
*/
@@ -1019,85 +940,85 @@ parserCreate(const XML_Char *encodingName,
if (!parser)
return parser;
- buffer = NULL;
- bufferLim = NULL;
+ parser->m_buffer = NULL;
+ parser->m_bufferLim = NULL;
- attsSize = INIT_ATTS_SIZE;
- atts = (ATTRIBUTE *)MALLOC(attsSize * sizeof(ATTRIBUTE));
- if (atts == NULL) {
- FREE(parser);
+ parser->m_attsSize = INIT_ATTS_SIZE;
+ parser->m_atts = (ATTRIBUTE *)MALLOC(parser, parser->m_attsSize * sizeof(ATTRIBUTE));
+ if (parser->m_atts == NULL) {
+ FREE(parser, parser);
return NULL;
}
#ifdef XML_ATTR_INFO
- attInfo = (XML_AttrInfo*)MALLOC(attsSize * sizeof(XML_AttrInfo));
- if (attInfo == NULL) {
- FREE(atts);
- FREE(parser);
+ parser->m_attInfo = (XML_AttrInfo*)MALLOC(parser, parser->m_attsSize * sizeof(XML_AttrInfo));
+ if (parser->m_attInfo == NULL) {
+ FREE(parser, parser->m_atts);
+ FREE(parser, parser);
return NULL;
}
#endif
- dataBuf = (XML_Char *)MALLOC(INIT_DATA_BUF_SIZE * sizeof(XML_Char));
- if (dataBuf == NULL) {
- FREE(atts);
+ parser->m_dataBuf = (XML_Char *)MALLOC(parser, INIT_DATA_BUF_SIZE * sizeof(XML_Char));
+ if (parser->m_dataBuf == NULL) {
+ FREE(parser, parser->m_atts);
#ifdef XML_ATTR_INFO
- FREE(attInfo);
+ FREE(parser, parser->m_attInfo);
#endif
- FREE(parser);
+ FREE(parser, parser);
return NULL;
}
- dataBufEnd = dataBuf + INIT_DATA_BUF_SIZE;
+ parser->m_dataBufEnd = parser->m_dataBuf + INIT_DATA_BUF_SIZE;
if (dtd)
- _dtd = dtd;
+ parser->m_dtd = dtd;
else {
- _dtd = dtdCreate(&parser->m_mem);
- if (_dtd == NULL) {
- FREE(dataBuf);
- FREE(atts);
+ parser->m_dtd = dtdCreate(&parser->m_mem);
+ if (parser->m_dtd == NULL) {
+ FREE(parser, parser->m_dataBuf);
+ FREE(parser, parser->m_atts);
#ifdef XML_ATTR_INFO
- FREE(attInfo);
+ FREE(parser, parser->m_attInfo);
#endif
- FREE(parser);
+ FREE(parser, parser);
return NULL;
}
}
- freeBindingList = NULL;
- freeTagList = NULL;
- freeInternalEntities = NULL;
+ parser->m_freeBindingList = NULL;
+ parser->m_freeTagList = NULL;
+ parser->m_freeInternalEntities = NULL;
- groupSize = 0;
- groupConnector = NULL;
+ parser->m_groupSize = 0;
+ parser->m_groupConnector = NULL;
- unknownEncodingHandler = NULL;
- unknownEncodingHandlerData = NULL;
+ parser->m_unknownEncodingHandler = NULL;
+ parser->m_unknownEncodingHandlerData = NULL;
- namespaceSeparator = ASCII_EXCL;
- ns = XML_FALSE;
- ns_triplets = XML_FALSE;
+ parser->m_namespaceSeparator = ASCII_EXCL;
+ parser->m_ns = XML_FALSE;
+ parser->m_ns_triplets = XML_FALSE;
- nsAtts = NULL;
- nsAttsVersion = 0;
- nsAttsPower = 0;
+ parser->m_nsAtts = NULL;
+ parser->m_nsAttsVersion = 0;
+ parser->m_nsAttsPower = 0;
- protocolEncodingName = NULL;
+ parser->m_protocolEncodingName = NULL;
- poolInit(&tempPool, &(parser->m_mem));
- poolInit(&temp2Pool, &(parser->m_mem));
+ poolInit(&parser->m_tempPool, &(parser->m_mem));
+ poolInit(&parser->m_temp2Pool, &(parser->m_mem));
parserInit(parser, encodingName);
- if (encodingName && !protocolEncodingName) {
+ if (encodingName && !parser->m_protocolEncodingName) {
XML_ParserFree(parser);
return NULL;
}
if (nameSep) {
- ns = XML_TRUE;
- internalEncoding = XmlGetInternalEncodingNS();
- namespaceSeparator = *nameSep;
+ parser->m_ns = XML_TRUE;
+ parser->m_internalEncoding = XmlGetInternalEncodingNS();
+ parser->m_namespaceSeparator = *nameSep;
}
else {
- internalEncoding = XmlGetInternalEncoding();
+ parser->m_internalEncoding = XmlGetInternalEncoding();
}
return parser;
@@ -1106,85 +1027,85 @@ parserCreate(const XML_Char *encodingName,
static void
parserInit(XML_Parser parser, const XML_Char *encodingName)
{
- processor = prologInitProcessor;
- XmlPrologStateInit(&prologState);
+ parser->m_processor = prologInitProcessor;
+ XmlPrologStateInit(&parser->m_prologState);
if (encodingName != NULL) {
- protocolEncodingName = copyString(encodingName, &(parser->m_mem));
- }
- curBase = NULL;
- XmlInitEncoding(&initEncoding, &encoding, 0);
- userData = NULL;
- handlerArg = NULL;
- startElementHandler = NULL;
- endElementHandler = NULL;
- characterDataHandler = NULL;
- processingInstructionHandler = NULL;
- commentHandler = NULL;
- startCdataSectionHandler = NULL;
- endCdataSectionHandler = NULL;
- defaultHandler = NULL;
- startDoctypeDeclHandler = NULL;
- endDoctypeDeclHandler = NULL;
- unparsedEntityDeclHandler = NULL;
- notationDeclHandler = NULL;
- startNamespaceDeclHandler = NULL;
- endNamespaceDeclHandler = NULL;
- notStandaloneHandler = NULL;
- externalEntityRefHandler = NULL;
- externalEntityRefHandlerArg = parser;
- skippedEntityHandler = NULL;
- elementDeclHandler = NULL;
- attlistDeclHandler = NULL;
- entityDeclHandler = NULL;
- xmlDeclHandler = NULL;
- bufferPtr = buffer;
- bufferEnd = buffer;
- parseEndByteIndex = 0;
- parseEndPtr = NULL;
- declElementType = NULL;
- declAttributeId = NULL;
- declEntity = NULL;
- doctypeName = NULL;
- doctypeSysid = NULL;
- doctypePubid = NULL;
- declAttributeType = NULL;
- declNotationName = NULL;
- declNotationPublicId = NULL;
- declAttributeIsCdata = XML_FALSE;
- declAttributeIsId = XML_FALSE;
- memset(&position, 0, sizeof(POSITION));
- errorCode = XML_ERROR_NONE;
- eventPtr = NULL;
- eventEndPtr = NULL;
- positionPtr = NULL;
- openInternalEntities = NULL;
- defaultExpandInternalEntities = XML_TRUE;
- tagLevel = 0;
- tagStack = NULL;
- inheritedBindings = NULL;
- nSpecifiedAtts = 0;
- unknownEncodingMem = NULL;
- unknownEncodingRelease = NULL;
- unknownEncodingData = NULL;
- parentParser = NULL;
- ps_parsing = XML_INITIALIZED;
+ parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem));
+ }
+ parser->m_curBase = NULL;
+ XmlInitEncoding(&parser->m_initEncoding, &parser->m_encoding, 0);
+ parser->m_userData = NULL;
+ parser->m_handlerArg = NULL;
+ parser->m_startElementHandler = NULL;
+ parser->m_endElementHandler = NULL;
+ parser->m_characterDataHandler = NULL;
+ parser->m_processingInstructionHandler = NULL;
+ parser->m_commentHandler = NULL;
+ parser->m_startCdataSectionHandler = NULL;
+ parser->m_endCdataSectionHandler = NULL;
+ parser->m_defaultHandler = NULL;
+ parser->m_startDoctypeDeclHandler = NULL;
+ parser->m_endDoctypeDeclHandler = NULL;
+ parser->m_unparsedEntityDeclHandler = NULL;
+ parser->m_notationDeclHandler = NULL;
+ parser->m_startNamespaceDeclHandler = NULL;
+ parser->m_endNamespaceDeclHandler = NULL;
+ parser->m_notStandaloneHandler = NULL;
+ parser->m_externalEntityRefHandler = NULL;
+ parser->m_externalEntityRefHandlerArg = parser;
+ parser->m_skippedEntityHandler = NULL;
+ parser->m_elementDeclHandler = NULL;
+ parser->m_attlistDeclHandler = NULL;
+ parser->m_entityDeclHandler = NULL;
+ parser->m_xmlDeclHandler = NULL;
+ parser->m_bufferPtr = parser->m_buffer;
+ parser->m_bufferEnd = parser->m_buffer;
+ parser->m_parseEndByteIndex = 0;
+ parser->m_parseEndPtr = NULL;
+ parser->m_declElementType = NULL;
+ parser->m_declAttributeId = NULL;
+ parser->m_declEntity = NULL;
+ parser->m_doctypeName = NULL;
+ parser->m_doctypeSysid = NULL;
+ parser->m_doctypePubid = NULL;
+ parser->m_declAttributeType = NULL;
+ parser->m_declNotationName = NULL;
+ parser->m_declNotationPublicId = NULL;
+ parser->m_declAttributeIsCdata = XML_FALSE;
+ parser->m_declAttributeIsId = XML_FALSE;
+ memset(&parser->m_position, 0, sizeof(POSITION));
+ parser->m_errorCode = XML_ERROR_NONE;
+ parser->m_eventPtr = NULL;
+ parser->m_eventEndPtr = NULL;
+ parser->m_positionPtr = NULL;
+ parser->m_openInternalEntities = NULL;
+ parser->m_defaultExpandInternalEntities = XML_TRUE;
+ parser->m_tagLevel = 0;
+ parser->m_tagStack = NULL;
+ parser->m_inheritedBindings = NULL;
+ parser->m_nSpecifiedAtts = 0;
+ parser->m_unknownEncodingMem = NULL;
+ parser->m_unknownEncodingRelease = NULL;
+ parser->m_unknownEncodingData = NULL;
+ parser->m_parentParser = NULL;
+ parser->m_parsingStatus.parsing = XML_INITIALIZED;
#ifdef XML_DTD
- isParamEntity = XML_FALSE;
- useForeignDTD = XML_FALSE;
- paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER;
+ parser->m_isParamEntity = XML_FALSE;
+ parser->m_useForeignDTD = XML_FALSE;
+ parser->m_paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER;
#endif
- hash_secret_salt = 0;
+ parser->m_hash_secret_salt = 0;
}
-/* moves list of bindings to freeBindingList */
+/* moves list of bindings to m_freeBindingList */
static void FASTCALL
moveToFreeBindingList(XML_Parser parser, BINDING *bindings)
{
while (bindings) {
BINDING *b = bindings;
bindings = bindings->nextTagBinding;
- b->nextTagBinding = freeBindingList;
- freeBindingList = b;
+ b->nextTagBinding = parser->m_freeBindingList;
+ parser->m_freeBindingList = b;
}
}
@@ -1197,36 +1118,36 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName)
if (parser == NULL)
return XML_FALSE;
- if (parentParser)
+ if (parser->m_parentParser)
return XML_FALSE;
- /* move tagStack to freeTagList */
- tStk = tagStack;
+ /* move m_tagStack to m_freeTagList */
+ tStk = parser->m_tagStack;
while (tStk) {
TAG *tag = tStk;
tStk = tStk->parent;
- tag->parent = freeTagList;
+ tag->parent = parser->m_freeTagList;
moveToFreeBindingList(parser, tag->bindings);
tag->bindings = NULL;
- freeTagList = tag;
+ parser->m_freeTagList = tag;
}
- /* move openInternalEntities to freeInternalEntities */
- openEntityList = openInternalEntities;
+ /* move m_openInternalEntities to m_freeInternalEntities */
+ openEntityList = parser->m_openInternalEntities;
while (openEntityList) {
OPEN_INTERNAL_ENTITY *openEntity = openEntityList;
openEntityList = openEntity->next;
- openEntity->next = freeInternalEntities;
- freeInternalEntities = openEntity;
- }
- moveToFreeBindingList(parser, inheritedBindings);
- FREE(unknownEncodingMem);
- if (unknownEncodingRelease)
- unknownEncodingRelease(unknownEncodingData);
- poolClear(&tempPool);
- poolClear(&temp2Pool);
- FREE((void *)protocolEncodingName);
- protocolEncodingName = NULL;
+ openEntity->next = parser->m_freeInternalEntities;
+ parser->m_freeInternalEntities = openEntity;
+ }
+ moveToFreeBindingList(parser, parser->m_inheritedBindings);
+ FREE(parser, parser->m_unknownEncodingMem);
+ if (parser->m_unknownEncodingRelease)
+ parser->m_unknownEncodingRelease(parser->m_unknownEncodingData);
+ poolClear(&parser->m_tempPool);
+ poolClear(&parser->m_temp2Pool);
+ FREE(parser, (void *)parser->m_protocolEncodingName);
+ parser->m_protocolEncodingName = NULL;
parserInit(parser, encodingName);
- dtdReset(_dtd, &parser->m_mem);
+ dtdReset(parser->m_dtd, &parser->m_mem);
return XML_TRUE;
}
@@ -1239,19 +1160,19 @@ XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName)
XXX There's no way for the caller to determine which of the
XXX possible error cases caused the XML_STATUS_ERROR return.
*/
- if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED)
+ if (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED)
return XML_STATUS_ERROR;
/* Get rid of any previous encoding name */
- FREE((void *)protocolEncodingName);
+ FREE(parser, (void *)parser->m_protocolEncodingName);
if (encodingName == NULL)
/* No new encoding name */
- protocolEncodingName = NULL;
+ parser->m_protocolEncodingName = NULL;
else {
/* Copy the new encoding name into allocated memory */
- protocolEncodingName = copyString(encodingName, &(parser->m_mem));
- if (!protocolEncodingName)
+ parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem));
+ if (!parser->m_protocolEncodingName)
return XML_STATUS_ERROR;
}
return XML_STATUS_OK;
@@ -1308,44 +1229,44 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser,
return NULL;
/* Stash the original parser contents on the stack */
- oldDtd = _dtd;
- oldStartElementHandler = startElementHandler;
- oldEndElementHandler = endElementHandler;
- oldCharacterDataHandler = characterDataHandler;
- oldProcessingInstructionHandler = processingInstructionHandler;
- oldCommentHandler = commentHandler;
- oldStartCdataSectionHandler = startCdataSectionHandler;
- oldEndCdataSectionHandler = endCdataSectionHandler;
- oldDefaultHandler = defaultHandler;
- oldUnparsedEntityDeclHandler = unparsedEntityDeclHandler;
- oldNotationDeclHandler = notationDeclHandler;
- oldStartNamespaceDeclHandler = startNamespaceDeclHandler;
- oldEndNamespaceDeclHandler = endNamespaceDeclHandler;
- oldNotStandaloneHandler = notStandaloneHandler;
- oldExternalEntityRefHandler = externalEntityRefHandler;
- oldSkippedEntityHandler = skippedEntityHandler;
- oldUnknownEncodingHandler = unknownEncodingHandler;
- oldElementDeclHandler = elementDeclHandler;
- oldAttlistDeclHandler = attlistDeclHandler;
- oldEntityDeclHandler = entityDeclHandler;
- oldXmlDeclHandler = xmlDeclHandler;
- oldDeclElementType = declElementType;
-
- oldUserData = userData;
- oldHandlerArg = handlerArg;
- oldDefaultExpandInternalEntities = defaultExpandInternalEntities;
- oldExternalEntityRefHandlerArg = externalEntityRefHandlerArg;
+ oldDtd = parser->m_dtd;
+ oldStartElementHandler = parser->m_startElementHandler;
+ oldEndElementHandler = parser->m_endElementHandler;
+ oldCharacterDataHandler = parser->m_characterDataHandler;
+ oldProcessingInstructionHandler = parser->m_processingInstructionHandler;
+ oldCommentHandler = parser->m_commentHandler;
+ oldStartCdataSectionHandler = parser->m_startCdataSectionHandler;
+ oldEndCdataSectionHandler = parser->m_endCdataSectionHandler;
+ oldDefaultHandler = parser->m_defaultHandler;
+ oldUnparsedEntityDeclHandler = parser->m_unparsedEntityDeclHandler;
+ oldNotationDeclHandler = parser->m_notationDeclHandler;
+ oldStartNamespaceDeclHandler = parser->m_startNamespaceDeclHandler;
+ oldEndNamespaceDeclHandler = parser->m_endNamespaceDeclHandler;
+ oldNotStandaloneHandler = parser->m_notStandaloneHandler;
+ oldExternalEntityRefHandler = parser->m_externalEntityRefHandler;
+ oldSkippedEntityHandler = parser->m_skippedEntityHandler;
+ oldUnknownEncodingHandler = parser->m_unknownEncodingHandler;
+ oldElementDeclHandler = parser->m_elementDeclHandler;
+ oldAttlistDeclHandler = parser->m_attlistDeclHandler;
+ oldEntityDeclHandler = parser->m_entityDeclHandler;
+ oldXmlDeclHandler = parser->m_xmlDeclHandler;
+ oldDeclElementType = parser->m_declElementType;
+
+ oldUserData = parser->m_userData;
+ oldHandlerArg = parser->m_handlerArg;
+ oldDefaultExpandInternalEntities = parser->m_defaultExpandInternalEntities;
+ oldExternalEntityRefHandlerArg = parser->m_externalEntityRefHandlerArg;
#ifdef XML_DTD
- oldParamEntityParsing = paramEntityParsing;
- oldInEntityValue = prologState.inEntityValue;
+ oldParamEntityParsing = parser->m_paramEntityParsing;
+ oldInEntityValue = parser->m_prologState.inEntityValue;
#endif
- oldns_triplets = ns_triplets;
+ oldns_triplets = parser->m_ns_triplets;
/* Note that the new parser shares the same hash secret as the old
parser, so that dtdCopy and copyEntityTable can lookup values
from hash tables associated with either parser without us having
to worry which hash secrets each table has.
*/
- oldhash_secret_salt = hash_secret_salt;
+ oldhash_secret_salt = parser->m_hash_secret_salt;
#ifdef XML_DTD
if (!context)
@@ -1357,9 +1278,9 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser,
here. This makes this function more painful to follow than it
would be otherwise.
*/
- if (ns) {
+ if (parser->m_ns) {
XML_Char tmp[2];
- *tmp = namespaceSeparator;
+ *tmp = parser->m_namespaceSeparator;
parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd);
}
else {
@@ -1369,62 +1290,62 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser,
if (!parser)
return NULL;
- startElementHandler = oldStartElementHandler;
- endElementHandler = oldEndElementHandler;
- characterDataHandler = oldCharacterDataHandler;
- processingInstructionHandler = oldProcessingInstructionHandler;
- commentHandler = oldCommentHandler;
- startCdataSectionHandler = oldStartCdataSectionHandler;
- endCdataSectionHandler = oldEndCdataSectionHandler;
- defaultHandler = oldDefaultHandler;
- unparsedEntityDeclHandler = oldUnparsedEntityDeclHandler;
- notationDeclHandler = oldNotationDeclHandler;
- startNamespaceDeclHandler = oldStartNamespaceDeclHandler;
- endNamespaceDeclHandler = oldEndNamespaceDeclHandler;
- notStandaloneHandler = oldNotStandaloneHandler;
- externalEntityRefHandler = oldExternalEntityRefHandler;
- skippedEntityHandler = oldSkippedEntityHandler;
- unknownEncodingHandler = oldUnknownEncodingHandler;
- elementDeclHandler = oldElementDeclHandler;
- attlistDeclHandler = oldAttlistDeclHandler;
- entityDeclHandler = oldEntityDeclHandler;
- xmlDeclHandler = oldXmlDeclHandler;
- declElementType = oldDeclElementType;
- userData = oldUserData;
+ parser->m_startElementHandler = oldStartElementHandler;
+ parser->m_endElementHandler = oldEndElementHandler;
+ parser->m_characterDataHandler = oldCharacterDataHandler;
+ parser->m_processingInstructionHandler = oldProcessingInstructionHandler;
+ parser->m_commentHandler = oldCommentHandler;
+ parser->m_startCdataSectionHandler = oldStartCdataSectionHandler;
+ parser->m_endCdataSectionHandler = oldEndCdataSectionHandler;
+ parser->m_defaultHandler = oldDefaultHandler;
+ parser->m_unparsedEntityDeclHandler = oldUnparsedEntityDeclHandler;
+ parser->m_notationDeclHandler = oldNotationDeclHandler;
+ parser->m_startNamespaceDeclHandler = oldStartNamespaceDeclHandler;
+ parser->m_endNamespaceDeclHandler = oldEndNamespaceDeclHandler;
+ parser->m_notStandaloneHandler = oldNotStandaloneHandler;
+ parser->m_externalEntityRefHandler = oldExternalEntityRefHandler;
+ parser->m_skippedEntityHandler = oldSkippedEntityHandler;
+ parser->m_unknownEncodingHandler = oldUnknownEncodingHandler;
+ parser->m_elementDeclHandler = oldElementDeclHandler;
+ parser->m_attlistDeclHandler = oldAttlistDeclHandler;
+ parser->m_entityDeclHandler = oldEntityDeclHandler;
+ parser->m_xmlDeclHandler = oldXmlDeclHandler;
+ parser->m_declElementType = oldDeclElementType;
+ parser->m_userData = oldUserData;
if (oldUserData == oldHandlerArg)
- handlerArg = userData;
+ parser->m_handlerArg = parser->m_userData;
else
- handlerArg = parser;
+ parser->m_handlerArg = parser;
if (oldExternalEntityRefHandlerArg != oldParser)
- externalEntityRefHandlerArg = oldExternalEntityRefHandlerArg;
- defaultExpandInternalEntities = oldDefaultExpandInternalEntities;
- ns_triplets = oldns_triplets;
- hash_secret_salt = oldhash_secret_salt;
- parentParser = oldParser;
+ parser->m_externalEntityRefHandlerArg = oldExternalEntityRefHandlerArg;
+ parser->m_defaultExpandInternalEntities = oldDefaultExpandInternalEntities;
+ parser->m_ns_triplets = oldns_triplets;
+ parser->m_hash_secret_salt = oldhash_secret_salt;
+ parser->m_parentParser = oldParser;
#ifdef XML_DTD
- paramEntityParsing = oldParamEntityParsing;
- prologState.inEntityValue = oldInEntityValue;
+ parser->m_paramEntityParsing = oldParamEntityParsing;
+ parser->m_prologState.inEntityValue = oldInEntityValue;
if (context) {
#endif /* XML_DTD */
- if (!dtdCopy(oldParser, _dtd, oldDtd, &parser->m_mem)
+ if (!dtdCopy(oldParser, parser->m_dtd, oldDtd, &parser->m_mem)
|| !setContext(parser, context)) {
XML_ParserFree(parser);
return NULL;
}
- processor = externalEntityInitProcessor;
+ parser->m_processor = externalEntityInitProcessor;
#ifdef XML_DTD
}
else {
- /* The DTD instance referenced by _dtd is shared between the document's
+ /* The DTD instance referenced by parser->m_dtd is shared between the document's
root parser and external PE parsers, therefore one does not need to
call setContext. In addition, one also *must* not call setContext,
because this would overwrite existing prefix->binding pointers in
- _dtd with ones that get destroyed with the external PE parser.
+ parser->m_dtd with ones that get destroyed with the external PE parser.
This would leave those prefixes with dangling pointers.
*/
- isParamEntity = XML_TRUE;
- XmlPrologStateInitExternalEntity(&prologState);
- processor = externalParEntInitProcessor;
+ parser->m_isParamEntity = XML_TRUE;
+ XmlPrologStateInitExternalEntity(&parser->m_prologState);
+ parser->m_processor = externalParEntInitProcessor;
}
#endif /* XML_DTD */
return parser;
@@ -1438,8 +1359,8 @@ destroyBindings(BINDING *bindings, XML_Parser parser)
if (!b)
break;
bindings = b->nextTagBinding;
- FREE(b->uri);
- FREE(b);
+ FREE(parser, b->uri);
+ FREE(parser, b);
}
}
@@ -1450,70 +1371,70 @@ XML_ParserFree(XML_Parser parser)
OPEN_INTERNAL_ENTITY *entityList;
if (parser == NULL)
return;
- /* free tagStack and freeTagList */
- tagList = tagStack;
+ /* free m_tagStack and m_freeTagList */
+ tagList = parser->m_tagStack;
for (;;) {
TAG *p;
if (tagList == NULL) {
- if (freeTagList == NULL)
+ if (parser->m_freeTagList == NULL)
break;
- tagList = freeTagList;
- freeTagList = NULL;
+ tagList = parser->m_freeTagList;
+ parser->m_freeTagList = NULL;
}
p = tagList;
tagList = tagList->parent;
- FREE(p->buf);
+ FREE(parser, p->buf);
destroyBindings(p->bindings, parser);
- FREE(p);
+ FREE(parser, p);
}
- /* free openInternalEntities and freeInternalEntities */
- entityList = openInternalEntities;
+ /* free m_openInternalEntities and m_freeInternalEntities */
+ entityList = parser->m_openInternalEntities;
for (;;) {
OPEN_INTERNAL_ENTITY *openEntity;
if (entityList == NULL) {
- if (freeInternalEntities == NULL)
+ if (parser->m_freeInternalEntities == NULL)
break;
- entityList = freeInternalEntities;
- freeInternalEntities = NULL;
+ entityList = parser->m_freeInternalEntities;
+ parser->m_freeInternalEntities = NULL;
}
openEntity = entityList;
entityList = entityList->next;
- FREE(openEntity);
+ FREE(parser, openEntity);
}
- destroyBindings(freeBindingList, parser);
- destroyBindings(inheritedBindings, parser);
- poolDestroy(&tempPool);
- poolDestroy(&temp2Pool);
- FREE((void *)protocolEncodingName);
+ destroyBindings(parser->m_freeBindingList, parser);
+ destroyBindings(parser->m_inheritedBindings, parser);
+ poolDestroy(&parser->m_tempPool);
+ poolDestroy(&parser->m_temp2Pool);
+ FREE(parser, (void *)parser->m_protocolEncodingName);
#ifdef XML_DTD
/* external parameter entity parsers share the DTD structure
parser->m_dtd with the root parser, so we must not destroy it
*/
- if (!isParamEntity && _dtd)
+ if (!parser->m_isParamEntity && parser->m_dtd)
#else
- if (_dtd)
+ if (parser->m_dtd)
#endif /* XML_DTD */
- dtdDestroy(_dtd, (XML_Bool)!parentParser, &parser->m_mem);
- FREE((void *)atts);
+ dtdDestroy(parser->m_dtd, (XML_Bool)!parser->m_parentParser, &parser->m_mem);
+ FREE(parser, (void *)parser->m_atts);
#ifdef XML_ATTR_INFO
- FREE((void *)attInfo);
+ FREE(parser, (void *)parser->m_attInfo);
#endif
- FREE(groupConnector);
- FREE(buffer);
- FREE(dataBuf);
- FREE(nsAtts);
- FREE(unknownEncodingMem);
- if (unknownEncodingRelease)
- unknownEncodingRelease(unknownEncodingData);
- FREE(parser);
+ FREE(parser, parser->m_groupConnector);
+ FREE(parser, parser->m_buffer);
+ FREE(parser, parser->m_dataBuf);
+ FREE(parser, parser->m_nsAtts);
+ FREE(parser, parser->m_unknownEncodingMem);
+ if (parser->m_unknownEncodingRelease)
+ parser->m_unknownEncodingRelease(parser->m_unknownEncodingData);
+ FREE(parser, parser);
}
void XMLCALL
XML_UseParserAsHandlerArg(XML_Parser parser)
{
if (parser != NULL)
- handlerArg = parser;
+ parser->m_handlerArg = parser;
}
enum XML_Error XMLCALL
@@ -1523,9 +1444,9 @@ XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD)
return XML_ERROR_INVALID_ARGUMENT;
#ifdef XML_DTD
/* block after XML_Parse()/XML_ParseBuffer() has been called */
- if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED)
+ if (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED)
return XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING;
- useForeignDTD = useDTD;
+ parser->m_useForeignDTD = useDTD;
return XML_ERROR_NONE;
#else
return XML_ERROR_FEATURE_REQUIRES_XML_DTD;
@@ -1538,9 +1459,9 @@ XML_SetReturnNSTriplet(XML_Parser parser, int do_nst)
if (parser == NULL)
return;
/* block after XML_Parse()/XML_ParseBuffer() has been called */
- if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED)
+ if (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED)
return;
- ns_triplets = do_nst ? XML_TRUE : XML_FALSE;
+ parser->m_ns_triplets = do_nst ? XML_TRUE : XML_FALSE;
}
void XMLCALL
@@ -1548,10 +1469,10 @@ XML_SetUserData(XML_Parser parser, void *p)
{
if (parser == NULL)
return;
- if (handlerArg == userData)
- handlerArg = userData = p;
+ if (parser->m_handlerArg == parser->m_userData)
+ parser->m_handlerArg = parser->m_userData = p;
else
- userData = p;
+ parser->m_userData = p;
}
enum XML_Status XMLCALL
@@ -1560,13 +1481,13 @@ XML_SetBase(XML_Parser parser, const XML_Char *p)
if (parser == NULL)
return XML_STATUS_ERROR;
if (p) {
- p = poolCopyString(&_dtd->pool, p);
+ p = poolCopyString(&parser->m_dtd->pool, p);
if (!p)
return XML_STATUS_ERROR;
- curBase = p;
+ parser->m_curBase = p;
}
else
- curBase = NULL;
+ parser->m_curBase = NULL;
return XML_STATUS_OK;
}
@@ -1575,7 +1496,7 @@ XML_GetBase(XML_Parser parser)
{
if (parser == NULL)
return NULL;
- return curBase;
+ return parser->m_curBase;
}
int XMLCALL
@@ -1583,7 +1504,7 @@ XML_GetSpecifiedAttributeCount(XML_Parser parser)
{
if (parser == NULL)
return -1;
- return nSpecifiedAtts;
+ return parser->m_nSpecifiedAtts;
}
int XMLCALL
@@ -1591,7 +1512,7 @@ XML_GetIdAttributeIndex(XML_Parser parser)
{
if (parser == NULL)
return -1;
- return idAttIndex;
+ return parser->m_idAttIndex;
}
#ifdef XML_ATTR_INFO
@@ -1600,7 +1521,7 @@ XML_GetAttributeInfo(XML_Parser parser)
{
if (parser == NULL)
return NULL;
- return attInfo;
+ return parser->m_attInfo;
}
#endif
@@ -1611,22 +1532,22 @@ XML_SetElementHandler(XML_Parser parser,
{
if (parser == NULL)
return;
- startElementHandler = start;
- endElementHandler = end;
+ parser->m_startElementHandler = start;
+ parser->m_endElementHandler = end;
}
void XMLCALL
XML_SetStartElementHandler(XML_Parser parser,
XML_StartElementHandler start) {
if (parser != NULL)
- startElementHandler = start;
+ parser->m_startElementHandler = start;
}
void XMLCALL
XML_SetEndElementHandler(XML_Parser parser,
XML_EndElementHandler end) {
if (parser != NULL)
- endElementHandler = end;
+ parser->m_endElementHandler = end;
}
void XMLCALL
@@ -1634,7 +1555,7 @@ XML_SetCharacterDataHandler(XML_Parser parser,
XML_CharacterDataHandler handler)
{
if (parser != NULL)
- characterDataHandler = handler;
+ parser->m_characterDataHandler = handler;
}
void XMLCALL
@@ -1642,7 +1563,7 @@ XML_SetProcessingInstructionHandler(XML_Parser parser,
XML_ProcessingInstructionHandler handler)
{
if (parser != NULL)
- processingInstructionHandler = handler;
+ parser->m_processingInstructionHandler = handler;
}
void XMLCALL
@@ -1650,7 +1571,7 @@ XML_SetCommentHandler(XML_Parser parser,
XML_CommentHandler handler)
{
if (parser != NULL)
- commentHandler = handler;
+ parser->m_commentHandler = handler;
}
void XMLCALL
@@ -1660,22 +1581,22 @@ XML_SetCdataSectionHandler(XML_Parser parser,
{
if (parser == NULL)
return;
- startCdataSectionHandler = start;
- endCdataSectionHandler = end;
+ parser->m_startCdataSectionHandler = start;
+ parser->m_endCdataSectionHandler = end;
}
void XMLCALL
XML_SetStartCdataSectionHandler(XML_Parser parser,
XML_StartCdataSectionHandler start) {
if (parser != NULL)
- startCdataSectionHandler = start;
+ parser->m_startCdataSectionHandler = start;
}
void XMLCALL
XML_SetEndCdataSectionHandler(XML_Parser parser,
XML_EndCdataSectionHandler end) {
if (parser != NULL)
- endCdataSectionHandler = end;
+ parser->m_endCdataSectionHandler = end;
}
void XMLCALL
@@ -1684,8 +1605,8 @@ XML_SetDefaultHandler(XML_Parser parser,
{
if (parser == NULL)
return;
- defaultHandler = handler;
- defaultExpandInternalEntities = XML_FALSE;
+ parser->m_defaultHandler = handler;
+ parser->m_defaultExpandInternalEntities = XML_FALSE;
}
void XMLCALL
@@ -1694,8 +1615,8 @@ XML_SetDefaultHandlerExpand(XML_Parser parser,
{
if (parser == NULL)
return;
- defaultHandler = handler;
- defaultExpandInternalEntities = XML_TRUE;
+ parser->m_defaultHandler = handler;
+ parser->m_defaultExpandInternalEntities = XML_TRUE;
}
void XMLCALL
@@ -1705,22 +1626,22 @@ XML_SetDoctypeDeclHandler(XML_Parser parser,
{
if (parser == NULL)
return;
- startDoctypeDeclHandler = start;
- endDoctypeDeclHandler = end;
+ parser->m_startDoctypeDeclHandler = start;
+ parser->m_endDoctypeDeclHandler = end;
}
void XMLCALL
XML_SetStartDoctypeDeclHandler(XML_Parser parser,
XML_StartDoctypeDeclHandler start) {
if (parser != NULL)
- startDoctypeDeclHandler = start;
+ parser->m_startDoctypeDeclHandler = start;
}
void XMLCALL
XML_SetEndDoctypeDeclHandler(XML_Parser parser,
XML_EndDoctypeDeclHandler end) {
if (parser != NULL)
- endDoctypeDeclHandler = end;
+ parser->m_endDoctypeDeclHandler = end;
}
void XMLCALL
@@ -1728,7 +1649,7 @@ XML_SetUnparsedEntityDeclHandler(XML_Parser parser,
XML_UnparsedEntityDeclHandler handler)
{
if (parser != NULL)
- unparsedEntityDeclHandler = handler;
+ parser->m_unparsedEntityDeclHandler = handler;
}
void XMLCALL
@@ -1736,7 +1657,7 @@ XML_SetNotationDeclHandler(XML_Parser parser,
XML_NotationDeclHandler handler)
{
if (parser != NULL)
- notationDeclHandler = handler;
+ parser->m_notationDeclHandler = handler;
}
void XMLCALL
@@ -1746,22 +1667,22 @@ XML_SetNamespaceDeclHandler(XML_Parser parser,
{
if (parser == NULL)
return;
- startNamespaceDeclHandler = start;
- endNamespaceDeclHandler = end;
+ parser->m_startNamespaceDeclHandler = start;
+ parser->m_endNamespaceDeclHandler = end;
}
void XMLCALL
XML_SetStartNamespaceDeclHandler(XML_Parser parser,
XML_StartNamespaceDeclHandler start) {
if (parser != NULL)
- startNamespaceDeclHandler = start;
+ parser->m_startNamespaceDeclHandler = start;
}
void XMLCALL
XML_SetEndNamespaceDeclHandler(XML_Parser parser,
XML_EndNamespaceDeclHandler end) {
if (parser != NULL)
- endNamespaceDeclHandler = end;
+ parser->m_endNamespaceDeclHandler = end;
}
void XMLCALL
@@ -1769,7 +1690,7 @@ XML_SetNotStandaloneHandler(XML_Parser parser,
XML_NotStandaloneHandler handler)
{
if (parser != NULL)
- notStandaloneHandler = handler;
+ parser->m_notStandaloneHandler = handler;
}
void XMLCALL
@@ -1777,7 +1698,7 @@ XML_SetExternalEntityRefHandler(XML_Parser parser,
XML_ExternalEntityRefHandler handler)
{
if (parser != NULL)
- externalEntityRefHandler = handler;
+ parser->m_externalEntityRefHandler = handler;
}
void XMLCALL
@@ -1786,9 +1707,9 @@ XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg)
if (parser == NULL)
return;
if (arg)
- externalEntityRefHandlerArg = (XML_Parser)arg;
+ parser->m_externalEntityRefHandlerArg = (XML_Parser)arg;
else
- externalEntityRefHandlerArg = parser;
+ parser->m_externalEntityRefHandlerArg = parser;
}
void XMLCALL
@@ -1796,7 +1717,7 @@ XML_SetSkippedEntityHandler(XML_Parser parser,
XML_SkippedEntityHandler handler)
{
if (parser != NULL)
- skippedEntityHandler = handler;
+ parser->m_skippedEntityHandler = handler;
}
void XMLCALL
@@ -1806,8 +1727,8 @@ XML_SetUnknownEncodingHandler(XML_Parser parser,
{
if (parser == NULL)
return;
- unknownEncodingHandler = handler;
- unknownEncodingHandlerData = data;
+ parser->m_unknownEncodingHandler = handler;
+ parser->m_unknownEncodingHandlerData = data;
}
void XMLCALL
@@ -1815,7 +1736,7 @@ XML_SetElementDeclHandler(XML_Parser parser,
XML_ElementDeclHandler eldecl)
{
if (parser != NULL)
- elementDeclHandler = eldecl;
+ parser->m_elementDeclHandler = eldecl;
}
void XMLCALL
@@ -1823,7 +1744,7 @@ XML_SetAttlistDeclHandler(XML_Parser parser,
XML_AttlistDeclHandler attdecl)
{
if (parser != NULL)
- attlistDeclHandler = attdecl;
+ parser->m_attlistDeclHandler = attdecl;
}
void XMLCALL
@@ -1831,14 +1752,14 @@ XML_SetEntityDeclHandler(XML_Parser parser,
XML_EntityDeclHandler handler)
{
if (parser != NULL)
- entityDeclHandler = handler;
+ parser->m_entityDeclHandler = handler;
}
void XMLCALL
XML_SetXmlDeclHandler(XML_Parser parser,
XML_XmlDeclHandler handler) {
if (parser != NULL)
- xmlDeclHandler = handler;
+ parser->m_xmlDeclHandler = handler;
}
int XMLCALL
@@ -1848,10 +1769,10 @@ XML_SetParamEntityParsing(XML_Parser parser,
if (parser == NULL)
return 0;
/* block after XML_Parse()/XML_ParseBuffer() has been called */
- if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED)
+ if (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED)
return 0;
#ifdef XML_DTD
- paramEntityParsing = peParsing;
+ parser->m_paramEntityParsing = peParsing;
return 1;
#else
return peParsing == XML_PARAM_ENTITY_PARSING_NEVER;
@@ -1867,9 +1788,9 @@ XML_SetHashSalt(XML_Parser parser,
if (parser->m_parentParser)
return XML_SetHashSalt(parser->m_parentParser, hash_salt);
/* block after XML_Parse()/XML_ParseBuffer() has been called */
- if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED)
+ if (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED)
return 0;
- hash_secret_salt = hash_salt;
+ parser->m_hash_secret_salt = hash_salt;
return 1;
}
@@ -1881,37 +1802,38 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal)
parser->m_errorCode = XML_ERROR_INVALID_ARGUMENT;
return XML_STATUS_ERROR;
}
- switch (ps_parsing) {
+ switch (parser->m_parsingStatus.parsing) {
case XML_SUSPENDED:
- errorCode = XML_ERROR_SUSPENDED;
+ parser->m_errorCode = XML_ERROR_SUSPENDED;
return XML_STATUS_ERROR;
case XML_FINISHED:
- errorCode = XML_ERROR_FINISHED;
+ parser->m_errorCode = XML_ERROR_FINISHED;
return XML_STATUS_ERROR;
case XML_INITIALIZED:
- if (parentParser == NULL && !startParsing(parser)) {
- errorCode = XML_ERROR_NO_MEMORY;
+ if (parser->m_parentParser == NULL && !startParsing(parser)) {
+ parser->m_errorCode = XML_ERROR_NO_MEMORY;
return XML_STATUS_ERROR;
}
+ /* fall through */
default:
- ps_parsing = XML_PARSING;
+ parser->m_parsingStatus.parsing = XML_PARSING;
}
if (len == 0) {
- ps_finalBuffer = (XML_Bool)isFinal;
+ parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal;
if (!isFinal)
return XML_STATUS_OK;
- positionPtr = bufferPtr;
- parseEndPtr = bufferEnd;
+ parser->m_positionPtr = parser->m_bufferPtr;
+ parser->m_parseEndPtr = parser->m_bufferEnd;
/* If data are left over from last buffer, and we now know that these
data are the final chunk of input, then we have to check them again
to detect errors based on that fact.
*/
- errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr);
+ parser->m_errorCode = parser->m_processor(parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr);
- if (errorCode == XML_ERROR_NONE) {
- switch (ps_parsing) {
+ if (parser->m_errorCode == XML_ERROR_NONE) {
+ switch (parser->m_parsingStatus.parsing) {
case XML_SUSPENDED:
/* It is hard to be certain, but it seems that this case
* cannot occur. This code is cleaning up a previous parse
@@ -1925,54 +1847,54 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal)
*
* LCOV_EXCL_START
*/
- XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position);
- positionPtr = bufferPtr;
+ XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, parser->m_bufferPtr, &parser->m_position);
+ parser->m_positionPtr = parser->m_bufferPtr;
return XML_STATUS_SUSPENDED;
/* LCOV_EXCL_STOP */
case XML_INITIALIZED:
case XML_PARSING:
- ps_parsing = XML_FINISHED;
+ parser->m_parsingStatus.parsing = XML_FINISHED;
/* fall through */
default:
return XML_STATUS_OK;
}
}
- eventEndPtr = eventPtr;
- processor = errorProcessor;
+ parser->m_eventEndPtr = parser->m_eventPtr;
+ parser->m_processor = errorProcessor;
return XML_STATUS_ERROR;
}
#ifndef XML_CONTEXT_BYTES
- else if (bufferPtr == bufferEnd) {
+ else if (parser->m_bufferPtr == parser->m_bufferEnd) {
const char *end;
int nLeftOver;
enum XML_Status result;
/* Detect overflow (a+b > MAX <==> b > MAX-a) */
- if (len > ((XML_Size)-1) / 2 - parseEndByteIndex) {
- errorCode = XML_ERROR_NO_MEMORY;
- eventPtr = eventEndPtr = NULL;
- processor = errorProcessor;
+ if (len > ((XML_Size)-1) / 2 - parser->m_parseEndByteIndex) {
+ parser->m_errorCode = XML_ERROR_NO_MEMORY;
+ parser->m_eventPtr = parser->m_eventEndPtr = NULL;
+ parser->m_processor = errorProcessor;
return XML_STATUS_ERROR;
}
- parseEndByteIndex += len;
- positionPtr = s;
- ps_finalBuffer = (XML_Bool)isFinal;
+ parser->m_parseEndByteIndex += len;
+ parser->m_positionPtr = s;
+ parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal;
- errorCode = processor(parser, s, parseEndPtr = s + len, &end);
+ parser->m_errorCode = parser->m_processor(parser, s, parser->m_parseEndPtr = s + len, &end);
- if (errorCode != XML_ERROR_NONE) {
- eventEndPtr = eventPtr;
- processor = errorProcessor;
+ if (parser->m_errorCode != XML_ERROR_NONE) {
+ parser->m_eventEndPtr = parser->m_eventPtr;
+ parser->m_processor = errorProcessor;
return XML_STATUS_ERROR;
}
else {
- switch (ps_parsing) {
+ switch (parser->m_parsingStatus.parsing) {
case XML_SUSPENDED:
result = XML_STATUS_SUSPENDED;
break;
case XML_INITIALIZED:
case XML_PARSING:
if (isFinal) {
- ps_parsing = XML_FINISHED;
+ parser->m_parsingStatus.parsing = XML_FINISHED;
return XML_STATUS_OK;
}
/* fall through */
@@ -1981,35 +1903,33 @@ XML_Parse(XML_Parser parser, const char *s, int len, int isFinal)
}
}
- XmlUpdatePosition(encoding, positionPtr, end, &position);
+ XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, end, &parser->m_position);
nLeftOver = s + len - end;
if (nLeftOver) {
- if (buffer == NULL || nLeftOver > bufferLim - buffer) {
+ if (parser->m_buffer == NULL || nLeftOver > parser->m_bufferLim - parser->m_buffer) {
/* avoid _signed_ integer overflow */
char *temp = NULL;
const int bytesToAllocate = (int)((unsigned)len * 2U);
if (bytesToAllocate > 0) {
- temp = (buffer == NULL
- ? (char *)MALLOC(bytesToAllocate)
- : (char *)REALLOC(buffer, bytesToAllocate));
+ temp = (char *)REALLOC(parser, parser->m_buffer, bytesToAllocate);
}
if (temp == NULL) {
- errorCode = XML_ERROR_NO_MEMORY;
- eventPtr = eventEndPtr = NULL;
- processor = errorProcessor;
+ parser->m_errorCode = XML_ERROR_NO_MEMORY;
+ parser->m_eventPtr = parser->m_eventEndPtr = NULL;
+ parser->m_processor = errorProcessor;
return XML_STATUS_ERROR;
}
- buffer = temp;
- bufferLim = buffer + bytesToAllocate;
+ parser->m_buffer = temp;
+ parser->m_bufferLim = parser->m_buffer + bytesToAllocate;
}
- memcpy(buffer, end, nLeftOver);
+ memcpy(parser->m_buffer, end, nLeftOver);
}
- bufferPtr = buffer;
- bufferEnd = buffer + nLeftOver;
- positionPtr = bufferPtr;
- parseEndPtr = bufferEnd;
- eventPtr = bufferPtr;
- eventEndPtr = bufferPtr;
+ parser->m_bufferPtr = parser->m_buffer;
+ parser->m_bufferEnd = parser->m_buffer + nLeftOver;
+ parser->m_positionPtr = parser->m_bufferPtr;
+ parser->m_parseEndPtr = parser->m_bufferEnd;
+ parser->m_eventPtr = parser->m_bufferPtr;
+ parser->m_eventEndPtr = parser->m_bufferPtr;
return result;
}
#endif /* not defined XML_CONTEXT_BYTES */
@@ -2032,53 +1952,54 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal)
if (parser == NULL)
return XML_STATUS_ERROR;
- switch (ps_parsing) {
+ switch (parser->m_parsingStatus.parsing) {
case XML_SUSPENDED:
- errorCode = XML_ERROR_SUSPENDED;
+ parser->m_errorCode = XML_ERROR_SUSPENDED;
return XML_STATUS_ERROR;
case XML_FINISHED:
- errorCode = XML_ERROR_FINISHED;
+ parser->m_errorCode = XML_ERROR_FINISHED;
return XML_STATUS_ERROR;
case XML_INITIALIZED:
- if (parentParser == NULL && !startParsing(parser)) {
- errorCode = XML_ERROR_NO_MEMORY;
+ if (parser->m_parentParser == NULL && !startParsing(parser)) {
+ parser->m_errorCode = XML_ERROR_NO_MEMORY;
return XML_STATUS_ERROR;
}
+ /* fall through */
default:
- ps_parsing = XML_PARSING;
+ parser->m_parsingStatus.parsing = XML_PARSING;
}
- start = bufferPtr;
- positionPtr = start;
- bufferEnd += len;
- parseEndPtr = bufferEnd;
- parseEndByteIndex += len;
- ps_finalBuffer = (XML_Bool)isFinal;
+ start = parser->m_bufferPtr;
+ parser->m_positionPtr = start;
+ parser->m_bufferEnd += len;
+ parser->m_parseEndPtr = parser->m_bufferEnd;
+ parser->m_parseEndByteIndex += len;
+ parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal;
- errorCode = processor(parser, start, parseEndPtr, &bufferPtr);
+ parser->m_errorCode = parser->m_processor(parser, start, parser->m_parseEndPtr, &parser->m_bufferPtr);
- if (errorCode != XML_ERROR_NONE) {
- eventEndPtr = eventPtr;
- processor = errorProcessor;
+ if (parser->m_errorCode != XML_ERROR_NONE) {
+ parser->m_eventEndPtr = parser->m_eventPtr;
+ parser->m_processor = errorProcessor;
return XML_STATUS_ERROR;
}
else {
- switch (ps_parsing) {
+ switch (parser->m_parsingStatus.parsing) {
case XML_SUSPENDED:
result = XML_STATUS_SUSPENDED;
break;
case XML_INITIALIZED:
case XML_PARSING:
if (isFinal) {
- ps_parsing = XML_FINISHED;
+ parser->m_parsingStatus.parsing = XML_FINISHED;
return result;
}
default: ; /* should not happen */
}
}
- XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position);
- positionPtr = bufferPtr;
+ XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, parser->m_bufferPtr, &parser->m_position);
+ parser->m_positionPtr = parser->m_bufferPtr;
return result;
}
@@ -2088,52 +2009,59 @@ XML_GetBuffer(XML_Parser parser, int len)
if (parser == NULL)
return NULL;
if (len < 0) {
- errorCode = XML_ERROR_NO_MEMORY;
+ parser->m_errorCode = XML_ERROR_NO_MEMORY;
return NULL;
}
- switch (ps_parsing) {
+ switch (parser->m_parsingStatus.parsing) {
case XML_SUSPENDED:
- errorCode = XML_ERROR_SUSPENDED;
+ parser->m_errorCode = XML_ERROR_SUSPENDED;
return NULL;
case XML_FINISHED:
- errorCode = XML_ERROR_FINISHED;
+ parser->m_errorCode = XML_ERROR_FINISHED;
return NULL;
default: ;
}
- if (len > bufferLim - bufferEnd) {
+ if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)) {
#ifdef XML_CONTEXT_BYTES
int keep;
#endif /* defined XML_CONTEXT_BYTES */
/* Do not invoke signed arithmetic overflow: */
- int neededSize = (int) ((unsigned)len + (unsigned)(bufferEnd - bufferPtr));
+ int neededSize = (int) ((unsigned)len +
+ (unsigned)EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd,
+ parser->m_bufferPtr));
if (neededSize < 0) {
- errorCode = XML_ERROR_NO_MEMORY;
+ parser->m_errorCode = XML_ERROR_NO_MEMORY;
return NULL;
}
#ifdef XML_CONTEXT_BYTES
- keep = (int)(bufferPtr - buffer);
+ keep = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer);
if (keep > XML_CONTEXT_BYTES)
keep = XML_CONTEXT_BYTES;
neededSize += keep;
#endif /* defined XML_CONTEXT_BYTES */
- if (neededSize <= bufferLim - buffer) {
+ if (neededSize <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) {
#ifdef XML_CONTEXT_BYTES
- if (keep < bufferPtr - buffer) {
- int offset = (int)(bufferPtr - buffer) - keep;
- memmove(buffer, &buffer[offset], bufferEnd - bufferPtr + keep);
- bufferEnd -= offset;
- bufferPtr -= offset;
+ if (keep < EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer)) {
+ int offset = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer) - keep;
+ /* The buffer pointers cannot be NULL here; we have at least some bytes in the buffer */
+ memmove(parser->m_buffer, &parser->m_buffer[offset], parser->m_bufferEnd - parser->m_bufferPtr + keep);
+ parser->m_bufferEnd -= offset;
+ parser->m_bufferPtr -= offset;
}
#else
- memmove(buffer, bufferPtr, bufferEnd - bufferPtr);
- bufferEnd = buffer + (bufferEnd - bufferPtr);
- bufferPtr = buffer;
+ if (parser->m_buffer && parser->m_bufferPtr) {
+ memmove(parser->m_buffer, parser->m_bufferPtr,
+ EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr));
+ parser->m_bufferEnd = parser->m_buffer +
+ EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr);
+ parser->m_bufferPtr = parser->m_buffer;
+ }
#endif /* not defined XML_CONTEXT_BYTES */
}
else {
char *newBuf;
- int bufferSize = (int)(bufferLim - bufferPtr);
+ int bufferSize = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferPtr);
if (bufferSize == 0)
bufferSize = INIT_BUFFER_SIZE;
do {
@@ -2141,43 +2069,52 @@ XML_GetBuffer(XML_Parser parser, int len)
bufferSize = (int) (2U * (unsigned) bufferSize);
} while (bufferSize < neededSize && bufferSize > 0);
if (bufferSize <= 0) {
- errorCode = XML_ERROR_NO_MEMORY;
+ parser->m_errorCode = XML_ERROR_NO_MEMORY;
return NULL;
}
- newBuf = (char *)MALLOC(bufferSize);
+ newBuf = (char *)MALLOC(parser, bufferSize);
if (newBuf == 0) {
- errorCode = XML_ERROR_NO_MEMORY;
+ parser->m_errorCode = XML_ERROR_NO_MEMORY;
return NULL;
}
- bufferLim = newBuf + bufferSize;
+ parser->m_bufferLim = newBuf + bufferSize;
#ifdef XML_CONTEXT_BYTES
- if (bufferPtr) {
- int keep = (int)(bufferPtr - buffer);
+ if (parser->m_bufferPtr) {
+ int keep = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer);
if (keep > XML_CONTEXT_BYTES)
keep = XML_CONTEXT_BYTES;
- memcpy(newBuf, &bufferPtr[-keep], bufferEnd - bufferPtr + keep);
- FREE(buffer);
- buffer = newBuf;
- bufferEnd = buffer + (bufferEnd - bufferPtr) + keep;
- bufferPtr = buffer + keep;
+ memcpy(newBuf, &parser->m_bufferPtr[-keep],
+ EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr) + keep);
+ FREE(parser, parser->m_buffer);
+ parser->m_buffer = newBuf;
+ parser->m_bufferEnd = parser->m_buffer +
+ EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr) + keep;
+ parser->m_bufferPtr = parser->m_buffer + keep;
}
else {
- bufferEnd = newBuf + (bufferEnd - bufferPtr);
- bufferPtr = buffer = newBuf;
+ /* This must be a brand new buffer with no data in it yet */
+ parser->m_bufferEnd = newBuf;
+ parser->m_bufferPtr = parser->m_buffer = newBuf;
}
#else
- if (bufferPtr) {
- memcpy(newBuf, bufferPtr, bufferEnd - bufferPtr);
- FREE(buffer);
+ if (parser->m_bufferPtr) {
+ memcpy(newBuf, parser->m_bufferPtr,
+ EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr));
+ FREE(parser, parser->m_buffer);
+ parser->m_bufferEnd = newBuf +
+ EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr);
+ }
+ else {
+ /* This must be a brand new buffer with no data in it yet */
+ parser->m_bufferEnd = newBuf;
}
- bufferEnd = newBuf + (bufferEnd - bufferPtr);
- bufferPtr = buffer = newBuf;
+ parser->m_bufferPtr = parser->m_buffer = newBuf;
#endif /* not defined XML_CONTEXT_BYTES */
}
- eventPtr = eventEndPtr = NULL;
- positionPtr = NULL;
+ parser->m_eventPtr = parser->m_eventEndPtr = NULL;
+ parser->m_positionPtr = NULL;
}
- return bufferEnd;
+ return parser->m_bufferEnd;
}
enum XML_Status XMLCALL
@@ -2185,29 +2122,29 @@ XML_StopParser(XML_Parser parser, XML_Bool resumable)
{
if (parser == NULL)
return XML_STATUS_ERROR;
- switch (ps_parsing) {
+ switch (parser->m_parsingStatus.parsing) {
case XML_SUSPENDED:
if (resumable) {
- errorCode = XML_ERROR_SUSPENDED;
+ parser->m_errorCode = XML_ERROR_SUSPENDED;
return XML_STATUS_ERROR;
}
- ps_parsing = XML_FINISHED;
+ parser->m_parsingStatus.parsing = XML_FINISHED;
break;
case XML_FINISHED:
- errorCode = XML_ERROR_FINISHED;
+ parser->m_errorCode = XML_ERROR_FINISHED;
return XML_STATUS_ERROR;
default:
if (resumable) {
#ifdef XML_DTD
- if (isParamEntity) {
- errorCode = XML_ERROR_SUSPEND_PE;
+ if (parser->m_isParamEntity) {
+ parser->m_errorCode = XML_ERROR_SUSPEND_PE;
return XML_STATUS_ERROR;
}
#endif
- ps_parsing = XML_SUSPENDED;
+ parser->m_parsingStatus.parsing = XML_SUSPENDED;
}
else
- ps_parsing = XML_FINISHED;
+ parser->m_parsingStatus.parsing = XML_FINISHED;
}
return XML_STATUS_OK;
}
@@ -2219,36 +2156,36 @@ XML_ResumeParser(XML_Parser parser)
if (parser == NULL)
return XML_STATUS_ERROR;
- if (ps_parsing != XML_SUSPENDED) {
- errorCode = XML_ERROR_NOT_SUSPENDED;
+ if (parser->m_parsingStatus.parsing != XML_SUSPENDED) {
+ parser->m_errorCode = XML_ERROR_NOT_SUSPENDED;
return XML_STATUS_ERROR;
}
- ps_parsing = XML_PARSING;
+ parser->m_parsingStatus.parsing = XML_PARSING;
- errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr);
+ parser->m_errorCode = parser->m_processor(parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr);
- if (errorCode != XML_ERROR_NONE) {
- eventEndPtr = eventPtr;
- processor = errorProcessor;
+ if (parser->m_errorCode != XML_ERROR_NONE) {
+ parser->m_eventEndPtr = parser->m_eventPtr;
+ parser->m_processor = errorProcessor;
return XML_STATUS_ERROR;
}
else {
- switch (ps_parsing) {
+ switch (parser->m_parsingStatus.parsing) {
case XML_SUSPENDED:
result = XML_STATUS_SUSPENDED;
break;
case XML_INITIALIZED:
case XML_PARSING:
- if (ps_finalBuffer) {
- ps_parsing = XML_FINISHED;
+ if (parser->m_parsingStatus.finalBuffer) {
+ parser->m_parsingStatus.parsing = XML_FINISHED;
return result;
}
default: ;
}
}
- XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position);
- positionPtr = bufferPtr;
+ XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, parser->m_bufferPtr, &parser->m_position);
+ parser->m_positionPtr = parser->m_bufferPtr;
return result;
}
@@ -2266,7 +2203,7 @@ XML_GetErrorCode(XML_Parser parser)
{
if (parser == NULL)
return XML_ERROR_INVALID_ARGUMENT;
- return errorCode;
+ return parser->m_errorCode;
}
XML_Index XMLCALL
@@ -2274,8 +2211,8 @@ XML_GetCurrentByteIndex(XML_Parser parser)
{
if (parser == NULL)
return -1;
- if (eventPtr)
- return (XML_Index)(parseEndByteIndex - (parseEndPtr - eventPtr));
+ if (parser->m_eventPtr)
+ return (XML_Index)(parser->m_parseEndByteIndex - (parser->m_parseEndPtr - parser->m_eventPtr));
return -1;
}
@@ -2284,8 +2221,8 @@ XML_GetCurrentByteCount(XML_Parser parser)
{
if (parser == NULL)
return 0;
- if (eventEndPtr && eventPtr)
- return (int)(eventEndPtr - eventPtr);
+ if (parser->m_eventEndPtr && parser->m_eventPtr)
+ return (int)(parser->m_eventEndPtr - parser->m_eventPtr);
return 0;
}
@@ -2295,12 +2232,12 @@ XML_GetInputContext(XML_Parser parser, int *offset, int *size)
#ifdef XML_CONTEXT_BYTES
if (parser == NULL)
return NULL;
- if (eventPtr && buffer) {
+ if (parser->m_eventPtr && parser->m_buffer) {
if (offset != NULL)
- *offset = (int)(eventPtr - buffer);
+ *offset = (int)(parser->m_eventPtr - parser->m_buffer);
if (size != NULL)
- *size = (int)(bufferEnd - buffer);
- return buffer;
+ *size = (int)(parser->m_bufferEnd - parser->m_buffer);
+ return parser->m_buffer;
}
#else
(void)parser;
@@ -2315,11 +2252,11 @@ XML_GetCurrentLineNumber(XML_Parser parser)
{
if (parser == NULL)
return 0;
- if (eventPtr && eventPtr >= positionPtr) {
- XmlUpdatePosition(encoding, positionPtr, eventPtr, &position);
- positionPtr = eventPtr;
+ if (parser->m_eventPtr && parser->m_eventPtr >= parser->m_positionPtr) {
+ XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, parser->m_eventPtr, &parser->m_position);
+ parser->m_positionPtr = parser->m_eventPtr;
}
- return position.lineNumber + 1;
+ return parser->m_position.lineNumber + 1;
}
XML_Size XMLCALL
@@ -2327,18 +2264,18 @@ XML_GetCurrentColumnNumber(XML_Parser parser)
{
if (parser == NULL)
return 0;
- if (eventPtr && eventPtr >= positionPtr) {
- XmlUpdatePosition(encoding, positionPtr, eventPtr, &position);
- positionPtr = eventPtr;
+ if (parser->m_eventPtr && parser->m_eventPtr >= parser->m_positionPtr) {
+ XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, parser->m_eventPtr, &parser->m_position);
+ parser->m_positionPtr = parser->m_eventPtr;
}
- return position.columnNumber;
+ return parser->m_position.columnNumber;
}
void XMLCALL
XML_FreeContentModel(XML_Parser parser, XML_Content *model)
{
if (parser != NULL)
- FREE(model);
+ FREE(parser, model);
}
void * XMLCALL
@@ -2346,7 +2283,7 @@ XML_MemMalloc(XML_Parser parser, size_t size)
{
if (parser == NULL)
return NULL;
- return MALLOC(size);
+ return MALLOC(parser, size);
}
void * XMLCALL
@@ -2354,14 +2291,14 @@ XML_MemRealloc(XML_Parser parser, void *ptr, size_t size)
{
if (parser == NULL)
return NULL;
- return REALLOC(ptr, size);
+ return REALLOC(parser, ptr, size);
}
void XMLCALL
XML_MemFree(XML_Parser parser, void *ptr)
{
if (parser != NULL)
- FREE(ptr);
+ FREE(parser, ptr);
}
void XMLCALL
@@ -2369,65 +2306,110 @@ XML_DefaultCurrent(XML_Parser parser)
{
if (parser == NULL)
return;
- if (defaultHandler) {
- if (openInternalEntities)
+ if (parser->m_defaultHandler) {
+ if (parser->m_openInternalEntities)
reportDefault(parser,
- internalEncoding,
- openInternalEntities->internalEventPtr,
- openInternalEntities->internalEventEndPtr);
+ parser->m_internalEncoding,
+ parser->m_openInternalEntities->internalEventPtr,
+ parser->m_openInternalEntities->internalEventEndPtr);
else
- reportDefault(parser, encoding, eventPtr, eventEndPtr);
+ reportDefault(parser, parser->m_encoding, parser->m_eventPtr, parser->m_eventEndPtr);
}
}
const XML_LChar * XMLCALL
XML_ErrorString(enum XML_Error code)
{
- static const XML_LChar* const message[] = {
- 0,
- XML_L("out of memory"),
- XML_L("syntax error"),
- XML_L("no element found"),
- XML_L("not well-formed (invalid token)"),
- XML_L("unclosed token"),
- XML_L("partial character"),
- XML_L("mismatched tag"),
- XML_L("duplicate attribute"),
- XML_L("junk after document element"),
- XML_L("illegal parameter entity reference"),
- XML_L("undefined entity"),
- XML_L("recursive entity reference"),
- XML_L("asynchronous entity"),
- XML_L("reference to invalid character number"),
- XML_L("reference to binary entity"),
- XML_L("reference to external entity in attribute"),
- XML_L("XML or text declaration not at start of entity"),
- XML_L("unknown encoding"),
- XML_L("encoding specified in XML declaration is incorrect"),
- XML_L("unclosed CDATA section"),
- XML_L("error in processing external entity reference"),
- XML_L("document is not standalone"),
- XML_L("unexpected parser state - please send a bug report"),
- XML_L("entity declared in parameter entity"),
- XML_L("requested feature requires XML_DTD support in Expat"),
- XML_L("cannot change setting once parsing has begun"),
- XML_L("unbound prefix"),
- XML_L("must not undeclare prefix"),
- XML_L("incomplete markup in parameter entity"),
- XML_L("XML declaration not well-formed"),
- XML_L("text declaration not well-formed"),
- XML_L("illegal character(s) in public id"),
- XML_L("parser suspended"),
- XML_L("parser not suspended"),
- XML_L("parsing aborted"),
- XML_L("parsing finished"),
- XML_L("cannot suspend in external parameter entity"),
- XML_L("reserved prefix (xml) must not be undeclared or bound to another namespace name"),
- XML_L("reserved prefix (xmlns) must not be declared or undeclared"),
- XML_L("prefix must not be bound to one of the reserved namespace names")
- };
- if (code > 0 && code < sizeof(message)/sizeof(message[0]))
- return message[code];
+ switch (code) {
+ case XML_ERROR_NONE:
+ return NULL;
+ case XML_ERROR_NO_MEMORY:
+ return XML_L("out of memory");
+ case XML_ERROR_SYNTAX:
+ return XML_L("syntax error");
+ case XML_ERROR_NO_ELEMENTS:
+ return XML_L("no element found");
+ case XML_ERROR_INVALID_TOKEN:
+ return XML_L("not well-formed (invalid token)");
+ case XML_ERROR_UNCLOSED_TOKEN:
+ return XML_L("unclosed token");
+ case XML_ERROR_PARTIAL_CHAR:
+ return XML_L("partial character");
+ case XML_ERROR_TAG_MISMATCH:
+ return XML_L("mismatched tag");
+ case XML_ERROR_DUPLICATE_ATTRIBUTE:
+ return XML_L("duplicate attribute");
+ case XML_ERROR_JUNK_AFTER_DOC_ELEMENT:
+ return XML_L("junk after document element");
+ case XML_ERROR_PARAM_ENTITY_REF:
+ return XML_L("illegal parameter entity reference");
+ case XML_ERROR_UNDEFINED_ENTITY:
+ return XML_L("undefined entity");
+ case XML_ERROR_RECURSIVE_ENTITY_REF:
+ return XML_L("recursive entity reference");
+ case XML_ERROR_ASYNC_ENTITY:
+ return XML_L("asynchronous entity");
+ case XML_ERROR_BAD_CHAR_REF:
+ return XML_L("reference to invalid character number");
+ case XML_ERROR_BINARY_ENTITY_REF:
+ return XML_L("reference to binary entity");
+ case XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF:
+ return XML_L("reference to external entity in attribute");
+ case XML_ERROR_MISPLACED_XML_PI:
+ return XML_L("XML or text declaration not at start of entity");
+ case XML_ERROR_UNKNOWN_ENCODING:
+ return XML_L("unknown encoding");
+ case XML_ERROR_INCORRECT_ENCODING:
+ return XML_L("encoding specified in XML declaration is incorrect");
+ case XML_ERROR_UNCLOSED_CDATA_SECTION:
+ return XML_L("unclosed CDATA section");
+ case XML_ERROR_EXTERNAL_ENTITY_HANDLING:
+ return XML_L("error in processing external entity reference");
+ case XML_ERROR_NOT_STANDALONE:
+ return XML_L("document is not standalone");
+ case XML_ERROR_UNEXPECTED_STATE:
+ return XML_L("unexpected parser state - please send a bug report");
+ case XML_ERROR_ENTITY_DECLARED_IN_PE:
+ return XML_L("entity declared in parameter entity");
+ case XML_ERROR_FEATURE_REQUIRES_XML_DTD:
+ return XML_L("requested feature requires XML_DTD support in Expat");
+ case XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING:
+ return XML_L("cannot change setting once parsing has begun");
+ /* Added in 1.95.7. */
+ case XML_ERROR_UNBOUND_PREFIX:
+ return XML_L("unbound prefix");
+ /* Added in 1.95.8. */
+ case XML_ERROR_UNDECLARING_PREFIX:
+ return XML_L("must not undeclare prefix");
+ case XML_ERROR_INCOMPLETE_PE:
+ return XML_L("incomplete markup in parameter entity");
+ case XML_ERROR_XML_DECL:
+ return XML_L("XML declaration not well-formed");
+ case XML_ERROR_TEXT_DECL:
+ return XML_L("text declaration not well-formed");
+ case XML_ERROR_PUBLICID:
+ return XML_L("illegal character(s) in public id");
+ case XML_ERROR_SUSPENDED:
+ return XML_L("parser suspended");
+ case XML_ERROR_NOT_SUSPENDED:
+ return XML_L("parser not suspended");
+ case XML_ERROR_ABORTED:
+ return XML_L("parsing aborted");
+ case XML_ERROR_FINISHED:
+ return XML_L("parsing finished");
+ case XML_ERROR_SUSPEND_PE:
+ return XML_L("cannot suspend in external parameter entity");
+ /* Added in 2.0.0. */
+ case XML_ERROR_RESERVED_PREFIX_XML:
+ return XML_L("reserved prefix (xml) must not be undeclared or bound to another namespace name");
+ case XML_ERROR_RESERVED_PREFIX_XMLNS:
+ return XML_L("reserved prefix (xmlns) must not be declared or undeclared");
+ case XML_ERROR_RESERVED_NAMESPACE_URI:
+ return XML_L("prefix must not be bound to one of the reserved namespace names");
+ /* Added in 2.2.5. */
+ case XML_ERROR_INVALID_ARGUMENT: /* Constant added in 2.2.1, already */
+ return XML_L("invalid argument");
+ }
return NULL;
}
@@ -2510,12 +2492,12 @@ XML_GetFeatureList(void)
static XML_Bool
storeRawNames(XML_Parser parser)
{
- TAG *tag = tagStack;
+ TAG *tag = parser->m_tagStack;
while (tag) {
int bufSize;
int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1);
char *rawNameBuf = tag->buf + nameLen;
- /* Stop if already stored. Since tagStack is a stack, we can stop
+ /* Stop if already stored. Since m_tagStack is a stack, we can stop
at the first entry that has already been copied; everything
below it in the stack is already been accounted for in a
previous call to this function.
@@ -2527,7 +2509,7 @@ storeRawNames(XML_Parser parser)
*/
bufSize = nameLen + ROUND_UP(tag->rawNameLength, sizeof(XML_Char));
if (bufSize > tag->bufEnd - tag->buf) {
- char *temp = (char *)REALLOC(tag->buf, bufSize);
+ char *temp = (char *)REALLOC(parser, tag->buf, bufSize);
if (temp == NULL)
return XML_FALSE;
/* if tag->name.str points to tag->buf (only when namespace
@@ -2558,8 +2540,8 @@ contentProcessor(XML_Parser parser,
const char *end,
const char **endPtr)
{
- enum XML_Error result = doContent(parser, 0, encoding, start, end,
- endPtr, (XML_Bool)!ps_finalBuffer);
+ enum XML_Error result = doContent(parser, 0, parser->m_encoding, start, end,
+ endPtr, (XML_Bool)!parser->m_parsingStatus.finalBuffer);
if (result == XML_ERROR_NONE) {
if (!storeRawNames(parser))
return XML_ERROR_NO_MEMORY;
@@ -2576,7 +2558,7 @@ externalEntityInitProcessor(XML_Parser parser,
enum XML_Error result = initializeEncoding(parser);
if (result != XML_ERROR_NONE)
return result;
- processor = externalEntityInitProcessor2;
+ parser->m_processor = externalEntityInitProcessor2;
return externalEntityInitProcessor2(parser, start, end, endPtr);
}
@@ -2587,7 +2569,7 @@ externalEntityInitProcessor2(XML_Parser parser,
const char **endPtr)
{
const char *next = start; /* XmlContentTok doesn't always set the last arg */
- int tok = XmlContentTok(encoding, start, end, &next);
+ int tok = XmlContentTok(parser->m_encoding, start, end, &next);
switch (tok) {
case XML_TOK_BOM:
/* If we are at the end of the buffer, this would cause the next stage,
@@ -2595,28 +2577,28 @@ externalEntityInitProcessor2(XML_Parser parser,
doContent (by detecting XML_TOK_NONE) without processing any xml text
declaration - causing the error XML_ERROR_MISPLACED_XML_PI in doContent.
*/
- if (next == end && !ps_finalBuffer) {
+ if (next == end && !parser->m_parsingStatus.finalBuffer) {
*endPtr = next;
return XML_ERROR_NONE;
}
start = next;
break;
case XML_TOK_PARTIAL:
- if (!ps_finalBuffer) {
+ if (!parser->m_parsingStatus.finalBuffer) {
*endPtr = start;
return XML_ERROR_NONE;
}
- eventPtr = start;
+ parser->m_eventPtr = start;
return XML_ERROR_UNCLOSED_TOKEN;
case XML_TOK_PARTIAL_CHAR:
- if (!ps_finalBuffer) {
+ if (!parser->m_parsingStatus.finalBuffer) {
*endPtr = start;
return XML_ERROR_NONE;
}
- eventPtr = start;
+ parser->m_eventPtr = start;
return XML_ERROR_PARTIAL_CHAR;
}
- processor = externalEntityInitProcessor3;
+ parser->m_processor = externalEntityInitProcessor3;
return externalEntityInitProcessor3(parser, start, end, endPtr);
}
@@ -2628,9 +2610,9 @@ externalEntityInitProcessor3(XML_Parser parser,
{
int tok;
const char *next = start; /* XmlContentTok doesn't always set the last arg */
- eventPtr = start;
- tok = XmlContentTok(encoding, start, end, &next);
- eventEndPtr = next;
+ parser->m_eventPtr = start;
+ tok = XmlContentTok(parser->m_encoding, start, end, &next);
+ parser->m_eventEndPtr = next;
switch (tok) {
case XML_TOK_XML_DECL:
@@ -2639,7 +2621,7 @@ externalEntityInitProcessor3(XML_Parser parser,
result = processXmlDecl(parser, 1, start, next);
if (result != XML_ERROR_NONE)
return result;
- switch (ps_parsing) {
+ switch (parser->m_parsingStatus.parsing) {
case XML_SUSPENDED:
*endPtr = next;
return XML_ERROR_NONE;
@@ -2651,20 +2633,20 @@ externalEntityInitProcessor3(XML_Parser parser,
}
break;
case XML_TOK_PARTIAL:
- if (!ps_finalBuffer) {
+ if (!parser->m_parsingStatus.finalBuffer) {
*endPtr = start;
return XML_ERROR_NONE;
}
return XML_ERROR_UNCLOSED_TOKEN;
case XML_TOK_PARTIAL_CHAR:
- if (!ps_finalBuffer) {
+ if (!parser->m_parsingStatus.finalBuffer) {
*endPtr = start;
return XML_ERROR_NONE;
}
return XML_ERROR_PARTIAL_CHAR;
}
- processor = externalEntityContentProcessor;
- tagLevel = 1;
+ parser->m_processor = externalEntityContentProcessor;
+ parser->m_tagLevel = 1;
return externalEntityContentProcessor(parser, start, end, endPtr);
}
@@ -2674,8 +2656,8 @@ externalEntityContentProcessor(XML_Parser parser,
const char *end,
const char **endPtr)
{
- enum XML_Error result = doContent(parser, 1, encoding, start, end,
- endPtr, (XML_Bool)!ps_finalBuffer);
+ enum XML_Error result = doContent(parser, 1, parser->m_encoding, start, end,
+ endPtr, (XML_Bool)!parser->m_parsingStatus.finalBuffer);
if (result == XML_ERROR_NONE) {
if (!storeRawNames(parser))
return XML_ERROR_NO_MEMORY;
@@ -2693,17 +2675,17 @@ doContent(XML_Parser parser,
XML_Bool haveMore)
{
/* save one level of indirection */
- DTD * const dtd = _dtd;
+ DTD * const dtd = parser->m_dtd;
const char **eventPP;
const char **eventEndPP;
- if (enc == encoding) {
- eventPP = &eventPtr;
- eventEndPP = &eventEndPtr;
+ if (enc == parser->m_encoding) {
+ eventPP = &parser->m_eventPtr;
+ eventEndPP = &parser->m_eventEndPtr;
}
else {
- eventPP = &(openInternalEntities->internalEventPtr);
- eventEndPP = &(openInternalEntities->internalEventEndPtr);
+ eventPP = &(parser->m_openInternalEntities->internalEventPtr);
+ eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr);
}
*eventPP = s;
@@ -2718,18 +2700,18 @@ doContent(XML_Parser parser,
return XML_ERROR_NONE;
}
*eventEndPP = end;
- if (characterDataHandler) {
+ if (parser->m_characterDataHandler) {
XML_Char c = 0xA;
- characterDataHandler(handlerArg, &c, 1);
+ parser->m_characterDataHandler(parser->m_handlerArg, &c, 1);
}
- else if (defaultHandler)
+ else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, end);
/* We are at the end of the final buffer, should we check for
XML_SUSPENDED, XML_FINISHED?
*/
if (startTagLevel == 0)
return XML_ERROR_NO_ELEMENTS;
- if (tagLevel != startTagLevel)
+ if (parser->m_tagLevel != startTagLevel)
return XML_ERROR_ASYNC_ENTITY;
*nextPtr = end;
return XML_ERROR_NONE;
@@ -2739,7 +2721,7 @@ doContent(XML_Parser parser,
return XML_ERROR_NONE;
}
if (startTagLevel > 0) {
- if (tagLevel != startTagLevel)
+ if (parser->m_tagLevel != startTagLevel)
return XML_ERROR_ASYNC_ENTITY;
*nextPtr = s;
return XML_ERROR_NONE;
@@ -2768,9 +2750,9 @@ doContent(XML_Parser parser,
s + enc->minBytesPerChar,
next - enc->minBytesPerChar);
if (ch) {
- if (characterDataHandler)
- characterDataHandler(handlerArg, &ch, 1);
- else if (defaultHandler)
+ if (parser->m_characterDataHandler)
+ parser->m_characterDataHandler(parser->m_handlerArg, &ch, 1);
+ else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
break;
}
@@ -2792,9 +2774,9 @@ doContent(XML_Parser parser,
return XML_ERROR_ENTITY_DECLARED_IN_PE;
}
else if (!entity) {
- if (skippedEntityHandler)
- skippedEntityHandler(handlerArg, name, 0);
- else if (defaultHandler)
+ if (parser->m_skippedEntityHandler)
+ parser->m_skippedEntityHandler(parser->m_handlerArg, name, 0);
+ else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
break;
}
@@ -2804,10 +2786,10 @@ doContent(XML_Parser parser,
return XML_ERROR_BINARY_ENTITY_REF;
if (entity->textPtr) {
enum XML_Error result;
- if (!defaultExpandInternalEntities) {
- if (skippedEntityHandler)
- skippedEntityHandler(handlerArg, entity->name, 0);
- else if (defaultHandler)
+ if (!parser->m_defaultExpandInternalEntities) {
+ if (parser->m_skippedEntityHandler)
+ parser->m_skippedEntityHandler(parser->m_handlerArg, entity->name, 0);
+ else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
break;
}
@@ -2815,22 +2797,22 @@ doContent(XML_Parser parser,
if (result != XML_ERROR_NONE)
return result;
}
- else if (externalEntityRefHandler) {
+ else if (parser->m_externalEntityRefHandler) {
const XML_Char *context;
entity->open = XML_TRUE;
context = getContext(parser);
entity->open = XML_FALSE;
if (!context)
return XML_ERROR_NO_MEMORY;
- if (!externalEntityRefHandler(externalEntityRefHandlerArg,
+ if (!parser->m_externalEntityRefHandler(parser->m_externalEntityRefHandlerArg,
context,
entity->base,
entity->systemId,
entity->publicId))
return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
- poolDiscard(&tempPool);
+ poolDiscard(&parser->m_tempPool);
}
- else if (defaultHandler)
+ else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
break;
}
@@ -2841,29 +2823,29 @@ doContent(XML_Parser parser,
TAG *tag;
enum XML_Error result;
XML_Char *toPtr;
- if (freeTagList) {
- tag = freeTagList;
- freeTagList = freeTagList->parent;
+ if (parser->m_freeTagList) {
+ tag = parser->m_freeTagList;
+ parser->m_freeTagList = parser->m_freeTagList->parent;
}
else {
- tag = (TAG *)MALLOC(sizeof(TAG));
+ tag = (TAG *)MALLOC(parser, sizeof(TAG));
if (!tag)
return XML_ERROR_NO_MEMORY;
- tag->buf = (char *)MALLOC(INIT_TAG_BUF_SIZE);
+ tag->buf = (char *)MALLOC(parser, INIT_TAG_BUF_SIZE);
if (!tag->buf) {
- FREE(tag);
+ FREE(parser, tag);
return XML_ERROR_NO_MEMORY;
}
tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE;
}
tag->bindings = NULL;
- tag->parent = tagStack;
- tagStack = tag;
+ tag->parent = parser->m_tagStack;
+ parser->m_tagStack = tag;
tag->name.localPart = NULL;
tag->name.prefix = NULL;
tag->rawName = s + enc->minBytesPerChar;
tag->rawNameLength = XmlNameLength(enc, tag->rawName);
- ++tagLevel;
+ ++parser->m_tagLevel;
{
const char *rawNameEnd = tag->rawName + tag->rawNameLength;
const char *fromPtr = tag->rawName;
@@ -2881,7 +2863,7 @@ doContent(XML_Parser parser,
}
bufSize = (int)(tag->bufEnd - tag->buf) << 1;
{
- char *temp = (char *)REALLOC(tag->buf, bufSize);
+ char *temp = (char *)REALLOC(parser, tag->buf, bufSize);
if (temp == NULL)
return XML_ERROR_NO_MEMORY;
tag->buf = temp;
@@ -2895,12 +2877,12 @@ doContent(XML_Parser parser,
result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings));
if (result)
return result;
- if (startElementHandler)
- startElementHandler(handlerArg, tag->name.str,
- (const XML_Char **)atts);
- else if (defaultHandler)
+ if (parser->m_startElementHandler)
+ parser->m_startElementHandler(parser->m_handlerArg, tag->name.str,
+ (const XML_Char **)parser->m_atts);
+ else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
- poolClear(&tempPool);
+ poolClear(&parser->m_tempPool);
break;
}
case XML_TOK_EMPTY_ELEMENT_NO_ATTS:
@@ -2912,45 +2894,49 @@ doContent(XML_Parser parser,
BINDING *bindings = NULL;
XML_Bool noElmHandlers = XML_TRUE;
TAG_NAME name;
- name.str = poolStoreString(&tempPool, enc, rawName,
+ name.str = poolStoreString(&parser->m_tempPool, enc, rawName,
rawName + XmlNameLength(enc, rawName));
if (!name.str)
return XML_ERROR_NO_MEMORY;
- poolFinish(&tempPool);
+ poolFinish(&parser->m_tempPool);
result = storeAtts(parser, enc, s, &name, &bindings);
if (result != XML_ERROR_NONE) {
freeBindings(parser, bindings);
return result;
}
- poolFinish(&tempPool);
- if (startElementHandler) {
- startElementHandler(handlerArg, name.str, (const XML_Char **)atts);
+ poolFinish(&parser->m_tempPool);
+ if (parser->m_startElementHandler) {
+ parser->m_startElementHandler(parser->m_handlerArg, name.str, (const XML_Char **)parser->m_atts);
noElmHandlers = XML_FALSE;
}
- if (endElementHandler) {
- if (startElementHandler)
+ if (parser->m_endElementHandler) {
+ if (parser->m_startElementHandler)
*eventPP = *eventEndPP;
- endElementHandler(handlerArg, name.str);
+ parser->m_endElementHandler(parser->m_handlerArg, name.str);
noElmHandlers = XML_FALSE;
}
- if (noElmHandlers && defaultHandler)
+ if (noElmHandlers && parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
- poolClear(&tempPool);
+ poolClear(&parser->m_tempPool);
freeBindings(parser, bindings);
}
- if (tagLevel == 0)
- return epilogProcessor(parser, next, end, nextPtr);
+ if ((parser->m_tagLevel == 0) && (parser->m_parsingStatus.parsing != XML_FINISHED)) {
+ if (parser->m_parsingStatus.parsing == XML_SUSPENDED)
+ parser->m_processor = epilogProcessor;
+ else
+ return epilogProcessor(parser, next, end, nextPtr);
+ }
break;
case XML_TOK_END_TAG:
- if (tagLevel == startTagLevel)
+ if (parser->m_tagLevel == startTagLevel)
return XML_ERROR_ASYNC_ENTITY;
else {
int len;
const char *rawName;
- TAG *tag = tagStack;
- tagStack = tag->parent;
- tag->parent = freeTagList;
- freeTagList = tag;
+ TAG *tag = parser->m_tagStack;
+ parser->m_tagStack = tag->parent;
+ tag->parent = parser->m_freeTagList;
+ parser->m_freeTagList = tag;
rawName = s + enc->minBytesPerChar*2;
len = XmlNameLength(enc, rawName);
if (len != tag->rawNameLength
@@ -2958,13 +2944,13 @@ doContent(XML_Parser parser,
*eventPP = rawName;
return XML_ERROR_TAG_MISMATCH;
}
- --tagLevel;
- if (endElementHandler) {
+ --parser->m_tagLevel;
+ if (parser->m_endElementHandler) {
const XML_Char *localPart;
const XML_Char *prefix;
XML_Char *uri;
localPart = tag->name.localPart;
- if (ns && localPart) {
+ if (parser->m_ns && localPart) {
/* localPart and prefix may have been overwritten in
tag->name.str, since this points to the binding->uri
buffer which gets re-used; so we have to add them again
@@ -2973,26 +2959,26 @@ doContent(XML_Parser parser,
/* don't need to check for space - already done in storeAtts() */
while (*localPart) *uri++ = *localPart++;
prefix = (XML_Char *)tag->name.prefix;
- if (ns_triplets && prefix) {
- *uri++ = namespaceSeparator;
+ if (parser->m_ns_triplets && prefix) {
+ *uri++ = parser->m_namespaceSeparator;
while (*prefix) *uri++ = *prefix++;
}
*uri = XML_T('\0');
}
- endElementHandler(handlerArg, tag->name.str);
+ parser->m_endElementHandler(parser->m_handlerArg, tag->name.str);
}
- else if (defaultHandler)
+ else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
while (tag->bindings) {
BINDING *b = tag->bindings;
- if (endNamespaceDeclHandler)
- endNamespaceDeclHandler(handlerArg, b->prefix->name);
+ if (parser->m_endNamespaceDeclHandler)
+ parser->m_endNamespaceDeclHandler(parser->m_handlerArg, b->prefix->name);
tag->bindings = tag->bindings->nextTagBinding;
- b->nextTagBinding = freeBindingList;
- freeBindingList = b;
+ b->nextTagBinding = parser->m_freeBindingList;
+ parser->m_freeBindingList = b;
b->prefix->binding = b->prevPrefixBinding;
}
- if (tagLevel == 0)
+ if (parser->m_tagLevel == 0)
return epilogProcessor(parser, next, end, nextPtr);
}
break;
@@ -3001,30 +2987,30 @@ doContent(XML_Parser parser,
int n = XmlCharRefNumber(enc, s);
if (n < 0)
return XML_ERROR_BAD_CHAR_REF;
- if (characterDataHandler) {
+ if (parser->m_characterDataHandler) {
XML_Char buf[XML_ENCODE_MAX];
- characterDataHandler(handlerArg, buf, XmlEncode(n, (ICHAR *)buf));
+ parser->m_characterDataHandler(parser->m_handlerArg, buf, XmlEncode(n, (ICHAR *)buf));
}
- else if (defaultHandler)
+ else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
}
break;
case XML_TOK_XML_DECL:
return XML_ERROR_MISPLACED_XML_PI;
case XML_TOK_DATA_NEWLINE:
- if (characterDataHandler) {
+ if (parser->m_characterDataHandler) {
XML_Char c = 0xA;
- characterDataHandler(handlerArg, &c, 1);
+ parser->m_characterDataHandler(parser->m_handlerArg, &c, 1);
}
- else if (defaultHandler)
+ else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
break;
case XML_TOK_CDATA_SECT_OPEN:
{
enum XML_Error result;
- if (startCdataSectionHandler)
- startCdataSectionHandler(handlerArg);
-#if 0
+ if (parser->m_startCdataSectionHandler)
+ parser->m_startCdataSectionHandler(parser->m_handlerArg);
+/* BEGIN disabled code */
/* Suppose you doing a transformation on a document that involves
changing only the character data. You set up a defaultHandler
and a characterDataHandler. The defaultHandler simply copies
@@ -3037,16 +3023,16 @@ doContent(XML_Parser parser,
However, now we have a start/endCdataSectionHandler, so it seems
easier to let the user deal with this.
*/
- else if (characterDataHandler)
- characterDataHandler(handlerArg, dataBuf, 0);
-#endif
- else if (defaultHandler)
+ else if (0 && parser->m_characterDataHandler)
+ parser->m_characterDataHandler(parser->m_handlerArg, parser->m_dataBuf, 0);
+/* END disabled code */
+ else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
result = doCdataSection(parser, enc, &next, end, nextPtr, haveMore);
if (result != XML_ERROR_NONE)
return result;
else if (!next) {
- processor = cdataSectionProcessor;
+ parser->m_processor = cdataSectionProcessor;
return result;
}
}
@@ -3056,19 +3042,19 @@ doContent(XML_Parser parser,
*nextPtr = s;
return XML_ERROR_NONE;
}
- if (characterDataHandler) {
+ if (parser->m_characterDataHandler) {
if (MUST_CONVERT(enc, s)) {
- ICHAR *dataPtr = (ICHAR *)dataBuf;
- XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd);
- characterDataHandler(handlerArg, dataBuf,
- (int)(dataPtr - (ICHAR *)dataBuf));
+ ICHAR *dataPtr = (ICHAR *)parser->m_dataBuf;
+ XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)parser->m_dataBufEnd);
+ parser->m_characterDataHandler(parser->m_handlerArg, parser->m_dataBuf,
+ (int)(dataPtr - (ICHAR *)parser->m_dataBuf));
}
else
- characterDataHandler(handlerArg,
+ parser->m_characterDataHandler(parser->m_handlerArg,
(XML_Char *)s,
(int)((XML_Char *)end - (XML_Char *)s));
}
- else if (defaultHandler)
+ else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, end);
/* We are at the end of the final buffer, should we check for
XML_SUSPENDED, XML_FINISHED?
@@ -3077,7 +3063,7 @@ doContent(XML_Parser parser,
*eventPP = end;
return XML_ERROR_NO_ELEMENTS;
}
- if (tagLevel != startTagLevel) {
+ if (parser->m_tagLevel != startTagLevel) {
*eventPP = end;
return XML_ERROR_ASYNC_ENTITY;
}
@@ -3085,26 +3071,26 @@ doContent(XML_Parser parser,
return XML_ERROR_NONE;
case XML_TOK_DATA_CHARS:
{
- XML_CharacterDataHandler charDataHandler = characterDataHandler;
+ XML_CharacterDataHandler charDataHandler = parser->m_characterDataHandler;
if (charDataHandler) {
if (MUST_CONVERT(enc, s)) {
for (;;) {
- ICHAR *dataPtr = (ICHAR *)dataBuf;
- const enum XML_Convert_Result convert_res = XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd);
+ ICHAR *dataPtr = (ICHAR *)parser->m_dataBuf;
+ const enum XML_Convert_Result convert_res = XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)parser->m_dataBufEnd);
*eventEndPP = s;
- charDataHandler(handlerArg, dataBuf,
- (int)(dataPtr - (ICHAR *)dataBuf));
+ charDataHandler(parser->m_handlerArg, parser->m_dataBuf,
+ (int)(dataPtr - (ICHAR *)parser->m_dataBuf));
if ((convert_res == XML_CONVERT_COMPLETED) || (convert_res == XML_CONVERT_INPUT_INCOMPLETE))
break;
*eventPP = s;
}
}
else
- charDataHandler(handlerArg,
+ charDataHandler(parser->m_handlerArg,
(XML_Char *)s,
(int)((XML_Char *)next - (XML_Char *)s));
}
- else if (defaultHandler)
+ else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
}
break;
@@ -3124,13 +3110,13 @@ doContent(XML_Parser parser,
*
* LCOV_EXCL_START
*/
- if (defaultHandler)
+ if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
break;
/* LCOV_EXCL_STOP */
}
*eventPP = s = next;
- switch (ps_parsing) {
+ switch (parser->m_parsingStatus.parsing) {
case XML_SUSPENDED:
*nextPtr = next;
return XML_ERROR_NONE;
@@ -3143,7 +3129,7 @@ doContent(XML_Parser parser,
}
/* This function does not call free() on the allocated memory, merely
- * moving it to the parser's freeBindingList where it can be freed or
+ * moving it to the parser's m_freeBindingList where it can be freed or
* reused as appropriate.
*/
static void
@@ -3152,15 +3138,15 @@ freeBindings(XML_Parser parser, BINDING *bindings)
while (bindings) {
BINDING *b = bindings;
- /* startNamespaceDeclHandler will have been called for this
+ /* m_startNamespaceDeclHandler will have been called for this
* binding in addBindings(), so call the end handler now.
*/
- if (endNamespaceDeclHandler)
- endNamespaceDeclHandler(handlerArg, b->prefix->name);
+ if (parser->m_endNamespaceDeclHandler)
+ parser->m_endNamespaceDeclHandler(parser->m_handlerArg, b->prefix->name);
bindings = bindings->nextTagBinding;
- b->nextTagBinding = freeBindingList;
- freeBindingList = b;
+ b->nextTagBinding = parser->m_freeBindingList;
+ parser->m_freeBindingList = b;
b->prefix->binding = b->prevPrefixBinding;
}
}
@@ -3180,7 +3166,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
const char *attStr, TAG_NAME *tagNamePtr,
BINDING **bindingsPtr)
{
- DTD * const dtd = _dtd; /* save one level of indirection */
+ DTD * const dtd = parser->m_dtd; /* save one level of indirection */
ELEMENT_TYPE *elementType;
int nDefaultAtts;
const XML_Char **appAtts; /* the attribute list for the application */
@@ -3203,43 +3189,43 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
sizeof(ELEMENT_TYPE));
if (!elementType)
return XML_ERROR_NO_MEMORY;
- if (ns && !setElementTypePrefix(parser, elementType))
+ if (parser->m_ns && !setElementTypePrefix(parser, elementType))
return XML_ERROR_NO_MEMORY;
}
nDefaultAtts = elementType->nDefaultAtts;
/* get the attributes from the tokenizer */
- n = XmlGetAttributes(enc, attStr, attsSize, atts);
- if (n + nDefaultAtts > attsSize) {
- int oldAttsSize = attsSize;
+ n = XmlGetAttributes(enc, attStr, parser->m_attsSize, parser->m_atts);
+ if (n + nDefaultAtts > parser->m_attsSize) {
+ int oldAttsSize = parser->m_attsSize;
ATTRIBUTE *temp;
#ifdef XML_ATTR_INFO
XML_AttrInfo *temp2;
#endif
- attsSize = n + nDefaultAtts + INIT_ATTS_SIZE;
- temp = (ATTRIBUTE *)REALLOC((void *)atts, attsSize * sizeof(ATTRIBUTE));
+ parser->m_attsSize = n + nDefaultAtts + INIT_ATTS_SIZE;
+ temp = (ATTRIBUTE *)REALLOC(parser, (void *)parser->m_atts, parser->m_attsSize * sizeof(ATTRIBUTE));
if (temp == NULL) {
- attsSize = oldAttsSize;
+ parser->m_attsSize = oldAttsSize;
return XML_ERROR_NO_MEMORY;
}
- atts = temp;
+ parser->m_atts = temp;
#ifdef XML_ATTR_INFO
- temp2 = (XML_AttrInfo *)REALLOC((void *)attInfo, attsSize * sizeof(XML_AttrInfo));
+ temp2 = (XML_AttrInfo *)REALLOC(parser, (void *)parser->m_attInfo, parser->m_attsSize * sizeof(XML_AttrInfo));
if (temp2 == NULL) {
- attsSize = oldAttsSize;
+ parser->m_attsSize = oldAttsSize;
return XML_ERROR_NO_MEMORY;
}
- attInfo = temp2;
+ parser->m_attInfo = temp2;
#endif
if (n > oldAttsSize)
- XmlGetAttributes(enc, attStr, n, atts);
+ XmlGetAttributes(enc, attStr, n, parser->m_atts);
}
- appAtts = (const XML_Char **)atts;
+ appAtts = (const XML_Char **)parser->m_atts;
for (i = 0; i < n; i++) {
- ATTRIBUTE *currAtt = &atts[i];
+ ATTRIBUTE *currAtt = &parser->m_atts[i];
#ifdef XML_ATTR_INFO
- XML_AttrInfo *currAttInfo = &attInfo[i];
+ XML_AttrInfo *currAttInfo = &parser->m_attInfo[i];
#endif
/* add the name and value to the attribute list */
ATTRIBUTE_ID *attId = getAttributeId(parser, enc, currAtt->name,
@@ -3248,25 +3234,25 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
if (!attId)
return XML_ERROR_NO_MEMORY;
#ifdef XML_ATTR_INFO
- currAttInfo->nameStart = parseEndByteIndex - (parseEndPtr - currAtt->name);
+ currAttInfo->nameStart = parser->m_parseEndByteIndex - (parser->m_parseEndPtr - currAtt->name);
currAttInfo->nameEnd = currAttInfo->nameStart +
XmlNameLength(enc, currAtt->name);
- currAttInfo->valueStart = parseEndByteIndex -
- (parseEndPtr - currAtt->valuePtr);
- currAttInfo->valueEnd = parseEndByteIndex - (parseEndPtr - currAtt->valueEnd);
+ currAttInfo->valueStart = parser->m_parseEndByteIndex -
+ (parser->m_parseEndPtr - currAtt->valuePtr);
+ currAttInfo->valueEnd = parser->m_parseEndByteIndex - (parser->m_parseEndPtr - currAtt->valueEnd);
#endif
/* Detect duplicate attributes by their QNames. This does not work when
namespace processing is turned on and different prefixes for the same
namespace are used. For this case we have a check further down.
*/
if ((attId->name)[-1]) {
- if (enc == encoding)
- eventPtr = atts[i].name;
+ if (enc == parser->m_encoding)
+ parser->m_eventPtr = parser->m_atts[i].name;
return XML_ERROR_DUPLICATE_ATTRIBUTE;
}
(attId->name)[-1] = 1;
appAtts[attIndex++] = attId->name;
- if (!atts[i].normalized) {
+ if (!parser->m_atts[i].normalized) {
enum XML_Error result;
XML_Bool isCdata = XML_TRUE;
@@ -3283,20 +3269,20 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
/* normalize the attribute value */
result = storeAttributeValue(parser, enc, isCdata,
- atts[i].valuePtr, atts[i].valueEnd,
- &tempPool);
+ parser->m_atts[i].valuePtr, parser->m_atts[i].valueEnd,
+ &parser->m_tempPool);
if (result)
return result;
- appAtts[attIndex] = poolStart(&tempPool);
- poolFinish(&tempPool);
+ appAtts[attIndex] = poolStart(&parser->m_tempPool);
+ poolFinish(&parser->m_tempPool);
}
else {
/* the value did not need normalizing */
- appAtts[attIndex] = poolStoreString(&tempPool, enc, atts[i].valuePtr,
- atts[i].valueEnd);
+ appAtts[attIndex] = poolStoreString(&parser->m_tempPool, enc, parser->m_atts[i].valuePtr,
+ parser->m_atts[i].valueEnd);
if (appAtts[attIndex] == 0)
return XML_ERROR_NO_MEMORY;
- poolFinish(&tempPool);
+ poolFinish(&parser->m_tempPool);
}
/* handle prefixed attribute names */
if (attId->prefix) {
@@ -3320,16 +3306,16 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
}
/* set-up for XML_GetSpecifiedAttributeCount and XML_GetIdAttributeIndex */
- nSpecifiedAtts = attIndex;
+ parser->m_nSpecifiedAtts = attIndex;
if (elementType->idAtt && (elementType->idAtt->name)[-1]) {
for (i = 0; i < attIndex; i += 2)
if (appAtts[i] == elementType->idAtt->name) {
- idAttIndex = i;
+ parser->m_idAttIndex = i;
break;
}
}
else
- idAttIndex = -1;
+ parser->m_idAttIndex = -1;
/* do attribute defaulting */
for (i = 0; i < nDefaultAtts; i++) {
@@ -3363,33 +3349,33 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
i = 0;
if (nPrefixes) {
int j; /* hash table index */
- unsigned long version = nsAttsVersion;
- int nsAttsSize = (int)1 << nsAttsPower;
- unsigned char oldNsAttsPower = nsAttsPower;
+ unsigned long version = parser->m_nsAttsVersion;
+ int nsAttsSize = (int)1 << parser->m_nsAttsPower;
+ unsigned char oldNsAttsPower = parser->m_nsAttsPower;
/* size of hash table must be at least 2 * (# of prefixed attributes) */
- if ((nPrefixes << 1) >> nsAttsPower) { /* true for nsAttsPower = 0 */
+ if ((nPrefixes << 1) >> parser->m_nsAttsPower) { /* true for m_nsAttsPower = 0 */
NS_ATT *temp;
/* hash table size must also be a power of 2 and >= 8 */
- while (nPrefixes >> nsAttsPower++);
- if (nsAttsPower < 3)
- nsAttsPower = 3;
- nsAttsSize = (int)1 << nsAttsPower;
- temp = (NS_ATT *)REALLOC(nsAtts, nsAttsSize * sizeof(NS_ATT));
+ while (nPrefixes >> parser->m_nsAttsPower++);
+ if (parser->m_nsAttsPower < 3)
+ parser->m_nsAttsPower = 3;
+ nsAttsSize = (int)1 << parser->m_nsAttsPower;
+ temp = (NS_ATT *)REALLOC(parser, parser->m_nsAtts, nsAttsSize * sizeof(NS_ATT));
if (!temp) {
- /* Restore actual size of memory in nsAtts */
- nsAttsPower = oldNsAttsPower;
+ /* Restore actual size of memory in m_nsAtts */
+ parser->m_nsAttsPower = oldNsAttsPower;
return XML_ERROR_NO_MEMORY;
}
- nsAtts = temp;
- version = 0; /* force re-initialization of nsAtts hash table */
+ parser->m_nsAtts = temp;
+ version = 0; /* force re-initialization of m_nsAtts hash table */
}
- /* using a version flag saves us from initializing nsAtts every time */
+ /* using a version flag saves us from initializing m_nsAtts every time */
if (!version) { /* initialize version flags when version wraps around */
version = INIT_ATTS_VERSION;
for (j = nsAttsSize; j != 0; )
- nsAtts[--j].version = version;
+ parser->m_nsAtts[--j].version = version;
}
- nsAttsVersion = --version;
+ parser->m_nsAttsVersion = --version;
/* expand prefixed names and check for duplicates */
for (; i < attIndex; i += 2) {
@@ -3429,7 +3415,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
for (j = 0; j < b->uriLen; j++) {
const XML_Char c = b->uri[j];
- if (!poolAppendChar(&tempPool, c))
+ if (!poolAppendChar(&parser->m_tempPool, c))
return XML_ERROR_NO_MEMORY;
}
@@ -3441,7 +3427,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
sip24_update(&sip_state, s, keylen(s) * sizeof(XML_Char));
do { /* copies null terminator */
- if (!poolAppendChar(&tempPool, *s))
+ if (!poolAppendChar(&parser->m_tempPool, *s))
return XML_ERROR_NO_MEMORY;
} while (*s++);
@@ -3453,40 +3439,40 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
unsigned char step = 0;
unsigned long mask = nsAttsSize - 1;
j = uriHash & mask; /* index into hash table */
- while (nsAtts[j].version == version) {
+ while (parser->m_nsAtts[j].version == version) {
/* for speed we compare stored hash values first */
- if (uriHash == nsAtts[j].hash) {
- const XML_Char *s1 = poolStart(&tempPool);
- const XML_Char *s2 = nsAtts[j].uriName;
+ if (uriHash == parser->m_nsAtts[j].hash) {
+ const XML_Char *s1 = poolStart(&parser->m_tempPool);
+ const XML_Char *s2 = parser->m_nsAtts[j].uriName;
/* s1 is null terminated, but not s2 */
for (; *s1 == *s2 && *s1 != 0; s1++, s2++);
if (*s1 == 0)
return XML_ERROR_DUPLICATE_ATTRIBUTE;
}
if (!step)
- step = PROBE_STEP(uriHash, mask, nsAttsPower);
+ step = PROBE_STEP(uriHash, mask, parser->m_nsAttsPower);
j < step ? (j += nsAttsSize - step) : (j -= step);
}
}
- if (ns_triplets) { /* append namespace separator and prefix */
- tempPool.ptr[-1] = namespaceSeparator;
+ if (parser->m_ns_triplets) { /* append namespace separator and prefix */
+ parser->m_tempPool.ptr[-1] = parser->m_namespaceSeparator;
s = b->prefix->name;
do {
- if (!poolAppendChar(&tempPool, *s))
+ if (!poolAppendChar(&parser->m_tempPool, *s))
return XML_ERROR_NO_MEMORY;
} while (*s++);
}
/* store expanded name in attribute list */
- s = poolStart(&tempPool);
- poolFinish(&tempPool);
+ s = poolStart(&parser->m_tempPool);
+ poolFinish(&parser->m_tempPool);
appAtts[i] = s;
/* fill empty slot with new version, uriName and hash value */
- nsAtts[j].version = version;
- nsAtts[j].hash = uriHash;
- nsAtts[j].uriName = s;
+ parser->m_nsAtts[j].version = version;
+ parser->m_nsAtts[j].hash = uriHash;
+ parser->m_nsAtts[j].uriName = s;
if (!--nPrefixes) {
i += 2;
@@ -3503,7 +3489,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
for (binding = *bindingsPtr; binding; binding = binding->nextTagBinding)
binding->attId->name[-1] = 0;
- if (!ns)
+ if (!parser->m_ns)
return XML_ERROR_NONE;
/* expand the element type name */
@@ -3522,7 +3508,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
else
return XML_ERROR_NONE;
prefixLen = 0;
- if (ns_triplets && binding->prefix->name) {
+ if (parser->m_ns_triplets && binding->prefix->name) {
for (; binding->prefix->name[prefixLen++];)
; /* prefixLen includes null terminator */
}
@@ -3535,24 +3521,24 @@ storeAtts(XML_Parser parser, const ENCODING *enc,
n = i + binding->uriLen + prefixLen;
if (n > binding->uriAlloc) {
TAG *p;
- uri = (XML_Char *)MALLOC((n + EXPAND_SPARE) * sizeof(XML_Char));
+ uri = (XML_Char *)MALLOC(parser, (n + EXPAND_SPARE) * sizeof(XML_Char));
if (!uri)
return XML_ERROR_NO_MEMORY;
binding->uriAlloc = n + EXPAND_SPARE;
memcpy(uri, binding->uri, binding->uriLen * sizeof(XML_Char));
- for (p = tagStack; p; p = p->parent)
+ for (p = parser->m_tagStack; p; p = p->parent)
if (p->name.str == binding->uri)
p->name.str = uri;
- FREE(binding->uri);
+ FREE(parser, binding->uri);
binding->uri = uri;
}
- /* if namespaceSeparator != '\0' then uri includes it already */
+ /* if m_namespaceSeparator != '\0' then uri includes it already */
uri = binding->uri + binding->uriLen;
memcpy(uri, localPart, i * sizeof(XML_Char));
/* we always have a namespace separator between localPart and prefix */
if (prefixLen) {
uri += i - 1;
- *uri = namespaceSeparator; /* replace null terminator */
+ *uri = parser->m_namespaceSeparator; /* replace null terminator */
memcpy(uri + 1, binding->prefix->name, prefixLen * sizeof(XML_Char));
}
tagNamePtr->str = binding->uri;
@@ -3630,48 +3616,48 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
if (isXMLNS)
return XML_ERROR_RESERVED_NAMESPACE_URI;
- if (namespaceSeparator)
+ if (parser->m_namespaceSeparator)
len++;
- if (freeBindingList) {
- b = freeBindingList;
+ if (parser->m_freeBindingList) {
+ b = parser->m_freeBindingList;
if (len > b->uriAlloc) {
- XML_Char *temp = (XML_Char *)REALLOC(b->uri,
+ XML_Char *temp = (XML_Char *)REALLOC(parser, b->uri,
sizeof(XML_Char) * (len + EXPAND_SPARE));
if (temp == NULL)
return XML_ERROR_NO_MEMORY;
b->uri = temp;
b->uriAlloc = len + EXPAND_SPARE;
}
- freeBindingList = b->nextTagBinding;
+ parser->m_freeBindingList = b->nextTagBinding;
}
else {
- b = (BINDING *)MALLOC(sizeof(BINDING));
+ b = (BINDING *)MALLOC(parser, sizeof(BINDING));
if (!b)
return XML_ERROR_NO_MEMORY;
- b->uri = (XML_Char *)MALLOC(sizeof(XML_Char) * (len + EXPAND_SPARE));
+ b->uri = (XML_Char *)MALLOC(parser, sizeof(XML_Char) * (len + EXPAND_SPARE));
if (!b->uri) {
- FREE(b);
+ FREE(parser, b);
return XML_ERROR_NO_MEMORY;
}
b->uriAlloc = len + EXPAND_SPARE;
}
b->uriLen = len;
memcpy(b->uri, uri, len * sizeof(XML_Char));
- if (namespaceSeparator)
- b->uri[len - 1] = namespaceSeparator;
+ if (parser->m_namespaceSeparator)
+ b->uri[len - 1] = parser->m_namespaceSeparator;
b->prefix = prefix;
b->attId = attId;
b->prevPrefixBinding = prefix->binding;
/* NULL binding when default namespace undeclared */
- if (*uri == XML_T('\0') && prefix == &_dtd->defaultPrefix)
+ if (*uri == XML_T('\0') && prefix == &parser->m_dtd->defaultPrefix)
prefix->binding = NULL;
else
prefix->binding = b;
b->nextTagBinding = *bindingsPtr;
*bindingsPtr = b;
/* if attId == NULL then we are not starting a namespace scope */
- if (attId && startNamespaceDeclHandler)
- startNamespaceDeclHandler(handlerArg, prefix->name,
+ if (attId && parser->m_startNamespaceDeclHandler)
+ parser->m_startNamespaceDeclHandler(parser->m_handlerArg, prefix->name,
prefix->binding ? uri : 0);
return XML_ERROR_NONE;
}
@@ -3685,17 +3671,17 @@ cdataSectionProcessor(XML_Parser parser,
const char *end,
const char **endPtr)
{
- enum XML_Error result = doCdataSection(parser, encoding, &start, end,
- endPtr, (XML_Bool)!ps_finalBuffer);
+ enum XML_Error result = doCdataSection(parser, parser->m_encoding, &start, end,
+ endPtr, (XML_Bool)!parser->m_parsingStatus.finalBuffer);
if (result != XML_ERROR_NONE)
return result;
if (start) {
- if (parentParser) { /* we are parsing an external entity */
- processor = externalEntityContentProcessor;
+ if (parser->m_parentParser) { /* we are parsing an external entity */
+ parser->m_processor = externalEntityContentProcessor;
return externalEntityContentProcessor(parser, start, end, endPtr);
}
else {
- processor = contentProcessor;
+ parser->m_processor = contentProcessor;
return contentProcessor(parser, start, end, endPtr);
}
}
@@ -3716,14 +3702,14 @@ doCdataSection(XML_Parser parser,
const char *s = *startPtr;
const char **eventPP;
const char **eventEndPP;
- if (enc == encoding) {
- eventPP = &eventPtr;
+ if (enc == parser->m_encoding) {
+ eventPP = &parser->m_eventPtr;
*eventPP = s;
- eventEndPP = &eventEndPtr;
+ eventEndPP = &parser->m_eventEndPtr;
}
else {
- eventPP = &(openInternalEntities->internalEventPtr);
- eventEndPP = &(openInternalEntities->internalEventEndPtr);
+ eventPP = &(parser->m_openInternalEntities->internalEventPtr);
+ eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr);
}
*eventPP = s;
*startPtr = NULL;
@@ -3734,51 +3720,51 @@ doCdataSection(XML_Parser parser,
*eventEndPP = next;
switch (tok) {
case XML_TOK_CDATA_SECT_CLOSE:
- if (endCdataSectionHandler)
- endCdataSectionHandler(handlerArg);
-#if 0
+ if (parser->m_endCdataSectionHandler)
+ parser->m_endCdataSectionHandler(parser->m_handlerArg);
+/* BEGIN disabled code */
/* see comment under XML_TOK_CDATA_SECT_OPEN */
- else if (characterDataHandler)
- characterDataHandler(handlerArg, dataBuf, 0);
-#endif
- else if (defaultHandler)
+ else if (0 && parser->m_characterDataHandler)
+ parser->m_characterDataHandler(parser->m_handlerArg, parser->m_dataBuf, 0);
+/* END disabled code */
+ else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
*startPtr = next;
*nextPtr = next;
- if (ps_parsing == XML_FINISHED)
+ if (parser->m_parsingStatus.parsing == XML_FINISHED)
return XML_ERROR_ABORTED;
else
return XML_ERROR_NONE;
case XML_TOK_DATA_NEWLINE:
- if (characterDataHandler) {
+ if (parser->m_characterDataHandler) {
XML_Char c = 0xA;
- characterDataHandler(handlerArg, &c, 1);
+ parser->m_characterDataHandler(parser->m_handlerArg, &c, 1);
}
- else if (defaultHandler)
+ else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
break;
case XML_TOK_DATA_CHARS:
{
- XML_CharacterDataHandler charDataHandler = characterDataHandler;
+ XML_CharacterDataHandler charDataHandler = parser->m_characterDataHandler;
if (charDataHandler) {
if (MUST_CONVERT(enc, s)) {
for (;;) {
- ICHAR *dataPtr = (ICHAR *)dataBuf;
- const enum XML_Convert_Result convert_res = XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd);
+ ICHAR *dataPtr = (ICHAR *)parser->m_dataBuf;
+ const enum XML_Convert_Result convert_res = XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)parser->m_dataBufEnd);
*eventEndPP = next;
- charDataHandler(handlerArg, dataBuf,
- (int)(dataPtr - (ICHAR *)dataBuf));
+ charDataHandler(parser->m_handlerArg, parser->m_dataBuf,
+ (int)(dataPtr - (ICHAR *)parser->m_dataBuf));
if ((convert_res == XML_CONVERT_COMPLETED) || (convert_res == XML_CONVERT_INPUT_INCOMPLETE))
break;
*eventPP = s;
}
}
else
- charDataHandler(handlerArg,
+ charDataHandler(parser->m_handlerArg,
(XML_Char *)s,
(int)((XML_Char *)next - (XML_Char *)s));
}
- else if (defaultHandler)
+ else if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
}
break;
@@ -3812,7 +3798,7 @@ doCdataSection(XML_Parser parser,
}
*eventPP = s = next;
- switch (ps_parsing) {
+ switch (parser->m_parsingStatus.parsing) {
case XML_SUSPENDED:
*nextPtr = next;
return XML_ERROR_NONE;
@@ -3835,12 +3821,12 @@ ignoreSectionProcessor(XML_Parser parser,
const char *end,
const char **endPtr)
{
- enum XML_Error result = doIgnoreSection(parser, encoding, &start, end,
- endPtr, (XML_Bool)!ps_finalBuffer);
+ enum XML_Error result = doIgnoreSection(parser, parser->m_encoding, &start, end,
+ endPtr, (XML_Bool)!parser->m_parsingStatus.finalBuffer);
if (result != XML_ERROR_NONE)
return result;
if (start) {
- processor = prologProcessor;
+ parser->m_processor = prologProcessor;
return prologProcessor(parser, start, end, endPtr);
}
return result;
@@ -3862,15 +3848,15 @@ doIgnoreSection(XML_Parser parser,
const char *s = *startPtr;
const char **eventPP;
const char **eventEndPP;
- if (enc == encoding) {
- eventPP = &eventPtr;
+ if (enc == parser->m_encoding) {
+ eventPP = &parser->m_eventPtr;
*eventPP = s;
- eventEndPP = &eventEndPtr;
+ eventEndPP = &parser->m_eventEndPtr;
}
else {
/* It's not entirely clear, but it seems the following two lines
* of code cannot be executed. The only occasions on which 'enc'
- * is not 'parser->m_encoding' are when this function is called
+ * is not 'encoding' are when this function is called
* from the internal entity processing, and IGNORE sections are an
* error in internal entities.
*
@@ -3879,8 +3865,8 @@ doIgnoreSection(XML_Parser parser,
*
* LCOV_EXCL_START
*/
- eventPP = &(openInternalEntities->internalEventPtr);
- eventEndPP = &(openInternalEntities->internalEventEndPtr);
+ eventPP = &(parser->m_openInternalEntities->internalEventPtr);
+ eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr);
/* LCOV_EXCL_STOP */
}
*eventPP = s;
@@ -3889,11 +3875,11 @@ doIgnoreSection(XML_Parser parser,
*eventEndPP = next;
switch (tok) {
case XML_TOK_IGNORE_SECT:
- if (defaultHandler)
+ if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
*startPtr = next;
*nextPtr = next;
- if (ps_parsing == XML_FINISHED)
+ if (parser->m_parsingStatus.parsing == XML_FINISHED)
return XML_ERROR_ABORTED;
else
return XML_ERROR_NONE;
@@ -3937,27 +3923,27 @@ initializeEncoding(XML_Parser parser)
#ifdef XML_UNICODE
char encodingBuf[128];
/* See comments abount `protoclEncodingName` in parserInit() */
- if (!protocolEncodingName)
+ if (!parser->m_protocolEncodingName)
s = NULL;
else {
int i;
- for (i = 0; protocolEncodingName[i]; i++) {
+ for (i = 0; parser->m_protocolEncodingName[i]; i++) {
if (i == sizeof(encodingBuf) - 1
- || (protocolEncodingName[i] & ~0x7f) != 0) {
+ || (parser->m_protocolEncodingName[i] & ~0x7f) != 0) {
encodingBuf[0] = '\0';
break;
}
- encodingBuf[i] = (char)protocolEncodingName[i];
+ encodingBuf[i] = (char)parser->m_protocolEncodingName[i];
}
encodingBuf[i] = '\0';
s = encodingBuf;
}
#else
- s = protocolEncodingName;
+ s = parser->m_protocolEncodingName;
#endif
- if ((ns ? XmlInitEncodingNS : XmlInitEncoding)(&initEncoding, &encoding, s))
+ if ((parser->m_ns ? XmlInitEncodingNS : XmlInitEncoding)(&parser->m_initEncoding, &parser->m_encoding, s))
return XML_ERROR_NONE;
- return handleUnknownEncoding(parser, protocolEncodingName);
+ return handleUnknownEncoding(parser, parser->m_protocolEncodingName);
}
static enum XML_Error
@@ -3971,13 +3957,13 @@ processXmlDecl(XML_Parser parser, int isGeneralTextEntity,
const char *versionend;
const XML_Char *storedversion = NULL;
int standalone = -1;
- if (!(ns
+ if (!(parser->m_ns
? XmlParseXmlDeclNS
: XmlParseXmlDecl)(isGeneralTextEntity,
- encoding,
+ parser->m_encoding,
s,
next,
- &eventPtr,
+ &parser->m_eventPtr,
&version,
&versionend,
&encodingName,
@@ -3989,69 +3975,69 @@ processXmlDecl(XML_Parser parser, int isGeneralTextEntity,
return XML_ERROR_XML_DECL;
}
if (!isGeneralTextEntity && standalone == 1) {
- _dtd->standalone = XML_TRUE;
+ parser->m_dtd->standalone = XML_TRUE;
#ifdef XML_DTD
- if (paramEntityParsing == XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE)
- paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER;
+ if (parser->m_paramEntityParsing == XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE)
+ parser->m_paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER;
#endif /* XML_DTD */
}
- if (xmlDeclHandler) {
+ if (parser->m_xmlDeclHandler) {
if (encodingName != NULL) {
- storedEncName = poolStoreString(&temp2Pool,
- encoding,
+ storedEncName = poolStoreString(&parser->m_temp2Pool,
+ parser->m_encoding,
encodingName,
encodingName
- + XmlNameLength(encoding, encodingName));
+ + XmlNameLength(parser->m_encoding, encodingName));
if (!storedEncName)
return XML_ERROR_NO_MEMORY;
- poolFinish(&temp2Pool);
+ poolFinish(&parser->m_temp2Pool);
}
if (version) {
- storedversion = poolStoreString(&temp2Pool,
- encoding,
+ storedversion = poolStoreString(&parser->m_temp2Pool,
+ parser->m_encoding,
version,
- versionend - encoding->minBytesPerChar);
+ versionend - parser->m_encoding->minBytesPerChar);
if (!storedversion)
return XML_ERROR_NO_MEMORY;
}
- xmlDeclHandler(handlerArg, storedversion, storedEncName, standalone);
+ parser->m_xmlDeclHandler(parser->m_handlerArg, storedversion, storedEncName, standalone);
}
- else if (defaultHandler)
- reportDefault(parser, encoding, s, next);
- if (protocolEncodingName == NULL) {
+ else if (parser->m_defaultHandler)
+ reportDefault(parser, parser->m_encoding, s, next);
+ if (parser->m_protocolEncodingName == NULL) {
if (newEncoding) {
/* Check that the specified encoding does not conflict with what
* the parser has already deduced. Do we have the same number
* of bytes in the smallest representation of a character? If
* this is UTF-16, is it the same endianness?
*/
- if (newEncoding->minBytesPerChar != encoding->minBytesPerChar
+ if (newEncoding->minBytesPerChar != parser->m_encoding->minBytesPerChar
|| (newEncoding->minBytesPerChar == 2 &&
- newEncoding != encoding)) {
- eventPtr = encodingName;
+ newEncoding != parser->m_encoding)) {
+ parser->m_eventPtr = encodingName;
return XML_ERROR_INCORRECT_ENCODING;
}
- encoding = newEncoding;
+ parser->m_encoding = newEncoding;
}
else if (encodingName) {
enum XML_Error result;
if (!storedEncName) {
storedEncName = poolStoreString(
- &temp2Pool, encoding, encodingName,
- encodingName + XmlNameLength(encoding, encodingName));
+ &parser->m_temp2Pool, parser->m_encoding, encodingName,
+ encodingName + XmlNameLength(parser->m_encoding, encodingName));
if (!storedEncName)
return XML_ERROR_NO_MEMORY;
}
result = handleUnknownEncoding(parser, storedEncName);
- poolClear(&temp2Pool);
+ poolClear(&parser->m_temp2Pool);
if (result == XML_ERROR_UNKNOWN_ENCODING)
- eventPtr = encodingName;
+ parser->m_eventPtr = encodingName;
return result;
}
}
if (storedEncName || storedversion)
- poolClear(&temp2Pool);
+ poolClear(&parser->m_temp2Pool);
return XML_ERROR_NONE;
}
@@ -4059,7 +4045,7 @@ processXmlDecl(XML_Parser parser, int isGeneralTextEntity,
static enum XML_Error
handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName)
{
- if (unknownEncodingHandler) {
+ if (parser->m_unknownEncodingHandler) {
XML_Encoding info;
int i;
for (i = 0; i < 256; i++)
@@ -4067,25 +4053,25 @@ handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName)
info.convert = NULL;
info.data = NULL;
info.release = NULL;
- if (unknownEncodingHandler(unknownEncodingHandlerData, encodingName,
+ if (parser->m_unknownEncodingHandler(parser->m_unknownEncodingHandlerData, encodingName,
&info)) {
ENCODING *enc;
- unknownEncodingMem = MALLOC(XmlSizeOfUnknownEncoding());
- if (!unknownEncodingMem) {
+ parser->m_unknownEncodingMem = MALLOC(parser, XmlSizeOfUnknownEncoding());
+ if (!parser->m_unknownEncodingMem) {
if (info.release)
info.release(info.data);
return XML_ERROR_NO_MEMORY;
}
- enc = (ns
+ enc = (parser->m_ns
? XmlInitUnknownEncodingNS
- : XmlInitUnknownEncoding)(unknownEncodingMem,
+ : XmlInitUnknownEncoding)(parser->m_unknownEncodingMem,
info.map,
info.convert,
info.data);
if (enc) {
- unknownEncodingData = info.data;
- unknownEncodingRelease = info.release;
- encoding = enc;
+ parser->m_unknownEncodingData = info.data;
+ parser->m_unknownEncodingRelease = info.release;
+ parser->m_encoding = enc;
return XML_ERROR_NONE;
}
}
@@ -4104,7 +4090,7 @@ prologInitProcessor(XML_Parser parser,
enum XML_Error result = initializeEncoding(parser);
if (result != XML_ERROR_NONE)
return result;
- processor = prologProcessor;
+ parser->m_processor = prologProcessor;
return prologProcessor(parser, s, end, nextPtr);
}
@@ -4122,14 +4108,14 @@ externalParEntInitProcessor(XML_Parser parser,
/* we know now that XML_Parse(Buffer) has been called,
so we consider the external parameter entity read */
- _dtd->paramEntityRead = XML_TRUE;
+ parser->m_dtd->paramEntityRead = XML_TRUE;
- if (prologState.inEntityValue) {
- processor = entityValueInitProcessor;
+ if (parser->m_prologState.inEntityValue) {
+ parser->m_processor = entityValueInitProcessor;
return entityValueInitProcessor(parser, s, end, nextPtr);
}
else {
- processor = externalParEntProcessor;
+ parser->m_processor = externalParEntProcessor;
return externalParEntProcessor(parser, s, end, nextPtr);
}
}
@@ -4143,13 +4129,13 @@ entityValueInitProcessor(XML_Parser parser,
int tok;
const char *start = s;
const char *next = start;
- eventPtr = start;
+ parser->m_eventPtr = start;
for (;;) {
- tok = XmlPrologTok(encoding, start, end, &next);
- eventEndPtr = next;
+ tok = XmlPrologTok(parser->m_encoding, start, end, &next);
+ parser->m_eventEndPtr = next;
if (tok <= 0) {
- if (!ps_finalBuffer && tok != XML_TOK_INVALID) {
+ if (!parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) {
*nextPtr = s;
return XML_ERROR_NONE;
}
@@ -4165,23 +4151,23 @@ entityValueInitProcessor(XML_Parser parser,
break;
}
/* found end of entity value - can store it now */
- return storeEntityValue(parser, encoding, s, end);
+ return storeEntityValue(parser, parser->m_encoding, s, end);
}
else if (tok == XML_TOK_XML_DECL) {
enum XML_Error result;
result = processXmlDecl(parser, 0, start, next);
if (result != XML_ERROR_NONE)
return result;
- /* At this point, ps_parsing cannot be XML_SUSPENDED. For that
+ /* At this point, m_parsingStatus.parsing cannot be XML_SUSPENDED. For that
* to happen, a parameter entity parsing handler must have
* attempted to suspend the parser, which fails and raises an
* error. The parser can be aborted, but can't be suspended.
*/
- if (ps_parsing == XML_FINISHED)
+ if (parser->m_parsingStatus.parsing == XML_FINISHED)
return XML_ERROR_ABORTED;
*nextPtr = next;
/* stop scanning for text declaration - we found one */
- processor = entityValueProcessor;
+ parser->m_processor = entityValueProcessor;
return entityValueProcessor(parser, next, end, nextPtr);
}
/* If we are at the end of the buffer, this would cause XmlPrologTok to
@@ -4191,7 +4177,7 @@ entityValueInitProcessor(XML_Parser parser,
then, when this routine is entered the next time, XmlPrologTok will
return XML_TOK_INVALID, since the BOM is still in the buffer
*/
- else if (tok == XML_TOK_BOM && next == end && !ps_finalBuffer) {
+ else if (tok == XML_TOK_BOM && next == end && !parser->m_parsingStatus.finalBuffer) {
*nextPtr = next;
return XML_ERROR_NONE;
}
@@ -4204,7 +4190,7 @@ entityValueInitProcessor(XML_Parser parser,
return XML_ERROR_SYNTAX;
}
start = next;
- eventPtr = start;
+ parser->m_eventPtr = start;
}
}
@@ -4217,9 +4203,9 @@ externalParEntProcessor(XML_Parser parser,
const char *next = s;
int tok;
- tok = XmlPrologTok(encoding, s, end, &next);
+ tok = XmlPrologTok(parser->m_encoding, s, end, &next);
if (tok <= 0) {
- if (!ps_finalBuffer && tok != XML_TOK_INVALID) {
+ if (!parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) {
*nextPtr = s;
return XML_ERROR_NONE;
}
@@ -4241,12 +4227,12 @@ externalParEntProcessor(XML_Parser parser,
*/
else if (tok == XML_TOK_BOM) {
s = next;
- tok = XmlPrologTok(encoding, s, end, &next);
+ tok = XmlPrologTok(parser->m_encoding, s, end, &next);
}
- processor = prologProcessor;
- return doProlog(parser, encoding, s, end, tok, next,
- nextPtr, (XML_Bool)!ps_finalBuffer);
+ parser->m_processor = prologProcessor;
+ return doProlog(parser, parser->m_encoding, s, end, tok, next,
+ nextPtr, (XML_Bool)!parser->m_parsingStatus.finalBuffer);
}
static enum XML_Error PTRCALL
@@ -4257,13 +4243,13 @@ entityValueProcessor(XML_Parser parser,
{
const char *start = s;
const char *next = s;
- const ENCODING *enc = encoding;
+ const ENCODING *enc = parser->m_encoding;
int tok;
for (;;) {
tok = XmlPrologTok(enc, start, end, &next);
if (tok <= 0) {
- if (!ps_finalBuffer && tok != XML_TOK_INVALID) {
+ if (!parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) {
*nextPtr = s;
return XML_ERROR_NONE;
}
@@ -4294,9 +4280,9 @@ prologProcessor(XML_Parser parser,
const char **nextPtr)
{
const char *next = s;
- int tok = XmlPrologTok(encoding, s, end, &next);
- return doProlog(parser, encoding, s, end, tok, next,
- nextPtr, (XML_Bool)!ps_finalBuffer);
+ int tok = XmlPrologTok(parser->m_encoding, s, end, &next);
+ return doProlog(parser, parser->m_encoding, s, end, tok, next,
+ nextPtr, (XML_Bool)!parser->m_parsingStatus.finalBuffer);
}
static enum XML_Error
@@ -4333,19 +4319,19 @@ doProlog(XML_Parser parser,
static const XML_Char enumValueStart[] = { ASCII_LPAREN, '\0' };
/* save one level of indirection */
- DTD * const dtd = _dtd;
+ DTD * const dtd = parser->m_dtd;
const char **eventPP;
const char **eventEndPP;
enum XML_Content_Quant quant;
- if (enc == encoding) {
- eventPP = &eventPtr;
- eventEndPP = &eventEndPtr;
+ if (enc == parser->m_encoding) {
+ eventPP = &parser->m_eventPtr;
+ eventEndPP = &parser->m_eventEndPtr;
}
else {
- eventPP = &(openInternalEntities->internalEventPtr);
- eventEndPP = &(openInternalEntities->internalEventEndPtr);
+ eventPP = &(parser->m_openInternalEntities->internalEventPtr);
+ eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr);
}
for (;;) {
@@ -4372,7 +4358,7 @@ doProlog(XML_Parser parser,
case XML_TOK_NONE:
#ifdef XML_DTD
/* for internal PE NOT referenced between declarations */
- if (enc != encoding && !openInternalEntities->betweenDecl) {
+ if (enc != parser->m_encoding && !parser->m_openInternalEntities->betweenDecl) {
*nextPtr = s;
return XML_ERROR_NONE;
}
@@ -4380,8 +4366,8 @@ doProlog(XML_Parser parser,
complete markup, not only for external PEs, but also for
internal PEs if the reference occurs between declarations.
*/
- if (isParamEntity || enc != encoding) {
- if (XmlTokenRole(&prologState, XML_TOK_NONE, end, end, enc)
+ if (parser->m_isParamEntity || enc != parser->m_encoding) {
+ if (XmlTokenRole(&parser->m_prologState, XML_TOK_NONE, end, end, enc)
== XML_ROLE_ERROR)
return XML_ERROR_INCOMPLETE_PE;
*nextPtr = s;
@@ -4395,34 +4381,34 @@ doProlog(XML_Parser parser,
break;
}
}
- role = XmlTokenRole(&prologState, tok, s, next, enc);
+ role = XmlTokenRole(&parser->m_prologState, tok, s, next, enc);
switch (role) {
case XML_ROLE_XML_DECL:
{
enum XML_Error result = processXmlDecl(parser, 0, s, next);
if (result != XML_ERROR_NONE)
return result;
- enc = encoding;
+ enc = parser->m_encoding;
handleDefault = XML_FALSE;
}
break;
case XML_ROLE_DOCTYPE_NAME:
- if (startDoctypeDeclHandler) {
- doctypeName = poolStoreString(&tempPool, enc, s, next);
- if (!doctypeName)
+ if (parser->m_startDoctypeDeclHandler) {
+ parser->m_doctypeName = poolStoreString(&parser->m_tempPool, enc, s, next);
+ if (!parser->m_doctypeName)
return XML_ERROR_NO_MEMORY;
- poolFinish(&tempPool);
- doctypePubid = NULL;
+ poolFinish(&parser->m_tempPool);
+ parser->m_doctypePubid = NULL;
handleDefault = XML_FALSE;
}
- doctypeSysid = NULL; /* always initialize to NULL */
+ parser->m_doctypeSysid = NULL; /* always initialize to NULL */
break;
case XML_ROLE_DOCTYPE_INTERNAL_SUBSET:
- if (startDoctypeDeclHandler) {
- startDoctypeDeclHandler(handlerArg, doctypeName, doctypeSysid,
- doctypePubid, 1);
- doctypeName = NULL;
- poolClear(&tempPool);
+ if (parser->m_startDoctypeDeclHandler) {
+ parser->m_startDoctypeDeclHandler(parser->m_handlerArg, parser->m_doctypeName, parser->m_doctypeSysid,
+ parser->m_doctypePubid, 1);
+ parser->m_doctypeName = NULL;
+ poolClear(&parser->m_tempPool);
handleDefault = XML_FALSE;
}
break;
@@ -4432,34 +4418,34 @@ doProlog(XML_Parser parser,
enum XML_Error result = processXmlDecl(parser, 1, s, next);
if (result != XML_ERROR_NONE)
return result;
- enc = encoding;
+ enc = parser->m_encoding;
handleDefault = XML_FALSE;
}
break;
#endif /* XML_DTD */
case XML_ROLE_DOCTYPE_PUBLIC_ID:
#ifdef XML_DTD
- useForeignDTD = XML_FALSE;
- declEntity = (ENTITY *)lookup(parser,
+ parser->m_useForeignDTD = XML_FALSE;
+ parser->m_declEntity = (ENTITY *)lookup(parser,
&dtd->paramEntities,
externalSubsetName,
sizeof(ENTITY));
- if (!declEntity)
+ if (!parser->m_declEntity)
return XML_ERROR_NO_MEMORY;
#endif /* XML_DTD */
dtd->hasParamEntityRefs = XML_TRUE;
- if (startDoctypeDeclHandler) {
+ if (parser->m_startDoctypeDeclHandler) {
XML_Char *pubId;
if (!XmlIsPublicId(enc, s, next, eventPP))
return XML_ERROR_PUBLICID;
- pubId = poolStoreString(&tempPool, enc,
+ pubId = poolStoreString(&parser->m_tempPool, enc,
s + enc->minBytesPerChar,
next - enc->minBytesPerChar);
if (!pubId)
return XML_ERROR_NO_MEMORY;
normalizePublicId(pubId);
- poolFinish(&tempPool);
- doctypePubid = pubId;
+ poolFinish(&parser->m_tempPool);
+ parser->m_doctypePubid = pubId;
handleDefault = XML_FALSE;
goto alreadyChecked;
}
@@ -4468,7 +4454,7 @@ doProlog(XML_Parser parser,
if (!XmlIsPublicId(enc, s, next, eventPP))
return XML_ERROR_PUBLICID;
alreadyChecked:
- if (dtd->keepProcessing && declEntity) {
+ if (dtd->keepProcessing && parser->m_declEntity) {
XML_Char *tem = poolStoreString(&dtd->pool,
enc,
s + enc->minBytesPerChar,
@@ -4476,28 +4462,31 @@ doProlog(XML_Parser parser,
if (!tem)
return XML_ERROR_NO_MEMORY;
normalizePublicId(tem);
- declEntity->publicId = tem;
+ parser->m_declEntity->publicId = tem;
poolFinish(&dtd->pool);
- if (entityDeclHandler)
+ /* Don't suppress the default handler if we fell through from
+ * the XML_ROLE_DOCTYPE_PUBLIC_ID case.
+ */
+ if (parser->m_entityDeclHandler && role == XML_ROLE_ENTITY_PUBLIC_ID)
handleDefault = XML_FALSE;
}
break;
case XML_ROLE_DOCTYPE_CLOSE:
- if (doctypeName) {
- startDoctypeDeclHandler(handlerArg, doctypeName,
- doctypeSysid, doctypePubid, 0);
- poolClear(&tempPool);
+ if (parser->m_doctypeName) {
+ parser->m_startDoctypeDeclHandler(parser->m_handlerArg, parser->m_doctypeName,
+ parser->m_doctypeSysid, parser->m_doctypePubid, 0);
+ poolClear(&parser->m_tempPool);
handleDefault = XML_FALSE;
}
- /* doctypeSysid will be non-NULL in the case of a previous
- XML_ROLE_DOCTYPE_SYSTEM_ID, even if startDoctypeDeclHandler
+ /* parser->m_doctypeSysid will be non-NULL in the case of a previous
+ XML_ROLE_DOCTYPE_SYSTEM_ID, even if parser->m_startDoctypeDeclHandler
was not set, indicating an external subset
*/
#ifdef XML_DTD
- if (doctypeSysid || useForeignDTD) {
+ if (parser->m_doctypeSysid || parser->m_useForeignDTD) {
XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs;
dtd->hasParamEntityRefs = XML_TRUE;
- if (paramEntityParsing && externalEntityRefHandler) {
+ if (parser->m_paramEntityParsing && parser->m_externalEntityRefHandler) {
ENTITY *entity = (ENTITY *)lookup(parser,
&dtd->paramEntities,
externalSubsetName,
@@ -4510,10 +4499,10 @@ doProlog(XML_Parser parser,
*/
return XML_ERROR_NO_MEMORY; /* LCOV_EXCL_LINE */
}
- if (useForeignDTD)
- entity->base = curBase;
+ if (parser->m_useForeignDTD)
+ entity->base = parser->m_curBase;
dtd->paramEntityRead = XML_FALSE;
- if (!externalEntityRefHandler(externalEntityRefHandlerArg,
+ if (!parser->m_externalEntityRefHandler(parser->m_externalEntityRefHandlerArg,
0,
entity->base,
entity->systemId,
@@ -4521,22 +4510,22 @@ doProlog(XML_Parser parser,
return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
if (dtd->paramEntityRead) {
if (!dtd->standalone &&
- notStandaloneHandler &&
- !notStandaloneHandler(handlerArg))
+ parser->m_notStandaloneHandler &&
+ !parser->m_notStandaloneHandler(parser->m_handlerArg))
return XML_ERROR_NOT_STANDALONE;
}
/* if we didn't read the foreign DTD then this means that there
is no external subset and we must reset dtd->hasParamEntityRefs
*/
- else if (!doctypeSysid)
+ else if (!parser->m_doctypeSysid)
dtd->hasParamEntityRefs = hadParamEntityRefs;
/* end of DTD - no need to update dtd->keepProcessing */
}
- useForeignDTD = XML_FALSE;
+ parser->m_useForeignDTD = XML_FALSE;
}
#endif /* XML_DTD */
- if (endDoctypeDeclHandler) {
- endDoctypeDeclHandler(handlerArg);
+ if (parser->m_endDoctypeDeclHandler) {
+ parser->m_endDoctypeDeclHandler(parser->m_handlerArg);
handleDefault = XML_FALSE;
}
break;
@@ -4545,18 +4534,18 @@ doProlog(XML_Parser parser,
/* if there is no DOCTYPE declaration then now is the
last chance to read the foreign DTD
*/
- if (useForeignDTD) {
+ if (parser->m_useForeignDTD) {
XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs;
dtd->hasParamEntityRefs = XML_TRUE;
- if (paramEntityParsing && externalEntityRefHandler) {
+ if (parser->m_paramEntityParsing && parser->m_externalEntityRefHandler) {
ENTITY *entity = (ENTITY *)lookup(parser, &dtd->paramEntities,
externalSubsetName,
sizeof(ENTITY));
if (!entity)
return XML_ERROR_NO_MEMORY;
- entity->base = curBase;
+ entity->base = parser->m_curBase;
dtd->paramEntityRead = XML_FALSE;
- if (!externalEntityRefHandler(externalEntityRefHandlerArg,
+ if (!parser->m_externalEntityRefHandler(parser->m_externalEntityRefHandlerArg,
0,
entity->base,
entity->systemId,
@@ -4564,8 +4553,8 @@ doProlog(XML_Parser parser,
return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
if (dtd->paramEntityRead) {
if (!dtd->standalone &&
- notStandaloneHandler &&
- !notStandaloneHandler(handlerArg))
+ parser->m_notStandaloneHandler &&
+ !parser->m_notStandaloneHandler(parser->m_handlerArg))
return XML_ERROR_NOT_STANDALONE;
}
/* if we didn't read the foreign DTD then this means that there
@@ -4577,55 +4566,55 @@ doProlog(XML_Parser parser,
}
}
#endif /* XML_DTD */
- processor = contentProcessor;
+ parser->m_processor = contentProcessor;
return contentProcessor(parser, s, end, nextPtr);
case XML_ROLE_ATTLIST_ELEMENT_NAME:
- declElementType = getElementType(parser, enc, s, next);
- if (!declElementType)
+ parser->m_declElementType = getElementType(parser, enc, s, next);
+ if (!parser->m_declElementType)
return XML_ERROR_NO_MEMORY;
goto checkAttListDeclHandler;
case XML_ROLE_ATTRIBUTE_NAME:
- declAttributeId = getAttributeId(parser, enc, s, next);
- if (!declAttributeId)
+ parser->m_declAttributeId = getAttributeId(parser, enc, s, next);
+ if (!parser->m_declAttributeId)
return XML_ERROR_NO_MEMORY;
- declAttributeIsCdata = XML_FALSE;
- declAttributeType = NULL;
- declAttributeIsId = XML_FALSE;
+ parser->m_declAttributeIsCdata = XML_FALSE;
+ parser->m_declAttributeType = NULL;
+ parser->m_declAttributeIsId = XML_FALSE;
goto checkAttListDeclHandler;
case XML_ROLE_ATTRIBUTE_TYPE_CDATA:
- declAttributeIsCdata = XML_TRUE;
- declAttributeType = atypeCDATA;
+ parser->m_declAttributeIsCdata = XML_TRUE;
+ parser->m_declAttributeType = atypeCDATA;
goto checkAttListDeclHandler;
case XML_ROLE_ATTRIBUTE_TYPE_ID:
- declAttributeIsId = XML_TRUE;
- declAttributeType = atypeID;
+ parser->m_declAttributeIsId = XML_TRUE;
+ parser->m_declAttributeType = atypeID;
goto checkAttListDeclHandler;
case XML_ROLE_ATTRIBUTE_TYPE_IDREF:
- declAttributeType = atypeIDREF;
+ parser->m_declAttributeType = atypeIDREF;
goto checkAttListDeclHandler;
case XML_ROLE_ATTRIBUTE_TYPE_IDREFS:
- declAttributeType = atypeIDREFS;
+ parser->m_declAttributeType = atypeIDREFS;
goto checkAttListDeclHandler;
case XML_ROLE_ATTRIBUTE_TYPE_ENTITY:
- declAttributeType = atypeENTITY;
+ parser->m_declAttributeType = atypeENTITY;
goto checkAttListDeclHandler;
case XML_ROLE_ATTRIBUTE_TYPE_ENTITIES:
- declAttributeType = atypeENTITIES;
+ parser->m_declAttributeType = atypeENTITIES;
goto checkAttListDeclHandler;
case XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN:
- declAttributeType = atypeNMTOKEN;
+ parser->m_declAttributeType = atypeNMTOKEN;
goto checkAttListDeclHandler;
case XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS:
- declAttributeType = atypeNMTOKENS;
+ parser->m_declAttributeType = atypeNMTOKENS;
checkAttListDeclHandler:
- if (dtd->keepProcessing && attlistDeclHandler)
+ if (dtd->keepProcessing && parser->m_attlistDeclHandler)
handleDefault = XML_FALSE;
break;
case XML_ROLE_ATTRIBUTE_ENUM_VALUE:
case XML_ROLE_ATTRIBUTE_NOTATION_VALUE:
- if (dtd->keepProcessing && attlistDeclHandler) {
+ if (dtd->keepProcessing && parser->m_attlistDeclHandler) {
const XML_Char *prefix;
- if (declAttributeType) {
+ if (parser->m_declAttributeType) {
prefix = enumValueSep;
}
else {
@@ -4633,37 +4622,37 @@ doProlog(XML_Parser parser,
? notationPrefix
: enumValueStart);
}
- if (!poolAppendString(&tempPool, prefix))
+ if (!poolAppendString(&parser->m_tempPool, prefix))
return XML_ERROR_NO_MEMORY;
- if (!poolAppend(&tempPool, enc, s, next))
+ if (!poolAppend(&parser->m_tempPool, enc, s, next))
return XML_ERROR_NO_MEMORY;
- declAttributeType = tempPool.start;
+ parser->m_declAttributeType = parser->m_tempPool.start;
handleDefault = XML_FALSE;
}
break;
case XML_ROLE_IMPLIED_ATTRIBUTE_VALUE:
case XML_ROLE_REQUIRED_ATTRIBUTE_VALUE:
if (dtd->keepProcessing) {
- if (!defineAttribute(declElementType, declAttributeId,
- declAttributeIsCdata, declAttributeIsId,
+ if (!defineAttribute(parser->m_declElementType, parser->m_declAttributeId,
+ parser->m_declAttributeIsCdata, parser->m_declAttributeIsId,
0, parser))
return XML_ERROR_NO_MEMORY;
- if (attlistDeclHandler && declAttributeType) {
- if (*declAttributeType == XML_T(ASCII_LPAREN)
- || (*declAttributeType == XML_T(ASCII_N)
- && declAttributeType[1] == XML_T(ASCII_O))) {
+ if (parser->m_attlistDeclHandler && parser->m_declAttributeType) {
+ if (*parser->m_declAttributeType == XML_T(ASCII_LPAREN)
+ || (*parser->m_declAttributeType == XML_T(ASCII_N)
+ && parser->m_declAttributeType[1] == XML_T(ASCII_O))) {
/* Enumerated or Notation type */
- if (!poolAppendChar(&tempPool, XML_T(ASCII_RPAREN))
- || !poolAppendChar(&tempPool, XML_T('\0')))
+ if (!poolAppendChar(&parser->m_tempPool, XML_T(ASCII_RPAREN))
+ || !poolAppendChar(&parser->m_tempPool, XML_T('\0')))
return XML_ERROR_NO_MEMORY;
- declAttributeType = tempPool.start;
- poolFinish(&tempPool);
+ parser->m_declAttributeType = parser->m_tempPool.start;
+ poolFinish(&parser->m_tempPool);
}
*eventEndPP = s;
- attlistDeclHandler(handlerArg, declElementType->name,
- declAttributeId->name, declAttributeType,
+ parser->m_attlistDeclHandler(parser->m_handlerArg, parser->m_declElementType->name,
+ parser->m_declAttributeId->name, parser->m_declAttributeType,
0, role == XML_ROLE_REQUIRED_ATTRIBUTE_VALUE);
- poolClear(&tempPool);
+ poolClear(&parser->m_tempPool);
handleDefault = XML_FALSE;
}
}
@@ -4673,7 +4662,7 @@ doProlog(XML_Parser parser,
if (dtd->keepProcessing) {
const XML_Char *attVal;
enum XML_Error result =
- storeAttributeValue(parser, enc, declAttributeIsCdata,
+ storeAttributeValue(parser, enc, parser->m_declAttributeIsCdata,
s + enc->minBytesPerChar,
next - enc->minBytesPerChar,
&dtd->pool);
@@ -4682,26 +4671,26 @@ doProlog(XML_Parser parser,
attVal = poolStart(&dtd->pool);
poolFinish(&dtd->pool);
/* ID attributes aren't allowed to have a default */
- if (!defineAttribute(declElementType, declAttributeId,
- declAttributeIsCdata, XML_FALSE, attVal, parser))
+ if (!defineAttribute(parser->m_declElementType, parser->m_declAttributeId,
+ parser->m_declAttributeIsCdata, XML_FALSE, attVal, parser))
return XML_ERROR_NO_MEMORY;
- if (attlistDeclHandler && declAttributeType) {
- if (*declAttributeType == XML_T(ASCII_LPAREN)
- || (*declAttributeType == XML_T(ASCII_N)
- && declAttributeType[1] == XML_T(ASCII_O))) {
+ if (parser->m_attlistDeclHandler && parser->m_declAttributeType) {
+ if (*parser->m_declAttributeType == XML_T(ASCII_LPAREN)
+ || (*parser->m_declAttributeType == XML_T(ASCII_N)
+ && parser->m_declAttributeType[1] == XML_T(ASCII_O))) {
/* Enumerated or Notation type */
- if (!poolAppendChar(&tempPool, XML_T(ASCII_RPAREN))
- || !poolAppendChar(&tempPool, XML_T('\0')))
+ if (!poolAppendChar(&parser->m_tempPool, XML_T(ASCII_RPAREN))
+ || !poolAppendChar(&parser->m_tempPool, XML_T('\0')))
return XML_ERROR_NO_MEMORY;
- declAttributeType = tempPool.start;
- poolFinish(&tempPool);
+ parser->m_declAttributeType = parser->m_tempPool.start;
+ poolFinish(&parser->m_tempPool);
}
*eventEndPP = s;
- attlistDeclHandler(handlerArg, declElementType->name,
- declAttributeId->name, declAttributeType,
+ parser->m_attlistDeclHandler(parser->m_handlerArg, parser->m_declElementType->name,
+ parser->m_declAttributeId->name, parser->m_declAttributeType,
attVal,
role == XML_ROLE_FIXED_ATTRIBUTE_VALUE);
- poolClear(&tempPool);
+ poolClear(&parser->m_tempPool);
handleDefault = XML_FALSE;
}
}
@@ -4711,18 +4700,18 @@ doProlog(XML_Parser parser,
enum XML_Error result = storeEntityValue(parser, enc,
s + enc->minBytesPerChar,
next - enc->minBytesPerChar);
- if (declEntity) {
- declEntity->textPtr = poolStart(&dtd->entityValuePool);
- declEntity->textLen = (int)(poolLength(&dtd->entityValuePool));
+ if (parser->m_declEntity) {
+ parser->m_declEntity->textPtr = poolStart(&dtd->entityValuePool);
+ parser->m_declEntity->textLen = (int)(poolLength(&dtd->entityValuePool));
poolFinish(&dtd->entityValuePool);
- if (entityDeclHandler) {
+ if (parser->m_entityDeclHandler) {
*eventEndPP = s;
- entityDeclHandler(handlerArg,
- declEntity->name,
- declEntity->is_param,
- declEntity->textPtr,
- declEntity->textLen,
- curBase, 0, 0, 0);
+ parser->m_entityDeclHandler(parser->m_handlerArg,
+ parser->m_declEntity->name,
+ parser->m_declEntity->is_param,
+ parser->m_declEntity->textPtr,
+ parser->m_declEntity->textLen,
+ parser->m_curBase, 0, 0, 0);
handleDefault = XML_FALSE;
}
}
@@ -4734,97 +4723,100 @@ doProlog(XML_Parser parser,
break;
case XML_ROLE_DOCTYPE_SYSTEM_ID:
#ifdef XML_DTD
- useForeignDTD = XML_FALSE;
+ parser->m_useForeignDTD = XML_FALSE;
#endif /* XML_DTD */
dtd->hasParamEntityRefs = XML_TRUE;
- if (startDoctypeDeclHandler) {
- doctypeSysid = poolStoreString(&tempPool, enc,
+ if (parser->m_startDoctypeDeclHandler) {
+ parser->m_doctypeSysid = poolStoreString(&parser->m_tempPool, enc,
s + enc->minBytesPerChar,
next - enc->minBytesPerChar);
- if (doctypeSysid == NULL)
+ if (parser->m_doctypeSysid == NULL)
return XML_ERROR_NO_MEMORY;
- poolFinish(&tempPool);
+ poolFinish(&parser->m_tempPool);
handleDefault = XML_FALSE;
}
#ifdef XML_DTD
else
- /* use externalSubsetName to make doctypeSysid non-NULL
- for the case where no startDoctypeDeclHandler is set */
- doctypeSysid = externalSubsetName;
+ /* use externalSubsetName to make parser->m_doctypeSysid non-NULL
+ for the case where no parser->m_startDoctypeDeclHandler is set */
+ parser->m_doctypeSysid = externalSubsetName;
#endif /* XML_DTD */
if (!dtd->standalone
#ifdef XML_DTD
- && !paramEntityParsing
+ && !parser->m_paramEntityParsing
#endif /* XML_DTD */
- && notStandaloneHandler
- && !notStandaloneHandler(handlerArg))
+ && parser->m_notStandaloneHandler
+ && !parser->m_notStandaloneHandler(parser->m_handlerArg))
return XML_ERROR_NOT_STANDALONE;
#ifndef XML_DTD
break;
#else /* XML_DTD */
- if (!declEntity) {
- declEntity = (ENTITY *)lookup(parser,
+ if (!parser->m_declEntity) {
+ parser->m_declEntity = (ENTITY *)lookup(parser,
&dtd->paramEntities,
externalSubsetName,
sizeof(ENTITY));
- if (!declEntity)
+ if (!parser->m_declEntity)
return XML_ERROR_NO_MEMORY;
- declEntity->publicId = NULL;
+ parser->m_declEntity->publicId = NULL;
}
- /* fall through */
#endif /* XML_DTD */
+ /* fall through */
case XML_ROLE_ENTITY_SYSTEM_ID:
- if (dtd->keepProcessing && declEntity) {
- declEntity->systemId = poolStoreString(&dtd->pool, enc,
+ if (dtd->keepProcessing && parser->m_declEntity) {
+ parser->m_declEntity->systemId = poolStoreString(&dtd->pool, enc,
s + enc->minBytesPerChar,
next - enc->minBytesPerChar);
- if (!declEntity->systemId)
+ if (!parser->m_declEntity->systemId)
return XML_ERROR_NO_MEMORY;
- declEntity->base = curBase;
+ parser->m_declEntity->base = parser->m_curBase;
poolFinish(&dtd->pool);
- if (entityDeclHandler)
+ /* Don't suppress the default handler if we fell through from
+ * the XML_ROLE_DOCTYPE_SYSTEM_ID case.
+ */
+ if (parser->m_entityDeclHandler && role == XML_ROLE_ENTITY_SYSTEM_ID)
handleDefault = XML_FALSE;
}
break;
case XML_ROLE_ENTITY_COMPLETE:
- if (dtd->keepProcessing && declEntity && entityDeclHandler) {
+ if (dtd->keepProcessing && parser->m_declEntity && parser->m_entityDeclHandler) {
*eventEndPP = s;
- entityDeclHandler(handlerArg,
- declEntity->name,
- declEntity->is_param,
+ parser->m_entityDeclHandler(parser->m_handlerArg,
+ parser->m_declEntity->name,
+ parser->m_declEntity->is_param,
0,0,
- declEntity->base,
- declEntity->systemId,
- declEntity->publicId,
+ parser->m_declEntity->base,
+ parser->m_declEntity->systemId,
+ parser->m_declEntity->publicId,
0);
handleDefault = XML_FALSE;
}
break;
case XML_ROLE_ENTITY_NOTATION_NAME:
- if (dtd->keepProcessing && declEntity) {
- declEntity->notation = poolStoreString(&dtd->pool, enc, s, next);
- if (!declEntity->notation)
+ if (dtd->keepProcessing && parser->m_declEntity) {
+ parser->m_declEntity->notation = poolStoreString(&dtd->pool, enc, s, next);
+ if (!parser->m_declEntity->notation)
return XML_ERROR_NO_MEMORY;
poolFinish(&dtd->pool);
- if (unparsedEntityDeclHandler) {
+ if (parser->m_unparsedEntityDeclHandler) {
*eventEndPP = s;
- unparsedEntityDeclHandler(handlerArg,
- declEntity->name,
- declEntity->base,
- declEntity->systemId,
- declEntity->publicId,
- declEntity->notation);
+ parser->m_unparsedEntityDeclHandler(parser->m_handlerArg,
+ parser->m_declEntity->name,
+ parser->m_declEntity->base,
+ parser->m_declEntity->systemId,
+ parser->m_declEntity->publicId,
+ parser->m_declEntity->notation);
handleDefault = XML_FALSE;
}
- else if (entityDeclHandler) {
+ else if (parser->m_entityDeclHandler) {
*eventEndPP = s;
- entityDeclHandler(handlerArg,
- declEntity->name,
+ parser->m_entityDeclHandler(parser->m_handlerArg,
+ parser->m_declEntity->name,
0,0,0,
- declEntity->base,
- declEntity->systemId,
- declEntity->publicId,
- declEntity->notation);
+ parser->m_declEntity->base,
+ parser->m_declEntity->systemId,
+ parser->m_declEntity->publicId,
+ parser->m_declEntity->notation);
handleDefault = XML_FALSE;
}
}
@@ -4832,36 +4824,36 @@ doProlog(XML_Parser parser,
case XML_ROLE_GENERAL_ENTITY_NAME:
{
if (XmlPredefinedEntityName(enc, s, next)) {
- declEntity = NULL;
+ parser->m_declEntity = NULL;
break;
}
if (dtd->keepProcessing) {
const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next);
if (!name)
return XML_ERROR_NO_MEMORY;
- declEntity = (ENTITY *)lookup(parser, &dtd->generalEntities, name,
+ parser->m_declEntity = (ENTITY *)lookup(parser, &dtd->generalEntities, name,
sizeof(ENTITY));
- if (!declEntity)
+ if (!parser->m_declEntity)
return XML_ERROR_NO_MEMORY;
- if (declEntity->name != name) {
+ if (parser->m_declEntity->name != name) {
poolDiscard(&dtd->pool);
- declEntity = NULL;
+ parser->m_declEntity = NULL;
}
else {
poolFinish(&dtd->pool);
- declEntity->publicId = NULL;
- declEntity->is_param = XML_FALSE;
+ parser->m_declEntity->publicId = NULL;
+ parser->m_declEntity->is_param = XML_FALSE;
/* if we have a parent parser or are reading an internal parameter
entity, then the entity declaration is not considered "internal"
*/
- declEntity->is_internal = !(parentParser || openInternalEntities);
- if (entityDeclHandler)
+ parser->m_declEntity->is_internal = !(parser->m_parentParser || parser->m_openInternalEntities);
+ if (parser->m_entityDeclHandler)
handleDefault = XML_FALSE;
}
}
else {
poolDiscard(&dtd->pool);
- declEntity = NULL;
+ parser->m_declEntity = NULL;
}
}
break;
@@ -4871,90 +4863,90 @@ doProlog(XML_Parser parser,
const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next);
if (!name)
return XML_ERROR_NO_MEMORY;
- declEntity = (ENTITY *)lookup(parser, &dtd->paramEntities,
+ parser->m_declEntity = (ENTITY *)lookup(parser, &dtd->paramEntities,
name, sizeof(ENTITY));
- if (!declEntity)
+ if (!parser->m_declEntity)
return XML_ERROR_NO_MEMORY;
- if (declEntity->name != name) {
+ if (parser->m_declEntity->name != name) {
poolDiscard(&dtd->pool);
- declEntity = NULL;
+ parser->m_declEntity = NULL;
}
else {
poolFinish(&dtd->pool);
- declEntity->publicId = NULL;
- declEntity->is_param = XML_TRUE;
+ parser->m_declEntity->publicId = NULL;
+ parser->m_declEntity->is_param = XML_TRUE;
/* if we have a parent parser or are reading an internal parameter
entity, then the entity declaration is not considered "internal"
*/
- declEntity->is_internal = !(parentParser || openInternalEntities);
- if (entityDeclHandler)
+ parser->m_declEntity->is_internal = !(parser->m_parentParser || parser->m_openInternalEntities);
+ if (parser->m_entityDeclHandler)
handleDefault = XML_FALSE;
}
}
else {
poolDiscard(&dtd->pool);
- declEntity = NULL;
+ parser->m_declEntity = NULL;
}
#else /* not XML_DTD */
- declEntity = NULL;
+ parser->m_declEntity = NULL;
#endif /* XML_DTD */
break;
case XML_ROLE_NOTATION_NAME:
- declNotationPublicId = NULL;
- declNotationName = NULL;
- if (notationDeclHandler) {
- declNotationName = poolStoreString(&tempPool, enc, s, next);
- if (!declNotationName)
+ parser->m_declNotationPublicId = NULL;
+ parser->m_declNotationName = NULL;
+ if (parser->m_notationDeclHandler) {
+ parser->m_declNotationName = poolStoreString(&parser->m_tempPool, enc, s, next);
+ if (!parser->m_declNotationName)
return XML_ERROR_NO_MEMORY;
- poolFinish(&tempPool);
+ poolFinish(&parser->m_tempPool);
handleDefault = XML_FALSE;
}
break;
case XML_ROLE_NOTATION_PUBLIC_ID:
if (!XmlIsPublicId(enc, s, next, eventPP))
return XML_ERROR_PUBLICID;
- if (declNotationName) { /* means notationDeclHandler != NULL */
- XML_Char *tem = poolStoreString(&tempPool,
+ if (parser->m_declNotationName) { /* means m_notationDeclHandler != NULL */
+ XML_Char *tem = poolStoreString(&parser->m_tempPool,
enc,
s + enc->minBytesPerChar,
next - enc->minBytesPerChar);
if (!tem)
return XML_ERROR_NO_MEMORY;
normalizePublicId(tem);
- declNotationPublicId = tem;
- poolFinish(&tempPool);
+ parser->m_declNotationPublicId = tem;
+ poolFinish(&parser->m_tempPool);
handleDefault = XML_FALSE;
}
break;
case XML_ROLE_NOTATION_SYSTEM_ID:
- if (declNotationName && notationDeclHandler) {
+ if (parser->m_declNotationName && parser->m_notationDeclHandler) {
const XML_Char *systemId
- = poolStoreString(&tempPool, enc,
+ = poolStoreString(&parser->m_tempPool, enc,
s + enc->minBytesPerChar,
next - enc->minBytesPerChar);
if (!systemId)
return XML_ERROR_NO_MEMORY;
*eventEndPP = s;
- notationDeclHandler(handlerArg,
- declNotationName,
- curBase,
+ parser->m_notationDeclHandler(parser->m_handlerArg,
+ parser->m_declNotationName,
+ parser->m_curBase,
systemId,
- declNotationPublicId);
+ parser->m_declNotationPublicId);
handleDefault = XML_FALSE;
}
- poolClear(&tempPool);
+ poolClear(&parser->m_tempPool);
break;
case XML_ROLE_NOTATION_NO_SYSTEM_ID:
- if (declNotationPublicId && notationDeclHandler) {
+ if (parser->m_declNotationPublicId && parser->m_notationDeclHandler) {
*eventEndPP = s;
- notationDeclHandler(handlerArg,
- declNotationName,
- curBase,
+ parser->m_notationDeclHandler(parser->m_handlerArg,
+ parser->m_declNotationName,
+ parser->m_curBase,
0,
- declNotationPublicId);
+ parser->m_declNotationPublicId);
handleDefault = XML_FALSE;
}
- poolClear(&tempPool);
+ poolClear(&parser->m_tempPool);
break;
case XML_ROLE_ERROR:
switch (tok) {
@@ -4971,45 +4963,45 @@ doProlog(XML_Parser parser,
case XML_ROLE_IGNORE_SECT:
{
enum XML_Error result;
- if (defaultHandler)
+ if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
handleDefault = XML_FALSE;
result = doIgnoreSection(parser, enc, &next, end, nextPtr, haveMore);
if (result != XML_ERROR_NONE)
return result;
else if (!next) {
- processor = ignoreSectionProcessor;
+ parser->m_processor = ignoreSectionProcessor;
return result;
}
}
break;
#endif /* XML_DTD */
case XML_ROLE_GROUP_OPEN:
- if (prologState.level >= groupSize) {
- if (groupSize) {
- char *temp = (char *)REALLOC(groupConnector, groupSize *= 2);
+ if (parser->m_prologState.level >= parser->m_groupSize) {
+ if (parser->m_groupSize) {
+ char *temp = (char *)REALLOC(parser, parser->m_groupConnector, parser->m_groupSize *= 2);
if (temp == NULL) {
- groupSize /= 2;
+ parser->m_groupSize /= 2;
return XML_ERROR_NO_MEMORY;
}
- groupConnector = temp;
+ parser->m_groupConnector = temp;
if (dtd->scaffIndex) {
- int *temp = (int *)REALLOC(dtd->scaffIndex,
- groupSize * sizeof(int));
+ int *temp = (int *)REALLOC(parser, dtd->scaffIndex,
+ parser->m_groupSize * sizeof(int));
if (temp == NULL)
return XML_ERROR_NO_MEMORY;
dtd->scaffIndex = temp;
}
}
else {
- groupConnector = (char *)MALLOC(groupSize = 32);
- if (!groupConnector) {
- groupSize = 0;
+ parser->m_groupConnector = (char *)MALLOC(parser, parser->m_groupSize = 32);
+ if (!parser->m_groupConnector) {
+ parser->m_groupSize = 0;
return XML_ERROR_NO_MEMORY;
}
}
}
- groupConnector[prologState.level] = 0;
+ parser->m_groupConnector[parser->m_prologState.level] = 0;
if (dtd->in_eldecl) {
int myindex = nextScaffoldPart(parser);
if (myindex < 0)
@@ -5017,37 +5009,37 @@ doProlog(XML_Parser parser,
dtd->scaffIndex[dtd->scaffLevel] = myindex;
dtd->scaffLevel++;
dtd->scaffold[myindex].type = XML_CTYPE_SEQ;
- if (elementDeclHandler)
+ if (parser->m_elementDeclHandler)
handleDefault = XML_FALSE;
}
break;
case XML_ROLE_GROUP_SEQUENCE:
- if (groupConnector[prologState.level] == ASCII_PIPE)
+ if (parser->m_groupConnector[parser->m_prologState.level] == ASCII_PIPE)
return XML_ERROR_SYNTAX;
- groupConnector[prologState.level] = ASCII_COMMA;
- if (dtd->in_eldecl && elementDeclHandler)
+ parser->m_groupConnector[parser->m_prologState.level] = ASCII_COMMA;
+ if (dtd->in_eldecl && parser->m_elementDeclHandler)
handleDefault = XML_FALSE;
break;
case XML_ROLE_GROUP_CHOICE:
- if (groupConnector[prologState.level] == ASCII_COMMA)
+ if (parser->m_groupConnector[parser->m_prologState.level] == ASCII_COMMA)
return XML_ERROR_SYNTAX;
if (dtd->in_eldecl
- && !groupConnector[prologState.level]
+ && !parser->m_groupConnector[parser->m_prologState.level]
&& (dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type
!= XML_CTYPE_MIXED)
) {
dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type
= XML_CTYPE_CHOICE;
- if (elementDeclHandler)
+ if (parser->m_elementDeclHandler)
handleDefault = XML_FALSE;
}
- groupConnector[prologState.level] = ASCII_PIPE;
+ parser->m_groupConnector[parser->m_prologState.level] = ASCII_PIPE;
break;
case XML_ROLE_PARAM_ENTITY_REF:
#ifdef XML_DTD
case XML_ROLE_INNER_PARAM_ENTITY_REF:
dtd->hasParamEntityRefs = XML_TRUE;
- if (!paramEntityParsing)
+ if (!parser->m_paramEntityParsing)
dtd->keepProcessing = dtd->standalone;
else {
const XML_Char *name;
@@ -5063,9 +5055,9 @@ doProlog(XML_Parser parser,
if yes, check that the entity exists, and that it is internal,
otherwise call the skipped entity handler
*/
- if (prologState.documentEntity &&
+ if (parser->m_prologState.documentEntity &&
(dtd->standalone
- ? !openInternalEntities
+ ? !parser->m_openInternalEntities
: !dtd->hasParamEntityRefs)) {
if (!entity)
return XML_ERROR_UNDEFINED_ENTITY;
@@ -5096,8 +5088,8 @@ doProlog(XML_Parser parser,
else if (!entity) {
dtd->keepProcessing = dtd->standalone;
/* cannot report skipped entities in declarations */
- if ((role == XML_ROLE_PARAM_ENTITY_REF) && skippedEntityHandler) {
- skippedEntityHandler(handlerArg, name, 1);
+ if ((role == XML_ROLE_PARAM_ENTITY_REF) && parser->m_skippedEntityHandler) {
+ parser->m_skippedEntityHandler(parser->m_handlerArg, name, 1);
handleDefault = XML_FALSE;
}
break;
@@ -5114,10 +5106,10 @@ doProlog(XML_Parser parser,
handleDefault = XML_FALSE;
break;
}
- if (externalEntityRefHandler) {
+ if (parser->m_externalEntityRefHandler) {
dtd->paramEntityRead = XML_FALSE;
entity->open = XML_TRUE;
- if (!externalEntityRefHandler(externalEntityRefHandlerArg,
+ if (!parser->m_externalEntityRefHandler(parser->m_externalEntityRefHandlerArg,
0,
entity->base,
entity->systemId,
@@ -5139,17 +5131,17 @@ doProlog(XML_Parser parser,
}
#endif /* XML_DTD */
if (!dtd->standalone &&
- notStandaloneHandler &&
- !notStandaloneHandler(handlerArg))
+ parser->m_notStandaloneHandler &&
+ !parser->m_notStandaloneHandler(parser->m_handlerArg))
return XML_ERROR_NOT_STANDALONE;
break;
/* Element declaration stuff */
case XML_ROLE_ELEMENT_NAME:
- if (elementDeclHandler) {
- declElementType = getElementType(parser, enc, s, next);
- if (!declElementType)
+ if (parser->m_elementDeclHandler) {
+ parser->m_declElementType = getElementType(parser, enc, s, next);
+ if (!parser->m_declElementType)
return XML_ERROR_NO_MEMORY;
dtd->scaffLevel = 0;
dtd->scaffCount = 0;
@@ -5161,8 +5153,8 @@ doProlog(XML_Parser parser,
case XML_ROLE_CONTENT_ANY:
case XML_ROLE_CONTENT_EMPTY:
if (dtd->in_eldecl) {
- if (elementDeclHandler) {
- XML_Content * content = (XML_Content *) MALLOC(sizeof(XML_Content));
+ if (parser->m_elementDeclHandler) {
+ XML_Content * content = (XML_Content *) MALLOC(parser, sizeof(XML_Content));
if (!content)
return XML_ERROR_NO_MEMORY;
content->quant = XML_CQUANT_NONE;
@@ -5173,7 +5165,7 @@ doProlog(XML_Parser parser,
XML_CTYPE_ANY :
XML_CTYPE_EMPTY);
*eventEndPP = s;
- elementDeclHandler(handlerArg, declElementType->name, content);
+ parser->m_elementDeclHandler(parser->m_handlerArg, parser->m_declElementType->name, content);
handleDefault = XML_FALSE;
}
dtd->in_eldecl = XML_FALSE;
@@ -5184,7 +5176,7 @@ doProlog(XML_Parser parser,
if (dtd->in_eldecl) {
dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type
= XML_CTYPE_MIXED;
- if (elementDeclHandler)
+ if (parser->m_elementDeclHandler)
handleDefault = XML_FALSE;
}
break;
@@ -5221,7 +5213,7 @@ doProlog(XML_Parser parser,
nameLen = 0;
for (; name[nameLen++]; );
dtd->contentStringLen += nameLen;
- if (elementDeclHandler)
+ if (parser->m_elementDeclHandler)
handleDefault = XML_FALSE;
}
break;
@@ -5239,7 +5231,7 @@ doProlog(XML_Parser parser,
quant = XML_CQUANT_PLUS;
closeGroup:
if (dtd->in_eldecl) {
- if (elementDeclHandler)
+ if (parser->m_elementDeclHandler)
handleDefault = XML_FALSE;
dtd->scaffLevel--;
dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel]].quant = quant;
@@ -5249,7 +5241,7 @@ doProlog(XML_Parser parser,
if (!model)
return XML_ERROR_NO_MEMORY;
*eventEndPP = s;
- elementDeclHandler(handlerArg, declElementType->name, model);
+ parser->m_elementDeclHandler(parser->m_handlerArg, parser->m_declElementType->name, model);
}
dtd->in_eldecl = XML_FALSE;
dtd->contentStringLen = 0;
@@ -5276,31 +5268,31 @@ doProlog(XML_Parser parser,
}
break;
case XML_ROLE_DOCTYPE_NONE:
- if (startDoctypeDeclHandler)
+ if (parser->m_startDoctypeDeclHandler)
handleDefault = XML_FALSE;
break;
case XML_ROLE_ENTITY_NONE:
- if (dtd->keepProcessing && entityDeclHandler)
+ if (dtd->keepProcessing && parser->m_entityDeclHandler)
handleDefault = XML_FALSE;
break;
case XML_ROLE_NOTATION_NONE:
- if (notationDeclHandler)
+ if (parser->m_notationDeclHandler)
handleDefault = XML_FALSE;
break;
case XML_ROLE_ATTLIST_NONE:
- if (dtd->keepProcessing && attlistDeclHandler)
+ if (dtd->keepProcessing && parser->m_attlistDeclHandler)
handleDefault = XML_FALSE;
break;
case XML_ROLE_ELEMENT_NONE:
- if (elementDeclHandler)
+ if (parser->m_elementDeclHandler)
handleDefault = XML_FALSE;
break;
} /* end of big switch */
- if (handleDefault && defaultHandler)
+ if (handleDefault && parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
- switch (ps_parsing) {
+ switch (parser->m_parsingStatus.parsing) {
case XML_SUSPENDED:
*nextPtr = next;
return XML_ERROR_NONE;
@@ -5320,18 +5312,18 @@ epilogProcessor(XML_Parser parser,
const char *end,
const char **nextPtr)
{
- processor = epilogProcessor;
- eventPtr = s;
+ parser->m_processor = epilogProcessor;
+ parser->m_eventPtr = s;
for (;;) {
const char *next = NULL;
- int tok = XmlPrologTok(encoding, s, end, &next);
- eventEndPtr = next;
+ int tok = XmlPrologTok(parser->m_encoding, s, end, &next);
+ parser->m_eventEndPtr = next;
switch (tok) {
/* report partial linebreak - it might be the last token */
case -XML_TOK_PROLOG_S:
- if (defaultHandler) {
- reportDefault(parser, encoding, s, next);
- if (ps_parsing == XML_FINISHED)
+ if (parser->m_defaultHandler) {
+ reportDefault(parser, parser->m_encoding, s, next);
+ if (parser->m_parsingStatus.parsing == XML_FINISHED)
return XML_ERROR_ABORTED;
}
*nextPtr = next;
@@ -5340,28 +5332,28 @@ epilogProcessor(XML_Parser parser,
*nextPtr = s;
return XML_ERROR_NONE;
case XML_TOK_PROLOG_S:
- if (defaultHandler)
- reportDefault(parser, encoding, s, next);
+ if (parser->m_defaultHandler)
+ reportDefault(parser, parser->m_encoding, s, next);
break;
case XML_TOK_PI:
- if (!reportProcessingInstruction(parser, encoding, s, next))
+ if (!reportProcessingInstruction(parser, parser->m_encoding, s, next))
return XML_ERROR_NO_MEMORY;
break;
case XML_TOK_COMMENT:
- if (!reportComment(parser, encoding, s, next))
+ if (!reportComment(parser, parser->m_encoding, s, next))
return XML_ERROR_NO_MEMORY;
break;
case XML_TOK_INVALID:
- eventPtr = next;
+ parser->m_eventPtr = next;
return XML_ERROR_INVALID_TOKEN;
case XML_TOK_PARTIAL:
- if (!ps_finalBuffer) {
+ if (!parser->m_parsingStatus.finalBuffer) {
*nextPtr = s;
return XML_ERROR_NONE;
}
return XML_ERROR_UNCLOSED_TOKEN;
case XML_TOK_PARTIAL_CHAR:
- if (!ps_finalBuffer) {
+ if (!parser->m_parsingStatus.finalBuffer) {
*nextPtr = s;
return XML_ERROR_NONE;
}
@@ -5369,8 +5361,8 @@ epilogProcessor(XML_Parser parser,
default:
return XML_ERROR_JUNK_AFTER_DOC_ELEMENT;
}
- eventPtr = s = next;
- switch (ps_parsing) {
+ parser->m_eventPtr = s = next;
+ switch (parser->m_parsingStatus.parsing) {
case XML_SUSPENDED:
*nextPtr = next;
return XML_ERROR_NONE;
@@ -5390,21 +5382,21 @@ processInternalEntity(XML_Parser parser, ENTITY *entity,
enum XML_Error result;
OPEN_INTERNAL_ENTITY *openEntity;
- if (freeInternalEntities) {
- openEntity = freeInternalEntities;
- freeInternalEntities = openEntity->next;
+ if (parser->m_freeInternalEntities) {
+ openEntity = parser->m_freeInternalEntities;
+ parser->m_freeInternalEntities = openEntity->next;
}
else {
- openEntity = (OPEN_INTERNAL_ENTITY *)MALLOC(sizeof(OPEN_INTERNAL_ENTITY));
+ openEntity = (OPEN_INTERNAL_ENTITY *)MALLOC(parser, sizeof(OPEN_INTERNAL_ENTITY));
if (!openEntity)
return XML_ERROR_NO_MEMORY;
}
entity->open = XML_TRUE;
entity->processed = 0;
- openEntity->next = openInternalEntities;
- openInternalEntities = openEntity;
+ openEntity->next = parser->m_openInternalEntities;
+ parser->m_openInternalEntities = openEntity;
openEntity->entity = entity;
- openEntity->startTagLevel = tagLevel;
+ openEntity->startTagLevel = parser->m_tagLevel;
openEntity->betweenDecl = betweenDecl;
openEntity->internalEventPtr = NULL;
openEntity->internalEventEndPtr = NULL;
@@ -5415,26 +5407,26 @@ processInternalEntity(XML_Parser parser, ENTITY *entity,
#ifdef XML_DTD
if (entity->is_param) {
- int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next);
- result = doProlog(parser, internalEncoding, textStart, textEnd, tok,
+ int tok = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
+ result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd, tok,
next, &next, XML_FALSE);
}
else
#endif /* XML_DTD */
- result = doContent(parser, tagLevel, internalEncoding, textStart,
+ result = doContent(parser, parser->m_tagLevel, parser->m_internalEncoding, textStart,
textEnd, &next, XML_FALSE);
if (result == XML_ERROR_NONE) {
- if (textEnd != next && ps_parsing == XML_SUSPENDED) {
+ if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) {
entity->processed = (int)(next - textStart);
- processor = internalEntityProcessor;
+ parser->m_processor = internalEntityProcessor;
}
else {
entity->open = XML_FALSE;
- openInternalEntities = openEntity->next;
+ parser->m_openInternalEntities = openEntity->next;
/* put openEntity back in list of free instances */
- openEntity->next = freeInternalEntities;
- freeInternalEntities = openEntity;
+ openEntity->next = parser->m_freeInternalEntities;
+ parser->m_freeInternalEntities = openEntity;
}
}
return result;
@@ -5450,7 +5442,7 @@ internalEntityProcessor(XML_Parser parser,
const char *textStart, *textEnd;
const char *next;
enum XML_Error result;
- OPEN_INTERNAL_ENTITY *openEntity = openInternalEntities;
+ OPEN_INTERNAL_ENTITY *openEntity = parser->m_openInternalEntities;
if (!openEntity)
return XML_ERROR_UNEXPECTED_STATE;
@@ -5462,44 +5454,44 @@ internalEntityProcessor(XML_Parser parser,
#ifdef XML_DTD
if (entity->is_param) {
- int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next);
- result = doProlog(parser, internalEncoding, textStart, textEnd, tok,
+ int tok = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
+ result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd, tok,
next, &next, XML_FALSE);
}
else
#endif /* XML_DTD */
- result = doContent(parser, openEntity->startTagLevel, internalEncoding,
+ result = doContent(parser, openEntity->startTagLevel, parser->m_internalEncoding,
textStart, textEnd, &next, XML_FALSE);
if (result != XML_ERROR_NONE)
return result;
- else if (textEnd != next && ps_parsing == XML_SUSPENDED) {
+ else if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) {
entity->processed = (int)(next - (char *)entity->textPtr);
return result;
}
else {
entity->open = XML_FALSE;
- openInternalEntities = openEntity->next;
+ parser->m_openInternalEntities = openEntity->next;
/* put openEntity back in list of free instances */
- openEntity->next = freeInternalEntities;
- freeInternalEntities = openEntity;
+ openEntity->next = parser->m_freeInternalEntities;
+ parser->m_freeInternalEntities = openEntity;
}
#ifdef XML_DTD
if (entity->is_param) {
int tok;
- processor = prologProcessor;
- tok = XmlPrologTok(encoding, s, end, &next);
- return doProlog(parser, encoding, s, end, tok, next, nextPtr,
- (XML_Bool)!ps_finalBuffer);
+ parser->m_processor = prologProcessor;
+ tok = XmlPrologTok(parser->m_encoding, s, end, &next);
+ return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
+ (XML_Bool)!parser->m_parsingStatus.finalBuffer);
}
else
#endif /* XML_DTD */
{
- processor = contentProcessor;
+ parser->m_processor = contentProcessor;
/* see externalEntityContentProcessor vs contentProcessor */
- return doContent(parser, parentParser ? 1 : 0, encoding, s, end,
- nextPtr, (XML_Bool)!ps_finalBuffer);
+ return doContent(parser, parser->m_parentParser ? 1 : 0, parser->m_encoding, s, end,
+ nextPtr, (XML_Bool)!parser->m_parsingStatus.finalBuffer);
}
}
@@ -5509,7 +5501,7 @@ errorProcessor(XML_Parser parser,
const char *UNUSED_P(end),
const char **UNUSED_P(nextPtr))
{
- return errorCode;
+ return parser->m_errorCode;
}
static enum XML_Error
@@ -5533,7 +5525,7 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
const char *ptr, const char *end,
STRING_POOL *pool)
{
- DTD * const dtd = _dtd; /* save one level of indirection */
+ DTD * const dtd = parser->m_dtd; /* save one level of indirection */
for (;;) {
const char *next;
int tok = XmlAttributeValueTok(enc, ptr, end, &next);
@@ -5541,12 +5533,12 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
case XML_TOK_NONE:
return XML_ERROR_NONE;
case XML_TOK_INVALID:
- if (enc == encoding)
- eventPtr = next;
+ if (enc == parser->m_encoding)
+ parser->m_eventPtr = next;
return XML_ERROR_INVALID_TOKEN;
case XML_TOK_PARTIAL:
- if (enc == encoding)
- eventPtr = ptr;
+ if (enc == parser->m_encoding)
+ parser->m_eventPtr = ptr;
return XML_ERROR_INVALID_TOKEN;
case XML_TOK_CHAR_REF:
{
@@ -5554,8 +5546,8 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
int i;
int n = XmlCharRefNumber(enc, ptr);
if (n < 0) {
- if (enc == encoding)
- eventPtr = ptr;
+ if (enc == parser->m_encoding)
+ parser->m_eventPtr = ptr;
return XML_ERROR_BAD_CHAR_REF;
}
if (!isCdata
@@ -5605,25 +5597,25 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
return XML_ERROR_NO_MEMORY;
break;
}
- name = poolStoreString(&temp2Pool, enc,
+ name = poolStoreString(&parser->m_temp2Pool, enc,
ptr + enc->minBytesPerChar,
next - enc->minBytesPerChar);
if (!name)
return XML_ERROR_NO_MEMORY;
entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0);
- poolDiscard(&temp2Pool);
+ poolDiscard(&parser->m_temp2Pool);
/* First, determine if a check for an existing declaration is needed;
if yes, check that the entity exists, and that it is internal.
*/
if (pool == &dtd->pool) /* are we called from prolog? */
checkEntityDecl =
#ifdef XML_DTD
- prologState.documentEntity &&
+ parser->m_prologState.documentEntity &&
#endif /* XML_DTD */
(dtd->standalone
- ? !openInternalEntities
+ ? !parser->m_openInternalEntities
: !dtd->hasParamEntityRefs);
- else /* if (pool == &tempPool): we are called from content */
+ else /* if (pool == &parser->m_tempPool): we are called from content */
checkEntityDecl = !dtd->hasParamEntityRefs || dtd->standalone;
if (checkEntityDecl) {
if (!entity)
@@ -5633,19 +5625,19 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
}
else if (!entity) {
/* Cannot report skipped entity here - see comments on
- skippedEntityHandler.
- if (skippedEntityHandler)
- skippedEntityHandler(handlerArg, name, 0);
+ parser->m_skippedEntityHandler.
+ if (parser->m_skippedEntityHandler)
+ parser->m_skippedEntityHandler(parser->m_handlerArg, name, 0);
*/
/* Cannot call the default handler because this would be
out of sync with the call to the startElementHandler.
- if ((pool == &tempPool) && defaultHandler)
+ if ((pool == &parser->m_tempPool) && parser->m_defaultHandler)
reportDefault(parser, enc, ptr, next);
*/
break;
}
if (entity->open) {
- if (enc == encoding) {
+ if (enc == parser->m_encoding) {
/* It does not appear that this line can be executed.
*
* The "if (entity->open)" check catches recursive entity
@@ -5663,25 +5655,25 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
* we keep the line and merely exclude it from coverage
* tests.
*/
- eventPtr = ptr; /* LCOV_EXCL_LINE */
+ parser->m_eventPtr = ptr; /* LCOV_EXCL_LINE */
}
return XML_ERROR_RECURSIVE_ENTITY_REF;
}
if (entity->notation) {
- if (enc == encoding)
- eventPtr = ptr;
+ if (enc == parser->m_encoding)
+ parser->m_eventPtr = ptr;
return XML_ERROR_BINARY_ENTITY_REF;
}
if (!entity->textPtr) {
- if (enc == encoding)
- eventPtr = ptr;
+ if (enc == parser->m_encoding)
+ parser->m_eventPtr = ptr;
return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF;
}
else {
enum XML_Error result;
const XML_Char *textEnd = entity->textPtr + entity->textLen;
entity->open = XML_TRUE;
- result = appendAttributeValue(parser, internalEncoding, isCdata,
+ result = appendAttributeValue(parser, parser->m_internalEncoding, isCdata,
(char *)entity->textPtr,
(char *)textEnd, pool);
entity->open = XML_FALSE;
@@ -5702,8 +5694,8 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
*
* LCOV_EXCL_START
*/
- if (enc == encoding)
- eventPtr = ptr;
+ if (enc == parser->m_encoding)
+ parser->m_eventPtr = ptr;
return XML_ERROR_UNEXPECTED_STATE;
/* LCOV_EXCL_STOP */
}
@@ -5718,12 +5710,12 @@ storeEntityValue(XML_Parser parser,
const char *entityTextPtr,
const char *entityTextEnd)
{
- DTD * const dtd = _dtd; /* save one level of indirection */
+ DTD * const dtd = parser->m_dtd; /* save one level of indirection */
STRING_POOL *pool = &(dtd->entityValuePool);
enum XML_Error result = XML_ERROR_NONE;
#ifdef XML_DTD
- int oldInEntityValue = prologState.inEntityValue;
- prologState.inEntityValue = 1;
+ int oldInEntityValue = parser->m_prologState.inEntityValue;
+ parser->m_prologState.inEntityValue = 1;
#endif /* XML_DTD */
/* never return Null for the value argument in EntityDeclHandler,
since this would indicate an external entity; therefore we
@@ -5739,10 +5731,10 @@ storeEntityValue(XML_Parser parser,
switch (tok) {
case XML_TOK_PARAM_ENTITY_REF:
#ifdef XML_DTD
- if (isParamEntity || enc != encoding) {
+ if (parser->m_isParamEntity || enc != parser->m_encoding) {
const XML_Char *name;
ENTITY *entity;
- name = poolStoreString(&tempPool, enc,
+ name = poolStoreString(&parser->m_tempPool, enc,
entityTextPtr + enc->minBytesPerChar,
next - enc->minBytesPerChar);
if (!name) {
@@ -5750,28 +5742,28 @@ storeEntityValue(XML_Parser parser,
goto endEntityValue;
}
entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0);
- poolDiscard(&tempPool);
+ poolDiscard(&parser->m_tempPool);
if (!entity) {
/* not a well-formedness error - see XML 1.0: WFC Entity Declared */
/* cannot report skipped entity here - see comments on
- skippedEntityHandler
- if (skippedEntityHandler)
- skippedEntityHandler(handlerArg, name, 0);
+ parser->m_skippedEntityHandler
+ if (parser->m_skippedEntityHandler)
+ parser->m_skippedEntityHandler(parser->m_handlerArg, name, 0);
*/
dtd->keepProcessing = dtd->standalone;
goto endEntityValue;
}
if (entity->open) {
- if (enc == encoding)
- eventPtr = entityTextPtr;
+ if (enc == parser->m_encoding)
+ parser->m_eventPtr = entityTextPtr;
result = XML_ERROR_RECURSIVE_ENTITY_REF;
goto endEntityValue;
}
if (entity->systemId) {
- if (externalEntityRefHandler) {
+ if (parser->m_externalEntityRefHandler) {
dtd->paramEntityRead = XML_FALSE;
entity->open = XML_TRUE;
- if (!externalEntityRefHandler(externalEntityRefHandlerArg,
+ if (!parser->m_externalEntityRefHandler(parser->m_externalEntityRefHandlerArg,
0,
entity->base,
entity->systemId,
@@ -5790,7 +5782,7 @@ storeEntityValue(XML_Parser parser,
else {
entity->open = XML_TRUE;
result = storeEntityValue(parser,
- internalEncoding,
+ parser->m_internalEncoding,
(char *)entity->textPtr,
(char *)(entity->textPtr
+ entity->textLen));
@@ -5803,7 +5795,7 @@ storeEntityValue(XML_Parser parser,
#endif /* XML_DTD */
/* In the internal subset, PE references are not legal
within markup declarations, e.g entity values in this case. */
- eventPtr = entityTextPtr;
+ parser->m_eventPtr = entityTextPtr;
result = XML_ERROR_PARAM_ENTITY_REF;
goto endEntityValue;
case XML_TOK_NONE:
@@ -5832,8 +5824,8 @@ storeEntityValue(XML_Parser parser,
int i;
int n = XmlCharRefNumber(enc, entityTextPtr);
if (n < 0) {
- if (enc == encoding)
- eventPtr = entityTextPtr;
+ if (enc == parser->m_encoding)
+ parser->m_eventPtr = entityTextPtr;
result = XML_ERROR_BAD_CHAR_REF;
goto endEntityValue;
}
@@ -5857,13 +5849,13 @@ storeEntityValue(XML_Parser parser,
}
break;
case XML_TOK_PARTIAL:
- if (enc == encoding)
- eventPtr = entityTextPtr;
+ if (enc == parser->m_encoding)
+ parser->m_eventPtr = entityTextPtr;
result = XML_ERROR_INVALID_TOKEN;
goto endEntityValue;
case XML_TOK_INVALID:
- if (enc == encoding)
- eventPtr = next;
+ if (enc == parser->m_encoding)
+ parser->m_eventPtr = next;
result = XML_ERROR_INVALID_TOKEN;
goto endEntityValue;
default:
@@ -5874,8 +5866,8 @@ storeEntityValue(XML_Parser parser,
*
* LCOV_EXCL_START
*/
- if (enc == encoding)
- eventPtr = entityTextPtr;
+ if (enc == parser->m_encoding)
+ parser->m_eventPtr = entityTextPtr;
result = XML_ERROR_UNEXPECTED_STATE;
goto endEntityValue;
/* LCOV_EXCL_STOP */
@@ -5884,7 +5876,7 @@ storeEntityValue(XML_Parser parser,
}
endEntityValue:
#ifdef XML_DTD
- prologState.inEntityValue = oldInEntityValue;
+ parser->m_prologState.inEntityValue = oldInEntityValue;
#endif /* XML_DTD */
return result;
}
@@ -5919,25 +5911,25 @@ reportProcessingInstruction(XML_Parser parser, const ENCODING *enc,
const XML_Char *target;
XML_Char *data;
const char *tem;
- if (!processingInstructionHandler) {
- if (defaultHandler)
+ if (!parser->m_processingInstructionHandler) {
+ if (parser->m_defaultHandler)
reportDefault(parser, enc, start, end);
return 1;
}
start += enc->minBytesPerChar * 2;
tem = start + XmlNameLength(enc, start);
- target = poolStoreString(&tempPool, enc, start, tem);
+ target = poolStoreString(&parser->m_tempPool, enc, start, tem);
if (!target)
return 0;
- poolFinish(&tempPool);
- data = poolStoreString(&tempPool, enc,
+ poolFinish(&parser->m_tempPool);
+ data = poolStoreString(&parser->m_tempPool, enc,
XmlSkipS(enc, tem),
end - enc->minBytesPerChar*2);
if (!data)
return 0;
normalizeLines(data);
- processingInstructionHandler(handlerArg, target, data);
- poolClear(&tempPool);
+ parser->m_processingInstructionHandler(parser->m_handlerArg, target, data);
+ poolClear(&parser->m_tempPool);
return 1;
}
@@ -5946,20 +5938,20 @@ reportComment(XML_Parser parser, const ENCODING *enc,
const char *start, const char *end)
{
XML_Char *data;
- if (!commentHandler) {
- if (defaultHandler)
+ if (!parser->m_commentHandler) {
+ if (parser->m_defaultHandler)
reportDefault(parser, enc, start, end);
return 1;
}
- data = poolStoreString(&tempPool,
+ data = poolStoreString(&parser->m_tempPool,
enc,
start + enc->minBytesPerChar * 4,
end - enc->minBytesPerChar * 3);
if (!data)
return 0;
normalizeLines(data);
- commentHandler(handlerArg, data);
- poolClear(&tempPool);
+ parser->m_commentHandler(parser->m_handlerArg, data);
+ poolClear(&parser->m_tempPool);
return 1;
}
@@ -5971,9 +5963,9 @@ reportDefault(XML_Parser parser, const ENCODING *enc,
enum XML_Convert_Result convert_res;
const char **eventPP;
const char **eventEndPP;
- if (enc == encoding) {
- eventPP = &eventPtr;
- eventEndPP = &eventEndPtr;
+ if (enc == parser->m_encoding) {
+ eventPP = &parser->m_eventPtr;
+ eventEndPP = &parser->m_eventEndPtr;
}
else {
/* To get here, two things must be true; the parser must be
@@ -5992,20 +5984,20 @@ reportDefault(XML_Parser parser, const ENCODING *enc,
*
* LCOV_EXCL_START
*/
- eventPP = &(openInternalEntities->internalEventPtr);
- eventEndPP = &(openInternalEntities->internalEventEndPtr);
+ eventPP = &(parser->m_openInternalEntities->internalEventPtr);
+ eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr);
/* LCOV_EXCL_STOP */
}
do {
- ICHAR *dataPtr = (ICHAR *)dataBuf;
- convert_res = XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd);
+ ICHAR *dataPtr = (ICHAR *)parser->m_dataBuf;
+ convert_res = XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)parser->m_dataBufEnd);
*eventEndPP = s;
- defaultHandler(handlerArg, dataBuf, (int)(dataPtr - (ICHAR *)dataBuf));
+ parser->m_defaultHandler(parser->m_handlerArg, parser->m_dataBuf, (int)(dataPtr - (ICHAR *)parser->m_dataBuf));
*eventPP = s;
} while ((convert_res != XML_CONVERT_COMPLETED) && (convert_res != XML_CONVERT_INPUT_INCOMPLETE));
}
else
- defaultHandler(handlerArg, (XML_Char *)s, (int)((XML_Char *)end - (XML_Char *)s));
+ parser->m_defaultHandler(parser->m_handlerArg, (XML_Char *)s, (int)((XML_Char *)end - (XML_Char *)s));
}
@@ -6027,16 +6019,18 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata,
if (type->nDefaultAtts == type->allocDefaultAtts) {
if (type->allocDefaultAtts == 0) {
type->allocDefaultAtts = 8;
- type->defaultAtts = (DEFAULT_ATTRIBUTE *)MALLOC(type->allocDefaultAtts
+ type->defaultAtts = (DEFAULT_ATTRIBUTE *)MALLOC(parser, type->allocDefaultAtts
* sizeof(DEFAULT_ATTRIBUTE));
- if (!type->defaultAtts)
+ if (!type->defaultAtts) {
+ type->allocDefaultAtts = 0;
return 0;
+ }
}
else {
DEFAULT_ATTRIBUTE *temp;
int count = type->allocDefaultAtts * 2;
temp = (DEFAULT_ATTRIBUTE *)
- REALLOC(type->defaultAtts, (count * sizeof(DEFAULT_ATTRIBUTE)));
+ REALLOC(parser, type->defaultAtts, (count * sizeof(DEFAULT_ATTRIBUTE)));
if (temp == NULL)
return 0;
type->allocDefaultAtts = count;
@@ -6056,7 +6050,7 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata,
static int
setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType)
{
- DTD * const dtd = _dtd; /* save one level of indirection */
+ DTD * const dtd = parser->m_dtd; /* save one level of indirection */
const XML_Char *name;
for (name = elementType->name; *name; name++) {
if (*name == XML_T(ASCII_COLON)) {
@@ -6077,7 +6071,7 @@ setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType)
else
poolDiscard(&dtd->pool);
elementType->prefix = prefix;
-
+ break;
}
}
return 1;
@@ -6087,7 +6081,7 @@ static ATTRIBUTE_ID *
getAttributeId(XML_Parser parser, const ENCODING *enc,
const char *start, const char *end)
{
- DTD * const dtd = _dtd; /* save one level of indirection */
+ DTD * const dtd = parser->m_dtd; /* save one level of indirection */
ATTRIBUTE_ID *id;
const XML_Char *name;
if (!poolAppendChar(&dtd->pool, XML_T('\0')))
@@ -6104,7 +6098,7 @@ getAttributeId(XML_Parser parser, const ENCODING *enc,
poolDiscard(&dtd->pool);
else {
poolFinish(&dtd->pool);
- if (!ns)
+ if (!parser->m_ns)
;
else if (name[0] == XML_T(ASCII_x)
&& name[1] == XML_T(ASCII_m)
@@ -6151,20 +6145,20 @@ getAttributeId(XML_Parser parser, const ENCODING *enc,
static const XML_Char *
getContext(XML_Parser parser)
{
- DTD * const dtd = _dtd; /* save one level of indirection */
+ DTD * const dtd = parser->m_dtd; /* save one level of indirection */
HASH_TABLE_ITER iter;
XML_Bool needSep = XML_FALSE;
if (dtd->defaultPrefix.binding) {
int i;
int len;
- if (!poolAppendChar(&tempPool, XML_T(ASCII_EQUALS)))
+ if (!poolAppendChar(&parser->m_tempPool, XML_T(ASCII_EQUALS)))
return NULL;
len = dtd->defaultPrefix.binding->uriLen;
- if (namespaceSeparator)
+ if (parser->m_namespaceSeparator)
len--;
for (i = 0; i < len; i++) {
- if (!poolAppendChar(&tempPool, dtd->defaultPrefix.binding->uri[i])) {
+ if (!poolAppendChar(&parser->m_tempPool, dtd->defaultPrefix.binding->uri[i])) {
/* Because of memory caching, I don't believe this line can be
* executed.
*
@@ -6207,18 +6201,18 @@ getContext(XML_Parser parser)
*/
continue; /* LCOV_EXCL_LINE */
}
- if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP))
+ if (needSep && !poolAppendChar(&parser->m_tempPool, CONTEXT_SEP))
return NULL;
for (s = prefix->name; *s; s++)
- if (!poolAppendChar(&tempPool, *s))
+ if (!poolAppendChar(&parser->m_tempPool, *s))
return NULL;
- if (!poolAppendChar(&tempPool, XML_T(ASCII_EQUALS)))
+ if (!poolAppendChar(&parser->m_tempPool, XML_T(ASCII_EQUALS)))
return NULL;
len = prefix->binding->uriLen;
- if (namespaceSeparator)
+ if (parser->m_namespaceSeparator)
len--;
for (i = 0; i < len; i++)
- if (!poolAppendChar(&tempPool, prefix->binding->uri[i]))
+ if (!poolAppendChar(&parser->m_tempPool, prefix->binding->uri[i]))
return NULL;
needSep = XML_TRUE;
}
@@ -6232,73 +6226,73 @@ getContext(XML_Parser parser)
break;
if (!e->open)
continue;
- if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP))
+ if (needSep && !poolAppendChar(&parser->m_tempPool, CONTEXT_SEP))
return NULL;
for (s = e->name; *s; s++)
- if (!poolAppendChar(&tempPool, *s))
+ if (!poolAppendChar(&parser->m_tempPool, *s))
return 0;
needSep = XML_TRUE;
}
- if (!poolAppendChar(&tempPool, XML_T('\0')))
+ if (!poolAppendChar(&parser->m_tempPool, XML_T('\0')))
return NULL;
- return tempPool.start;
+ return parser->m_tempPool.start;
}
static XML_Bool
setContext(XML_Parser parser, const XML_Char *context)
{
- DTD * const dtd = _dtd; /* save one level of indirection */
+ DTD * const dtd = parser->m_dtd; /* save one level of indirection */
const XML_Char *s = context;
while (*context != XML_T('\0')) {
if (*s == CONTEXT_SEP || *s == XML_T('\0')) {
ENTITY *e;
- if (!poolAppendChar(&tempPool, XML_T('\0')))
+ if (!poolAppendChar(&parser->m_tempPool, XML_T('\0')))
return XML_FALSE;
- e = (ENTITY *)lookup(parser, &dtd->generalEntities, poolStart(&tempPool), 0);
+ e = (ENTITY *)lookup(parser, &dtd->generalEntities, poolStart(&parser->m_tempPool), 0);
if (e)
e->open = XML_TRUE;
if (*s != XML_T('\0'))
s++;
context = s;
- poolDiscard(&tempPool);
+ poolDiscard(&parser->m_tempPool);
}
else if (*s == XML_T(ASCII_EQUALS)) {
PREFIX *prefix;
- if (poolLength(&tempPool) == 0)
+ if (poolLength(&parser->m_tempPool) == 0)
prefix = &dtd->defaultPrefix;
else {
- if (!poolAppendChar(&tempPool, XML_T('\0')))
+ if (!poolAppendChar(&parser->m_tempPool, XML_T('\0')))
return XML_FALSE;
- prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&tempPool),
+ prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&parser->m_tempPool),
sizeof(PREFIX));
if (!prefix)
return XML_FALSE;
- if (prefix->name == poolStart(&tempPool)) {
+ if (prefix->name == poolStart(&parser->m_tempPool)) {
prefix->name = poolCopyString(&dtd->pool, prefix->name);
if (!prefix->name)
return XML_FALSE;
}
- poolDiscard(&tempPool);
+ poolDiscard(&parser->m_tempPool);
}
for (context = s + 1;
*context != CONTEXT_SEP && *context != XML_T('\0');
context++)
- if (!poolAppendChar(&tempPool, *context))
+ if (!poolAppendChar(&parser->m_tempPool, *context))
return XML_FALSE;
- if (!poolAppendChar(&tempPool, XML_T('\0')))
+ if (!poolAppendChar(&parser->m_tempPool, XML_T('\0')))
return XML_FALSE;
- if (addBinding(parser, prefix, NULL, poolStart(&tempPool),
- &inheritedBindings) != XML_ERROR_NONE)
+ if (addBinding(parser, prefix, NULL, poolStart(&parser->m_tempPool),
+ &parser->m_inheritedBindings) != XML_ERROR_NONE)
return XML_FALSE;
- poolDiscard(&tempPool);
+ poolDiscard(&parser->m_tempPool);
if (*context != XML_T('\0'))
++context;
s = context;
}
else {
- if (!poolAppendChar(&tempPool, *s))
+ if (!poolAppendChar(&parser->m_tempPool, *s))
return XML_FALSE;
s++;
}
@@ -6663,7 +6657,6 @@ hash(XML_Parser parser, KEY s)
{
struct siphash state;
struct sipkey key;
- (void)sip_tobin;
(void)sip24_valid;
copy_salt_to_sipkey(parser, &key);
sip24_init(&state, &key);
@@ -6983,8 +6976,8 @@ poolGrow(STRING_POOL *pool)
int blockSize = (int)((unsigned)(pool->end - pool->start)*2U);
size_t bytesToAllocate;
- // NOTE: Needs to be calculated prior to calling `realloc`
- // to avoid dangling pointers:
+ /* NOTE: Needs to be calculated prior to calling `realloc`
+ to avoid dangling pointers: */
const ptrdiff_t offsetInsideBlock = pool->ptr - pool->start;
if (blockSize < 0) {
@@ -7062,12 +7055,12 @@ poolGrow(STRING_POOL *pool)
static int FASTCALL
nextScaffoldPart(XML_Parser parser)
{
- DTD * const dtd = _dtd; /* save one level of indirection */
+ DTD * const dtd = parser->m_dtd; /* save one level of indirection */
CONTENT_SCAFFOLD * me;
int next;
if (!dtd->scaffIndex) {
- dtd->scaffIndex = (int *)MALLOC(groupSize * sizeof(int));
+ dtd->scaffIndex = (int *)MALLOC(parser, parser->m_groupSize * sizeof(int));
if (!dtd->scaffIndex)
return -1;
dtd->scaffIndex[0] = 0;
@@ -7077,13 +7070,13 @@ nextScaffoldPart(XML_Parser parser)
CONTENT_SCAFFOLD *temp;
if (dtd->scaffold) {
temp = (CONTENT_SCAFFOLD *)
- REALLOC(dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD));
+ REALLOC(parser, dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD));
if (temp == NULL)
return -1;
dtd->scaffSize *= 2;
}
else {
- temp = (CONTENT_SCAFFOLD *)MALLOC(INIT_SCAFFOLD_ELEMENTS
+ temp = (CONTENT_SCAFFOLD *)MALLOC(parser, INIT_SCAFFOLD_ELEMENTS
* sizeof(CONTENT_SCAFFOLD));
if (temp == NULL)
return -1;
@@ -7114,7 +7107,7 @@ build_node(XML_Parser parser,
XML_Content **contpos,
XML_Char **strpos)
{
- DTD * const dtd = _dtd; /* save one level of indirection */
+ DTD * const dtd = parser->m_dtd; /* save one level of indirection */
dest->type = dtd->scaffold[src_node].type;
dest->quant = dtd->scaffold[src_node].quant;
if (dest->type == XML_CTYPE_NAME) {
@@ -7148,14 +7141,14 @@ build_node(XML_Parser parser,
static XML_Content *
build_model (XML_Parser parser)
{
- DTD * const dtd = _dtd; /* save one level of indirection */
+ DTD * const dtd = parser->m_dtd; /* save one level of indirection */
XML_Content *ret;
XML_Content *cpos;
XML_Char * str;
int allocsize = (dtd->scaffCount * sizeof(XML_Content)
+ (dtd->contentStringLen * sizeof(XML_Char)));
- ret = (XML_Content *)MALLOC(allocsize);
+ ret = (XML_Content *)MALLOC(parser, allocsize);
if (!ret)
return NULL;
@@ -7172,7 +7165,7 @@ getElementType(XML_Parser parser,
const char *ptr,
const char *end)
{
- DTD * const dtd = _dtd; /* save one level of indirection */
+ DTD * const dtd = parser->m_dtd; /* save one level of indirection */
const XML_Char *name = poolStoreString(&dtd->pool, enc, ptr, end);
ELEMENT_TYPE *ret;
diff --git a/Utilities/cmexpat/lib/xmlrole.c b/Utilities/cmexpat/lib/xmlrole.c
index c809ee514..708507d57 100644
--- a/Utilities/cmexpat/lib/xmlrole.c
+++ b/Utilities/cmexpat/lib/xmlrole.c
@@ -1,5 +1,33 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
- See the file COPYING for copying permission.
+/*
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+ Copyright (c) 2000-2017 Expat development team
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stddef.h>
diff --git a/Utilities/cmexpat/lib/xmlrole.h b/Utilities/cmexpat/lib/xmlrole.h
index 4dd9f06f9..e5f048eab 100644
--- a/Utilities/cmexpat/lib/xmlrole.h
+++ b/Utilities/cmexpat/lib/xmlrole.h
@@ -1,5 +1,33 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
- See the file COPYING for copying permission.
+/*
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+ Copyright (c) 2000-2017 Expat development team
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef XmlRole_INCLUDED
diff --git a/Utilities/cmexpat/lib/xmltok.c b/Utilities/cmexpat/lib/xmltok.c
index db4a5c8ca..6b415d839 100644
--- a/Utilities/cmexpat/lib/xmltok.c
+++ b/Utilities/cmexpat/lib/xmltok.c
@@ -1,8 +1,47 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
- See the file COPYING for copying permission.
+/*
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+ Copyright (c) 2000-2017 Expat development team
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stddef.h>
+#include <string.h> /* memcpy */
+
+#if defined(_MSC_VER) && (_MSC_VER <= 1700)
+ /* for vs2012/11.0/1700 and earlier Visual Studio compilers */
+# define bool int
+# define false 0
+# define true 1
+#else
+# include <stdbool.h>
+#endif
+
#ifdef _WIN32
#include "winconfig.h"
@@ -27,7 +66,6 @@
{ PREFIX(prologTok), PREFIX(contentTok), \
PREFIX(cdataSectionTok) IGNORE_SECTION_TOK_VTABLE }, \
{ PREFIX(attributeValueTok), PREFIX(entityValueTok) }, \
- PREFIX(sameName), \
PREFIX(nameMatchesAscii), \
PREFIX(nameLength), \
PREFIX(skipS), \
@@ -324,7 +362,7 @@ enum { /* UTF8_cvalN is value of masked first byte of N byte sequence */
};
void
-align_limit_to_full_utf8_characters(const char * from, const char ** fromLimRef)
+_INTERNAL_trim_to_complete_utf8_characters(const char * from, const char ** fromLimRef)
{
const char * fromLim = *fromLimRef;
size_t walked = 0;
@@ -363,22 +401,37 @@ utf8_toUtf8(const ENCODING *UNUSED_P(enc),
const char **fromP, const char *fromLim,
char **toP, const char *toLim)
{
- char *to;
- const char *from;
- const char *fromLimInitial = fromLim;
+ bool input_incomplete = false;
+ bool output_exhausted = false;
+
+ /* Avoid copying partial characters (due to limited space). */
+ const ptrdiff_t bytesAvailable = fromLim - *fromP;
+ const ptrdiff_t bytesStorable = toLim - *toP;
+ if (bytesAvailable > bytesStorable) {
+ fromLim = *fromP + bytesStorable;
+ output_exhausted = true;
+ }
- /* Avoid copying partial characters. */
- align_limit_to_full_utf8_characters(*fromP, &fromLim);
+ /* Avoid copying partial characters (from incomplete input). */
+ {
+ const char * const fromLimBefore = fromLim;
+ _INTERNAL_trim_to_complete_utf8_characters(*fromP, &fromLim);
+ if (fromLim < fromLimBefore) {
+ input_incomplete = true;
+ }
+ }
- for (to = *toP, from = *fromP; (from < fromLim) && (to < toLim); from++, to++)
- *to = *from;
- *fromP = from;
- *toP = to;
+ {
+ const ptrdiff_t bytesToCopy = fromLim - *fromP;
+ memcpy(*toP, *fromP, bytesToCopy);
+ *fromP += bytesToCopy;
+ *toP += bytesToCopy;
+ }
- if (fromLim < fromLimInitial)
- return XML_CONVERT_INPUT_INCOMPLETE;
- else if ((to == toLim) && (from < fromLim))
+ if (output_exhausted) /* needs to go first */
return XML_CONVERT_OUTPUT_EXHAUSTED;
+ else if (input_incomplete)
+ return XML_CONVERT_INPUT_INCOMPLETE;
else
return XML_CONVERT_COMPLETED;
}
@@ -1411,9 +1464,8 @@ unknown_toUtf8(const ENCODING *enc,
return XML_CONVERT_OUTPUT_EXHAUSTED;
(*fromP)++;
}
- do {
- *(*toP)++ = *utf8++;
- } while (--n != 0);
+ memcpy(*toP, utf8, n);
+ *toP += n;
}
}
diff --git a/Utilities/cmexpat/lib/xmltok.h b/Utilities/cmexpat/lib/xmltok.h
index 752007e8b..50926f38a 100644
--- a/Utilities/cmexpat/lib/xmltok.h
+++ b/Utilities/cmexpat/lib/xmltok.h
@@ -1,5 +1,33 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
- See the file COPYING for copying permission.
+/*
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+ Copyright (c) 2000-2017 Expat development team
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef XmlTok_INCLUDED
@@ -139,9 +167,6 @@ enum XML_Convert_Result {
struct encoding {
SCANNER scanners[XML_N_STATES];
SCANNER literalScanners[XML_N_LITERAL_TYPES];
- int (PTRCALL *sameName)(const ENCODING *,
- const char *,
- const char *);
int (PTRCALL *nameMatchesAscii)(const ENCODING *,
const char *,
const char *,
@@ -232,8 +257,6 @@ struct encoding {
#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \
XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr)
-#define XmlSameName(enc, ptr1, ptr2) (((enc)->sameName)(enc, ptr1, ptr2))
-
#define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \
(((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2))
diff --git a/Utilities/cmexpat/lib/xmltok_impl.c b/Utilities/cmexpat/lib/xmltok_impl.c
index 4fa1ff679..4d9ae7dc3 100644
--- a/Utilities/cmexpat/lib/xmltok_impl.c
+++ b/Utilities/cmexpat/lib/xmltok_impl.c
@@ -1,8 +1,35 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
- See the file COPYING for copying permission.
+/* This file is included!
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+ Copyright (c) 2000-2017 Expat development team
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-/* This file is included! */
#ifdef XML_TOK_IMPL_C
#ifndef IS_INVALID_CHAR
@@ -47,6 +74,7 @@
*nextTokPtr = ptr; \
return XML_TOK_INVALID; \
} \
+ /* fall through */ \
case BT_NMSTRT: \
case BT_HEX: \
case BT_DIGIT: \
@@ -75,6 +103,7 @@
*nextTokPtr = ptr; \
return XML_TOK_INVALID; \
} \
+ /* fall through */ \
case BT_NMSTRT: \
case BT_HEX: \
ptr += MINBPC(enc); \
@@ -575,7 +604,7 @@ PREFIX(scanAtts)(const ENCODING *enc, const char *ptr, const char *end,
return XML_TOK_INVALID;
}
}
- /* fall through */
+ /* fall through */
case BT_EQUALS:
{
int open;
@@ -1415,6 +1444,7 @@ PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end,
case BT_NMSTRT:
if (!(BYTE_TO_ASCII(enc, ptr) & ~0x7f))
break;
+ /* fall through */
default:
switch (BYTE_TO_ASCII(enc, ptr)) {
case 0x24: /* $ */
@@ -1626,87 +1656,14 @@ PREFIX(predefinedEntityName)(const ENCODING *UNUSED_P(enc), const char *ptr,
return 0;
}
-/* This function does not appear to be called from anywhere within the
- * library code. It is used via the macro XmlSameName(), which is
- * defined but never used. Since it appears in the encoding function
- * table, removing it is not a thing to be undertaken lightly. For
- * the moment, we simply exclude it from coverage tests.
- *
- * LCOV_EXCL_START
- */
-static int PTRCALL
-PREFIX(sameName)(const ENCODING *enc, const char *ptr1, const char *ptr2)
-{
- for (;;) {
- switch (BYTE_TYPE(enc, ptr1)) {
-#define LEAD_CASE(n) \
- case BT_LEAD ## n: \
- if (*ptr1++ != *ptr2++) \
- return 0;
- LEAD_CASE(4) LEAD_CASE(3) LEAD_CASE(2)
-#undef LEAD_CASE
- /* fall through */
- if (*ptr1++ != *ptr2++)
- return 0;
- break;
- case BT_NONASCII:
- case BT_NMSTRT:
-#ifdef XML_NS
- case BT_COLON:
-#endif
- case BT_HEX:
- case BT_DIGIT:
- case BT_NAME:
- case BT_MINUS:
- if (*ptr2++ != *ptr1++)
- return 0;
- if (MINBPC(enc) > 1) {
- if (*ptr2++ != *ptr1++)
- return 0;
- if (MINBPC(enc) > 2) {
- if (*ptr2++ != *ptr1++)
- return 0;
- if (MINBPC(enc) > 3) {
- if (*ptr2++ != *ptr1++)
- return 0;
- }
- }
- }
- break;
- default:
- if (MINBPC(enc) == 1 && *ptr1 == *ptr2)
- return 1;
- switch (BYTE_TYPE(enc, ptr2)) {
- case BT_LEAD2:
- case BT_LEAD3:
- case BT_LEAD4:
- case BT_NONASCII:
- case BT_NMSTRT:
-#ifdef XML_NS
- case BT_COLON:
-#endif
- case BT_HEX:
- case BT_DIGIT:
- case BT_NAME:
- case BT_MINUS:
- return 0;
- default:
- return 1;
- }
- }
- }
- /* not reached */
-}
-/* LCOV_EXCL_STOP */
-
static int PTRCALL
PREFIX(nameMatchesAscii)(const ENCODING *UNUSED_P(enc), const char *ptr1,
const char *end1, const char *ptr2)
{
for (; *ptr2; ptr1 += MINBPC(enc), ptr2++) {
if (end1 - ptr1 < MINBPC(enc)) {
- /* This line cannot be executed. THe incoming data has already
- * been tokenized once, so imcomplete characters like this have
+ /* This line cannot be executed. The incoming data has already
+ * been tokenized once, so incomplete characters like this have
* already been eliminated from the input. Retaining the
* paranoia check is still valuable, however.
*/
diff --git a/Utilities/cmexpat/lib/xmltok_impl.h b/Utilities/cmexpat/lib/xmltok_impl.h
index da0ea60a6..a6420f48e 100644
--- a/Utilities/cmexpat/lib/xmltok_impl.h
+++ b/Utilities/cmexpat/lib/xmltok_impl.h
@@ -1,6 +1,33 @@
/*
-Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
-See the file COPYING for copying permission.
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+ Copyright (c) 2000-2017 Expat development team
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
enum {
diff --git a/Utilities/cmexpat/lib/xmltok_ns.c b/Utilities/cmexpat/lib/xmltok_ns.c
index c3b88fdf4..23d31e8e4 100644
--- a/Utilities/cmexpat/lib/xmltok_ns.c
+++ b/Utilities/cmexpat/lib/xmltok_ns.c
@@ -1,8 +1,35 @@
-/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd
- See the file COPYING for copying permission.
+/* This file is included!
+ __ __ _
+ ___\ \/ /_ __ __ _| |_
+ / _ \\ /| '_ \ / _` | __|
+ | __// \| |_) | (_| | |_
+ \___/_/\_\ .__/ \__,_|\__|
+ |_| XML parser
+
+ Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
+ Copyright (c) 2000-2017 Expat development team
+ Licensed under the MIT license:
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to permit
+ persons to whom the Software is furnished to do so, subject to the
+ following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-/* This file is included! */
#ifdef XML_TOK_NS_C
const ENCODING *
diff --git a/Utilities/cmjsoncpp/CMakeLists.txt b/Utilities/cmjsoncpp/CMakeLists.txt
index ef370cc2c..764be8d55 100644
--- a/Utilities/cmjsoncpp/CMakeLists.txt
+++ b/Utilities/cmjsoncpp/CMakeLists.txt
@@ -2,7 +2,7 @@ project(JsonCpp CXX)
# Disable warnings to avoid changing 3rd party code.
if(CMAKE_CXX_COMPILER_ID MATCHES
- "^(GNU|Clang|AppleClang|XL|VisualAge|SunPro|MIPSpro|HP|Intel)$")
+ "^(GNU|Clang|AppleClang|XLClang|XL|VisualAge|SunPro|HP|Intel)$")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -w")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "PathScale")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -woffall")
diff --git a/Utilities/cmjsoncpp/include/json/config.h b/Utilities/cmjsoncpp/include/json/config.h
index eb52e71e4..2cc8462b3 100644
--- a/Utilities/cmjsoncpp/include/json/config.h
+++ b/Utilities/cmjsoncpp/include/json/config.h
@@ -5,13 +5,14 @@
#ifndef JSON_CONFIG_H_INCLUDED
#define JSON_CONFIG_H_INCLUDED
-#include <stddef.h>
-#include <string> //typedef String
-#include <stdint.h> //typedef int64_t, uint64_t
// Include KWSys Large File Support configuration.
#include <cmsys/Configure.h>
+#include <stddef.h>
+#include <string> //typedef String
+#include <stdint.h> //typedef int64_t, uint64_t
+
#if defined(_MSC_VER)
# pragma warning(push,1)
#endif
@@ -78,11 +79,6 @@
# pragma warning(disable : 4786)
# endif // MSVC 6
-# if _MSC_VER >= 1500 // MSVC 2008
- /// Indicates that the following function is deprecated.
-# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
-# endif
-
#endif // defined(_MSC_VER)
// In c++11 the override keyword allows you to explicity define that a function
@@ -136,7 +132,10 @@
# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
# define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
# endif // GNUC version
-#endif // __clang__ || __GNUC__
+#elif defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008
+ /// Indicates that the following function is deprecated.
+# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
+#endif // __clang__ || __GNUC__ || _MSC_VER
#undef JSONCPP_DEPRECATED // no deprecations in CMake copy
#if !defined(JSONCPP_DEPRECATED)
diff --git a/Utilities/cmlibarchive/CMakeLists.txt b/Utilities/cmlibarchive/CMakeLists.txt
index d7af6e2b6..60c8316e3 100644
--- a/Utilities/cmlibarchive/CMakeLists.txt
+++ b/Utilities/cmlibarchive/CMakeLists.txt
@@ -60,7 +60,7 @@ SET(CMAKE_REQUIRED_FLAGS)
# Disable warnings to avoid changing 3rd party code.
IF(CMAKE_C_COMPILER_ID MATCHES
- "^(GNU|Clang|AppleClang|XL|VisualAge|SunPro|MIPSpro|HP|Intel)$")
+ "^(GNU|Clang|AppleClang|XLClang|XL|VisualAge|SunPro|HP|Intel)$")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w")
ELSEIF(CMAKE_C_COMPILER_ID STREQUAL "PathScale")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -woffall")
@@ -376,6 +376,7 @@ IF(LZ4_FOUND)
ENDIF(LZ4_FOUND)
MARK_AS_ADVANCED(CLEAR LZ4_INCLUDE_DIR)
MARK_AS_ADVANCED(CLEAR LZ4_LIBRARY)
+ENDIF()
#
# Find Zstd
#
@@ -392,16 +393,13 @@ IF(ZSTD_FOUND)
SET(HAVE_ZSTD_H 1)
INCLUDE_DIRECTORIES(${ZSTD_INCLUDE_DIR})
LIST(APPEND ADDITIONAL_LIBS ${ZSTD_LIBRARY})
- SET(CMAKE_REQUIRED_LIBRARIES ${ZSTD_LIBRARY})
- SET(CMAKE_REQUIRED_INCLUDES ${ZSTD_INCLUDE_DIR})
- CHECK_FUNCTION_EXISTS(ZSTD_compressStream HAVE_LIBZSTD)
+ SET(HAVE_LIBZSTD 1)
#
# TODO: test for static library.
#
ENDIF(ZSTD_FOUND)
MARK_AS_ADVANCED(CLEAR ZSTD_INCLUDE_DIR)
MARK_AS_ADVANCED(CLEAR ZSTD_LIBRARY)
-ENDIF()
#
# Check headers
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c
index c8bb36be7..451370693 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c
@@ -45,7 +45,7 @@ __FBSDID("$FreeBSD$");
#include <unistd.h>
#endif
#if HAVE_ZSTD_H
-#include <zstd.h>
+#include <cm_zstd.h>
#endif
#include "archive.h"
diff --git a/Utilities/cmlibarchive/libarchive/archive_version_details.c b/Utilities/cmlibarchive/libarchive/archive_version_details.c
index e773e5e45..b9af6d781 100644
--- a/Utilities/cmlibarchive/libarchive/archive_version_details.c
+++ b/Utilities/cmlibarchive/libarchive/archive_version_details.c
@@ -46,7 +46,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:1
#include <lz4.h>
#endif
#ifdef HAVE_ZSTD_H
-#include <zstd.h>
+#include <cm_zstd.h>
#endif
#include "archive.h"
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c
index 671fc6aff..251b17da5 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c
@@ -38,7 +38,7 @@ __FBSDID("$FreeBSD$");
#include <string.h>
#endif
#ifdef HAVE_ZSTD_H
-#include <zstd.h>
+#include <cm_zstd.h>
#endif
#include "archive.h"
diff --git a/Utilities/cmliblzma/CMakeLists.txt b/Utilities/cmliblzma/CMakeLists.txt
index e9f88266d..b443fd6bc 100644
--- a/Utilities/cmliblzma/CMakeLists.txt
+++ b/Utilities/cmliblzma/CMakeLists.txt
@@ -183,7 +183,7 @@ INCLUDE_DIRECTORIES(
# Disable warnings to avoid changing 3rd party code.
IF(CMAKE_C_COMPILER_ID MATCHES
- "^(GNU|Clang|AppleClang|XL|VisualAge|SunPro|MIPSpro|HP|Intel)$")
+ "^(GNU|Clang|AppleClang|XLClang|XL|VisualAge|SunPro|HP|Intel)$")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w")
ELSEIF(CMAKE_C_COMPILER_ID STREQUAL "PathScale")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -woffall")
diff --git a/Utilities/cmlibrhash/CMakeLists.txt b/Utilities/cmlibrhash/CMakeLists.txt
index aa280554c..6067b7dcb 100644
--- a/Utilities/cmlibrhash/CMakeLists.txt
+++ b/Utilities/cmlibrhash/CMakeLists.txt
@@ -2,7 +2,7 @@ project(librhash C)
# Disable warnings to avoid changing 3rd party code.
if(CMAKE_C_COMPILER_ID MATCHES
- "^(GNU|Clang|AppleClang|XL|VisualAge|SunPro|MIPSpro|HP|Intel)$")
+ "^(GNU|Clang|AppleClang|XLClang|XL|VisualAge|SunPro|HP|Intel)$")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w")
elseif(CMAKE_C_COMPILER_ID STREQUAL "PathScale")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -woffall")
diff --git a/Utilities/cmlibuv/CMakeLists.txt b/Utilities/cmlibuv/CMakeLists.txt
index a62c516ef..2e781f155 100644
--- a/Utilities/cmlibuv/CMakeLists.txt
+++ b/Utilities/cmlibuv/CMakeLists.txt
@@ -2,7 +2,7 @@ project(libuv C)
# Disable warnings to avoid changing 3rd party code.
if(CMAKE_C_COMPILER_ID MATCHES
- "^(GNU|Clang|AppleClang|XL|VisualAge|SunPro|MIPSpro|HP|Intel)$")
+ "^(GNU|Clang|AppleClang|XLClang|XL|VisualAge|SunPro|HP|Intel)$")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w")
elseif(CMAKE_C_COMPILER_ID STREQUAL "PathScale")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -woffall")
diff --git a/Utilities/cmlibuv/src/unix/atomic-ops.h b/Utilities/cmlibuv/src/unix/atomic-ops.h
index 7cac1f988..be741dcb7 100644
--- a/Utilities/cmlibuv/src/unix/atomic-ops.h
+++ b/Utilities/cmlibuv/src/unix/atomic-ops.h
@@ -37,7 +37,7 @@ UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) {
: "r" (newval), "0" (oldval)
: "memory");
return out;
-#elif defined(_AIX) && defined(__xlC__)
+#elif defined(_AIX) && (defined(__xlC__) || defined(__ibmxl__))
const int out = (*(volatile int*) ptr);
__compare_and_swap(ptr, &oldval, newval);
return out;
@@ -63,7 +63,7 @@ UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval)) {
: "r" (newval), "0" (oldval)
: "memory");
return out;
-#elif defined(_AIX) && defined(__xlC__)
+#elif defined(_AIX) && (defined(__xlC__) || defined(__ibmxl__))
const long out = (*(volatile int*) ptr);
# if defined(__64BIT__)
__compare_and_swaplp(ptr, &oldval, newval);
diff --git a/Utilities/cmzlib/CMakeLists.txt b/Utilities/cmzlib/CMakeLists.txt
index 0be48f107..888c3ff77 100644
--- a/Utilities/cmzlib/CMakeLists.txt
+++ b/Utilities/cmzlib/CMakeLists.txt
@@ -2,7 +2,7 @@ PROJECT(CMZLIB)
# Disable warnings to avoid changing 3rd party code.
if(CMAKE_C_COMPILER_ID MATCHES
- "^(GNU|Clang|AppleClang|XL|VisualAge|SunPro|MIPSpro|HP|Intel)$")
+ "^(GNU|Clang|AppleClang|XLClang|XL|VisualAge|SunPro|HP|Intel)$")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w")
elseif(CMAKE_C_COMPILER_ID STREQUAL "PathScale")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -woffall")
diff --git a/Utilities/cmzstd/CMakeLists.txt b/Utilities/cmzstd/CMakeLists.txt
new file mode 100644
index 000000000..8ed04d8d1
--- /dev/null
+++ b/Utilities/cmzstd/CMakeLists.txt
@@ -0,0 +1,47 @@
+project(zstd C)
+
+# Disable warnings to avoid changing 3rd party code.
+if(CMAKE_C_COMPILER_ID MATCHES
+ "^(GNU|Clang|AppleClang|XLClang|XL|VisualAge|SunPro|HP|Intel)$")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w")
+elseif(CMAKE_C_COMPILER_ID STREQUAL "PathScale")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -woffall")
+endif()
+
+include_directories(lib lib/common)
+
+add_library(cmzstd STATIC
+ lib/common/entropy_common.c
+ lib/common/error_private.c
+ lib/common/fse_decompress.c
+ lib/common/pool.c
+ lib/common/threading.c
+ lib/common/xxhash.c
+ lib/common/zstd_common.c
+ lib/compress/fse_compress.c
+ lib/compress/hist.c
+ lib/compress/huf_compress.c
+ lib/compress/zstd_compress.c
+ lib/compress/zstd_double_fast.c
+ lib/compress/zstd_fast.c
+ lib/compress/zstd_lazy.c
+ lib/compress/zstd_ldm.c
+ lib/compress/zstdmt_compress.c
+ lib/compress/zstd_opt.c
+ lib/decompress/huf_decompress.c
+ lib/decompress/zstd_ddict.c
+ lib/decompress/zstd_decompress_block.c
+ lib/decompress/zstd_decompress.c
+ lib/deprecated/zbuff_common.c
+ lib/deprecated/zbuff_compress.c
+ lib/deprecated/zbuff_decompress.c
+ lib/dictBuilder/cover.c
+ lib/dictBuilder/divsufsort.c
+ lib/dictBuilder/fastcover.c
+ lib/dictBuilder/zdict.c
+ )
+
+# BMI2 instructions are not supported in older environments.
+set_property(TARGET cmzstd PROPERTY COMPILE_DEFINITIONS DYNAMIC_BMI2=0)
+
+install(FILES LICENSE DESTINATION ${CMAKE_DOC_DIR}/cmzstd)
diff --git a/Utilities/cmzstd/LICENSE b/Utilities/cmzstd/LICENSE
new file mode 100644
index 000000000..a793a8028
--- /dev/null
+++ b/Utilities/cmzstd/LICENSE
@@ -0,0 +1,30 @@
+BSD License
+
+For Zstandard software
+
+Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
+
+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.
+
+ * Neither the name Facebook nor the names of its contributors may be used to
+ endorse or promote products derived from this software without specific
+ prior written permission.
+
+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.
diff --git a/Utilities/cmzstd/README.md b/Utilities/cmzstd/README.md
new file mode 100644
index 000000000..4b6d19e7e
--- /dev/null
+++ b/Utilities/cmzstd/README.md
@@ -0,0 +1,167 @@
+<p align="center"><img src="https://raw.githubusercontent.com/facebook/zstd/dev/doc/images/zstd_logo86.png" alt="Zstandard"></p>
+
+__Zstandard__, or `zstd` as short version, is a fast lossless compression algorithm,
+targeting real-time compression scenarios at zlib-level and better compression ratios.
+It's backed by a very fast entropy stage, provided by [Huff0 and FSE library](https://github.com/Cyan4973/FiniteStateEntropy).
+
+The project is provided as an open-source dual [BSD](LICENSE) and [GPLv2](COPYING) licensed **C** library,
+and a command line utility producing and decoding `.zst`, `.gz`, `.xz` and `.lz4` files.
+Should your project require another programming language,
+a list of known ports and bindings is provided on [Zstandard homepage](http://www.zstd.net/#other-languages).
+
+**Development branch status:**
+
+[![Build Status][travisDevBadge]][travisLink]
+[![Build status][AppveyorDevBadge]][AppveyorLink]
+[![Build status][CircleDevBadge]][CircleLink]
+
+[travisDevBadge]: https://travis-ci.org/facebook/zstd.svg?branch=dev "Continuous Integration test suite"
+[travisLink]: https://travis-ci.org/facebook/zstd
+[AppveyorDevBadge]: https://ci.appveyor.com/api/projects/status/xt38wbdxjk5mrbem/branch/dev?svg=true "Windows test suite"
+[AppveyorLink]: https://ci.appveyor.com/project/YannCollet/zstd-p0yf0
+[CircleDevBadge]: https://circleci.com/gh/facebook/zstd/tree/dev.svg?style=shield "Short test suite"
+[CircleLink]: https://circleci.com/gh/facebook/zstd
+
+## Benchmarks
+
+For reference, several fast compression algorithms were tested and compared
+on a server running Linux Debian (`Linux version 4.14.0-3-amd64`),
+with a Core i7-6700K CPU @ 4.0GHz,
+using [lzbench], an open-source in-memory benchmark by @inikep
+compiled with [gcc] 7.3.0,
+on the [Silesia compression corpus].
+
+[lzbench]: https://github.com/inikep/lzbench
+[Silesia compression corpus]: http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia
+[gcc]: https://gcc.gnu.org/
+
+| Compressor name | Ratio | Compression| Decompress.|
+| --------------- | ------| -----------| ---------- |
+| **zstd 1.3.4 -1** | 2.877 | 470 MB/s | 1380 MB/s |
+| zlib 1.2.11 -1 | 2.743 | 110 MB/s | 400 MB/s |
+| brotli 1.0.2 -0 | 2.701 | 410 MB/s | 430 MB/s |
+| quicklz 1.5.0 -1 | 2.238 | 550 MB/s | 710 MB/s |
+| lzo1x 2.09 -1 | 2.108 | 650 MB/s | 830 MB/s |
+| lz4 1.8.1 | 2.101 | 750 MB/s | 3700 MB/s |
+| snappy 1.1.4 | 2.091 | 530 MB/s | 1800 MB/s |
+| lzf 3.6 -1 | 2.077 | 400 MB/s | 860 MB/s |
+
+[zlib]: http://www.zlib.net/
+[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 increments.
+Decompression speed is preserved and remains roughly the same at all settings,
+a property shared by most LZ compression algorithms, such as [zlib] or lzma.
+
+The following tests were run
+on a server running Linux Debian (`Linux version 4.14.0-3-amd64`)
+with a Core i7-6700K CPU @ 4.0GHz,
+using [lzbench], an open-source in-memory benchmark by @inikep
+compiled with [gcc] 7.3.0,
+on the [Silesia compression corpus].
+
+Compression Speed vs Ratio | Decompression Speed
+---------------------------|--------------------
+![Compression Speed vs Ratio](doc/images/CSpeed2.png "Compression Speed vs Ratio") | ![Decompression Speed](doc/images/DSpeed3.png "Decompression Speed")
+
+A few other algorithms can produce higher compression ratios at slower speeds, falling outside of the graph.
+For a larger picture including slow modes, [click on this link](doc/images/DCspeed5.png).
+
+
+## The case for Small Data 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 compress. This problem is common to all compression algorithms, and reason is, compression algorithms learn from past data how to compress future data. But at the beginning of a new data set, 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.
+Training Zstandard is achieved by providing it with a few samples (one file per sample). The result of this training is stored in a file called "dictionary", which must be loaded before compression and decompression.
+Using this dictionary, the compression ratio achievable on small data improves dramatically.
+
+The following example uses the `github-users` [sample set](https://github.com/facebook/zstd/releases/tag/v1.1.3), created from [github public API](https://developer.github.com/v3/users/#get-all-users).
+It consists of roughly 10K records weighing about 1KB each.
+
+Compression Ratio | Compression Speed | Decompression Speed
+------------------|-------------------|--------------------
+![Compression Ratio](doc/images/dict-cr.png "Compression Ratio") | ![Compression Speed](doc/images/dict-cs.png "Compression Speed") | ![Decompression Speed](doc/images/dict-ds.png "Decompression Speed")
+
+
+These compression gains are achieved while simultaneously providing _faster_ compression and decompression speeds.
+
+Training works if there is some correlation in a family of small data samples. The more data-specific a dictionary is, the more efficient it is (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 gradually use previously decoded content to better compress the rest of the file.
+
+### Dictionary compression How To:
+
+1. Create the dictionary
+
+ `zstd --train FullPathToTrainingSet/* -o dictionaryName`
+
+2. Compress with dictionary
+
+ `zstd -D dictionaryName FILE`
+
+3. Decompress with dictionary
+
+ `zstd -D dictionaryName --decompress FILE.zst`
+
+
+## Build instructions
+
+### Makefile
+
+If your system is compatible with standard `make` (or `gmake`),
+invoking `make` in root directory will generate `zstd` cli in root directory.
+
+Other available options include:
+- `make install` : create and install zstd cli, library and man pages
+- `make check` : create and run `zstd`, tests its behavior 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.
+
+By default, `CMAKE_BUILD_TYPE` is set to `Release`.
+
+### Meson
+
+A Meson project is provided within [`build/meson`](build/meson). Follow
+build instructions in that directory.
+
+You can also take a look at [`.travis.yml`](.travis.yml) file for an
+example about how Meson is used to build this project.
+
+Note that default build type is **release**.
+
+### Visual Studio (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, VS2015 and VS2017.
+- Automated build scripts for Visual compiler by [@KrzysFR](https://github.com/KrzysFR), in `build/VS_scripts`,
+ which will build `zstd` cli and `libzstd` library without any need to open Visual Studio solution.
+
+### Buck
+
+You can build the zstd binary via buck by executing: `buck build programs:zstd` from the root of the repo.
+The output binary will be in `buck-out/gen/programs/`.
+
+## Status
+
+Zstandard is currently deployed within Facebook. It is used continuously to compress large amounts of data in multiple formats and use cases.
+Zstandard is considered safe for production environments.
+
+## License
+
+Zstandard is dual-licensed under [BSD](LICENSE) and [GPLv2](COPYING).
+
+## Contributing
+
+The "dev" branch is the one where all contributions are merged before reaching "master".
+If you plan to propose a patch, please commit into the "dev" branch, or its own feature branch.
+Direct commit to "master" are not permitted.
+For more information, please read [CONTRIBUTING](CONTRIBUTING.md).
diff --git a/Utilities/cmzstd/lib/common/bitstream.h b/Utilities/cmzstd/lib/common/bitstream.h
new file mode 100644
index 000000000..d955bd677
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/bitstream.h
@@ -0,0 +1,455 @@
+/* ******************************************************************
+ bitstream
+ Part of FSE library
+ Copyright (C) 2013-present, 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/FiniteStateEntropy
+****************************************************************** */
+#ifndef BITSTREAM_H_MODULE
+#define BITSTREAM_H_MODULE
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+/*
+* This API consists of small unitary functions, which must be inlined for best performance.
+* Since link-time-optimization is not available for all compilers,
+* these functions are defined into a .h to be included.
+*/
+
+/*-****************************************
+* Dependencies
+******************************************/
+#include "mem.h" /* unaligned access routines */
+#include "debug.h" /* assert(), DEBUGLOG(), RAWLOG() */
+#include "error_private.h" /* error codes and messages */
+
+
+/*=========================================
+* Target specific
+=========================================*/
+#if defined(__BMI__) && defined(__GNUC__)
+# include <immintrin.h> /* support for bextr (experimental) */
+#endif
+
+#define STREAM_ACCUMULATOR_MIN_32 25
+#define STREAM_ACCUMULATOR_MIN_64 57
+#define STREAM_ACCUMULATOR_MIN ((U32)(MEM_32bits() ? STREAM_ACCUMULATOR_MIN_32 : STREAM_ACCUMULATOR_MIN_64))
+
+
+/*-******************************************
+* bitStream encoding API (write forward)
+********************************************/
+/* bitStream can mix input from multiple sources.
+ * A critical property of these streams is that they encode and decode in **reverse** direction.
+ * So the first bit sequence you add will be the last to be read, like a LIFO stack.
+ */
+typedef struct {
+ size_t bitContainer;
+ unsigned bitPos;
+ char* startPtr;
+ char* ptr;
+ char* endPtr;
+} BIT_CStream_t;
+
+MEM_STATIC size_t BIT_initCStream(BIT_CStream_t* bitC, void* dstBuffer, size_t dstCapacity);
+MEM_STATIC void BIT_addBits(BIT_CStream_t* bitC, size_t value, unsigned nbBits);
+MEM_STATIC void BIT_flushBits(BIT_CStream_t* bitC);
+MEM_STATIC size_t BIT_closeCStream(BIT_CStream_t* bitC);
+
+/* Start with initCStream, providing the size of buffer to write into.
+* bitStream will never write outside of this buffer.
+* `dstCapacity` must be >= sizeof(bitD->bitContainer), otherwise @return will be an error code.
+*
+* bits are first added to a local register.
+* Local register is size_t, hence 64-bits on 64-bits systems, or 32-bits on 32-bits systems.
+* Writing data into memory is an explicit operation, performed by the flushBits function.
+* Hence keep track how many bits are potentially stored into local register to avoid register overflow.
+* After a flushBits, a maximum of 7 bits might still be stored into local register.
+*
+* Avoid storing elements of more than 24 bits if you want compatibility with 32-bits bitstream readers.
+*
+* Last operation is to close the bitStream.
+* The function returns the final size of CStream in bytes.
+* If data couldn't fit into `dstBuffer`, it will return a 0 ( == not storable)
+*/
+
+
+/*-********************************************
+* bitStream decoding API (read backward)
+**********************************************/
+typedef struct {
+ size_t bitContainer;
+ unsigned bitsConsumed;
+ const char* ptr;
+ const char* start;
+ const char* limitPtr;
+} BIT_DStream_t;
+
+typedef enum { BIT_DStream_unfinished = 0,
+ BIT_DStream_endOfBuffer = 1,
+ BIT_DStream_completed = 2,
+ BIT_DStream_overflow = 3 } BIT_DStream_status; /* result of BIT_reloadDStream() */
+ /* 1,2,4,8 would be better for bitmap combinations, but slows down performance a bit ... :( */
+
+MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, size_t srcSize);
+MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, unsigned nbBits);
+MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD);
+MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* bitD);
+
+
+/* Start by invoking BIT_initDStream().
+* A chunk of the bitStream is then stored into a local register.
+* Local register size is 64-bits on 64-bits systems, 32-bits on 32-bits systems (size_t).
+* You can then retrieve bitFields stored into the local register, **in reverse order**.
+* Local register is explicitly reloaded from memory by the BIT_reloadDStream() method.
+* A reload guarantee a minimum of ((8*sizeof(bitD->bitContainer))-7) bits when its result is BIT_DStream_unfinished.
+* Otherwise, it can be less than that, so proceed accordingly.
+* Checking if DStream has reached its end can be performed with BIT_endOfDStream().
+*/
+
+
+/*-****************************************
+* unsafe API
+******************************************/
+MEM_STATIC void BIT_addBitsFast(BIT_CStream_t* bitC, size_t value, unsigned nbBits);
+/* faster, but works only if value is "clean", meaning all high bits above nbBits are 0 */
+
+MEM_STATIC void BIT_flushBitsFast(BIT_CStream_t* bitC);
+/* unsafe version; does not check buffer overflow */
+
+MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits);
+/* faster, but works only if nbBits >= 1 */
+
+
+
+/*-**************************************************************
+* Internal functions
+****************************************************************/
+MEM_STATIC unsigned BIT_highbit32 (U32 val)
+{
+ assert(val != 0);
+ {
+# if defined(_MSC_VER) /* Visual */
+ unsigned long r=0;
+ _BitScanReverse ( &r, val );
+ return (unsigned) r;
+# elif defined(__GNUC__) && (__GNUC__ >= 3) /* Use GCC Intrinsic */
+ return 31 - __builtin_clz (val);
+# else /* Software version */
+ static const unsigned DeBruijnClz[32] = { 0, 9, 1, 10, 13, 21, 2, 29,
+ 11, 14, 16, 18, 22, 25, 3, 30,
+ 8, 12, 20, 28, 15, 17, 24, 7,
+ 19, 27, 23, 6, 26, 5, 4, 31 };
+ U32 v = val;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ return DeBruijnClz[ (U32) (v * 0x07C4ACDDU) >> 27];
+# endif
+ }
+}
+
+/*===== Local Constants =====*/
+static const unsigned BIT_mask[] = {
+ 0, 1, 3, 7, 0xF, 0x1F,
+ 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF,
+ 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0x1FFFF,
+ 0x3FFFF, 0x7FFFF, 0xFFFFF, 0x1FFFFF, 0x3FFFFF, 0x7FFFFF,
+ 0xFFFFFF, 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF, 0x1FFFFFFF,
+ 0x3FFFFFFF, 0x7FFFFFFF}; /* up to 31 bits */
+#define BIT_MASK_SIZE (sizeof(BIT_mask) / sizeof(BIT_mask[0]))
+
+/*-**************************************************************
+* bitStream encoding
+****************************************************************/
+/*! BIT_initCStream() :
+ * `dstCapacity` must be > sizeof(size_t)
+ * @return : 0 if success,
+ * otherwise an error code (can be tested using ERR_isError()) */
+MEM_STATIC size_t BIT_initCStream(BIT_CStream_t* bitC,
+ void* startPtr, size_t dstCapacity)
+{
+ bitC->bitContainer = 0;
+ bitC->bitPos = 0;
+ bitC->startPtr = (char*)startPtr;
+ bitC->ptr = bitC->startPtr;
+ bitC->endPtr = bitC->startPtr + dstCapacity - sizeof(bitC->bitContainer);
+ if (dstCapacity <= sizeof(bitC->bitContainer)) return ERROR(dstSize_tooSmall);
+ return 0;
+}
+
+/*! BIT_addBits() :
+ * can add up to 31 bits into `bitC`.
+ * Note : does not check for register overflow ! */
+MEM_STATIC void BIT_addBits(BIT_CStream_t* bitC,
+ size_t value, unsigned nbBits)
+{
+ MEM_STATIC_ASSERT(BIT_MASK_SIZE == 32);
+ assert(nbBits < BIT_MASK_SIZE);
+ assert(nbBits + bitC->bitPos < sizeof(bitC->bitContainer) * 8);
+ bitC->bitContainer |= (value & BIT_mask[nbBits]) << bitC->bitPos;
+ bitC->bitPos += nbBits;
+}
+
+/*! BIT_addBitsFast() :
+ * works only if `value` is _clean_,
+ * meaning all high bits above nbBits are 0 */
+MEM_STATIC void BIT_addBitsFast(BIT_CStream_t* bitC,
+ size_t value, unsigned nbBits)
+{
+ assert((value>>nbBits) == 0);
+ assert(nbBits + bitC->bitPos < sizeof(bitC->bitContainer) * 8);
+ bitC->bitContainer |= value << bitC->bitPos;
+ bitC->bitPos += nbBits;
+}
+
+/*! BIT_flushBitsFast() :
+ * assumption : bitContainer has not overflowed
+ * unsafe version; does not check buffer overflow */
+MEM_STATIC void BIT_flushBitsFast(BIT_CStream_t* bitC)
+{
+ size_t const nbBytes = bitC->bitPos >> 3;
+ assert(bitC->bitPos < sizeof(bitC->bitContainer) * 8);
+ MEM_writeLEST(bitC->ptr, bitC->bitContainer);
+ bitC->ptr += nbBytes;
+ assert(bitC->ptr <= bitC->endPtr);
+ bitC->bitPos &= 7;
+ bitC->bitContainer >>= nbBytes*8;
+}
+
+/*! BIT_flushBits() :
+ * assumption : bitContainer has not overflowed
+ * safe version; check for buffer overflow, and prevents it.
+ * note : does not signal buffer overflow.
+ * overflow will be revealed later on using BIT_closeCStream() */
+MEM_STATIC void BIT_flushBits(BIT_CStream_t* bitC)
+{
+ size_t const nbBytes = bitC->bitPos >> 3;
+ assert(bitC->bitPos < sizeof(bitC->bitContainer) * 8);
+ MEM_writeLEST(bitC->ptr, bitC->bitContainer);
+ bitC->ptr += nbBytes;
+ if (bitC->ptr > bitC->endPtr) bitC->ptr = bitC->endPtr;
+ bitC->bitPos &= 7;
+ bitC->bitContainer >>= nbBytes*8;
+}
+
+/*! BIT_closeCStream() :
+ * @return : size of CStream, in bytes,
+ * or 0 if it could not fit into dstBuffer */
+MEM_STATIC size_t BIT_closeCStream(BIT_CStream_t* bitC)
+{
+ BIT_addBitsFast(bitC, 1, 1); /* endMark */
+ BIT_flushBits(bitC);
+ if (bitC->ptr >= bitC->endPtr) return 0; /* overflow detected */
+ return (bitC->ptr - bitC->startPtr) + (bitC->bitPos > 0);
+}
+
+
+/*-********************************************************
+* bitStream decoding
+**********************************************************/
+/*! BIT_initDStream() :
+ * Initialize a BIT_DStream_t.
+ * `bitD` : a pointer to an already allocated BIT_DStream_t structure.
+ * `srcSize` must be the *exact* size of the bitStream, in bytes.
+ * @return : size of stream (== srcSize), or an errorCode if a problem is detected
+ */
+MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, size_t srcSize)
+{
+ if (srcSize < 1) { memset(bitD, 0, sizeof(*bitD)); return ERROR(srcSize_wrong); }
+
+ bitD->start = (const char*)srcBuffer;
+ bitD->limitPtr = bitD->start + sizeof(bitD->bitContainer);
+
+ if (srcSize >= sizeof(bitD->bitContainer)) { /* normal case */
+ bitD->ptr = (const char*)srcBuffer + srcSize - sizeof(bitD->bitContainer);
+ bitD->bitContainer = MEM_readLEST(bitD->ptr);
+ { BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1];
+ bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; /* ensures bitsConsumed is always set */
+ if (lastByte == 0) return ERROR(GENERIC); /* endMark not present */ }
+ } else {
+ bitD->ptr = bitD->start;
+ bitD->bitContainer = *(const BYTE*)(bitD->start);
+ switch(srcSize)
+ {
+ case 7: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[6]) << (sizeof(bitD->bitContainer)*8 - 16);
+ /* fall-through */
+
+ case 6: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[5]) << (sizeof(bitD->bitContainer)*8 - 24);
+ /* fall-through */
+
+ case 5: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[4]) << (sizeof(bitD->bitContainer)*8 - 32);
+ /* fall-through */
+
+ case 4: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[3]) << 24;
+ /* fall-through */
+
+ case 3: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[2]) << 16;
+ /* fall-through */
+
+ case 2: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[1]) << 8;
+ /* fall-through */
+
+ default: break;
+ }
+ { BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1];
+ bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0;
+ if (lastByte == 0) return ERROR(corruption_detected); /* endMark not present */
+ }
+ bitD->bitsConsumed += (U32)(sizeof(bitD->bitContainer) - srcSize)*8;
+ }
+
+ return srcSize;
+}
+
+MEM_STATIC size_t BIT_getUpperBits(size_t bitContainer, U32 const start)
+{
+ return bitContainer >> start;
+}
+
+MEM_STATIC size_t BIT_getMiddleBits(size_t bitContainer, U32 const start, U32 const nbBits)
+{
+ U32 const regMask = sizeof(bitContainer)*8 - 1;
+ /* if start > regMask, bitstream is corrupted, and result is undefined */
+ assert(nbBits < BIT_MASK_SIZE);
+ return (bitContainer >> (start & regMask)) & BIT_mask[nbBits];
+}
+
+MEM_STATIC size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits)
+{
+ assert(nbBits < BIT_MASK_SIZE);
+ return bitContainer & BIT_mask[nbBits];
+}
+
+/*! BIT_lookBits() :
+ * Provides next n bits from local register.
+ * local register is not modified.
+ * On 32-bits, maxNbBits==24.
+ * On 64-bits, maxNbBits==56.
+ * @return : value extracted */
+MEM_STATIC size_t BIT_lookBits(const BIT_DStream_t* bitD, U32 nbBits)
+{
+ /* arbitrate between double-shift and shift+mask */
+#if 1
+ /* if bitD->bitsConsumed + nbBits > sizeof(bitD->bitContainer)*8,
+ * bitstream is likely corrupted, and result is undefined */
+ return BIT_getMiddleBits(bitD->bitContainer, (sizeof(bitD->bitContainer)*8) - bitD->bitsConsumed - nbBits, nbBits);
+#else
+ /* this code path is slower on my os-x laptop */
+ U32 const regMask = sizeof(bitD->bitContainer)*8 - 1;
+ return ((bitD->bitContainer << (bitD->bitsConsumed & regMask)) >> 1) >> ((regMask-nbBits) & regMask);
+#endif
+}
+
+/*! BIT_lookBitsFast() :
+ * unsafe version; only works if nbBits >= 1 */
+MEM_STATIC size_t BIT_lookBitsFast(const BIT_DStream_t* bitD, U32 nbBits)
+{
+ U32 const regMask = sizeof(bitD->bitContainer)*8 - 1;
+ assert(nbBits >= 1);
+ return (bitD->bitContainer << (bitD->bitsConsumed & regMask)) >> (((regMask+1)-nbBits) & regMask);
+}
+
+MEM_STATIC void BIT_skipBits(BIT_DStream_t* bitD, U32 nbBits)
+{
+ bitD->bitsConsumed += nbBits;
+}
+
+/*! BIT_readBits() :
+ * Read (consume) next n bits from local register and update.
+ * Pay attention to not read more than nbBits contained into local register.
+ * @return : extracted value. */
+MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, unsigned nbBits)
+{
+ size_t const value = BIT_lookBits(bitD, nbBits);
+ BIT_skipBits(bitD, nbBits);
+ return value;
+}
+
+/*! BIT_readBitsFast() :
+ * unsafe version; only works only if nbBits >= 1 */
+MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits)
+{
+ size_t const value = BIT_lookBitsFast(bitD, nbBits);
+ assert(nbBits >= 1);
+ BIT_skipBits(bitD, nbBits);
+ return value;
+}
+
+/*! BIT_reloadDStream() :
+ * Refill `bitD` from buffer previously set in BIT_initDStream() .
+ * This function is safe, it guarantees it will not read beyond src buffer.
+ * @return : status of `BIT_DStream_t` internal register.
+ * when status == BIT_DStream_unfinished, internal register is filled with at least 25 or 57 bits */
+MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD)
+{
+ if (bitD->bitsConsumed > (sizeof(bitD->bitContainer)*8)) /* overflow detected, like end of stream */
+ return BIT_DStream_overflow;
+
+ if (bitD->ptr >= bitD->limitPtr) {
+ bitD->ptr -= bitD->bitsConsumed >> 3;
+ bitD->bitsConsumed &= 7;
+ bitD->bitContainer = MEM_readLEST(bitD->ptr);
+ return BIT_DStream_unfinished;
+ }
+ if (bitD->ptr == bitD->start) {
+ if (bitD->bitsConsumed < sizeof(bitD->bitContainer)*8) return BIT_DStream_endOfBuffer;
+ return BIT_DStream_completed;
+ }
+ /* start < ptr < limitPtr */
+ { U32 nbBytes = bitD->bitsConsumed >> 3;
+ BIT_DStream_status result = BIT_DStream_unfinished;
+ if (bitD->ptr - nbBytes < bitD->start) {
+ nbBytes = (U32)(bitD->ptr - bitD->start); /* ptr > start */
+ result = BIT_DStream_endOfBuffer;
+ }
+ bitD->ptr -= nbBytes;
+ bitD->bitsConsumed -= nbBytes*8;
+ bitD->bitContainer = MEM_readLEST(bitD->ptr); /* reminder : srcSize > sizeof(bitD->bitContainer), otherwise bitD->ptr == bitD->start */
+ return result;
+ }
+}
+
+/*! BIT_endOfDStream() :
+ * @return : 1 if DStream has _exactly_ reached its end (all bits consumed).
+ */
+MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* DStream)
+{
+ return ((DStream->ptr == DStream->start) && (DStream->bitsConsumed == sizeof(DStream->bitContainer)*8));
+}
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* BITSTREAM_H_MODULE */
diff --git a/Utilities/cmzstd/lib/common/compiler.h b/Utilities/cmzstd/lib/common/compiler.h
new file mode 100644
index 000000000..7f561282c
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/compiler.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#ifndef ZSTD_COMPILER_H
+#define ZSTD_COMPILER_H
+
+/*-*******************************************************
+* Compiler specifics
+*********************************************************/
+/* force inlining */
+
+#if !defined(ZSTD_NO_INLINE)
+#if defined (__GNUC__) || defined(__cplusplus) || defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
+# define INLINE_KEYWORD inline
+#else
+# define INLINE_KEYWORD
+#endif
+
+#if defined(__GNUC__)
+# define FORCE_INLINE_ATTR __attribute__((always_inline))
+#elif defined(_MSC_VER)
+# define FORCE_INLINE_ATTR __forceinline
+#else
+# define FORCE_INLINE_ATTR
+#endif
+
+#else
+
+#define INLINE_KEYWORD
+#define FORCE_INLINE_ATTR
+
+#endif
+
+/**
+ * FORCE_INLINE_TEMPLATE is used to define C "templates", which take constant
+ * parameters. They must be inlined for the compiler to elimininate the constant
+ * branches.
+ */
+#define FORCE_INLINE_TEMPLATE static INLINE_KEYWORD FORCE_INLINE_ATTR
+/**
+ * HINT_INLINE is used to help the compiler generate better code. It is *not*
+ * used for "templates", so it can be tweaked based on the compilers
+ * performance.
+ *
+ * gcc-4.8 and gcc-4.9 have been shown to benefit from leaving off the
+ * always_inline attribute.
+ *
+ * clang up to 5.0.0 (trunk) benefit tremendously from the always_inline
+ * attribute.
+ */
+#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 8 && __GNUC__ < 5
+# define HINT_INLINE static INLINE_KEYWORD
+#else
+# define HINT_INLINE static INLINE_KEYWORD FORCE_INLINE_ATTR
+#endif
+
+/* force no inlining */
+#ifdef _MSC_VER
+# define FORCE_NOINLINE static __declspec(noinline)
+#else
+# ifdef __GNUC__
+# define FORCE_NOINLINE static __attribute__((__noinline__))
+# else
+# define FORCE_NOINLINE static
+# endif
+#endif
+
+/* target attribute */
+#ifndef __has_attribute
+ #define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
+#endif
+#if defined(__GNUC__)
+# define TARGET_ATTRIBUTE(target) __attribute__((__target__(target)))
+#else
+# define TARGET_ATTRIBUTE(target)
+#endif
+
+/* Enable runtime BMI2 dispatch based on the CPU.
+ * Enabled for clang & gcc >=4.8 on x86 when BMI2 isn't enabled by default.
+ */
+#ifndef DYNAMIC_BMI2
+ #if ((defined(__clang__) && __has_attribute(__target__)) \
+ || (defined(__GNUC__) \
+ && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))) \
+ && (defined(__x86_64__) || defined(_M_X86)) \
+ && !defined(__BMI2__)
+ # define DYNAMIC_BMI2 1
+ #else
+ # define DYNAMIC_BMI2 0
+ #endif
+#endif
+
+/* prefetch
+ * can be disabled, by declaring NO_PREFETCH build macro */
+#if defined(NO_PREFETCH)
+# define PREFETCH_L1(ptr) (void)(ptr) /* disabled */
+# define PREFETCH_L2(ptr) (void)(ptr) /* disabled */
+#else
+# if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_I86)) /* _mm_prefetch() is not defined outside of x86/x64 */
+# include <mmintrin.h> /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */
+# define PREFETCH_L1(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T0)
+# define PREFETCH_L2(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T1)
+# elif defined(__GNUC__) && ( (__GNUC__ >= 4) || ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) ) )
+# define PREFETCH_L1(ptr) __builtin_prefetch((ptr), 0 /* rw==read */, 3 /* locality */)
+# define PREFETCH_L2(ptr) __builtin_prefetch((ptr), 0 /* rw==read */, 2 /* locality */)
+# else
+# define PREFETCH_L1(ptr) (void)(ptr) /* disabled */
+# define PREFETCH_L2(ptr) (void)(ptr) /* disabled */
+# endif
+#endif /* NO_PREFETCH */
+
+#define CACHELINE_SIZE 64
+
+#define PREFETCH_AREA(p, s) { \
+ const char* const _ptr = (const char*)(p); \
+ size_t const _size = (size_t)(s); \
+ size_t _pos; \
+ for (_pos=0; _pos<_size; _pos+=CACHELINE_SIZE) { \
+ PREFETCH_L2(_ptr + _pos); \
+ } \
+}
+
+/* disable warnings */
+#ifdef _MSC_VER /* Visual Studio */
+# include <intrin.h> /* For Visual 2005 */
+# pragma warning(disable : 4100) /* disable: C4100: unreferenced formal parameter */
+# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
+# pragma warning(disable : 4204) /* disable: C4204: non-constant aggregate initializer */
+# pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */
+# pragma warning(disable : 4324) /* disable: C4324: padded structure */
+#endif
+
+#endif /* ZSTD_COMPILER_H */
diff --git a/Utilities/cmzstd/lib/common/cpu.h b/Utilities/cmzstd/lib/common/cpu.h
new file mode 100644
index 000000000..5f0923fc9
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/cpu.h
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2018-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#ifndef ZSTD_COMMON_CPU_H
+#define ZSTD_COMMON_CPU_H
+
+/**
+ * Implementation taken from folly/CpuId.h
+ * https://github.com/facebook/folly/blob/master/folly/CpuId.h
+ */
+
+#include <string.h>
+
+#include "mem.h"
+
+#ifdef _MSC_VER
+#include <intrin.h>
+#endif
+
+typedef struct {
+ U32 f1c;
+ U32 f1d;
+ U32 f7b;
+ U32 f7c;
+} ZSTD_cpuid_t;
+
+MEM_STATIC ZSTD_cpuid_t ZSTD_cpuid(void) {
+ U32 f1c = 0;
+ U32 f1d = 0;
+ U32 f7b = 0;
+ U32 f7c = 0;
+#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86))
+ int reg[4];
+ __cpuid((int*)reg, 0);
+ {
+ int const n = reg[0];
+ if (n >= 1) {
+ __cpuid((int*)reg, 1);
+ f1c = (U32)reg[2];
+ f1d = (U32)reg[3];
+ }
+ if (n >= 7) {
+ __cpuidex((int*)reg, 7, 0);
+ f7b = (U32)reg[1];
+ f7c = (U32)reg[2];
+ }
+ }
+#elif defined(__i386__) && defined(__PIC__) && !defined(__clang__) && defined(__GNUC__)
+ /* The following block like the normal cpuid branch below, but gcc
+ * reserves ebx for use of its pic register so we must specially
+ * handle the save and restore to avoid clobbering the register
+ */
+ U32 n;
+ __asm__(
+ "pushl %%ebx\n\t"
+ "cpuid\n\t"
+ "popl %%ebx\n\t"
+ : "=a"(n)
+ : "a"(0)
+ : "ecx", "edx");
+ if (n >= 1) {
+ U32 f1a;
+ __asm__(
+ "pushl %%ebx\n\t"
+ "cpuid\n\t"
+ "popl %%ebx\n\t"
+ : "=a"(f1a), "=c"(f1c), "=d"(f1d)
+ : "a"(1));
+ }
+ if (n >= 7) {
+ __asm__(
+ "pushl %%ebx\n\t"
+ "cpuid\n\t"
+ "movl %%ebx, %%eax\n\t"
+ "popl %%ebx"
+ : "=a"(f7b), "=c"(f7c)
+ : "a"(7), "c"(0)
+ : "edx");
+ }
+#elif defined(__x86_64__) || defined(_M_X64) || defined(__i386__)
+ U32 n;
+ __asm__("cpuid" : "=a"(n) : "a"(0) : "ebx", "ecx", "edx");
+ if (n >= 1) {
+ U32 f1a;
+ __asm__("cpuid" : "=a"(f1a), "=c"(f1c), "=d"(f1d) : "a"(1) : "ebx");
+ }
+ if (n >= 7) {
+ U32 f7a;
+ __asm__("cpuid"
+ : "=a"(f7a), "=b"(f7b), "=c"(f7c)
+ : "a"(7), "c"(0)
+ : "edx");
+ }
+#endif
+ {
+ ZSTD_cpuid_t cpuid;
+ cpuid.f1c = f1c;
+ cpuid.f1d = f1d;
+ cpuid.f7b = f7b;
+ cpuid.f7c = f7c;
+ return cpuid;
+ }
+}
+
+#define X(name, r, bit) \
+ MEM_STATIC int ZSTD_cpuid_##name(ZSTD_cpuid_t const cpuid) { \
+ return ((cpuid.r) & (1U << bit)) != 0; \
+ }
+
+/* cpuid(1): Processor Info and Feature Bits. */
+#define C(name, bit) X(name, f1c, bit)
+ C(sse3, 0)
+ C(pclmuldq, 1)
+ C(dtes64, 2)
+ C(monitor, 3)
+ C(dscpl, 4)
+ C(vmx, 5)
+ C(smx, 6)
+ C(eist, 7)
+ C(tm2, 8)
+ C(ssse3, 9)
+ C(cnxtid, 10)
+ C(fma, 12)
+ C(cx16, 13)
+ C(xtpr, 14)
+ C(pdcm, 15)
+ C(pcid, 17)
+ C(dca, 18)
+ C(sse41, 19)
+ C(sse42, 20)
+ C(x2apic, 21)
+ C(movbe, 22)
+ C(popcnt, 23)
+ C(tscdeadline, 24)
+ C(aes, 25)
+ C(xsave, 26)
+ C(osxsave, 27)
+ C(avx, 28)
+ C(f16c, 29)
+ C(rdrand, 30)
+#undef C
+#define D(name, bit) X(name, f1d, bit)
+ D(fpu, 0)
+ D(vme, 1)
+ D(de, 2)
+ D(pse, 3)
+ D(tsc, 4)
+ D(msr, 5)
+ D(pae, 6)
+ D(mce, 7)
+ D(cx8, 8)
+ D(apic, 9)
+ D(sep, 11)
+ D(mtrr, 12)
+ D(pge, 13)
+ D(mca, 14)
+ D(cmov, 15)
+ D(pat, 16)
+ D(pse36, 17)
+ D(psn, 18)
+ D(clfsh, 19)
+ D(ds, 21)
+ D(acpi, 22)
+ D(mmx, 23)
+ D(fxsr, 24)
+ D(sse, 25)
+ D(sse2, 26)
+ D(ss, 27)
+ D(htt, 28)
+ D(tm, 29)
+ D(pbe, 31)
+#undef D
+
+/* cpuid(7): Extended Features. */
+#define B(name, bit) X(name, f7b, bit)
+ B(bmi1, 3)
+ B(hle, 4)
+ B(avx2, 5)
+ B(smep, 7)
+ B(bmi2, 8)
+ B(erms, 9)
+ B(invpcid, 10)
+ B(rtm, 11)
+ B(mpx, 14)
+ B(avx512f, 16)
+ B(avx512dq, 17)
+ B(rdseed, 18)
+ B(adx, 19)
+ B(smap, 20)
+ B(avx512ifma, 21)
+ B(pcommit, 22)
+ B(clflushopt, 23)
+ B(clwb, 24)
+ B(avx512pf, 26)
+ B(avx512er, 27)
+ B(avx512cd, 28)
+ B(sha, 29)
+ B(avx512bw, 30)
+ B(avx512vl, 31)
+#undef B
+#define C(name, bit) X(name, f7c, bit)
+ C(prefetchwt1, 0)
+ C(avx512vbmi, 1)
+#undef C
+
+#undef X
+
+#endif /* ZSTD_COMMON_CPU_H */
diff --git a/Utilities/cmzstd/lib/common/debug.c b/Utilities/cmzstd/lib/common/debug.c
new file mode 100644
index 000000000..3ebdd1cb1
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/debug.c
@@ -0,0 +1,44 @@
+/* ******************************************************************
+ debug
+ Part of FSE library
+ Copyright (C) 2013-present, 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/FiniteStateEntropy
+****************************************************************** */
+
+
+/*
+ * This module only hosts one global variable
+ * which can be used to dynamically influence the verbosity of traces,
+ * such as DEBUGLOG and RAWLOG
+ */
+
+#include "debug.h"
+
+int g_debuglevel = DEBUGLEVEL;
diff --git a/Utilities/cmzstd/lib/common/debug.h b/Utilities/cmzstd/lib/common/debug.h
new file mode 100644
index 000000000..b4fc89d49
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/debug.h
@@ -0,0 +1,134 @@
+/* ******************************************************************
+ debug
+ Part of FSE library
+ Copyright (C) 2013-present, 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/FiniteStateEntropy
+****************************************************************** */
+
+
+/*
+ * The purpose of this header is to enable debug functions.
+ * They regroup assert(), DEBUGLOG() and RAWLOG() for run-time,
+ * and DEBUG_STATIC_ASSERT() for compile-time.
+ *
+ * By default, DEBUGLEVEL==0, which means run-time debug is disabled.
+ *
+ * Level 1 enables assert() only.
+ * Starting level 2, traces can be generated and pushed to stderr.
+ * The higher the level, the more verbose the traces.
+ *
+ * It's possible to dynamically adjust level using variable g_debug_level,
+ * which is only declared if DEBUGLEVEL>=2,
+ * and is a global variable, not multi-thread protected (use with care)
+ */
+
+#ifndef DEBUG_H_12987983217
+#define DEBUG_H_12987983217
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+
+/* static assert is triggered at compile time, leaving no runtime artefact.
+ * static assert only works with compile-time constants.
+ * Also, this variant can only be used inside a function. */
+#define DEBUG_STATIC_ASSERT(c) (void)sizeof(char[(c) ? 1 : -1])
+
+
+/* DEBUGLEVEL is expected to be defined externally,
+ * typically through compiler command line.
+ * Value must be a number. */
+#ifndef DEBUGLEVEL
+# define DEBUGLEVEL 0
+#endif
+
+
+/* DEBUGFILE can be defined externally,
+ * typically through compiler command line.
+ * note : currently useless.
+ * Value must be stderr or stdout */
+#ifndef DEBUGFILE
+# define DEBUGFILE stderr
+#endif
+
+
+/* recommended values for DEBUGLEVEL :
+ * 0 : release mode, no debug, all run-time checks disabled
+ * 1 : enables assert() only, no display
+ * 2 : reserved, for currently active debug path
+ * 3 : events once per object lifetime (CCtx, CDict, etc.)
+ * 4 : events once per frame
+ * 5 : events once per block
+ * 6 : events once per sequence (verbose)
+ * 7+: events at every position (*very* verbose)
+ *
+ * It's generally inconvenient to output traces > 5.
+ * In which case, it's possible to selectively trigger high verbosity levels
+ * by modifying g_debug_level.
+ */
+
+#if (DEBUGLEVEL>=1)
+# include <assert.h>
+#else
+# ifndef assert /* assert may be already defined, due to prior #include <assert.h> */
+# define assert(condition) ((void)0) /* disable assert (default) */
+# endif
+#endif
+
+#if (DEBUGLEVEL>=2)
+# include <stdio.h>
+extern int g_debuglevel; /* the variable is only declared,
+ it actually lives in debug.c,
+ and is shared by the whole process.
+ It's not thread-safe.
+ It's useful when enabling very verbose levels
+ on selective conditions (such as position in src) */
+
+# define RAWLOG(l, ...) { \
+ if (l<=g_debuglevel) { \
+ fprintf(stderr, __VA_ARGS__); \
+ } }
+# define DEBUGLOG(l, ...) { \
+ if (l<=g_debuglevel) { \
+ fprintf(stderr, __FILE__ ": " __VA_ARGS__); \
+ fprintf(stderr, " \n"); \
+ } }
+#else
+# define RAWLOG(l, ...) {} /* disabled */
+# define DEBUGLOG(l, ...) {} /* disabled */
+#endif
+
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* DEBUG_H_12987983217 */
diff --git a/Utilities/cmzstd/lib/common/entropy_common.c b/Utilities/cmzstd/lib/common/entropy_common.c
new file mode 100644
index 000000000..b12944e1d
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/entropy_common.c
@@ -0,0 +1,236 @@
+/*
+ Common functions of New Generation Entropy library
+ 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 :
+ - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy
+ - Public forum : https://groups.google.com/forum/#!forum/lz4c
+*************************************************************************** */
+
+/* *************************************
+* Dependencies
+***************************************/
+#include "mem.h"
+#include "error_private.h" /* ERR_*, ERROR */
+#define FSE_STATIC_LINKING_ONLY /* FSE_MIN_TABLELOG */
+#include "fse.h"
+#define HUF_STATIC_LINKING_ONLY /* HUF_TABLELOG_ABSOLUTEMAX */
+#include "huf.h"
+
+
+/*=== Version ===*/
+unsigned FSE_versionNumber(void) { return FSE_VERSION_NUMBER; }
+
+
+/*=== Error Management ===*/
+unsigned FSE_isError(size_t code) { return ERR_isError(code); }
+const char* FSE_getErrorName(size_t code) { return ERR_getErrorName(code); }
+
+unsigned HUF_isError(size_t code) { return ERR_isError(code); }
+const char* HUF_getErrorName(size_t code) { return ERR_getErrorName(code); }
+
+
+/*-**************************************************************
+* FSE NCount encoding-decoding
+****************************************************************/
+size_t FSE_readNCount (short* normalizedCounter, unsigned* maxSVPtr, unsigned* tableLogPtr,
+ const void* headerBuffer, size_t hbSize)
+{
+ const BYTE* const istart = (const BYTE*) headerBuffer;
+ const BYTE* const iend = istart + hbSize;
+ const BYTE* ip = istart;
+ int nbBits;
+ int remaining;
+ int threshold;
+ U32 bitStream;
+ int bitCount;
+ unsigned charnum = 0;
+ int previous0 = 0;
+
+ if (hbSize < 4) {
+ /* This function only works when hbSize >= 4 */
+ char buffer[4];
+ memset(buffer, 0, sizeof(buffer));
+ memcpy(buffer, headerBuffer, hbSize);
+ { size_t const countSize = FSE_readNCount(normalizedCounter, maxSVPtr, tableLogPtr,
+ buffer, sizeof(buffer));
+ if (FSE_isError(countSize)) return countSize;
+ if (countSize > hbSize) return ERROR(corruption_detected);
+ return countSize;
+ } }
+ assert(hbSize >= 4);
+
+ /* init */
+ memset(normalizedCounter, 0, (*maxSVPtr+1) * sizeof(normalizedCounter[0])); /* all symbols not present in NCount have a frequency of 0 */
+ bitStream = MEM_readLE32(ip);
+ nbBits = (bitStream & 0xF) + FSE_MIN_TABLELOG; /* extract tableLog */
+ if (nbBits > FSE_TABLELOG_ABSOLUTE_MAX) return ERROR(tableLog_tooLarge);
+ bitStream >>= 4;
+ bitCount = 4;
+ *tableLogPtr = nbBits;
+ remaining = (1<<nbBits)+1;
+ threshold = 1<<nbBits;
+ nbBits++;
+
+ while ((remaining>1) & (charnum<=*maxSVPtr)) {
+ if (previous0) {
+ unsigned n0 = charnum;
+ while ((bitStream & 0xFFFF) == 0xFFFF) {
+ n0 += 24;
+ if (ip < iend-5) {
+ ip += 2;
+ bitStream = MEM_readLE32(ip) >> bitCount;
+ } else {
+ bitStream >>= 16;
+ bitCount += 16;
+ } }
+ while ((bitStream & 3) == 3) {
+ n0 += 3;
+ bitStream >>= 2;
+ bitCount += 2;
+ }
+ n0 += bitStream & 3;
+ bitCount += 2;
+ if (n0 > *maxSVPtr) return ERROR(maxSymbolValue_tooSmall);
+ while (charnum < n0) normalizedCounter[charnum++] = 0;
+ if ((ip <= iend-7) || (ip + (bitCount>>3) <= iend-4)) {
+ assert((bitCount >> 3) <= 3); /* For first condition to work */
+ ip += bitCount>>3;
+ bitCount &= 7;
+ bitStream = MEM_readLE32(ip) >> bitCount;
+ } else {
+ bitStream >>= 2;
+ } }
+ { int const max = (2*threshold-1) - remaining;
+ int count;
+
+ if ((bitStream & (threshold-1)) < (U32)max) {
+ count = bitStream & (threshold-1);
+ bitCount += nbBits-1;
+ } else {
+ count = bitStream & (2*threshold-1);
+ if (count >= threshold) count -= max;
+ bitCount += nbBits;
+ }
+
+ count--; /* extra accuracy */
+ remaining -= count < 0 ? -count : count; /* -1 means +1 */
+ normalizedCounter[charnum++] = (short)count;
+ previous0 = !count;
+ while (remaining < threshold) {
+ nbBits--;
+ threshold >>= 1;
+ }
+
+ if ((ip <= iend-7) || (ip + (bitCount>>3) <= iend-4)) {
+ ip += bitCount>>3;
+ bitCount &= 7;
+ } else {
+ bitCount -= (int)(8 * (iend - 4 - ip));
+ ip = iend - 4;
+ }
+ bitStream = MEM_readLE32(ip) >> (bitCount & 31);
+ } } /* while ((remaining>1) & (charnum<=*maxSVPtr)) */
+ if (remaining != 1) return ERROR(corruption_detected);
+ if (bitCount > 32) return ERROR(corruption_detected);
+ *maxSVPtr = charnum-1;
+
+ ip += (bitCount+7)>>3;
+ return ip-istart;
+}
+
+
+/*! HUF_readStats() :
+ Read compact Huffman tree, saved by HUF_writeCTable().
+ `huffWeight` is destination buffer.
+ `rankStats` is assumed to be a table of at least HUF_TABLELOG_MAX U32.
+ @return : size read from `src` , or an error Code .
+ Note : Needed by HUF_readCTable() and HUF_readDTableX?() .
+*/
+size_t HUF_readStats(BYTE* huffWeight, size_t hwSize, U32* rankStats,
+ U32* nbSymbolsPtr, U32* tableLogPtr,
+ const void* src, size_t srcSize)
+{
+ U32 weightTotal;
+ const BYTE* ip = (const BYTE*) src;
+ size_t iSize;
+ size_t oSize;
+
+ if (!srcSize) return ERROR(srcSize_wrong);
+ iSize = ip[0];
+ /* memset(huffWeight, 0, hwSize); *//* is not necessary, even though some analyzer complain ... */
+
+ if (iSize >= 128) { /* special header */
+ oSize = iSize - 127;
+ iSize = ((oSize+1)/2);
+ if (iSize+1 > srcSize) return ERROR(srcSize_wrong);
+ if (oSize >= hwSize) return ERROR(corruption_detected);
+ ip += 1;
+ { U32 n;
+ for (n=0; n<oSize; n+=2) {
+ huffWeight[n] = ip[n/2] >> 4;
+ huffWeight[n+1] = ip[n/2] & 15;
+ } } }
+ else { /* header compressed with FSE (normal case) */
+ FSE_DTable fseWorkspace[FSE_DTABLE_SIZE_U32(6)]; /* 6 is max possible tableLog for HUF header (maybe even 5, to be tested) */
+ if (iSize+1 > srcSize) return ERROR(srcSize_wrong);
+ oSize = FSE_decompress_wksp(huffWeight, hwSize-1, ip+1, iSize, fseWorkspace, 6); /* max (hwSize-1) values decoded, as last one is implied */
+ if (FSE_isError(oSize)) return oSize;
+ }
+
+ /* collect weight stats */
+ memset(rankStats, 0, (HUF_TABLELOG_MAX + 1) * sizeof(U32));
+ weightTotal = 0;
+ { U32 n; for (n=0; n<oSize; n++) {
+ if (huffWeight[n] >= HUF_TABLELOG_MAX) return ERROR(corruption_detected);
+ rankStats[huffWeight[n]]++;
+ weightTotal += (1 << huffWeight[n]) >> 1;
+ } }
+ if (weightTotal == 0) return ERROR(corruption_detected);
+
+ /* get last non-null symbol weight (implied, total must be 2^n) */
+ { U32 const tableLog = BIT_highbit32(weightTotal) + 1;
+ if (tableLog > HUF_TABLELOG_MAX) return ERROR(corruption_detected);
+ *tableLogPtr = tableLog;
+ /* determine last weight */
+ { U32 const total = 1 << tableLog;
+ U32 const rest = total - weightTotal;
+ U32 const verif = 1 << BIT_highbit32(rest);
+ U32 const lastWeight = BIT_highbit32(rest) + 1;
+ if (verif != rest) return ERROR(corruption_detected); /* last value must be a clean power of 2 */
+ huffWeight[oSize] = (BYTE)lastWeight;
+ rankStats[lastWeight]++;
+ } }
+
+ /* check tree construction validity */
+ if ((rankStats[1] < 2) || (rankStats[1] & 1)) return ERROR(corruption_detected); /* by construction : at least 2 elts of rank 1, must be even */
+
+ /* results */
+ *nbSymbolsPtr = (U32)(oSize+1);
+ return iSize+1;
+}
diff --git a/Utilities/cmzstd/lib/common/error_private.c b/Utilities/cmzstd/lib/common/error_private.c
new file mode 100644
index 000000000..7c1bb67a2
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/error_private.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+/* The purpose of this file is to have a single list of error strings embedded in binary */
+
+#include "error_private.h"
+
+const char* ERR_getErrorString(ERR_enum code)
+{
+#ifdef ZSTD_STRIP_ERROR_STRINGS
+ (void)code;
+ return "Error strings stripped";
+#else
+ static const char* const 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(version_unsupported): return "Version not supported";
+ case PREFIX(frameParameter_unsupported): return "Unsupported frame parameter";
+ case PREFIX(frameParameter_windowTooLarge): return "Frame requires too much memory for decoding";
+ case PREFIX(corruption_detected): return "Corrupted block detected";
+ case PREFIX(checksum_wrong): return "Restored data doesn't match checksum";
+ case PREFIX(parameter_unsupported): return "Unsupported parameter";
+ case PREFIX(parameter_outOfBound): return "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(workSpace_tooSmall): return "workSpace buffer is not large enough";
+ case PREFIX(stage_wrong): return "Operation not authorized at current processing stage";
+ 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(dictionaryCreation_failed): return "Cannot create Dictionary from provided samples";
+ case PREFIX(dstSize_tooSmall): return "Destination buffer is too small";
+ case PREFIX(srcSize_wrong): return "Src size is incorrect";
+ case PREFIX(dstBuffer_null): return "Operation on NULL destination buffer";
+ /* following error codes are not stable and may be removed or changed in a future version */
+ case PREFIX(frameIndex_tooLarge): return "Frame index is too large";
+ case PREFIX(seekableIO): return "An I/O error occurred when reading/seeking";
+ case PREFIX(maxCode):
+ default: return notErrorCode;
+ }
+#endif
+}
diff --git a/Utilities/cmzstd/lib/common/error_private.h b/Utilities/cmzstd/lib/common/error_private.h
new file mode 100644
index 000000000..0d2fa7e34
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/error_private.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+/* 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
+
+
+/* ****************************************
+* Dependencies
+******************************************/
+#include <stddef.h> /* size_t */
+#include "zstd_errors.h" /* enum list */
+
+
+/* ****************************************
+* 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 ZSTD_ErrorCode ERR_enum;
+#define PREFIX(name) ZSTD_error_##name
+
+
+/*-****************************************
+* Error codes handling
+******************************************/
+#undef ERROR /* reported already defined on VS 2015 (Rich Geldreich) */
+#define ERROR(name) ZSTD_ERROR(name)
+#define ZSTD_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
+******************************************/
+
+const char* ERR_getErrorString(ERR_enum code); /* error_private.c */
+
+ERR_STATIC const char* ERR_getErrorName(size_t code)
+{
+ return ERR_getErrorString(ERR_getErrorCode(code));
+}
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* ERROR_H_MODULE */
diff --git a/Utilities/cmzstd/lib/common/fse.h b/Utilities/cmzstd/lib/common/fse.h
new file mode 100644
index 000000000..f72c519b2
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/fse.h
@@ -0,0 +1,708 @@
+/* ******************************************************************
+ FSE : Finite State Entropy codec
+ Public Prototypes declaration
+ 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/FiniteStateEntropy
+****************************************************************** */
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#ifndef FSE_H
+#define FSE_H
+
+
+/*-*****************************************
+* Dependencies
+******************************************/
+#include <stddef.h> /* size_t, ptrdiff_t */
+
+
+/*-*****************************************
+* FSE_PUBLIC_API : control library symbols visibility
+******************************************/
+#if defined(FSE_DLL_EXPORT) && (FSE_DLL_EXPORT==1) && defined(__GNUC__) && (__GNUC__ >= 4)
+# define FSE_PUBLIC_API __attribute__ ((visibility ("default")))
+#elif defined(FSE_DLL_EXPORT) && (FSE_DLL_EXPORT==1) /* Visual expected */
+# define FSE_PUBLIC_API __declspec(dllexport)
+#elif defined(FSE_DLL_IMPORT) && (FSE_DLL_IMPORT==1)
+# define FSE_PUBLIC_API __declspec(dllimport) /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
+#else
+# define FSE_PUBLIC_API
+#endif
+
+/*------ Version ------*/
+#define FSE_VERSION_MAJOR 0
+#define FSE_VERSION_MINOR 9
+#define FSE_VERSION_RELEASE 0
+
+#define FSE_LIB_VERSION FSE_VERSION_MAJOR.FSE_VERSION_MINOR.FSE_VERSION_RELEASE
+#define FSE_QUOTE(str) #str
+#define FSE_EXPAND_AND_QUOTE(str) FSE_QUOTE(str)
+#define FSE_VERSION_STRING FSE_EXPAND_AND_QUOTE(FSE_LIB_VERSION)
+
+#define FSE_VERSION_NUMBER (FSE_VERSION_MAJOR *100*100 + FSE_VERSION_MINOR *100 + FSE_VERSION_RELEASE)
+FSE_PUBLIC_API unsigned FSE_versionNumber(void); /**< library version number; to be used when checking dll version */
+
+
+/*-****************************************
+* FSE simple functions
+******************************************/
+/*! FSE_compress() :
+ Compress content of buffer 'src', of size 'srcSize', into destination buffer 'dst'.
+ 'dst' buffer must be already allocated. Compression runs faster is dstCapacity >= FSE_compressBound(srcSize).
+ @return : size of compressed data (<= dstCapacity).
+ Special values : if return == 0, srcData is not compressible => Nothing is stored within dst !!!
+ if return == 1, srcData is a single byte symbol * srcSize times. Use RLE compression instead.
+ if FSE_isError(return), compression failed (more details using FSE_getErrorName())
+*/
+FSE_PUBLIC_API size_t FSE_compress(void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize);
+
+/*! FSE_decompress():
+ Decompress FSE data from buffer 'cSrc', of size 'cSrcSize',
+ into already allocated destination buffer 'dst', of size 'dstCapacity'.
+ @return : size of regenerated data (<= maxDstSize),
+ or an error code, which can be tested using FSE_isError() .
+
+ ** Important ** : FSE_decompress() does not decompress non-compressible nor RLE data !!!
+ Why ? : making this distinction requires a header.
+ Header management is intentionally delegated to the user layer, which can better manage special cases.
+*/
+FSE_PUBLIC_API size_t FSE_decompress(void* dst, size_t dstCapacity,
+ const void* cSrc, size_t cSrcSize);
+
+
+/*-*****************************************
+* Tool functions
+******************************************/
+FSE_PUBLIC_API size_t FSE_compressBound(size_t size); /* maximum compressed size */
+
+/* Error Management */
+FSE_PUBLIC_API unsigned FSE_isError(size_t code); /* tells if a return value is an error code */
+FSE_PUBLIC_API const char* FSE_getErrorName(size_t code); /* provides error code string (useful for debugging) */
+
+
+/*-*****************************************
+* FSE advanced functions
+******************************************/
+/*! FSE_compress2() :
+ Same as FSE_compress(), but allows the selection of 'maxSymbolValue' and 'tableLog'
+ Both parameters can be defined as '0' to mean : use default value
+ @return : size of compressed data
+ Special values : if return == 0, srcData is not compressible => Nothing is stored within cSrc !!!
+ if return == 1, srcData is a single byte symbol * srcSize times. Use RLE compression.
+ if FSE_isError(return), it's an error code.
+*/
+FSE_PUBLIC_API size_t FSE_compress2 (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog);
+
+
+/*-*****************************************
+* FSE detailed API
+******************************************/
+/*!
+FSE_compress() does the following:
+1. count symbol occurrence from source[] into table count[] (see hist.h)
+2. normalize counters so that sum(count[]) == Power_of_2 (2^tableLog)
+3. save normalized counters to memory buffer using writeNCount()
+4. build encoding table 'CTable' from normalized counters
+5. encode the data stream using encoding table 'CTable'
+
+FSE_decompress() does the following:
+1. read normalized counters with readNCount()
+2. build decoding table 'DTable' from normalized counters
+3. decode the data stream using decoding table 'DTable'
+
+The following API allows targeting specific sub-functions for advanced tasks.
+For example, it's possible to compress several blocks using the same 'CTable',
+or to save and provide normalized distribution using external method.
+*/
+
+/* *** COMPRESSION *** */
+
+/*! FSE_optimalTableLog():
+ dynamically downsize 'tableLog' when conditions are met.
+ It saves CPU time, by using smaller tables, while preserving or even improving compression ratio.
+ @return : recommended tableLog (necessarily <= 'maxTableLog') */
+FSE_PUBLIC_API unsigned FSE_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue);
+
+/*! FSE_normalizeCount():
+ normalize counts so that sum(count[]) == Power_of_2 (2^tableLog)
+ 'normalizedCounter' is a table of short, of minimum size (maxSymbolValue+1).
+ @return : tableLog,
+ or an errorCode, which can be tested using FSE_isError() */
+FSE_PUBLIC_API size_t FSE_normalizeCount(short* normalizedCounter, unsigned tableLog,
+ const unsigned* count, size_t srcSize, unsigned maxSymbolValue);
+
+/*! FSE_NCountWriteBound():
+ Provides the maximum possible size of an FSE normalized table, given 'maxSymbolValue' and 'tableLog'.
+ Typically useful for allocation purpose. */
+FSE_PUBLIC_API size_t FSE_NCountWriteBound(unsigned maxSymbolValue, unsigned tableLog);
+
+/*! FSE_writeNCount():
+ Compactly save 'normalizedCounter' into 'buffer'.
+ @return : size of the compressed table,
+ or an errorCode, which can be tested using FSE_isError(). */
+FSE_PUBLIC_API size_t FSE_writeNCount (void* buffer, size_t bufferSize,
+ const short* normalizedCounter,
+ unsigned maxSymbolValue, unsigned tableLog);
+
+/*! Constructor and Destructor of FSE_CTable.
+ Note that FSE_CTable size depends on 'tableLog' and 'maxSymbolValue' */
+typedef unsigned FSE_CTable; /* don't allocate that. It's only meant to be more restrictive than void* */
+FSE_PUBLIC_API FSE_CTable* FSE_createCTable (unsigned maxSymbolValue, unsigned tableLog);
+FSE_PUBLIC_API void FSE_freeCTable (FSE_CTable* ct);
+
+/*! FSE_buildCTable():
+ Builds `ct`, which must be already allocated, using FSE_createCTable().
+ @return : 0, or an errorCode, which can be tested using FSE_isError() */
+FSE_PUBLIC_API size_t FSE_buildCTable(FSE_CTable* ct, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog);
+
+/*! FSE_compress_usingCTable():
+ Compress `src` using `ct` into `dst` which must be already allocated.
+ @return : size of compressed data (<= `dstCapacity`),
+ or 0 if compressed data could not fit into `dst`,
+ or an errorCode, which can be tested using FSE_isError() */
+FSE_PUBLIC_API size_t FSE_compress_usingCTable (void* dst, size_t dstCapacity, const void* src, size_t srcSize, const FSE_CTable* ct);
+
+/*!
+Tutorial :
+----------
+The first step is to count all symbols. FSE_count() does this job very fast.
+Result will be saved into 'count', a table of unsigned int, which must be already allocated, and have 'maxSymbolValuePtr[0]+1' cells.
+'src' is a table of bytes of size 'srcSize'. All values within 'src' MUST be <= maxSymbolValuePtr[0]
+maxSymbolValuePtr[0] will be updated, with its real value (necessarily <= original value)
+FSE_count() will return the number of occurrence of the most frequent symbol.
+This can be used to know if there is a single symbol within 'src', and to quickly evaluate its compressibility.
+If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError()).
+
+The next step is to normalize the frequencies.
+FSE_normalizeCount() will ensure that sum of frequencies is == 2 ^'tableLog'.
+It also guarantees a minimum of 1 to any Symbol with frequency >= 1.
+You can use 'tableLog'==0 to mean "use default tableLog value".
+If you are unsure of which tableLog value to use, you can ask FSE_optimalTableLog(),
+which will provide the optimal valid tableLog given sourceSize, maxSymbolValue, and a user-defined maximum (0 means "default").
+
+The result of FSE_normalizeCount() will be saved into a table,
+called 'normalizedCounter', which is a table of signed short.
+'normalizedCounter' must be already allocated, and have at least 'maxSymbolValue+1' cells.
+The return value is tableLog if everything proceeded as expected.
+It is 0 if there is a single symbol within distribution.
+If there is an error (ex: invalid tableLog value), the function will return an ErrorCode (which can be tested using FSE_isError()).
+
+'normalizedCounter' can be saved in a compact manner to a memory area using FSE_writeNCount().
+'buffer' must be already allocated.
+For guaranteed success, buffer size must be at least FSE_headerBound().
+The result of the function is the number of bytes written into 'buffer'.
+If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError(); ex : buffer size too small).
+
+'normalizedCounter' can then be used to create the compression table 'CTable'.
+The space required by 'CTable' must be already allocated, using FSE_createCTable().
+You can then use FSE_buildCTable() to fill 'CTable'.
+If there is an error, both functions will return an ErrorCode (which can be tested using FSE_isError()).
+
+'CTable' can then be used to compress 'src', with FSE_compress_usingCTable().
+Similar to FSE_count(), the convention is that 'src' is assumed to be a table of char of size 'srcSize'
+The function returns the size of compressed data (without header), necessarily <= `dstCapacity`.
+If it returns '0', compressed data could not fit into 'dst'.
+If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError()).
+*/
+
+
+/* *** DECOMPRESSION *** */
+
+/*! FSE_readNCount():
+ Read compactly saved 'normalizedCounter' from 'rBuffer'.
+ @return : size read from 'rBuffer',
+ or an errorCode, which can be tested using FSE_isError().
+ maxSymbolValuePtr[0] and tableLogPtr[0] will also be updated with their respective values */
+FSE_PUBLIC_API size_t FSE_readNCount (short* normalizedCounter,
+ unsigned* maxSymbolValuePtr, unsigned* tableLogPtr,
+ const void* rBuffer, size_t rBuffSize);
+
+/*! Constructor and Destructor of FSE_DTable.
+ Note that its size depends on 'tableLog' */
+typedef unsigned FSE_DTable; /* don't allocate that. It's just a way to be more restrictive than void* */
+FSE_PUBLIC_API FSE_DTable* FSE_createDTable(unsigned tableLog);
+FSE_PUBLIC_API void FSE_freeDTable(FSE_DTable* dt);
+
+/*! FSE_buildDTable():
+ Builds 'dt', which must be already allocated, using FSE_createDTable().
+ return : 0, or an errorCode, which can be tested using FSE_isError() */
+FSE_PUBLIC_API size_t FSE_buildDTable (FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog);
+
+/*! FSE_decompress_usingDTable():
+ Decompress compressed source `cSrc` of size `cSrcSize` using `dt`
+ into `dst` which must be already allocated.
+ @return : size of regenerated data (necessarily <= `dstCapacity`),
+ or an errorCode, which can be tested using FSE_isError() */
+FSE_PUBLIC_API size_t FSE_decompress_usingDTable(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, const FSE_DTable* dt);
+
+/*!
+Tutorial :
+----------
+(Note : these functions only decompress FSE-compressed blocks.
+ If block is uncompressed, use memcpy() instead
+ If block is a single repeated byte, use memset() instead )
+
+The first step is to obtain the normalized frequencies of symbols.
+This can be performed by FSE_readNCount() if it was saved using FSE_writeNCount().
+'normalizedCounter' must be already allocated, and have at least 'maxSymbolValuePtr[0]+1' cells of signed short.
+In practice, that means it's necessary to know 'maxSymbolValue' beforehand,
+or size the table to handle worst case situations (typically 256).
+FSE_readNCount() will provide 'tableLog' and 'maxSymbolValue'.
+The result of FSE_readNCount() is the number of bytes read from 'rBuffer'.
+Note that 'rBufferSize' must be at least 4 bytes, even if useful information is less than that.
+If there is an error, the function will return an error code, which can be tested using FSE_isError().
+
+The next step is to build the decompression tables 'FSE_DTable' from 'normalizedCounter'.
+This is performed by the function FSE_buildDTable().
+The space required by 'FSE_DTable' must be already allocated using FSE_createDTable().
+If there is an error, the function will return an error code, which can be tested using FSE_isError().
+
+`FSE_DTable` can then be used to decompress `cSrc`, with FSE_decompress_usingDTable().
+`cSrcSize` must be strictly correct, otherwise decompression will fail.
+FSE_decompress_usingDTable() result will tell how many bytes were regenerated (<=`dstCapacity`).
+If there is an error, the function will return an error code, which can be tested using FSE_isError(). (ex: dst buffer too small)
+*/
+
+#endif /* FSE_H */
+
+#if defined(FSE_STATIC_LINKING_ONLY) && !defined(FSE_H_FSE_STATIC_LINKING_ONLY)
+#define FSE_H_FSE_STATIC_LINKING_ONLY
+
+/* *** Dependency *** */
+#include "bitstream.h"
+
+
+/* *****************************************
+* Static allocation
+*******************************************/
+/* FSE buffer bounds */
+#define FSE_NCOUNTBOUND 512
+#define FSE_BLOCKBOUND(size) (size + (size>>7))
+#define FSE_COMPRESSBOUND(size) (FSE_NCOUNTBOUND + FSE_BLOCKBOUND(size)) /* Macro version, useful for static allocation */
+
+/* It is possible to statically allocate FSE CTable/DTable as a table of FSE_CTable/FSE_DTable using below macros */
+#define FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue) (1 + (1<<(maxTableLog-1)) + ((maxSymbolValue+1)*2))
+#define FSE_DTABLE_SIZE_U32(maxTableLog) (1 + (1<<maxTableLog))
+
+/* or use the size to malloc() space directly. Pay attention to alignment restrictions though */
+#define FSE_CTABLE_SIZE(maxTableLog, maxSymbolValue) (FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue) * sizeof(FSE_CTable))
+#define FSE_DTABLE_SIZE(maxTableLog) (FSE_DTABLE_SIZE_U32(maxTableLog) * sizeof(FSE_DTable))
+
+
+/* *****************************************
+ * FSE advanced API
+ ***************************************** */
+
+unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus);
+/**< same as FSE_optimalTableLog(), which used `minus==2` */
+
+/* FSE_compress_wksp() :
+ * Same as FSE_compress2(), but using an externally allocated scratch buffer (`workSpace`).
+ * FSE_WKSP_SIZE_U32() provides the minimum size required for `workSpace` as a table of FSE_CTable.
+ */
+#define FSE_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) ( FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue) + ((maxTableLog > 12) ? (1 << (maxTableLog - 2)) : 1024) )
+size_t FSE_compress_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize);
+
+size_t FSE_buildCTable_raw (FSE_CTable* ct, unsigned nbBits);
+/**< build a fake FSE_CTable, designed for a flat distribution, where each symbol uses nbBits */
+
+size_t FSE_buildCTable_rle (FSE_CTable* ct, unsigned char symbolValue);
+/**< build a fake FSE_CTable, designed to compress always the same symbolValue */
+
+/* FSE_buildCTable_wksp() :
+ * Same as FSE_buildCTable(), but using an externally allocated scratch buffer (`workSpace`).
+ * `wkspSize` must be >= `(1<<tableLog)`.
+ */
+size_t FSE_buildCTable_wksp(FSE_CTable* ct, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize);
+
+size_t FSE_buildDTable_raw (FSE_DTable* dt, unsigned nbBits);
+/**< build a fake FSE_DTable, designed to read a flat distribution where each symbol uses nbBits */
+
+size_t FSE_buildDTable_rle (FSE_DTable* dt, unsigned char symbolValue);
+/**< build a fake FSE_DTable, designed to always generate the same symbolValue */
+
+size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, FSE_DTable* workSpace, unsigned maxLog);
+/**< same as FSE_decompress(), using an externally allocated `workSpace` produced with `FSE_DTABLE_SIZE_U32(maxLog)` */
+
+typedef enum {
+ FSE_repeat_none, /**< Cannot use the previous table */
+ FSE_repeat_check, /**< Can use the previous table but it must be checked */
+ FSE_repeat_valid /**< Can use the previous table and it is asumed to be valid */
+ } FSE_repeat;
+
+/* *****************************************
+* FSE symbol compression API
+*******************************************/
+/*!
+ This API consists of small unitary functions, which highly benefit from being inlined.
+ Hence their body are included in next section.
+*/
+typedef struct {
+ ptrdiff_t value;
+ const void* stateTable;
+ const void* symbolTT;
+ unsigned stateLog;
+} FSE_CState_t;
+
+static void FSE_initCState(FSE_CState_t* CStatePtr, const FSE_CTable* ct);
+
+static void FSE_encodeSymbol(BIT_CStream_t* bitC, FSE_CState_t* CStatePtr, unsigned symbol);
+
+static void FSE_flushCState(BIT_CStream_t* bitC, const FSE_CState_t* CStatePtr);
+
+/**<
+These functions are inner components of FSE_compress_usingCTable().
+They allow the creation of custom streams, mixing multiple tables and bit sources.
+
+A key property to keep in mind is that encoding and decoding are done **in reverse direction**.
+So the first symbol you will encode is the last you will decode, like a LIFO stack.
+
+You will need a few variables to track your CStream. They are :
+
+FSE_CTable ct; // Provided by FSE_buildCTable()
+BIT_CStream_t bitStream; // bitStream tracking structure
+FSE_CState_t state; // State tracking structure (can have several)
+
+
+The first thing to do is to init bitStream and state.
+ size_t errorCode = BIT_initCStream(&bitStream, dstBuffer, maxDstSize);
+ FSE_initCState(&state, ct);
+
+Note that BIT_initCStream() can produce an error code, so its result should be tested, using FSE_isError();
+You can then encode your input data, byte after byte.
+FSE_encodeSymbol() outputs a maximum of 'tableLog' bits at a time.
+Remember decoding will be done in reverse direction.
+ FSE_encodeByte(&bitStream, &state, symbol);
+
+At any time, you can also add any bit sequence.
+Note : maximum allowed nbBits is 25, for compatibility with 32-bits decoders
+ BIT_addBits(&bitStream, bitField, nbBits);
+
+The above methods don't commit data to memory, they just store it into local register, for speed.
+Local register size is 64-bits on 64-bits systems, 32-bits on 32-bits systems (size_t).
+Writing data to memory is a manual operation, performed by the flushBits function.
+ BIT_flushBits(&bitStream);
+
+Your last FSE encoding operation shall be to flush your last state value(s).
+ FSE_flushState(&bitStream, &state);
+
+Finally, you must close the bitStream.
+The function returns the size of CStream in bytes.
+If data couldn't fit into dstBuffer, it will return a 0 ( == not compressible)
+If there is an error, it returns an errorCode (which can be tested using FSE_isError()).
+ size_t size = BIT_closeCStream(&bitStream);
+*/
+
+
+/* *****************************************
+* FSE symbol decompression API
+*******************************************/
+typedef struct {
+ size_t state;
+ const void* table; /* precise table may vary, depending on U16 */
+} FSE_DState_t;
+
+
+static void FSE_initDState(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD, const FSE_DTable* dt);
+
+static unsigned char FSE_decodeSymbol(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD);
+
+static unsigned FSE_endOfDState(const FSE_DState_t* DStatePtr);
+
+/**<
+Let's now decompose FSE_decompress_usingDTable() into its unitary components.
+You will decode FSE-encoded symbols from the bitStream,
+and also any other bitFields you put in, **in reverse order**.
+
+You will need a few variables to track your bitStream. They are :
+
+BIT_DStream_t DStream; // Stream context
+FSE_DState_t DState; // State context. Multiple ones are possible
+FSE_DTable* DTablePtr; // Decoding table, provided by FSE_buildDTable()
+
+The first thing to do is to init the bitStream.
+ errorCode = BIT_initDStream(&DStream, srcBuffer, srcSize);
+
+You should then retrieve your initial state(s)
+(in reverse flushing order if you have several ones) :
+ errorCode = FSE_initDState(&DState, &DStream, DTablePtr);
+
+You can then decode your data, symbol after symbol.
+For information the maximum number of bits read by FSE_decodeSymbol() is 'tableLog'.
+Keep in mind that symbols are decoded in reverse order, like a LIFO stack (last in, first out).
+ unsigned char symbol = FSE_decodeSymbol(&DState, &DStream);
+
+You can retrieve any bitfield you eventually stored into the bitStream (in reverse order)
+Note : maximum allowed nbBits is 25, for 32-bits compatibility
+ size_t bitField = BIT_readBits(&DStream, nbBits);
+
+All above operations only read from local register (which size depends on size_t).
+Refueling the register from memory is manually performed by the reload method.
+ endSignal = FSE_reloadDStream(&DStream);
+
+BIT_reloadDStream() result tells if there is still some more data to read from DStream.
+BIT_DStream_unfinished : there is still some data left into the DStream.
+BIT_DStream_endOfBuffer : Dstream reached end of buffer. Its container may no longer be completely filled.
+BIT_DStream_completed : Dstream reached its exact end, corresponding in general to decompression completed.
+BIT_DStream_tooFar : Dstream went too far. Decompression result is corrupted.
+
+When reaching end of buffer (BIT_DStream_endOfBuffer), progress slowly, notably if you decode multiple symbols per loop,
+to properly detect the exact end of stream.
+After each decoded symbol, check if DStream is fully consumed using this simple test :
+ BIT_reloadDStream(&DStream) >= BIT_DStream_completed
+
+When it's done, verify decompression is fully completed, by checking both DStream and the relevant states.
+Checking if DStream has reached its end is performed by :
+ BIT_endOfDStream(&DStream);
+Check also the states. There might be some symbols left there, if some high probability ones (>50%) are possible.
+ FSE_endOfDState(&DState);
+*/
+
+
+/* *****************************************
+* FSE unsafe API
+*******************************************/
+static unsigned char FSE_decodeSymbolFast(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD);
+/* faster, but works only if nbBits is always >= 1 (otherwise, result will be corrupted) */
+
+
+/* *****************************************
+* Implementation of inlined functions
+*******************************************/
+typedef struct {
+ int deltaFindState;
+ U32 deltaNbBits;
+} FSE_symbolCompressionTransform; /* total 8 bytes */
+
+MEM_STATIC void FSE_initCState(FSE_CState_t* statePtr, const FSE_CTable* ct)
+{
+ const void* ptr = ct;
+ const U16* u16ptr = (const U16*) ptr;
+ const U32 tableLog = MEM_read16(ptr);
+ statePtr->value = (ptrdiff_t)1<<tableLog;
+ statePtr->stateTable = u16ptr+2;
+ statePtr->symbolTT = ct + 1 + (tableLog ? (1<<(tableLog-1)) : 1);
+ statePtr->stateLog = tableLog;
+}
+
+
+/*! FSE_initCState2() :
+* Same as FSE_initCState(), but the first symbol to include (which will be the last to be read)
+* uses the smallest state value possible, saving the cost of this symbol */
+MEM_STATIC void FSE_initCState2(FSE_CState_t* statePtr, const FSE_CTable* ct, U32 symbol)
+{
+ FSE_initCState(statePtr, ct);
+ { const FSE_symbolCompressionTransform symbolTT = ((const FSE_symbolCompressionTransform*)(statePtr->symbolTT))[symbol];
+ const U16* stateTable = (const U16*)(statePtr->stateTable);
+ U32 nbBitsOut = (U32)((symbolTT.deltaNbBits + (1<<15)) >> 16);
+ statePtr->value = (nbBitsOut << 16) - symbolTT.deltaNbBits;
+ statePtr->value = stateTable[(statePtr->value >> nbBitsOut) + symbolTT.deltaFindState];
+ }
+}
+
+MEM_STATIC void FSE_encodeSymbol(BIT_CStream_t* bitC, FSE_CState_t* statePtr, unsigned symbol)
+{
+ FSE_symbolCompressionTransform const symbolTT = ((const FSE_symbolCompressionTransform*)(statePtr->symbolTT))[symbol];
+ const U16* const stateTable = (const U16*)(statePtr->stateTable);
+ U32 const nbBitsOut = (U32)((statePtr->value + symbolTT.deltaNbBits) >> 16);
+ BIT_addBits(bitC, statePtr->value, nbBitsOut);
+ statePtr->value = stateTable[ (statePtr->value >> nbBitsOut) + symbolTT.deltaFindState];
+}
+
+MEM_STATIC void FSE_flushCState(BIT_CStream_t* bitC, const FSE_CState_t* statePtr)
+{
+ BIT_addBits(bitC, statePtr->value, statePtr->stateLog);
+ BIT_flushBits(bitC);
+}
+
+
+/* FSE_getMaxNbBits() :
+ * Approximate maximum cost of a symbol, in bits.
+ * Fractional get rounded up (i.e : a symbol with a normalized frequency of 3 gives the same result as a frequency of 2)
+ * note 1 : assume symbolValue is valid (<= maxSymbolValue)
+ * note 2 : if freq[symbolValue]==0, @return a fake cost of tableLog+1 bits */
+MEM_STATIC U32 FSE_getMaxNbBits(const void* symbolTTPtr, U32 symbolValue)
+{
+ const FSE_symbolCompressionTransform* symbolTT = (const FSE_symbolCompressionTransform*) symbolTTPtr;
+ return (symbolTT[symbolValue].deltaNbBits + ((1<<16)-1)) >> 16;
+}
+
+/* FSE_bitCost() :
+ * Approximate symbol cost, as fractional value, using fixed-point format (accuracyLog fractional bits)
+ * note 1 : assume symbolValue is valid (<= maxSymbolValue)
+ * note 2 : if freq[symbolValue]==0, @return a fake cost of tableLog+1 bits */
+MEM_STATIC U32 FSE_bitCost(const void* symbolTTPtr, U32 tableLog, U32 symbolValue, U32 accuracyLog)
+{
+ const FSE_symbolCompressionTransform* symbolTT = (const FSE_symbolCompressionTransform*) symbolTTPtr;
+ U32 const minNbBits = symbolTT[symbolValue].deltaNbBits >> 16;
+ U32 const threshold = (minNbBits+1) << 16;
+ assert(tableLog < 16);
+ assert(accuracyLog < 31-tableLog); /* ensure enough room for renormalization double shift */
+ { U32 const tableSize = 1 << tableLog;
+ U32 const deltaFromThreshold = threshold - (symbolTT[symbolValue].deltaNbBits + tableSize);
+ U32 const normalizedDeltaFromThreshold = (deltaFromThreshold << accuracyLog) >> tableLog; /* linear interpolation (very approximate) */
+ U32 const bitMultiplier = 1 << accuracyLog;
+ assert(symbolTT[symbolValue].deltaNbBits + tableSize <= threshold);
+ assert(normalizedDeltaFromThreshold <= bitMultiplier);
+ return (minNbBits+1)*bitMultiplier - normalizedDeltaFromThreshold;
+ }
+}
+
+
+/* ====== Decompression ====== */
+
+typedef struct {
+ U16 tableLog;
+ U16 fastMode;
+} FSE_DTableHeader; /* sizeof U32 */
+
+typedef struct
+{
+ unsigned short newState;
+ unsigned char symbol;
+ unsigned char nbBits;
+} FSE_decode_t; /* size == U32 */
+
+MEM_STATIC void FSE_initDState(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD, const FSE_DTable* dt)
+{
+ const void* ptr = dt;
+ const FSE_DTableHeader* const DTableH = (const FSE_DTableHeader*)ptr;
+ DStatePtr->state = BIT_readBits(bitD, DTableH->tableLog);
+ BIT_reloadDStream(bitD);
+ DStatePtr->table = dt + 1;
+}
+
+MEM_STATIC BYTE FSE_peekSymbol(const FSE_DState_t* DStatePtr)
+{
+ FSE_decode_t const DInfo = ((const FSE_decode_t*)(DStatePtr->table))[DStatePtr->state];
+ return DInfo.symbol;
+}
+
+MEM_STATIC void FSE_updateState(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD)
+{
+ FSE_decode_t const DInfo = ((const FSE_decode_t*)(DStatePtr->table))[DStatePtr->state];
+ U32 const nbBits = DInfo.nbBits;
+ size_t const lowBits = BIT_readBits(bitD, nbBits);
+ DStatePtr->state = DInfo.newState + lowBits;
+}
+
+MEM_STATIC BYTE FSE_decodeSymbol(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD)
+{
+ FSE_decode_t const DInfo = ((const FSE_decode_t*)(DStatePtr->table))[DStatePtr->state];
+ U32 const nbBits = DInfo.nbBits;
+ BYTE const symbol = DInfo.symbol;
+ size_t const lowBits = BIT_readBits(bitD, nbBits);
+
+ DStatePtr->state = DInfo.newState + lowBits;
+ return symbol;
+}
+
+/*! FSE_decodeSymbolFast() :
+ unsafe, only works if no symbol has a probability > 50% */
+MEM_STATIC BYTE FSE_decodeSymbolFast(FSE_DState_t* DStatePtr, BIT_DStream_t* bitD)
+{
+ FSE_decode_t const DInfo = ((const FSE_decode_t*)(DStatePtr->table))[DStatePtr->state];
+ U32 const nbBits = DInfo.nbBits;
+ BYTE const symbol = DInfo.symbol;
+ size_t const lowBits = BIT_readBitsFast(bitD, nbBits);
+
+ DStatePtr->state = DInfo.newState + lowBits;
+ return symbol;
+}
+
+MEM_STATIC unsigned FSE_endOfDState(const FSE_DState_t* DStatePtr)
+{
+ return DStatePtr->state == 0;
+}
+
+
+
+#ifndef FSE_COMMONDEFS_ONLY
+
+/* **************************************************************
+* Tuning parameters
+****************************************************************/
+/*!MEMORY_USAGE :
+* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
+* Increasing memory usage improves compression ratio
+* Reduced memory usage can improve speed, due to cache effect
+* Recommended max value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */
+#ifndef FSE_MAX_MEMORY_USAGE
+# define FSE_MAX_MEMORY_USAGE 14
+#endif
+#ifndef FSE_DEFAULT_MEMORY_USAGE
+# define FSE_DEFAULT_MEMORY_USAGE 13
+#endif
+
+/*!FSE_MAX_SYMBOL_VALUE :
+* Maximum symbol value authorized.
+* Required for proper stack allocation */
+#ifndef FSE_MAX_SYMBOL_VALUE
+# define FSE_MAX_SYMBOL_VALUE 255
+#endif
+
+/* **************************************************************
+* template functions type & suffix
+****************************************************************/
+#define FSE_FUNCTION_TYPE BYTE
+#define FSE_FUNCTION_EXTENSION
+#define FSE_DECODE_TYPE FSE_decode_t
+
+
+#endif /* !FSE_COMMONDEFS_ONLY */
+
+
+/* ***************************************************************
+* Constants
+*****************************************************************/
+#define FSE_MAX_TABLELOG (FSE_MAX_MEMORY_USAGE-2)
+#define FSE_MAX_TABLESIZE (1U<<FSE_MAX_TABLELOG)
+#define FSE_MAXTABLESIZE_MASK (FSE_MAX_TABLESIZE-1)
+#define FSE_DEFAULT_TABLELOG (FSE_DEFAULT_MEMORY_USAGE-2)
+#define FSE_MIN_TABLELOG 5
+
+#define FSE_TABLELOG_ABSOLUTE_MAX 15
+#if FSE_MAX_TABLELOG > FSE_TABLELOG_ABSOLUTE_MAX
+# error "FSE_MAX_TABLELOG > FSE_TABLELOG_ABSOLUTE_MAX is not supported"
+#endif
+
+#define FSE_TABLESTEP(tableSize) ((tableSize>>1) + (tableSize>>3) + 3)
+
+
+#endif /* FSE_STATIC_LINKING_ONLY */
+
+
+#if defined (__cplusplus)
+}
+#endif
diff --git a/Utilities/cmzstd/lib/common/fse_decompress.c b/Utilities/cmzstd/lib/common/fse_decompress.c
new file mode 100644
index 000000000..72bbead5b
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/fse_decompress.c
@@ -0,0 +1,309 @@
+/* ******************************************************************
+ FSE : Finite State Entropy decoder
+ Copyright (C) 2013-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 :
+ - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy
+ - Public forum : https://groups.google.com/forum/#!forum/lz4c
+****************************************************************** */
+
+
+/* **************************************************************
+* Includes
+****************************************************************/
+#include <stdlib.h> /* malloc, free, qsort */
+#include <string.h> /* memcpy, memset */
+#include "bitstream.h"
+#include "compiler.h"
+#define FSE_STATIC_LINKING_ONLY
+#include "fse.h"
+#include "error_private.h"
+
+
+/* **************************************************************
+* Error Management
+****************************************************************/
+#define FSE_isError ERR_isError
+#define FSE_STATIC_ASSERT(c) DEBUG_STATIC_ASSERT(c) /* use only *after* variable declarations */
+
+/* check and forward error code */
+#define CHECK_F(f) { size_t const e = f; if (FSE_isError(e)) return e; }
+
+
+/* **************************************************************
+* Templates
+****************************************************************/
+/*
+ designed to be included
+ for type-specific functions (template emulation in C)
+ Objective is to write these functions only once, for improved maintenance
+*/
+
+/* safety checks */
+#ifndef FSE_FUNCTION_EXTENSION
+# error "FSE_FUNCTION_EXTENSION must be defined"
+#endif
+#ifndef FSE_FUNCTION_TYPE
+# error "FSE_FUNCTION_TYPE must be defined"
+#endif
+
+/* Function names */
+#define FSE_CAT(X,Y) X##Y
+#define FSE_FUNCTION_NAME(X,Y) FSE_CAT(X,Y)
+#define FSE_TYPE_NAME(X,Y) FSE_CAT(X,Y)
+
+
+/* Function templates */
+FSE_DTable* FSE_createDTable (unsigned tableLog)
+{
+ if (tableLog > FSE_TABLELOG_ABSOLUTE_MAX) tableLog = FSE_TABLELOG_ABSOLUTE_MAX;
+ return (FSE_DTable*)malloc( FSE_DTABLE_SIZE_U32(tableLog) * sizeof (U32) );
+}
+
+void FSE_freeDTable (FSE_DTable* dt)
+{
+ free(dt);
+}
+
+size_t FSE_buildDTable(FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog)
+{
+ void* const tdPtr = dt+1; /* because *dt is unsigned, 32-bits aligned on 32-bits */
+ FSE_DECODE_TYPE* const tableDecode = (FSE_DECODE_TYPE*) (tdPtr);
+ U16 symbolNext[FSE_MAX_SYMBOL_VALUE+1];
+
+ U32 const maxSV1 = maxSymbolValue + 1;
+ U32 const tableSize = 1 << tableLog;
+ U32 highThreshold = tableSize-1;
+
+ /* Sanity Checks */
+ if (maxSymbolValue > FSE_MAX_SYMBOL_VALUE) return ERROR(maxSymbolValue_tooLarge);
+ if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge);
+
+ /* Init, lay down lowprob symbols */
+ { FSE_DTableHeader DTableH;
+ DTableH.tableLog = (U16)tableLog;
+ DTableH.fastMode = 1;
+ { S16 const largeLimit= (S16)(1 << (tableLog-1));
+ U32 s;
+ for (s=0; s<maxSV1; s++) {
+ if (normalizedCounter[s]==-1) {
+ tableDecode[highThreshold--].symbol = (FSE_FUNCTION_TYPE)s;
+ symbolNext[s] = 1;
+ } else {
+ if (normalizedCounter[s] >= largeLimit) DTableH.fastMode=0;
+ symbolNext[s] = normalizedCounter[s];
+ } } }
+ memcpy(dt, &DTableH, sizeof(DTableH));
+ }
+
+ /* Spread symbols */
+ { U32 const tableMask = tableSize-1;
+ U32 const step = FSE_TABLESTEP(tableSize);
+ U32 s, position = 0;
+ for (s=0; s<maxSV1; s++) {
+ int i;
+ for (i=0; i<normalizedCounter[s]; i++) {
+ tableDecode[position].symbol = (FSE_FUNCTION_TYPE)s;
+ position = (position + step) & tableMask;
+ while (position > highThreshold) position = (position + step) & tableMask; /* lowprob area */
+ } }
+ if (position!=0) return ERROR(GENERIC); /* position must reach all cells once, otherwise normalizedCounter is incorrect */
+ }
+
+ /* Build Decoding table */
+ { U32 u;
+ for (u=0; u<tableSize; u++) {
+ FSE_FUNCTION_TYPE const symbol = (FSE_FUNCTION_TYPE)(tableDecode[u].symbol);
+ U32 const nextState = symbolNext[symbol]++;
+ tableDecode[u].nbBits = (BYTE) (tableLog - BIT_highbit32(nextState) );
+ tableDecode[u].newState = (U16) ( (nextState << tableDecode[u].nbBits) - tableSize);
+ } }
+
+ return 0;
+}
+
+
+#ifndef FSE_COMMONDEFS_ONLY
+
+/*-*******************************************************
+* Decompression (Byte symbols)
+*********************************************************/
+size_t FSE_buildDTable_rle (FSE_DTable* dt, BYTE symbolValue)
+{
+ void* ptr = dt;
+ FSE_DTableHeader* const DTableH = (FSE_DTableHeader*)ptr;
+ void* dPtr = dt + 1;
+ FSE_decode_t* const cell = (FSE_decode_t*)dPtr;
+
+ DTableH->tableLog = 0;
+ DTableH->fastMode = 0;
+
+ cell->newState = 0;
+ cell->symbol = symbolValue;
+ cell->nbBits = 0;
+
+ return 0;
+}
+
+
+size_t FSE_buildDTable_raw (FSE_DTable* dt, unsigned nbBits)
+{
+ void* ptr = dt;
+ FSE_DTableHeader* const DTableH = (FSE_DTableHeader*)ptr;
+ void* dPtr = dt + 1;
+ FSE_decode_t* const dinfo = (FSE_decode_t*)dPtr;
+ const unsigned tableSize = 1 << nbBits;
+ const unsigned tableMask = tableSize - 1;
+ const unsigned maxSV1 = tableMask+1;
+ unsigned s;
+
+ /* Sanity checks */
+ if (nbBits < 1) return ERROR(GENERIC); /* min size */
+
+ /* Build Decoding Table */
+ DTableH->tableLog = (U16)nbBits;
+ DTableH->fastMode = 1;
+ for (s=0; s<maxSV1; s++) {
+ dinfo[s].newState = 0;
+ dinfo[s].symbol = (BYTE)s;
+ dinfo[s].nbBits = (BYTE)nbBits;
+ }
+
+ return 0;
+}
+
+FORCE_INLINE_TEMPLATE size_t FSE_decompress_usingDTable_generic(
+ void* dst, size_t maxDstSize,
+ const void* cSrc, size_t cSrcSize,
+ const FSE_DTable* dt, const unsigned fast)
+{
+ BYTE* const ostart = (BYTE*) dst;
+ BYTE* op = ostart;
+ BYTE* const omax = op + maxDstSize;
+ BYTE* const olimit = omax-3;
+
+ BIT_DStream_t bitD;
+ FSE_DState_t state1;
+ FSE_DState_t state2;
+
+ /* Init */
+ CHECK_F(BIT_initDStream(&bitD, cSrc, cSrcSize));
+
+ FSE_initDState(&state1, &bitD, dt);
+ FSE_initDState(&state2, &bitD, dt);
+
+#define FSE_GETSYMBOL(statePtr) fast ? FSE_decodeSymbolFast(statePtr, &bitD) : FSE_decodeSymbol(statePtr, &bitD)
+
+ /* 4 symbols per loop */
+ for ( ; (BIT_reloadDStream(&bitD)==BIT_DStream_unfinished) & (op<olimit) ; op+=4) {
+ op[0] = FSE_GETSYMBOL(&state1);
+
+ if (FSE_MAX_TABLELOG*2+7 > sizeof(bitD.bitContainer)*8) /* This test must be static */
+ BIT_reloadDStream(&bitD);
+
+ op[1] = FSE_GETSYMBOL(&state2);
+
+ if (FSE_MAX_TABLELOG*4+7 > sizeof(bitD.bitContainer)*8) /* This test must be static */
+ { if (BIT_reloadDStream(&bitD) > BIT_DStream_unfinished) { op+=2; break; } }
+
+ op[2] = FSE_GETSYMBOL(&state1);
+
+ if (FSE_MAX_TABLELOG*2+7 > sizeof(bitD.bitContainer)*8) /* This test must be static */
+ BIT_reloadDStream(&bitD);
+
+ op[3] = FSE_GETSYMBOL(&state2);
+ }
+
+ /* tail */
+ /* note : BIT_reloadDStream(&bitD) >= FSE_DStream_partiallyFilled; Ends at exactly BIT_DStream_completed */
+ while (1) {
+ if (op>(omax-2)) return ERROR(dstSize_tooSmall);
+ *op++ = FSE_GETSYMBOL(&state1);
+ if (BIT_reloadDStream(&bitD)==BIT_DStream_overflow) {
+ *op++ = FSE_GETSYMBOL(&state2);
+ break;
+ }
+
+ if (op>(omax-2)) return ERROR(dstSize_tooSmall);
+ *op++ = FSE_GETSYMBOL(&state2);
+ if (BIT_reloadDStream(&bitD)==BIT_DStream_overflow) {
+ *op++ = FSE_GETSYMBOL(&state1);
+ break;
+ } }
+
+ return op-ostart;
+}
+
+
+size_t FSE_decompress_usingDTable(void* dst, size_t originalSize,
+ const void* cSrc, size_t cSrcSize,
+ const FSE_DTable* dt)
+{
+ const void* ptr = dt;
+ const FSE_DTableHeader* DTableH = (const FSE_DTableHeader*)ptr;
+ const U32 fastMode = DTableH->fastMode;
+
+ /* select fast mode (static) */
+ if (fastMode) return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 1);
+ return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 0);
+}
+
+
+size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, FSE_DTable* workSpace, unsigned maxLog)
+{
+ const BYTE* const istart = (const BYTE*)cSrc;
+ const BYTE* ip = istart;
+ short counting[FSE_MAX_SYMBOL_VALUE+1];
+ unsigned tableLog;
+ unsigned maxSymbolValue = FSE_MAX_SYMBOL_VALUE;
+
+ /* normal FSE decoding mode */
+ size_t const NCountLength = FSE_readNCount (counting, &maxSymbolValue, &tableLog, istart, cSrcSize);
+ if (FSE_isError(NCountLength)) return NCountLength;
+ //if (NCountLength >= cSrcSize) return ERROR(srcSize_wrong); /* too small input size; supposed to be already checked in NCountLength, only remaining case : NCountLength==cSrcSize */
+ if (tableLog > maxLog) return ERROR(tableLog_tooLarge);
+ ip += NCountLength;
+ cSrcSize -= NCountLength;
+
+ CHECK_F( FSE_buildDTable (workSpace, counting, maxSymbolValue, tableLog) );
+
+ return FSE_decompress_usingDTable (dst, dstCapacity, ip, cSrcSize, workSpace); /* always return, even if it is an error code */
+}
+
+
+typedef FSE_DTable DTable_max_t[FSE_DTABLE_SIZE_U32(FSE_MAX_TABLELOG)];
+
+size_t FSE_decompress(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize)
+{
+ DTable_max_t dt; /* Static analyzer seems unable to understand this table will be properly initialized later */
+ return FSE_decompress_wksp(dst, dstCapacity, cSrc, cSrcSize, dt, FSE_MAX_TABLELOG);
+}
+
+
+
+#endif /* FSE_COMMONDEFS_ONLY */
diff --git a/Utilities/cmzstd/lib/common/huf.h b/Utilities/cmzstd/lib/common/huf.h
new file mode 100644
index 000000000..6b572c448
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/huf.h
@@ -0,0 +1,358 @@
+/* ******************************************************************
+ huff0 huffman codec,
+ part of Finite State Entropy library
+ Copyright (C) 2013-present, 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/FiniteStateEntropy
+****************************************************************** */
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#ifndef HUF_H_298734234
+#define HUF_H_298734234
+
+/* *** Dependencies *** */
+#include <stddef.h> /* size_t */
+
+
+/* *** library symbols visibility *** */
+/* Note : when linking with -fvisibility=hidden on gcc, or by default on Visual,
+ * HUF symbols remain "private" (internal symbols for library only).
+ * Set macro FSE_DLL_EXPORT to 1 if you want HUF symbols visible on DLL interface */
+#if defined(FSE_DLL_EXPORT) && (FSE_DLL_EXPORT==1) && defined(__GNUC__) && (__GNUC__ >= 4)
+# define HUF_PUBLIC_API __attribute__ ((visibility ("default")))
+#elif defined(FSE_DLL_EXPORT) && (FSE_DLL_EXPORT==1) /* Visual expected */
+# define HUF_PUBLIC_API __declspec(dllexport)
+#elif defined(FSE_DLL_IMPORT) && (FSE_DLL_IMPORT==1)
+# define HUF_PUBLIC_API __declspec(dllimport) /* not required, just to generate faster code (saves a function pointer load from IAT and an indirect jump) */
+#else
+# define HUF_PUBLIC_API
+#endif
+
+
+/* ========================== */
+/* *** simple functions *** */
+/* ========================== */
+
+/** HUF_compress() :
+ * Compress content from buffer 'src', of size 'srcSize', into buffer 'dst'.
+ * 'dst' buffer must be already allocated.
+ * Compression runs faster if `dstCapacity` >= HUF_compressBound(srcSize).
+ * `srcSize` must be <= `HUF_BLOCKSIZE_MAX` == 128 KB.
+ * @return : size of compressed data (<= `dstCapacity`).
+ * Special values : if return == 0, srcData is not compressible => Nothing is stored within dst !!!
+ * if HUF_isError(return), compression failed (more details using HUF_getErrorName())
+ */
+HUF_PUBLIC_API size_t HUF_compress(void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize);
+
+/** HUF_decompress() :
+ * Decompress HUF data from buffer 'cSrc', of size 'cSrcSize',
+ * into already allocated buffer 'dst', of minimum size 'dstSize'.
+ * `originalSize` : **must** be the ***exact*** size of original (uncompressed) data.
+ * Note : in contrast with FSE, HUF_decompress can regenerate
+ * RLE (cSrcSize==1) and uncompressed (cSrcSize==dstSize) data,
+ * because it knows size to regenerate (originalSize).
+ * @return : size of regenerated data (== originalSize),
+ * or an error code, which can be tested using HUF_isError()
+ */
+HUF_PUBLIC_API size_t HUF_decompress(void* dst, size_t originalSize,
+ const void* cSrc, size_t cSrcSize);
+
+
+/* *** Tool functions *** */
+#define HUF_BLOCKSIZE_MAX (128 * 1024) /**< maximum input size for a single block compressed with HUF_compress */
+HUF_PUBLIC_API size_t HUF_compressBound(size_t size); /**< maximum compressed size (worst case) */
+
+/* Error Management */
+HUF_PUBLIC_API unsigned HUF_isError(size_t code); /**< tells if a return value is an error code */
+HUF_PUBLIC_API const char* HUF_getErrorName(size_t code); /**< provides error code string (useful for debugging) */
+
+
+/* *** Advanced function *** */
+
+/** HUF_compress2() :
+ * Same as HUF_compress(), but offers control over `maxSymbolValue` and `tableLog`.
+ * `maxSymbolValue` must be <= HUF_SYMBOLVALUE_MAX .
+ * `tableLog` must be `<= HUF_TABLELOG_MAX` . */
+HUF_PUBLIC_API size_t HUF_compress2 (void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ unsigned maxSymbolValue, unsigned tableLog);
+
+/** HUF_compress4X_wksp() :
+ * Same as HUF_compress2(), but uses externally allocated `workSpace`.
+ * `workspace` must have minimum alignment of 4, and be at least as large as HUF_WORKSPACE_SIZE */
+#define HUF_WORKSPACE_SIZE (6 << 10)
+#define HUF_WORKSPACE_SIZE_U32 (HUF_WORKSPACE_SIZE / sizeof(U32))
+HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ unsigned maxSymbolValue, unsigned tableLog,
+ void* workSpace, size_t wkspSize);
+
+#endif /* HUF_H_298734234 */
+
+/* ******************************************************************
+ * WARNING !!
+ * The following section contains advanced and experimental definitions
+ * which shall never be used in the context of a dynamic library,
+ * because they are not guaranteed to remain stable in the future.
+ * Only consider them in association with static linking.
+ * *****************************************************************/
+#if defined(HUF_STATIC_LINKING_ONLY) && !defined(HUF_H_HUF_STATIC_LINKING_ONLY)
+#define HUF_H_HUF_STATIC_LINKING_ONLY
+
+/* *** Dependencies *** */
+#include "mem.h" /* U32 */
+
+
+/* *** Constants *** */
+#define HUF_TABLELOG_MAX 12 /* max runtime value of tableLog (due to static allocation); can be modified up to HUF_ABSOLUTEMAX_TABLELOG */
+#define HUF_TABLELOG_DEFAULT 11 /* default tableLog value when none specified */
+#define HUF_SYMBOLVALUE_MAX 255
+
+#define HUF_TABLELOG_ABSOLUTEMAX 15 /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */
+#if (HUF_TABLELOG_MAX > HUF_TABLELOG_ABSOLUTEMAX)
+# error "HUF_TABLELOG_MAX is too large !"
+#endif
+
+
+/* ****************************************
+* Static allocation
+******************************************/
+/* HUF buffer bounds */
+#define HUF_CTABLEBOUND 129
+#define HUF_BLOCKBOUND(size) (size + (size>>8) + 8) /* only true when incompressible is pre-filtered with fast heuristic */
+#define HUF_COMPRESSBOUND(size) (HUF_CTABLEBOUND + HUF_BLOCKBOUND(size)) /* Macro version, useful for static allocation */
+
+/* static allocation of HUF's Compression Table */
+#define HUF_CTABLE_SIZE_U32(maxSymbolValue) ((maxSymbolValue)+1) /* Use tables of U32, for proper alignment */
+#define HUF_CTABLE_SIZE(maxSymbolValue) (HUF_CTABLE_SIZE_U32(maxSymbolValue) * sizeof(U32))
+#define HUF_CREATE_STATIC_CTABLE(name, maxSymbolValue) \
+ U32 name##hb[HUF_CTABLE_SIZE_U32(maxSymbolValue)]; \
+ void* name##hv = &(name##hb); \
+ HUF_CElt* name = (HUF_CElt*)(name##hv) /* no final ; */
+
+/* static allocation of HUF's DTable */
+typedef U32 HUF_DTable;
+#define HUF_DTABLE_SIZE(maxTableLog) (1 + (1<<(maxTableLog)))
+#define HUF_CREATE_STATIC_DTABLEX1(DTable, maxTableLog) \
+ HUF_DTable DTable[HUF_DTABLE_SIZE((maxTableLog)-1)] = { ((U32)((maxTableLog)-1) * 0x01000001) }
+#define HUF_CREATE_STATIC_DTABLEX2(DTable, maxTableLog) \
+ HUF_DTable DTable[HUF_DTABLE_SIZE(maxTableLog)] = { ((U32)(maxTableLog) * 0x01000001) }
+
+
+/* ****************************************
+* Advanced decompression functions
+******************************************/
+size_t HUF_decompress4X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */
+#ifndef HUF_FORCE_DECOMPRESS_X1
+size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */
+#endif
+
+size_t HUF_decompress4X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< decodes RLE and uncompressed */
+size_t HUF_decompress4X_hufOnly(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< considers RLE and uncompressed as errors */
+size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< considers RLE and uncompressed as errors */
+size_t HUF_decompress4X1_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */
+size_t HUF_decompress4X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< single-symbol decoder */
+#ifndef HUF_FORCE_DECOMPRESS_X1
+size_t HUF_decompress4X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */
+size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< double-symbols decoder */
+#endif
+
+
+/* ****************************************
+ * HUF detailed API
+ * ****************************************/
+
+/*! HUF_compress() does the following:
+ * 1. count symbol occurrence from source[] into table count[] using FSE_count() (exposed within "fse.h")
+ * 2. (optional) refine tableLog using HUF_optimalTableLog()
+ * 3. build Huffman table from count using HUF_buildCTable()
+ * 4. save Huffman table to memory buffer using HUF_writeCTable()
+ * 5. encode the data stream using HUF_compress4X_usingCTable()
+ *
+ * The following API allows targeting specific sub-functions for advanced tasks.
+ * For example, it's possible to compress several blocks using the same 'CTable',
+ * or to save and regenerate 'CTable' using external methods.
+ */
+unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue);
+typedef struct HUF_CElt_s HUF_CElt; /* incomplete type */
+size_t HUF_buildCTable (HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits); /* @return : maxNbBits; CTable and count can overlap. In which case, CTable will overwrite count content */
+size_t HUF_writeCTable (void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog);
+size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable);
+
+typedef enum {
+ HUF_repeat_none, /**< Cannot use the previous table */
+ HUF_repeat_check, /**< Can use the previous table but it must be checked. Note : The previous table must have been constructed by HUF_compress{1, 4}X_repeat */
+ HUF_repeat_valid /**< Can use the previous table and it is assumed to be valid */
+ } HUF_repeat;
+/** HUF_compress4X_repeat() :
+ * Same as HUF_compress4X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none.
+ * If it uses hufTable it does not modify hufTable or repeat.
+ * If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used.
+ * If preferRepeat then the old table will always be used if valid. */
+size_t HUF_compress4X_repeat(void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ unsigned maxSymbolValue, unsigned tableLog,
+ void* workSpace, size_t wkspSize, /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */
+ HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2);
+
+/** HUF_buildCTable_wksp() :
+ * Same as HUF_buildCTable(), but using externally allocated scratch buffer.
+ * `workSpace` must be aligned on 4-bytes boundaries, and its size must be >= HUF_CTABLE_WORKSPACE_SIZE.
+ */
+#define HUF_CTABLE_WORKSPACE_SIZE_U32 (2*HUF_SYMBOLVALUE_MAX +1 +1)
+#define HUF_CTABLE_WORKSPACE_SIZE (HUF_CTABLE_WORKSPACE_SIZE_U32 * sizeof(unsigned))
+size_t HUF_buildCTable_wksp (HUF_CElt* tree,
+ const unsigned* count, U32 maxSymbolValue, U32 maxNbBits,
+ void* workSpace, size_t wkspSize);
+
+/*! HUF_readStats() :
+ * Read compact Huffman tree, saved by HUF_writeCTable().
+ * `huffWeight` is destination buffer.
+ * @return : size read from `src` , or an error Code .
+ * Note : Needed by HUF_readCTable() and HUF_readDTableXn() . */
+size_t HUF_readStats(BYTE* huffWeight, size_t hwSize,
+ U32* rankStats, U32* nbSymbolsPtr, U32* tableLogPtr,
+ const void* src, size_t srcSize);
+
+/** HUF_readCTable() :
+ * Loading a CTable saved with HUF_writeCTable() */
+size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize);
+
+/** HUF_getNbBits() :
+ * Read nbBits from CTable symbolTable, for symbol `symbolValue` presumed <= HUF_SYMBOLVALUE_MAX
+ * Note 1 : is not inlined, as HUF_CElt definition is private
+ * Note 2 : const void* used, so that it can provide a statically allocated table as argument (which uses type U32) */
+U32 HUF_getNbBits(const void* symbolTable, U32 symbolValue);
+
+/*
+ * HUF_decompress() does the following:
+ * 1. select the decompression algorithm (X1, X2) based on pre-computed heuristics
+ * 2. build Huffman table from save, using HUF_readDTableX?()
+ * 3. decode 1 or 4 segments in parallel using HUF_decompress?X?_usingDTable()
+ */
+
+/** HUF_selectDecoder() :
+ * Tells which decoder is likely to decode faster,
+ * based on a set of pre-computed metrics.
+ * @return : 0==HUF_decompress4X1, 1==HUF_decompress4X2 .
+ * Assumption : 0 < dstSize <= 128 KB */
+U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize);
+
+/**
+ * The minimum workspace size for the `workSpace` used in
+ * HUF_readDTableX1_wksp() and HUF_readDTableX2_wksp().
+ *
+ * The space used depends on HUF_TABLELOG_MAX, ranging from ~1500 bytes when
+ * HUF_TABLE_LOG_MAX=12 to ~1850 bytes when HUF_TABLE_LOG_MAX=15.
+ * Buffer overflow errors may potentially occur if code modifications result in
+ * a required workspace size greater than that specified in the following
+ * macro.
+ */
+#define HUF_DECOMPRESS_WORKSPACE_SIZE (2 << 10)
+#define HUF_DECOMPRESS_WORKSPACE_SIZE_U32 (HUF_DECOMPRESS_WORKSPACE_SIZE / sizeof(U32))
+
+#ifndef HUF_FORCE_DECOMPRESS_X2
+size_t HUF_readDTableX1 (HUF_DTable* DTable, const void* src, size_t srcSize);
+size_t HUF_readDTableX1_wksp (HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize);
+#endif
+#ifndef HUF_FORCE_DECOMPRESS_X1
+size_t HUF_readDTableX2 (HUF_DTable* DTable, const void* src, size_t srcSize);
+size_t HUF_readDTableX2_wksp (HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize);
+#endif
+
+size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable);
+#ifndef HUF_FORCE_DECOMPRESS_X2
+size_t HUF_decompress4X1_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable);
+#endif
+#ifndef HUF_FORCE_DECOMPRESS_X1
+size_t HUF_decompress4X2_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable);
+#endif
+
+
+/* ====================== */
+/* single stream variants */
+/* ====================== */
+
+size_t HUF_compress1X (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog);
+size_t HUF_compress1X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize); /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */
+size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable);
+/** HUF_compress1X_repeat() :
+ * Same as HUF_compress1X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none.
+ * If it uses hufTable it does not modify hufTable or repeat.
+ * If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used.
+ * If preferRepeat then the old table will always be used if valid. */
+size_t HUF_compress1X_repeat(void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ unsigned maxSymbolValue, unsigned tableLog,
+ void* workSpace, size_t wkspSize, /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */
+ HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2);
+
+size_t HUF_decompress1X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* single-symbol decoder */
+#ifndef HUF_FORCE_DECOMPRESS_X1
+size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /* double-symbol decoder */
+#endif
+
+size_t HUF_decompress1X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize);
+size_t HUF_decompress1X_DCtx_wksp (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize);
+#ifndef HUF_FORCE_DECOMPRESS_X2
+size_t HUF_decompress1X1_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< single-symbol decoder */
+size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< single-symbol decoder */
+#endif
+#ifndef HUF_FORCE_DECOMPRESS_X1
+size_t HUF_decompress1X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< double-symbols decoder */
+size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< double-symbols decoder */
+#endif
+
+size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable); /**< automatic selection of sing or double symbol decoder, based on DTable */
+#ifndef HUF_FORCE_DECOMPRESS_X2
+size_t HUF_decompress1X1_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable);
+#endif
+#ifndef HUF_FORCE_DECOMPRESS_X1
+size_t HUF_decompress1X2_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable);
+#endif
+
+/* BMI2 variants.
+ * If the CPU has BMI2 support, pass bmi2=1, otherwise pass bmi2=0.
+ */
+size_t HUF_decompress1X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2);
+#ifndef HUF_FORCE_DECOMPRESS_X2
+size_t HUF_decompress1X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2);
+#endif
+size_t HUF_decompress4X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2);
+size_t HUF_decompress4X_hufOnly_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2);
+
+#endif /* HUF_STATIC_LINKING_ONLY */
+
+#if defined (__cplusplus)
+}
+#endif
diff --git a/Utilities/cmzstd/lib/common/mem.h b/Utilities/cmzstd/lib/common/mem.h
new file mode 100644
index 000000000..5da248756
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/mem.h
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#ifndef MEM_H_MODULE
+#define MEM_H_MODULE
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+/*-****************************************
+* Dependencies
+******************************************/
+#include <stddef.h> /* size_t, ptrdiff_t */
+#include <string.h> /* memcpy */
+
+
+/*-****************************************
+* Compiler specifics
+******************************************/
+#if defined(_MSC_VER) /* Visual Studio */
+# include <stdlib.h> /* _byteswap_ulong */
+# include <intrin.h> /* _byteswap_* */
+#endif
+#if defined(__GNUC__)
+# define MEM_STATIC static __inline __attribute__((unused))
+#elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
+# define MEM_STATIC static inline
+#elif defined(_MSC_VER)
+# define MEM_STATIC static __inline
+#else
+# define MEM_STATIC static /* this version may generate warnings for unused static functions; disable the relevant warning */
+#endif
+
+#ifndef __has_builtin
+# define __has_builtin(x) 0 /* compat. with non-clang compilers */
+#endif
+
+/* code only tested on 32 and 64 bits systems */
+#define MEM_STATIC_ASSERT(c) { enum { MEM_static_assert = 1/(int)(!!(c)) }; }
+MEM_STATIC void MEM_check(void) { MEM_STATIC_ASSERT((sizeof(size_t)==4) || (sizeof(size_t)==8)); }
+
+
+/*-**************************************************************
+* Basic Types
+*****************************************************************/
+#if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
+# include <stdint.h>
+ typedef uint8_t BYTE;
+ typedef uint16_t U16;
+ typedef int16_t S16;
+ typedef uint32_t U32;
+ typedef int32_t S32;
+ typedef uint64_t U64;
+ typedef int64_t S64;
+#else
+# include <limits.h>
+#if CHAR_BIT != 8
+# error "this implementation requires char to be exactly 8-bit type"
+#endif
+ typedef unsigned char BYTE;
+#if USHRT_MAX != 65535
+# error "this implementation requires short to be exactly 16-bit type"
+#endif
+ typedef unsigned short U16;
+ typedef signed short S16;
+#if UINT_MAX != 4294967295
+# error "this implementation requires int to be exactly 32-bit type"
+#endif
+ typedef unsigned int U32;
+ typedef signed int S32;
+/* note : there are no limits defined for long long type in C90.
+ * limits exist in C99, however, in such case, <stdint.h> is preferred */
+ typedef unsigned long long U64;
+ typedef signed long long S64;
+#endif
+
+
+/*-**************************************************************
+* Memory I/O
+*****************************************************************/
+/* MEM_FORCE_MEMORY_ACCESS :
+ * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.
+ * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.
+ * The below switch allow to select different access method for improved performance.
+ * Method 0 (default) : use `memcpy()`. Safe and portable.
+ * Method 1 : `__packed` statement. It depends on compiler extension (i.e., not portable).
+ * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.
+ * Method 2 : direct access. This method is portable but violate C standard.
+ * It can generate buggy code on targets depending on alignment.
+ * In some circumstances, it's the only known way to get the most performance (i.e. GCC + ARMv6)
+ * See http://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details.
+ * Prefer these methods in priority order (0 > 1 > 2)
+ */
+#ifndef MEM_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */
+# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )
+# define MEM_FORCE_MEMORY_ACCESS 2
+# elif defined(__INTEL_COMPILER) || defined(__GNUC__)
+# define MEM_FORCE_MEMORY_ACCESS 1
+# endif
+#endif
+
+MEM_STATIC unsigned MEM_32bits(void) { return sizeof(size_t)==4; }
+MEM_STATIC unsigned MEM_64bits(void) { return sizeof(size_t)==8; }
+
+MEM_STATIC unsigned MEM_isLittleEndian(void)
+{
+ const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */
+ return one.c[0];
+}
+
+#if defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==2)
+
+/* violates C standard, by lying on structure alignment.
+Only use if no other choice to achieve best performance on target platform */
+MEM_STATIC U16 MEM_read16(const void* memPtr) { return *(const U16*) memPtr; }
+MEM_STATIC U32 MEM_read32(const void* memPtr) { return *(const U32*) memPtr; }
+MEM_STATIC U64 MEM_read64(const void* memPtr) { return *(const U64*) memPtr; }
+MEM_STATIC size_t MEM_readST(const void* memPtr) { return *(const size_t*) memPtr; }
+
+MEM_STATIC void MEM_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; }
+MEM_STATIC void MEM_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; }
+MEM_STATIC void MEM_write64(void* memPtr, U64 value) { *(U64*)memPtr = value; }
+
+#elif defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==1)
+
+/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
+/* currently only defined for gcc and icc */
+#if defined(_MSC_VER) || (defined(__INTEL_COMPILER) && defined(WIN32))
+ __pragma( pack(push, 1) )
+ typedef struct { U16 v; } unalign16;
+ typedef struct { U32 v; } unalign32;
+ typedef struct { U64 v; } unalign64;
+ typedef struct { size_t v; } unalignArch;
+ __pragma( pack(pop) )
+#else
+ typedef struct { U16 v; } __attribute__((packed)) unalign16;
+ typedef struct { U32 v; } __attribute__((packed)) unalign32;
+ typedef struct { U64 v; } __attribute__((packed)) unalign64;
+ typedef struct { size_t v; } __attribute__((packed)) unalignArch;
+#endif
+
+MEM_STATIC U16 MEM_read16(const void* ptr) { return ((const unalign16*)ptr)->v; }
+MEM_STATIC U32 MEM_read32(const void* ptr) { return ((const unalign32*)ptr)->v; }
+MEM_STATIC U64 MEM_read64(const void* ptr) { return ((const unalign64*)ptr)->v; }
+MEM_STATIC size_t MEM_readST(const void* ptr) { return ((const unalignArch*)ptr)->v; }
+
+MEM_STATIC void MEM_write16(void* memPtr, U16 value) { ((unalign16*)memPtr)->v = value; }
+MEM_STATIC void MEM_write32(void* memPtr, U32 value) { ((unalign32*)memPtr)->v = value; }
+MEM_STATIC void MEM_write64(void* memPtr, U64 value) { ((unalign64*)memPtr)->v = value; }
+
+#else
+
+/* default method, safe and standard.
+ can sometimes prove slower */
+
+MEM_STATIC U16 MEM_read16(const void* memPtr)
+{
+ U16 val; memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+MEM_STATIC U32 MEM_read32(const void* memPtr)
+{
+ U32 val; memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+MEM_STATIC U64 MEM_read64(const void* memPtr)
+{
+ U64 val; memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+MEM_STATIC size_t MEM_readST(const void* memPtr)
+{
+ size_t val; memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+MEM_STATIC void MEM_write16(void* memPtr, U16 value)
+{
+ memcpy(memPtr, &value, sizeof(value));
+}
+
+MEM_STATIC void MEM_write32(void* memPtr, U32 value)
+{
+ memcpy(memPtr, &value, sizeof(value));
+}
+
+MEM_STATIC void MEM_write64(void* memPtr, U64 value)
+{
+ memcpy(memPtr, &value, sizeof(value));
+}
+
+#endif /* MEM_FORCE_MEMORY_ACCESS */
+
+MEM_STATIC U32 MEM_swap32(U32 in)
+{
+#if defined(_MSC_VER) /* Visual Studio */
+ return _byteswap_ulong(in);
+#elif (defined (__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 403)) \
+ || (defined(__clang__) && __has_builtin(__builtin_bswap32))
+ return __builtin_bswap32(in);
+#else
+ return ((in << 24) & 0xff000000 ) |
+ ((in << 8) & 0x00ff0000 ) |
+ ((in >> 8) & 0x0000ff00 ) |
+ ((in >> 24) & 0x000000ff );
+#endif
+}
+
+MEM_STATIC U64 MEM_swap64(U64 in)
+{
+#if defined(_MSC_VER) /* Visual Studio */
+ return _byteswap_uint64(in);
+#elif (defined (__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 403)) \
+ || (defined(__clang__) && __has_builtin(__builtin_bswap64))
+ return __builtin_bswap64(in);
+#else
+ return ((in << 56) & 0xff00000000000000ULL) |
+ ((in << 40) & 0x00ff000000000000ULL) |
+ ((in << 24) & 0x0000ff0000000000ULL) |
+ ((in << 8) & 0x000000ff00000000ULL) |
+ ((in >> 8) & 0x00000000ff000000ULL) |
+ ((in >> 24) & 0x0000000000ff0000ULL) |
+ ((in >> 40) & 0x000000000000ff00ULL) |
+ ((in >> 56) & 0x00000000000000ffULL);
+#endif
+}
+
+MEM_STATIC size_t MEM_swapST(size_t in)
+{
+ if (MEM_32bits())
+ return (size_t)MEM_swap32((U32)in);
+ else
+ return (size_t)MEM_swap64((U64)in);
+}
+
+/*=== Little endian r/w ===*/
+
+MEM_STATIC U16 MEM_readLE16(const void* memPtr)
+{
+ if (MEM_isLittleEndian())
+ return MEM_read16(memPtr);
+ else {
+ const BYTE* p = (const BYTE*)memPtr;
+ return (U16)(p[0] + (p[1]<<8));
+ }
+}
+
+MEM_STATIC void MEM_writeLE16(void* memPtr, U16 val)
+{
+ if (MEM_isLittleEndian()) {
+ MEM_write16(memPtr, val);
+ } else {
+ BYTE* p = (BYTE*)memPtr;
+ p[0] = (BYTE)val;
+ p[1] = (BYTE)(val>>8);
+ }
+}
+
+MEM_STATIC U32 MEM_readLE24(const void* memPtr)
+{
+ return MEM_readLE16(memPtr) + (((const BYTE*)memPtr)[2] << 16);
+}
+
+MEM_STATIC void MEM_writeLE24(void* memPtr, U32 val)
+{
+ MEM_writeLE16(memPtr, (U16)val);
+ ((BYTE*)memPtr)[2] = (BYTE)(val>>16);
+}
+
+MEM_STATIC U32 MEM_readLE32(const void* memPtr)
+{
+ if (MEM_isLittleEndian())
+ return MEM_read32(memPtr);
+ else
+ return MEM_swap32(MEM_read32(memPtr));
+}
+
+MEM_STATIC void MEM_writeLE32(void* memPtr, U32 val32)
+{
+ if (MEM_isLittleEndian())
+ MEM_write32(memPtr, val32);
+ else
+ MEM_write32(memPtr, MEM_swap32(val32));
+}
+
+MEM_STATIC U64 MEM_readLE64(const void* memPtr)
+{
+ if (MEM_isLittleEndian())
+ return MEM_read64(memPtr);
+ else
+ return MEM_swap64(MEM_read64(memPtr));
+}
+
+MEM_STATIC void MEM_writeLE64(void* memPtr, U64 val64)
+{
+ if (MEM_isLittleEndian())
+ MEM_write64(memPtr, val64);
+ else
+ MEM_write64(memPtr, MEM_swap64(val64));
+}
+
+MEM_STATIC size_t MEM_readLEST(const void* memPtr)
+{
+ if (MEM_32bits())
+ return (size_t)MEM_readLE32(memPtr);
+ else
+ return (size_t)MEM_readLE64(memPtr);
+}
+
+MEM_STATIC void MEM_writeLEST(void* memPtr, size_t val)
+{
+ if (MEM_32bits())
+ MEM_writeLE32(memPtr, (U32)val);
+ else
+ MEM_writeLE64(memPtr, (U64)val);
+}
+
+/*=== Big endian r/w ===*/
+
+MEM_STATIC U32 MEM_readBE32(const void* memPtr)
+{
+ if (MEM_isLittleEndian())
+ return MEM_swap32(MEM_read32(memPtr));
+ else
+ return MEM_read32(memPtr);
+}
+
+MEM_STATIC void MEM_writeBE32(void* memPtr, U32 val32)
+{
+ if (MEM_isLittleEndian())
+ MEM_write32(memPtr, MEM_swap32(val32));
+ else
+ MEM_write32(memPtr, val32);
+}
+
+MEM_STATIC U64 MEM_readBE64(const void* memPtr)
+{
+ if (MEM_isLittleEndian())
+ return MEM_swap64(MEM_read64(memPtr));
+ else
+ return MEM_read64(memPtr);
+}
+
+MEM_STATIC void MEM_writeBE64(void* memPtr, U64 val64)
+{
+ if (MEM_isLittleEndian())
+ MEM_write64(memPtr, MEM_swap64(val64));
+ else
+ MEM_write64(memPtr, val64);
+}
+
+MEM_STATIC size_t MEM_readBEST(const void* memPtr)
+{
+ if (MEM_32bits())
+ return (size_t)MEM_readBE32(memPtr);
+ else
+ return (size_t)MEM_readBE64(memPtr);
+}
+
+MEM_STATIC void MEM_writeBEST(void* memPtr, size_t val)
+{
+ if (MEM_32bits())
+ MEM_writeBE32(memPtr, (U32)val);
+ else
+ MEM_writeBE64(memPtr, (U64)val);
+}
+
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* MEM_H_MODULE */
diff --git a/Utilities/cmzstd/lib/common/pool.c b/Utilities/cmzstd/lib/common/pool.c
new file mode 100644
index 000000000..7a8294543
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/pool.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+
+/* ====== Dependencies ======= */
+#include <stddef.h> /* size_t */
+#include "debug.h" /* assert */
+#include "zstd_internal.h" /* ZSTD_malloc, ZSTD_free */
+#include "pool.h"
+
+/* ====== Compiler specifics ====== */
+#if defined(_MSC_VER)
+# pragma warning(disable : 4204) /* disable: C4204: non-constant aggregate initializer */
+#endif
+
+
+#ifdef ZSTD_MULTITHREAD
+
+#include "threading.h" /* pthread adaptation */
+
+/* A job is a function and an opaque argument */
+typedef struct POOL_job_s {
+ POOL_function function;
+ void *opaque;
+} POOL_job;
+
+struct POOL_ctx_s {
+ ZSTD_customMem customMem;
+ /* Keep track of the threads */
+ ZSTD_pthread_t* threads;
+ size_t threadCapacity;
+ size_t threadLimit;
+
+ /* The queue is a circular buffer */
+ POOL_job *queue;
+ size_t queueHead;
+ size_t queueTail;
+ size_t queueSize;
+
+ /* The number of threads working on jobs */
+ size_t numThreadsBusy;
+ /* Indicates if the queue is empty */
+ int queueEmpty;
+
+ /* The mutex protects the queue */
+ ZSTD_pthread_mutex_t queueMutex;
+ /* Condition variable for pushers to wait on when the queue is full */
+ ZSTD_pthread_cond_t queuePushCond;
+ /* Condition variables for poppers to wait on when the queue is empty */
+ ZSTD_pthread_cond_t queuePopCond;
+ /* Indicates if the queue is shutting down */
+ int shutdown;
+};
+
+/* POOL_thread() :
+ * Work thread for the thread pool.
+ * Waits for jobs and executes them.
+ * @returns : NULL on failure else non-null.
+ */
+static void* POOL_thread(void* opaque) {
+ POOL_ctx* const ctx = (POOL_ctx*)opaque;
+ if (!ctx) { return NULL; }
+ for (;;) {
+ /* Lock the mutex and wait for a non-empty queue or until shutdown */
+ ZSTD_pthread_mutex_lock(&ctx->queueMutex);
+
+ while ( ctx->queueEmpty
+ || (ctx->numThreadsBusy >= ctx->threadLimit) ) {
+ if (ctx->shutdown) {
+ /* even if !queueEmpty, (possible if numThreadsBusy >= threadLimit),
+ * a few threads will be shutdown while !queueEmpty,
+ * but enough threads will remain active to finish the queue */
+ ZSTD_pthread_mutex_unlock(&ctx->queueMutex);
+ return opaque;
+ }
+ ZSTD_pthread_cond_wait(&ctx->queuePopCond, &ctx->queueMutex);
+ }
+ /* Pop a job off the queue */
+ { POOL_job const job = ctx->queue[ctx->queueHead];
+ ctx->queueHead = (ctx->queueHead + 1) % ctx->queueSize;
+ ctx->numThreadsBusy++;
+ ctx->queueEmpty = ctx->queueHead == ctx->queueTail;
+ /* Unlock the mutex, signal a pusher, and run the job */
+ ZSTD_pthread_cond_signal(&ctx->queuePushCond);
+ ZSTD_pthread_mutex_unlock(&ctx->queueMutex);
+
+ job.function(job.opaque);
+
+ /* If the intended queue size was 0, signal after finishing job */
+ ZSTD_pthread_mutex_lock(&ctx->queueMutex);
+ ctx->numThreadsBusy--;
+ if (ctx->queueSize == 1) {
+ ZSTD_pthread_cond_signal(&ctx->queuePushCond);
+ }
+ ZSTD_pthread_mutex_unlock(&ctx->queueMutex);
+ }
+ } /* for (;;) */
+ assert(0); /* Unreachable */
+}
+
+POOL_ctx* POOL_create(size_t numThreads, size_t queueSize) {
+ return POOL_create_advanced(numThreads, queueSize, ZSTD_defaultCMem);
+}
+
+POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize,
+ ZSTD_customMem customMem) {
+ POOL_ctx* ctx;
+ /* Check parameters */
+ if (!numThreads) { return NULL; }
+ /* Allocate the context and zero initialize */
+ ctx = (POOL_ctx*)ZSTD_calloc(sizeof(POOL_ctx), customMem);
+ if (!ctx) { return NULL; }
+ /* Initialize the job queue.
+ * It needs one extra space since one space is wasted to differentiate
+ * empty and full queues.
+ */
+ ctx->queueSize = queueSize + 1;
+ ctx->queue = (POOL_job*)ZSTD_malloc(ctx->queueSize * sizeof(POOL_job), customMem);
+ ctx->queueHead = 0;
+ ctx->queueTail = 0;
+ ctx->numThreadsBusy = 0;
+ ctx->queueEmpty = 1;
+ (void)ZSTD_pthread_mutex_init(&ctx->queueMutex, NULL);
+ (void)ZSTD_pthread_cond_init(&ctx->queuePushCond, NULL);
+ (void)ZSTD_pthread_cond_init(&ctx->queuePopCond, NULL);
+ ctx->shutdown = 0;
+ /* Allocate space for the thread handles */
+ ctx->threads = (ZSTD_pthread_t*)ZSTD_malloc(numThreads * sizeof(ZSTD_pthread_t), customMem);
+ ctx->threadCapacity = 0;
+ ctx->customMem = customMem;
+ /* Check for errors */
+ if (!ctx->threads || !ctx->queue) { POOL_free(ctx); return NULL; }
+ /* Initialize the threads */
+ { size_t i;
+ for (i = 0; i < numThreads; ++i) {
+ if (ZSTD_pthread_create(&ctx->threads[i], NULL, &POOL_thread, ctx)) {
+ ctx->threadCapacity = i;
+ POOL_free(ctx);
+ return NULL;
+ } }
+ ctx->threadCapacity = numThreads;
+ ctx->threadLimit = numThreads;
+ }
+ return ctx;
+}
+
+/*! POOL_join() :
+ Shutdown the queue, wake any sleeping threads, and join all of the threads.
+*/
+static void POOL_join(POOL_ctx* ctx) {
+ /* Shut down the queue */
+ ZSTD_pthread_mutex_lock(&ctx->queueMutex);
+ ctx->shutdown = 1;
+ ZSTD_pthread_mutex_unlock(&ctx->queueMutex);
+ /* Wake up sleeping threads */
+ ZSTD_pthread_cond_broadcast(&ctx->queuePushCond);
+ ZSTD_pthread_cond_broadcast(&ctx->queuePopCond);
+ /* Join all of the threads */
+ { size_t i;
+ for (i = 0; i < ctx->threadCapacity; ++i) {
+ ZSTD_pthread_join(ctx->threads[i], NULL); /* note : could fail */
+ } }
+}
+
+void POOL_free(POOL_ctx *ctx) {
+ if (!ctx) { return; }
+ POOL_join(ctx);
+ ZSTD_pthread_mutex_destroy(&ctx->queueMutex);
+ ZSTD_pthread_cond_destroy(&ctx->queuePushCond);
+ ZSTD_pthread_cond_destroy(&ctx->queuePopCond);
+ ZSTD_free(ctx->queue, ctx->customMem);
+ ZSTD_free(ctx->threads, ctx->customMem);
+ ZSTD_free(ctx, ctx->customMem);
+}
+
+
+
+size_t POOL_sizeof(POOL_ctx *ctx) {
+ if (ctx==NULL) return 0; /* supports sizeof NULL */
+ return sizeof(*ctx)
+ + ctx->queueSize * sizeof(POOL_job)
+ + ctx->threadCapacity * sizeof(ZSTD_pthread_t);
+}
+
+
+/* @return : 0 on success, 1 on error */
+static int POOL_resize_internal(POOL_ctx* ctx, size_t numThreads)
+{
+ if (numThreads <= ctx->threadCapacity) {
+ if (!numThreads) return 1;
+ ctx->threadLimit = numThreads;
+ return 0;
+ }
+ /* numThreads > threadCapacity */
+ { ZSTD_pthread_t* const threadPool = (ZSTD_pthread_t*)ZSTD_malloc(numThreads * sizeof(ZSTD_pthread_t), ctx->customMem);
+ if (!threadPool) return 1;
+ /* replace existing thread pool */
+ memcpy(threadPool, ctx->threads, ctx->threadCapacity * sizeof(*threadPool));
+ ZSTD_free(ctx->threads, ctx->customMem);
+ ctx->threads = threadPool;
+ /* Initialize additional threads */
+ { size_t threadId;
+ for (threadId = ctx->threadCapacity; threadId < numThreads; ++threadId) {
+ if (ZSTD_pthread_create(&threadPool[threadId], NULL, &POOL_thread, ctx)) {
+ ctx->threadCapacity = threadId;
+ return 1;
+ } }
+ } }
+ /* successfully expanded */
+ ctx->threadCapacity = numThreads;
+ ctx->threadLimit = numThreads;
+ return 0;
+}
+
+/* @return : 0 on success, 1 on error */
+int POOL_resize(POOL_ctx* ctx, size_t numThreads)
+{
+ int result;
+ if (ctx==NULL) return 1;
+ ZSTD_pthread_mutex_lock(&ctx->queueMutex);
+ result = POOL_resize_internal(ctx, numThreads);
+ ZSTD_pthread_cond_broadcast(&ctx->queuePopCond);
+ ZSTD_pthread_mutex_unlock(&ctx->queueMutex);
+ return result;
+}
+
+/**
+ * Returns 1 if the queue is full and 0 otherwise.
+ *
+ * When queueSize is 1 (pool was created with an intended queueSize of 0),
+ * then a queue is empty if there is a thread free _and_ no job is waiting.
+ */
+static int isQueueFull(POOL_ctx const* ctx) {
+ if (ctx->queueSize > 1) {
+ return ctx->queueHead == ((ctx->queueTail + 1) % ctx->queueSize);
+ } else {
+ return (ctx->numThreadsBusy == ctx->threadLimit) ||
+ !ctx->queueEmpty;
+ }
+}
+
+
+static void POOL_add_internal(POOL_ctx* ctx, POOL_function function, void *opaque)
+{
+ POOL_job const job = {function, opaque};
+ assert(ctx != NULL);
+ if (ctx->shutdown) return;
+
+ ctx->queueEmpty = 0;
+ ctx->queue[ctx->queueTail] = job;
+ ctx->queueTail = (ctx->queueTail + 1) % ctx->queueSize;
+ ZSTD_pthread_cond_signal(&ctx->queuePopCond);
+}
+
+void POOL_add(POOL_ctx* ctx, POOL_function function, void* opaque)
+{
+ assert(ctx != NULL);
+ ZSTD_pthread_mutex_lock(&ctx->queueMutex);
+ /* Wait until there is space in the queue for the new job */
+ while (isQueueFull(ctx) && (!ctx->shutdown)) {
+ ZSTD_pthread_cond_wait(&ctx->queuePushCond, &ctx->queueMutex);
+ }
+ POOL_add_internal(ctx, function, opaque);
+ ZSTD_pthread_mutex_unlock(&ctx->queueMutex);
+}
+
+
+int POOL_tryAdd(POOL_ctx* ctx, POOL_function function, void* opaque)
+{
+ assert(ctx != NULL);
+ ZSTD_pthread_mutex_lock(&ctx->queueMutex);
+ if (isQueueFull(ctx)) {
+ ZSTD_pthread_mutex_unlock(&ctx->queueMutex);
+ return 0;
+ }
+ POOL_add_internal(ctx, function, opaque);
+ ZSTD_pthread_mutex_unlock(&ctx->queueMutex);
+ return 1;
+}
+
+
+#else /* ZSTD_MULTITHREAD not defined */
+
+/* ========================== */
+/* No multi-threading support */
+/* ========================== */
+
+
+/* We don't need any data, but if it is empty, malloc() might return NULL. */
+struct POOL_ctx_s {
+ int dummy;
+};
+static POOL_ctx g_ctx;
+
+POOL_ctx* POOL_create(size_t numThreads, size_t queueSize) {
+ return POOL_create_advanced(numThreads, queueSize, ZSTD_defaultCMem);
+}
+
+POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, ZSTD_customMem customMem) {
+ (void)numThreads;
+ (void)queueSize;
+ (void)customMem;
+ return &g_ctx;
+}
+
+void POOL_free(POOL_ctx* ctx) {
+ assert(!ctx || ctx == &g_ctx);
+ (void)ctx;
+}
+
+int POOL_resize(POOL_ctx* ctx, size_t numThreads) {
+ (void)ctx; (void)numThreads;
+ return 0;
+}
+
+void POOL_add(POOL_ctx* ctx, POOL_function function, void* opaque) {
+ (void)ctx;
+ function(opaque);
+}
+
+int POOL_tryAdd(POOL_ctx* ctx, POOL_function function, void* opaque) {
+ (void)ctx;
+ function(opaque);
+ return 1;
+}
+
+size_t POOL_sizeof(POOL_ctx* ctx) {
+ if (ctx==NULL) return 0; /* supports sizeof NULL */
+ assert(ctx == &g_ctx);
+ return sizeof(*ctx);
+}
+
+#endif /* ZSTD_MULTITHREAD */
diff --git a/Utilities/cmzstd/lib/common/pool.h b/Utilities/cmzstd/lib/common/pool.h
new file mode 100644
index 000000000..458d37f13
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/pool.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#ifndef POOL_H
+#define POOL_H
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+
+#include <stddef.h> /* size_t */
+#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_customMem */
+#include "zstd.h"
+
+typedef struct POOL_ctx_s POOL_ctx;
+
+/*! POOL_create() :
+ * Create a thread pool with at most `numThreads` threads.
+ * `numThreads` must be at least 1.
+ * The maximum number of queued jobs before blocking is `queueSize`.
+ * @return : POOL_ctx pointer on success, else NULL.
+*/
+POOL_ctx* POOL_create(size_t numThreads, size_t queueSize);
+
+POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize,
+ ZSTD_customMem customMem);
+
+/*! POOL_free() :
+ * Free a thread pool returned by POOL_create().
+ */
+void POOL_free(POOL_ctx* ctx);
+
+/*! POOL_resize() :
+ * Expands or shrinks pool's number of threads.
+ * This is more efficient than releasing + creating a new context,
+ * since it tries to preserve and re-use existing threads.
+ * `numThreads` must be at least 1.
+ * @return : 0 when resize was successful,
+ * !0 (typically 1) if there is an error.
+ * note : only numThreads can be resized, queueSize remains unchanged.
+ */
+int POOL_resize(POOL_ctx* ctx, size_t numThreads);
+
+/*! POOL_sizeof() :
+ * @return threadpool memory usage
+ * note : compatible with NULL (returns 0 in this case)
+ */
+size_t POOL_sizeof(POOL_ctx* ctx);
+
+/*! POOL_function :
+ * The function type that can be added to a thread pool.
+ */
+typedef void (*POOL_function)(void*);
+
+/*! POOL_add() :
+ * Add the job `function(opaque)` to the thread pool. `ctx` must be valid.
+ * Possibly blocks until there is room in the queue.
+ * Note : The function may be executed asynchronously,
+ * therefore, `opaque` must live until function has been completed.
+ */
+void POOL_add(POOL_ctx* ctx, POOL_function function, void* opaque);
+
+
+/*! POOL_tryAdd() :
+ * Add the job `function(opaque)` to thread pool _if_ a worker is available.
+ * Returns immediately even if not (does not block).
+ * @return : 1 if successful, 0 if not.
+ */
+int POOL_tryAdd(POOL_ctx* ctx, POOL_function function, void* opaque);
+
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif
diff --git a/Utilities/cmzstd/lib/common/threading.c b/Utilities/cmzstd/lib/common/threading.c
new file mode 100644
index 000000000..8be8c8da9
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/threading.c
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2016 Tino Reichardt
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ *
+ * You can contact the author at:
+ * - zstdmt source repository: https://github.com/mcmilk/zstdmt
+ */
+
+/**
+ * This file will hold wrapper for systems, which do not support pthreads
+ */
+
+/* create fake symbol to avoid empty trnaslation unit warning */
+int g_ZSTD_threading_useles_symbol;
+
+#if defined(ZSTD_MULTITHREAD) && defined(_WIN32)
+
+/**
+ * Windows minimalist Pthread Wrapper, based on :
+ * http://www.cse.wustl.edu/~schmidt/win32-cv-1.html
+ */
+
+
+/* === Dependencies === */
+#include <process.h>
+#include <errno.h>
+#include "threading.h"
+
+
+/* === Implementation === */
+
+static unsigned __stdcall worker(void *arg)
+{
+ ZSTD_pthread_t* const thread = (ZSTD_pthread_t*) arg;
+ thread->arg = thread->start_routine(thread->arg);
+ return 0;
+}
+
+int ZSTD_pthread_create(ZSTD_pthread_t* thread, const void* unused,
+ void* (*start_routine) (void*), void* arg)
+{
+ (void)unused;
+ thread->arg = arg;
+ thread->start_routine = start_routine;
+ thread->handle = (HANDLE) _beginthreadex(NULL, 0, worker, thread, 0, NULL);
+
+ if (!thread->handle)
+ return errno;
+ else
+ return 0;
+}
+
+int ZSTD_pthread_join(ZSTD_pthread_t thread, void **value_ptr)
+{
+ DWORD result;
+
+ if (!thread.handle) return 0;
+
+ result = WaitForSingleObject(thread.handle, INFINITE);
+ switch (result) {
+ case WAIT_OBJECT_0:
+ if (value_ptr) *value_ptr = thread.arg;
+ return 0;
+ case WAIT_ABANDONED:
+ return EINVAL;
+ default:
+ return GetLastError();
+ }
+}
+
+#endif /* ZSTD_MULTITHREAD */
diff --git a/Utilities/cmzstd/lib/common/threading.h b/Utilities/cmzstd/lib/common/threading.h
new file mode 100644
index 000000000..d806c89d0
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/threading.h
@@ -0,0 +1,123 @@
+/**
+ * Copyright (c) 2016 Tino Reichardt
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ *
+ * You can contact the author at:
+ * - zstdmt source repository: https://github.com/mcmilk/zstdmt
+ */
+
+#ifndef THREADING_H_938743
+#define THREADING_H_938743
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#if defined(ZSTD_MULTITHREAD) && defined(_WIN32)
+
+/**
+ * Windows minimalist Pthread Wrapper, based on :
+ * http://www.cse.wustl.edu/~schmidt/win32-cv-1.html
+ */
+#ifdef WINVER
+# undef WINVER
+#endif
+#define WINVER 0x0600
+
+#ifdef _WIN32_WINNT
+# undef _WIN32_WINNT
+#endif
+#define _WIN32_WINNT 0x0600
+
+#ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+
+#undef ERROR /* reported already defined on VS 2015 (Rich Geldreich) */
+#include <windows.h>
+#undef ERROR
+#define ERROR(name) ZSTD_ERROR(name)
+
+
+/* mutex */
+#define ZSTD_pthread_mutex_t CRITICAL_SECTION
+#define ZSTD_pthread_mutex_init(a, b) ((void)(b), InitializeCriticalSection((a)), 0)
+#define ZSTD_pthread_mutex_destroy(a) DeleteCriticalSection((a))
+#define ZSTD_pthread_mutex_lock(a) EnterCriticalSection((a))
+#define ZSTD_pthread_mutex_unlock(a) LeaveCriticalSection((a))
+
+/* condition variable */
+#define ZSTD_pthread_cond_t CONDITION_VARIABLE
+#define ZSTD_pthread_cond_init(a, b) ((void)(b), InitializeConditionVariable((a)), 0)
+#define ZSTD_pthread_cond_destroy(a) ((void)(a))
+#define ZSTD_pthread_cond_wait(a, b) SleepConditionVariableCS((a), (b), INFINITE)
+#define ZSTD_pthread_cond_signal(a) WakeConditionVariable((a))
+#define ZSTD_pthread_cond_broadcast(a) WakeAllConditionVariable((a))
+
+/* ZSTD_pthread_create() and ZSTD_pthread_join() */
+typedef struct {
+ HANDLE handle;
+ void* (*start_routine)(void*);
+ void* arg;
+} ZSTD_pthread_t;
+
+int ZSTD_pthread_create(ZSTD_pthread_t* thread, const void* unused,
+ void* (*start_routine) (void*), void* arg);
+
+int ZSTD_pthread_join(ZSTD_pthread_t thread, void** value_ptr);
+
+/**
+ * add here more wrappers as required
+ */
+
+
+#elif defined(ZSTD_MULTITHREAD) /* posix assumed ; need a better detection method */
+/* === POSIX Systems === */
+# include <pthread.h>
+
+#define ZSTD_pthread_mutex_t pthread_mutex_t
+#define ZSTD_pthread_mutex_init(a, b) pthread_mutex_init((a), (b))
+#define ZSTD_pthread_mutex_destroy(a) pthread_mutex_destroy((a))
+#define ZSTD_pthread_mutex_lock(a) pthread_mutex_lock((a))
+#define ZSTD_pthread_mutex_unlock(a) pthread_mutex_unlock((a))
+
+#define ZSTD_pthread_cond_t pthread_cond_t
+#define ZSTD_pthread_cond_init(a, b) pthread_cond_init((a), (b))
+#define ZSTD_pthread_cond_destroy(a) pthread_cond_destroy((a))
+#define ZSTD_pthread_cond_wait(a, b) pthread_cond_wait((a), (b))
+#define ZSTD_pthread_cond_signal(a) pthread_cond_signal((a))
+#define ZSTD_pthread_cond_broadcast(a) pthread_cond_broadcast((a))
+
+#define ZSTD_pthread_t pthread_t
+#define ZSTD_pthread_create(a, b, c, d) pthread_create((a), (b), (c), (d))
+#define ZSTD_pthread_join(a, b) pthread_join((a),(b))
+
+#else /* ZSTD_MULTITHREAD not defined */
+/* No multithreading support */
+
+typedef int ZSTD_pthread_mutex_t;
+#define ZSTD_pthread_mutex_init(a, b) ((void)(a), (void)(b), 0)
+#define ZSTD_pthread_mutex_destroy(a) ((void)(a))
+#define ZSTD_pthread_mutex_lock(a) ((void)(a))
+#define ZSTD_pthread_mutex_unlock(a) ((void)(a))
+
+typedef int ZSTD_pthread_cond_t;
+#define ZSTD_pthread_cond_init(a, b) ((void)(a), (void)(b), 0)
+#define ZSTD_pthread_cond_destroy(a) ((void)(a))
+#define ZSTD_pthread_cond_wait(a, b) ((void)(a), (void)(b))
+#define ZSTD_pthread_cond_signal(a) ((void)(a))
+#define ZSTD_pthread_cond_broadcast(a) ((void)(a))
+
+/* do not use ZSTD_pthread_t */
+
+#endif /* ZSTD_MULTITHREAD */
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* THREADING_H_938743 */
diff --git a/Utilities/cmzstd/lib/common/xxhash.c b/Utilities/cmzstd/lib/common/xxhash.c
new file mode 100644
index 000000000..532b81619
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/xxhash.c
@@ -0,0 +1,876 @@
+/*
+* xxHash - Fast Hash algorithm
+* Copyright (C) 2012-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 :
+* - xxHash homepage: http://www.xxhash.com
+* - xxHash source repository : https://github.com/Cyan4973/xxHash
+*/
+
+
+/* *************************************
+* Tuning parameters
+***************************************/
+/*!XXH_FORCE_MEMORY_ACCESS :
+ * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.
+ * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.
+ * The below switch allow to select different access method for improved performance.
+ * Method 0 (default) : use `memcpy()`. Safe and portable.
+ * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).
+ * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.
+ * Method 2 : direct access. This method doesn't depend on compiler but violate C standard.
+ * It can generate buggy code on targets which do not support unaligned memory accesses.
+ * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)
+ * See http://stackoverflow.com/a/32095106/646947 for details.
+ * Prefer these methods in priority order (0 > 1 > 2)
+ */
+#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */
+# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )
+# define XXH_FORCE_MEMORY_ACCESS 2
+# elif (defined(__INTEL_COMPILER) && !defined(WIN32)) || \
+ (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) ))
+# define XXH_FORCE_MEMORY_ACCESS 1
+# endif
+#endif
+
+/*!XXH_ACCEPT_NULL_INPUT_POINTER :
+ * If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer.
+ * When this option is enabled, xxHash output for null input pointers will be the same as a null-length input.
+ * By default, this option is disabled. To enable it, uncomment below define :
+ */
+/* #define XXH_ACCEPT_NULL_INPUT_POINTER 1 */
+
+/*!XXH_FORCE_NATIVE_FORMAT :
+ * By default, xxHash library provides endian-independant Hash values, based on little-endian convention.
+ * Results are therefore identical for little-endian and big-endian CPU.
+ * This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format.
+ * Should endian-independance be of no importance for your application, you may set the #define below to 1,
+ * to improve speed for Big-endian CPU.
+ * This option has no impact on Little_Endian CPU.
+ */
+#ifndef XXH_FORCE_NATIVE_FORMAT /* can be defined externally */
+# define XXH_FORCE_NATIVE_FORMAT 0
+#endif
+
+/*!XXH_FORCE_ALIGN_CHECK :
+ * This is a minor performance trick, only useful with lots of very small keys.
+ * It means : check for aligned/unaligned input.
+ * The check costs one initial branch per hash; set to 0 when the input data
+ * is guaranteed to be aligned.
+ */
+#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */
+# if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
+# define XXH_FORCE_ALIGN_CHECK 0
+# else
+# define XXH_FORCE_ALIGN_CHECK 1
+# endif
+#endif
+
+
+/* *************************************
+* Includes & Memory related functions
+***************************************/
+/* Modify the local functions below should you wish to use some other memory routines */
+/* for malloc(), free() */
+#include <stdlib.h>
+#include <stddef.h> /* size_t */
+static void* XXH_malloc(size_t s) { return malloc(s); }
+static void XXH_free (void* p) { free(p); }
+/* for memcpy() */
+#include <string.h>
+static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); }
+
+#ifndef XXH_STATIC_LINKING_ONLY
+# define XXH_STATIC_LINKING_ONLY
+#endif
+#include "xxhash.h"
+
+
+/* *************************************
+* Compiler Specific Options
+***************************************/
+#if defined (__GNUC__) || defined(__cplusplus) || defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
+# define INLINE_KEYWORD inline
+#else
+# define INLINE_KEYWORD
+#endif
+
+#if defined(__GNUC__)
+# define FORCE_INLINE_ATTR __attribute__((always_inline))
+#elif defined(_MSC_VER)
+# define FORCE_INLINE_ATTR __forceinline
+#else
+# define FORCE_INLINE_ATTR
+#endif
+
+#define FORCE_INLINE_TEMPLATE static INLINE_KEYWORD FORCE_INLINE_ATTR
+
+
+#ifdef _MSC_VER
+# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
+#endif
+
+
+/* *************************************
+* Basic Types
+***************************************/
+#ifndef MEM_MODULE
+# define MEM_MODULE
+# if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
+# include <stdint.h>
+ typedef uint8_t BYTE;
+ typedef uint16_t U16;
+ typedef uint32_t U32;
+ typedef int32_t S32;
+ typedef uint64_t U64;
+# else
+ typedef unsigned char BYTE;
+ typedef unsigned short U16;
+ typedef unsigned int U32;
+ typedef signed int S32;
+ typedef unsigned long long U64; /* if your compiler doesn't support unsigned long long, replace by another 64-bit type here. Note that xxhash.h will also need to be updated. */
+# endif
+#endif
+
+
+#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))
+
+/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */
+static U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; }
+static U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; }
+
+#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))
+
+/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
+/* currently only defined for gcc and icc */
+typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign;
+
+static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; }
+static U64 XXH_read64(const void* ptr) { return ((const unalign*)ptr)->u64; }
+
+#else
+
+/* portable and safe solution. Generally efficient.
+ * see : http://stackoverflow.com/a/32095106/646947
+ */
+
+static U32 XXH_read32(const void* memPtr)
+{
+ U32 val;
+ memcpy(&val, memPtr, sizeof(val));
+ return val;
+}
+
+static U64 XXH_read64(const void* memPtr)
+{
+ U64 val;
+ memcpy(&val, memPtr, sizeof(val));
+ return val;
+}
+
+#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */
+
+
+/* ****************************************
+* Compiler-specific Functions and Macros
+******************************************/
+#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+
+/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */
+#if defined(_MSC_VER)
+# define XXH_rotl32(x,r) _rotl(x,r)
+# define XXH_rotl64(x,r) _rotl64(x,r)
+#else
+# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r)))
+# define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r)))
+#endif
+
+#if defined(_MSC_VER) /* Visual Studio */
+# define XXH_swap32 _byteswap_ulong
+# define XXH_swap64 _byteswap_uint64
+#elif GCC_VERSION >= 403
+# define XXH_swap32 __builtin_bswap32
+# define XXH_swap64 __builtin_bswap64
+#else
+static U32 XXH_swap32 (U32 x)
+{
+ return ((x << 24) & 0xff000000 ) |
+ ((x << 8) & 0x00ff0000 ) |
+ ((x >> 8) & 0x0000ff00 ) |
+ ((x >> 24) & 0x000000ff );
+}
+static U64 XXH_swap64 (U64 x)
+{
+ return ((x << 56) & 0xff00000000000000ULL) |
+ ((x << 40) & 0x00ff000000000000ULL) |
+ ((x << 24) & 0x0000ff0000000000ULL) |
+ ((x << 8) & 0x000000ff00000000ULL) |
+ ((x >> 8) & 0x00000000ff000000ULL) |
+ ((x >> 24) & 0x0000000000ff0000ULL) |
+ ((x >> 40) & 0x000000000000ff00ULL) |
+ ((x >> 56) & 0x00000000000000ffULL);
+}
+#endif
+
+
+/* *************************************
+* Architecture Macros
+***************************************/
+typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess;
+
+/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */
+#ifndef XXH_CPU_LITTLE_ENDIAN
+ static const int g_one = 1;
+# define XXH_CPU_LITTLE_ENDIAN (*(const char*)(&g_one))
+#endif
+
+
+/* ***************************
+* Memory reads
+*****************************/
+typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment;
+
+FORCE_INLINE_TEMPLATE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align)
+{
+ if (align==XXH_unaligned)
+ return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr));
+ else
+ return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr);
+}
+
+FORCE_INLINE_TEMPLATE U32 XXH_readLE32(const void* ptr, XXH_endianess endian)
+{
+ return XXH_readLE32_align(ptr, endian, XXH_unaligned);
+}
+
+static U32 XXH_readBE32(const void* ptr)
+{
+ return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr);
+}
+
+FORCE_INLINE_TEMPLATE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align)
+{
+ if (align==XXH_unaligned)
+ return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr));
+ else
+ return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr);
+}
+
+FORCE_INLINE_TEMPLATE U64 XXH_readLE64(const void* ptr, XXH_endianess endian)
+{
+ return XXH_readLE64_align(ptr, endian, XXH_unaligned);
+}
+
+static U64 XXH_readBE64(const void* ptr)
+{
+ return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr);
+}
+
+
+/* *************************************
+* Macros
+***************************************/
+#define XXH_STATIC_ASSERT(c) { enum { XXH_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */
+
+
+/* *************************************
+* Constants
+***************************************/
+static const U32 PRIME32_1 = 2654435761U;
+static const U32 PRIME32_2 = 2246822519U;
+static const U32 PRIME32_3 = 3266489917U;
+static const U32 PRIME32_4 = 668265263U;
+static const U32 PRIME32_5 = 374761393U;
+
+static const U64 PRIME64_1 = 11400714785074694791ULL;
+static const U64 PRIME64_2 = 14029467366897019727ULL;
+static const U64 PRIME64_3 = 1609587929392839161ULL;
+static const U64 PRIME64_4 = 9650029242287828579ULL;
+static const U64 PRIME64_5 = 2870177450012600261ULL;
+
+XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; }
+
+
+/* **************************
+* Utils
+****************************/
+XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* restrict dstState, const XXH32_state_t* restrict srcState)
+{
+ memcpy(dstState, srcState, sizeof(*dstState));
+}
+
+XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* restrict dstState, const XXH64_state_t* restrict srcState)
+{
+ memcpy(dstState, srcState, sizeof(*dstState));
+}
+
+
+/* ***************************
+* Simple Hash Functions
+*****************************/
+
+static U32 XXH32_round(U32 seed, U32 input)
+{
+ seed += input * PRIME32_2;
+ seed = XXH_rotl32(seed, 13);
+ seed *= PRIME32_1;
+ return seed;
+}
+
+FORCE_INLINE_TEMPLATE U32 XXH32_endian_align(const void* input, size_t len, U32 seed, XXH_endianess endian, XXH_alignment align)
+{
+ const BYTE* p = (const BYTE*)input;
+ const BYTE* bEnd = p + len;
+ U32 h32;
+#define XXH_get32bits(p) XXH_readLE32_align(p, endian, align)
+
+#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
+ if (p==NULL) {
+ len=0;
+ bEnd=p=(const BYTE*)(size_t)16;
+ }
+#endif
+
+ if (len>=16) {
+ const BYTE* const limit = bEnd - 16;
+ U32 v1 = seed + PRIME32_1 + PRIME32_2;
+ U32 v2 = seed + PRIME32_2;
+ U32 v3 = seed + 0;
+ U32 v4 = seed - PRIME32_1;
+
+ do {
+ v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4;
+ v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4;
+ v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4;
+ v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4;
+ } while (p<=limit);
+
+ h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);
+ } else {
+ h32 = seed + PRIME32_5;
+ }
+
+ h32 += (U32) len;
+
+ while (p+4<=bEnd) {
+ h32 += XXH_get32bits(p) * PRIME32_3;
+ h32 = XXH_rotl32(h32, 17) * PRIME32_4 ;
+ p+=4;
+ }
+
+ while (p<bEnd) {
+ h32 += (*p) * PRIME32_5;
+ h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;
+ p++;
+ }
+
+ h32 ^= h32 >> 15;
+ h32 *= PRIME32_2;
+ h32 ^= h32 >> 13;
+ h32 *= PRIME32_3;
+ h32 ^= h32 >> 16;
+
+ return h32;
+}
+
+
+XXH_PUBLIC_API unsigned int XXH32 (const void* input, size_t len, unsigned int seed)
+{
+#if 0
+ /* Simple version, good for code maintenance, but unfortunately slow for small inputs */
+ XXH32_CREATESTATE_STATIC(state);
+ XXH32_reset(state, seed);
+ XXH32_update(state, input, len);
+ return XXH32_digest(state);
+#else
+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+ if (XXH_FORCE_ALIGN_CHECK) {
+ if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);
+ else
+ return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);
+ } }
+
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);
+ else
+ return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);
+#endif
+}
+
+
+static U64 XXH64_round(U64 acc, U64 input)
+{
+ acc += input * PRIME64_2;
+ acc = XXH_rotl64(acc, 31);
+ acc *= PRIME64_1;
+ return acc;
+}
+
+static U64 XXH64_mergeRound(U64 acc, U64 val)
+{
+ val = XXH64_round(0, val);
+ acc ^= val;
+ acc = acc * PRIME64_1 + PRIME64_4;
+ return acc;
+}
+
+FORCE_INLINE_TEMPLATE U64 XXH64_endian_align(const void* input, size_t len, U64 seed, XXH_endianess endian, XXH_alignment align)
+{
+ const BYTE* p = (const BYTE*)input;
+ const BYTE* const bEnd = p + len;
+ U64 h64;
+#define XXH_get64bits(p) XXH_readLE64_align(p, endian, align)
+
+#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
+ if (p==NULL) {
+ len=0;
+ bEnd=p=(const BYTE*)(size_t)32;
+ }
+#endif
+
+ if (len>=32) {
+ const BYTE* const limit = bEnd - 32;
+ U64 v1 = seed + PRIME64_1 + PRIME64_2;
+ U64 v2 = seed + PRIME64_2;
+ U64 v3 = seed + 0;
+ U64 v4 = seed - PRIME64_1;
+
+ do {
+ v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8;
+ v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8;
+ v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8;
+ v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8;
+ } while (p<=limit);
+
+ h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);
+ h64 = XXH64_mergeRound(h64, v1);
+ h64 = XXH64_mergeRound(h64, v2);
+ h64 = XXH64_mergeRound(h64, v3);
+ h64 = XXH64_mergeRound(h64, v4);
+
+ } else {
+ h64 = seed + PRIME64_5;
+ }
+
+ h64 += (U64) len;
+
+ while (p+8<=bEnd) {
+ U64 const k1 = XXH64_round(0, XXH_get64bits(p));
+ h64 ^= k1;
+ h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4;
+ p+=8;
+ }
+
+ if (p+4<=bEnd) {
+ h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1;
+ h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;
+ p+=4;
+ }
+
+ while (p<bEnd) {
+ h64 ^= (*p) * PRIME64_5;
+ h64 = XXH_rotl64(h64, 11) * PRIME64_1;
+ p++;
+ }
+
+ h64 ^= h64 >> 33;
+ h64 *= PRIME64_2;
+ h64 ^= h64 >> 29;
+ h64 *= PRIME64_3;
+ h64 ^= h64 >> 32;
+
+ return h64;
+}
+
+
+XXH_PUBLIC_API unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed)
+{
+#if 0
+ /* Simple version, good for code maintenance, but unfortunately slow for small inputs */
+ XXH64_CREATESTATE_STATIC(state);
+ XXH64_reset(state, seed);
+ XXH64_update(state, input, len);
+ return XXH64_digest(state);
+#else
+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+ if (XXH_FORCE_ALIGN_CHECK) {
+ if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);
+ else
+ return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);
+ } }
+
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);
+ else
+ return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);
+#endif
+}
+
+
+/* **************************************************
+* Advanced Hash Functions
+****************************************************/
+
+XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void)
+{
+ return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t));
+}
+XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr)
+{
+ XXH_free(statePtr);
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void)
+{
+ return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t));
+}
+XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr)
+{
+ XXH_free(statePtr);
+ return XXH_OK;
+}
+
+
+/*** Hash feed ***/
+
+XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed)
+{
+ XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */
+ memset(&state, 0, sizeof(state)-4); /* do not write into reserved, for future removal */
+ state.v1 = seed + PRIME32_1 + PRIME32_2;
+ state.v2 = seed + PRIME32_2;
+ state.v3 = seed + 0;
+ state.v4 = seed - PRIME32_1;
+ memcpy(statePtr, &state, sizeof(state));
+ return XXH_OK;
+}
+
+
+XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed)
+{
+ XXH64_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */
+ memset(&state, 0, sizeof(state)-8); /* do not write into reserved, for future removal */
+ state.v1 = seed + PRIME64_1 + PRIME64_2;
+ state.v2 = seed + PRIME64_2;
+ state.v3 = seed + 0;
+ state.v4 = seed - PRIME64_1;
+ memcpy(statePtr, &state, sizeof(state));
+ return XXH_OK;
+}
+
+
+FORCE_INLINE_TEMPLATE XXH_errorcode XXH32_update_endian (XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian)
+{
+ const BYTE* p = (const BYTE*)input;
+ const BYTE* const bEnd = p + len;
+
+#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
+ if (input==NULL) return XXH_ERROR;
+#endif
+
+ state->total_len_32 += (unsigned)len;
+ state->large_len |= (len>=16) | (state->total_len_32>=16);
+
+ if (state->memsize + len < 16) { /* fill in tmp buffer */
+ XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len);
+ state->memsize += (unsigned)len;
+ return XXH_OK;
+ }
+
+ if (state->memsize) { /* some data left from previous update */
+ XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize);
+ { const U32* p32 = state->mem32;
+ state->v1 = XXH32_round(state->v1, XXH_readLE32(p32, endian)); p32++;
+ state->v2 = XXH32_round(state->v2, XXH_readLE32(p32, endian)); p32++;
+ state->v3 = XXH32_round(state->v3, XXH_readLE32(p32, endian)); p32++;
+ state->v4 = XXH32_round(state->v4, XXH_readLE32(p32, endian)); p32++;
+ }
+ p += 16-state->memsize;
+ state->memsize = 0;
+ }
+
+ if (p <= bEnd-16) {
+ const BYTE* const limit = bEnd - 16;
+ U32 v1 = state->v1;
+ U32 v2 = state->v2;
+ U32 v3 = state->v3;
+ U32 v4 = state->v4;
+
+ do {
+ v1 = XXH32_round(v1, XXH_readLE32(p, endian)); p+=4;
+ v2 = XXH32_round(v2, XXH_readLE32(p, endian)); p+=4;
+ v3 = XXH32_round(v3, XXH_readLE32(p, endian)); p+=4;
+ v4 = XXH32_round(v4, XXH_readLE32(p, endian)); p+=4;
+ } while (p<=limit);
+
+ state->v1 = v1;
+ state->v2 = v2;
+ state->v3 = v3;
+ state->v4 = v4;
+ }
+
+ if (p < bEnd) {
+ XXH_memcpy(state->mem32, p, (size_t)(bEnd-p));
+ state->memsize = (unsigned)(bEnd-p);
+ }
+
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len)
+{
+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH32_update_endian(state_in, input, len, XXH_littleEndian);
+ else
+ return XXH32_update_endian(state_in, input, len, XXH_bigEndian);
+}
+
+
+
+FORCE_INLINE_TEMPLATE U32 XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian)
+{
+ const BYTE * p = (const BYTE*)state->mem32;
+ const BYTE* const bEnd = (const BYTE*)(state->mem32) + state->memsize;
+ U32 h32;
+
+ if (state->large_len) {
+ h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18);
+ } else {
+ h32 = state->v3 /* == seed */ + PRIME32_5;
+ }
+
+ h32 += state->total_len_32;
+
+ while (p+4<=bEnd) {
+ h32 += XXH_readLE32(p, endian) * PRIME32_3;
+ h32 = XXH_rotl32(h32, 17) * PRIME32_4;
+ p+=4;
+ }
+
+ while (p<bEnd) {
+ h32 += (*p) * PRIME32_5;
+ h32 = XXH_rotl32(h32, 11) * PRIME32_1;
+ p++;
+ }
+
+ h32 ^= h32 >> 15;
+ h32 *= PRIME32_2;
+ h32 ^= h32 >> 13;
+ h32 *= PRIME32_3;
+ h32 ^= h32 >> 16;
+
+ return h32;
+}
+
+
+XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in)
+{
+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH32_digest_endian(state_in, XXH_littleEndian);
+ else
+ return XXH32_digest_endian(state_in, XXH_bigEndian);
+}
+
+
+
+/* **** XXH64 **** */
+
+FORCE_INLINE_TEMPLATE XXH_errorcode XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian)
+{
+ const BYTE* p = (const BYTE*)input;
+ const BYTE* const bEnd = p + len;
+
+#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
+ if (input==NULL) return XXH_ERROR;
+#endif
+
+ state->total_len += len;
+
+ if (state->memsize + len < 32) { /* fill in tmp buffer */
+ XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len);
+ state->memsize += (U32)len;
+ return XXH_OK;
+ }
+
+ if (state->memsize) { /* tmp buffer is full */
+ XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize);
+ state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0, endian));
+ state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1, endian));
+ state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2, endian));
+ state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3, endian));
+ p += 32-state->memsize;
+ state->memsize = 0;
+ }
+
+ if (p+32 <= bEnd) {
+ const BYTE* const limit = bEnd - 32;
+ U64 v1 = state->v1;
+ U64 v2 = state->v2;
+ U64 v3 = state->v3;
+ U64 v4 = state->v4;
+
+ do {
+ v1 = XXH64_round(v1, XXH_readLE64(p, endian)); p+=8;
+ v2 = XXH64_round(v2, XXH_readLE64(p, endian)); p+=8;
+ v3 = XXH64_round(v3, XXH_readLE64(p, endian)); p+=8;
+ v4 = XXH64_round(v4, XXH_readLE64(p, endian)); p+=8;
+ } while (p<=limit);
+
+ state->v1 = v1;
+ state->v2 = v2;
+ state->v3 = v3;
+ state->v4 = v4;
+ }
+
+ if (p < bEnd) {
+ XXH_memcpy(state->mem64, p, (size_t)(bEnd-p));
+ state->memsize = (unsigned)(bEnd-p);
+ }
+
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len)
+{
+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH64_update_endian(state_in, input, len, XXH_littleEndian);
+ else
+ return XXH64_update_endian(state_in, input, len, XXH_bigEndian);
+}
+
+
+
+FORCE_INLINE_TEMPLATE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian)
+{
+ const BYTE * p = (const BYTE*)state->mem64;
+ const BYTE* const bEnd = (const BYTE*)state->mem64 + state->memsize;
+ U64 h64;
+
+ if (state->total_len >= 32) {
+ U64 const v1 = state->v1;
+ U64 const v2 = state->v2;
+ U64 const v3 = state->v3;
+ U64 const v4 = state->v4;
+
+ h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);
+ h64 = XXH64_mergeRound(h64, v1);
+ h64 = XXH64_mergeRound(h64, v2);
+ h64 = XXH64_mergeRound(h64, v3);
+ h64 = XXH64_mergeRound(h64, v4);
+ } else {
+ h64 = state->v3 + PRIME64_5;
+ }
+
+ h64 += (U64) state->total_len;
+
+ while (p+8<=bEnd) {
+ U64 const k1 = XXH64_round(0, XXH_readLE64(p, endian));
+ h64 ^= k1;
+ h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4;
+ p+=8;
+ }
+
+ if (p+4<=bEnd) {
+ h64 ^= (U64)(XXH_readLE32(p, endian)) * PRIME64_1;
+ h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;
+ p+=4;
+ }
+
+ while (p<bEnd) {
+ h64 ^= (*p) * PRIME64_5;
+ h64 = XXH_rotl64(h64, 11) * PRIME64_1;
+ p++;
+ }
+
+ h64 ^= h64 >> 33;
+ h64 *= PRIME64_2;
+ h64 ^= h64 >> 29;
+ h64 *= PRIME64_3;
+ h64 ^= h64 >> 32;
+
+ return h64;
+}
+
+
+XXH_PUBLIC_API unsigned long long XXH64_digest (const XXH64_state_t* state_in)
+{
+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH64_digest_endian(state_in, XXH_littleEndian);
+ else
+ return XXH64_digest_endian(state_in, XXH_bigEndian);
+}
+
+
+/* **************************
+* Canonical representation
+****************************/
+
+/*! Default XXH result types are basic unsigned 32 and 64 bits.
+* The canonical representation follows human-readable write convention, aka big-endian (large digits first).
+* These functions allow transformation of hash result into and from its canonical format.
+* This way, hash values can be written into a file or buffer, and remain comparable across different systems and programs.
+*/
+
+XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash)
+{
+ XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t));
+ if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash);
+ memcpy(dst, &hash, sizeof(*dst));
+}
+
+XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash)
+{
+ XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t));
+ if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash);
+ memcpy(dst, &hash, sizeof(*dst));
+}
+
+XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src)
+{
+ return XXH_readBE32(src);
+}
+
+XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src)
+{
+ return XXH_readBE64(src);
+}
diff --git a/Utilities/cmzstd/lib/common/xxhash.h b/Utilities/cmzstd/lib/common/xxhash.h
new file mode 100644
index 000000000..9bad1f59f
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/xxhash.h
@@ -0,0 +1,305 @@
+/*
+ xxHash - Extremely Fast Hash algorithm
+ Header File
+ Copyright (C) 2012-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 :
+ - xxHash source repository : https://github.com/Cyan4973/xxHash
+*/
+
+/* Notice extracted from xxHash homepage :
+
+xxHash is an extremely fast Hash algorithm, running at RAM speed limits.
+It also successfully passes all tests from the SMHasher suite.
+
+Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz)
+
+Name Speed Q.Score Author
+xxHash 5.4 GB/s 10
+CrapWow 3.2 GB/s 2 Andrew
+MumurHash 3a 2.7 GB/s 10 Austin Appleby
+SpookyHash 2.0 GB/s 10 Bob Jenkins
+SBox 1.4 GB/s 9 Bret Mulvey
+Lookup3 1.2 GB/s 9 Bob Jenkins
+SuperFastHash 1.2 GB/s 1 Paul Hsieh
+CityHash64 1.05 GB/s 10 Pike & Alakuijala
+FNV 0.55 GB/s 5 Fowler, Noll, Vo
+CRC32 0.43 GB/s 9
+MD5-32 0.33 GB/s 10 Ronald L. Rivest
+SHA1-32 0.28 GB/s 10
+
+Q.Score is a measure of quality of the hash function.
+It depends on successfully passing SMHasher test set.
+10 is a perfect score.
+
+A 64-bits version, named XXH64, is available since r35.
+It offers much better speed, but for 64-bits applications only.
+Name Speed on 64 bits Speed on 32 bits
+XXH64 13.8 GB/s 1.9 GB/s
+XXH32 6.8 GB/s 6.0 GB/s
+*/
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#ifndef XXHASH_H_5627135585666179
+#define XXHASH_H_5627135585666179 1
+
+
+/* ****************************
+* Definitions
+******************************/
+#include <stddef.h> /* size_t */
+typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;
+
+
+/* ****************************
+* API modifier
+******************************/
+/** XXH_PRIVATE_API
+* This is useful if you want to include xxhash functions in `static` mode
+* in order to inline them, and remove their symbol from the public list.
+* Methodology :
+* #define XXH_PRIVATE_API
+* #include "xxhash.h"
+* `xxhash.c` is automatically included.
+* It's not useful to compile and link it as a separate module anymore.
+*/
+#ifdef XXH_PRIVATE_API
+# ifndef XXH_STATIC_LINKING_ONLY
+# define XXH_STATIC_LINKING_ONLY
+# endif
+# if defined(__GNUC__)
+# define XXH_PUBLIC_API static __inline __attribute__((unused))
+# elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
+# define XXH_PUBLIC_API static inline
+# elif defined(_MSC_VER)
+# define XXH_PUBLIC_API static __inline
+# else
+# define XXH_PUBLIC_API static /* this version may generate warnings for unused static functions; disable the relevant warning */
+# endif
+#else
+# define XXH_PUBLIC_API /* do nothing */
+#endif /* XXH_PRIVATE_API */
+
+/*!XXH_NAMESPACE, aka Namespace Emulation :
+
+If you want to include _and expose_ xxHash functions from within your own library,
+but also want to avoid symbol collisions with another library which also includes xxHash,
+
+you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library
+with the value of XXH_NAMESPACE (so avoid to keep it NULL and avoid numeric values).
+
+Note that no change is required within the calling program as long as it includes `xxhash.h` :
+regular symbol name will be automatically translated by this header.
+*/
+#ifdef XXH_NAMESPACE
+# define XXH_CAT(A,B) A##B
+# define XXH_NAME2(A,B) XXH_CAT(A,B)
+# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32)
+# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64)
+# define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber)
+# define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState)
+# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState)
+# define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState)
+# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState)
+# define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset)
+# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset)
+# define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update)
+# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update)
+# define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest)
+# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest)
+# define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState)
+# define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState)
+# define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash)
+# define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash)
+# define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical)
+# define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical)
+#endif
+
+
+/* *************************************
+* Version
+***************************************/
+#define XXH_VERSION_MAJOR 0
+#define XXH_VERSION_MINOR 6
+#define XXH_VERSION_RELEASE 2
+#define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE)
+XXH_PUBLIC_API unsigned XXH_versionNumber (void);
+
+
+/* ****************************
+* Simple Hash Functions
+******************************/
+typedef unsigned int XXH32_hash_t;
+typedef unsigned long long XXH64_hash_t;
+
+XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, unsigned int seed);
+XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, unsigned long long seed);
+
+/*!
+XXH32() :
+ Calculate the 32-bits hash of sequence "length" bytes stored at memory address "input".
+ The memory between input & input+length must be valid (allocated and read-accessible).
+ "seed" can be used to alter the result predictably.
+ Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s
+XXH64() :
+ Calculate the 64-bits hash of sequence of length "len" stored at memory address "input".
+ "seed" can be used to alter the result predictably.
+ This function runs 2x faster on 64-bits systems, but slower on 32-bits systems (see benchmark).
+*/
+
+
+/* ****************************
+* Streaming Hash Functions
+******************************/
+typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */
+typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */
+
+/*! State allocation, compatible with dynamic libraries */
+
+XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void);
+XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr);
+
+XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void);
+XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr);
+
+
+/* hash streaming */
+
+XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, unsigned int seed);
+XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length);
+XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr);
+
+XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, unsigned long long seed);
+XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length);
+XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr);
+
+/*
+These functions generate the xxHash of an input provided in multiple segments.
+Note that, for small input, they are slower than single-call functions, due to state management.
+For small input, prefer `XXH32()` and `XXH64()` .
+
+XXH state must first be allocated, using XXH*_createState() .
+
+Start a new hash by initializing state with a seed, using XXH*_reset().
+
+Then, feed the hash state by calling XXH*_update() as many times as necessary.
+Obviously, input must be allocated and read accessible.
+The function returns an error code, with 0 meaning OK, and any other value meaning there is an error.
+
+Finally, a hash value can be produced anytime, by using XXH*_digest().
+This function returns the nn-bits hash as an int or long long.
+
+It's still possible to continue inserting input into the hash state after a digest,
+and generate some new hashes later on, by calling again XXH*_digest().
+
+When done, free XXH state space if it was allocated dynamically.
+*/
+
+
+/* **************************
+* Utils
+****************************/
+#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) /* ! C99 */
+# define restrict /* disable restrict */
+#endif
+
+XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* restrict dst_state, const XXH32_state_t* restrict src_state);
+XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* restrict dst_state, const XXH64_state_t* restrict src_state);
+
+
+/* **************************
+* Canonical representation
+****************************/
+/* Default result type for XXH functions are primitive unsigned 32 and 64 bits.
+* The canonical representation uses human-readable write convention, aka big-endian (large digits first).
+* These functions allow transformation of hash result into and from its canonical format.
+* This way, hash values can be written into a file / memory, and remain comparable on different systems and programs.
+*/
+typedef struct { unsigned char digest[4]; } XXH32_canonical_t;
+typedef struct { unsigned char digest[8]; } XXH64_canonical_t;
+
+XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash);
+XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash);
+
+XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src);
+XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src);
+
+#endif /* XXHASH_H_5627135585666179 */
+
+
+
+/* ================================================================================================
+ This section contains definitions which are not guaranteed to remain stable.
+ They may change in future versions, becoming incompatible with a different version of the library.
+ They shall only be used with static linking.
+ Never use these definitions in association with dynamic linking !
+=================================================================================================== */
+#if defined(XXH_STATIC_LINKING_ONLY) && !defined(XXH_STATIC_H_3543687687345)
+#define XXH_STATIC_H_3543687687345
+
+/* These definitions are only meant to allow allocation of XXH state
+ statically, on stack, or in a struct for example.
+ Do not use members directly. */
+
+ struct XXH32_state_s {
+ unsigned total_len_32;
+ unsigned large_len;
+ unsigned v1;
+ unsigned v2;
+ unsigned v3;
+ unsigned v4;
+ unsigned mem32[4]; /* buffer defined as U32 for alignment */
+ unsigned memsize;
+ unsigned reserved; /* never read nor write, will be removed in a future version */
+ }; /* typedef'd to XXH32_state_t */
+
+ struct XXH64_state_s {
+ unsigned long long total_len;
+ unsigned long long v1;
+ unsigned long long v2;
+ unsigned long long v3;
+ unsigned long long v4;
+ unsigned long long mem64[4]; /* buffer defined as U64 for alignment */
+ unsigned memsize;
+ unsigned reserved[2]; /* never read nor write, will be removed in a future version */
+ }; /* typedef'd to XXH64_state_t */
+
+
+# ifdef XXH_PRIVATE_API
+# include "xxhash.c" /* include xxhash functions as `static`, for inlining */
+# endif
+
+#endif /* XXH_STATIC_LINKING_ONLY && XXH_STATIC_H_3543687687345 */
+
+
+#if defined (__cplusplus)
+}
+#endif
diff --git a/Utilities/cmzstd/lib/common/zstd_common.c b/Utilities/cmzstd/lib/common/zstd_common.c
new file mode 100644
index 000000000..667f4a27f
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/zstd_common.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+
+
+/*-*************************************
+* Dependencies
+***************************************/
+#include <stdlib.h> /* malloc, calloc, free */
+#include <string.h> /* memset */
+#include "error_private.h"
+#include "zstd_internal.h"
+
+
+/*-****************************************
+* Version
+******************************************/
+unsigned ZSTD_versionNumber(void) { return ZSTD_VERSION_NUMBER; }
+
+const char* ZSTD_versionString(void) { return ZSTD_VERSION_STRING; }
+
+
+/*-****************************************
+* ZSTD Error Management
+******************************************/
+#undef ZSTD_isError /* defined within zstd_internal.h */
+/*! ZSTD_isError() :
+ * tells if a return value is an error code
+ * symbol is required for external callers */
+unsigned ZSTD_isError(size_t code) { return ERR_isError(code); }
+
+/*! ZSTD_getErrorName() :
+ * provides error code string from function result (useful for debugging) */
+const char* ZSTD_getErrorName(size_t code) { return ERR_getErrorName(code); }
+
+/*! ZSTD_getError() :
+ * convert a `size_t` function result into a proper ZSTD_errorCode enum */
+ZSTD_ErrorCode ZSTD_getErrorCode(size_t code) { return ERR_getErrorCode(code); }
+
+/*! ZSTD_getErrorString() :
+ * provides error code string from enum */
+const char* ZSTD_getErrorString(ZSTD_ErrorCode code) { return ERR_getErrorString(code); }
+
+
+
+/*=**************************************************************
+* Custom allocator
+****************************************************************/
+void* ZSTD_malloc(size_t size, ZSTD_customMem customMem)
+{
+ if (customMem.customAlloc)
+ return customMem.customAlloc(customMem.opaque, size);
+ return malloc(size);
+}
+
+void* ZSTD_calloc(size_t size, ZSTD_customMem customMem)
+{
+ if (customMem.customAlloc) {
+ /* calloc implemented as malloc+memset;
+ * not as efficient as calloc, but next best guess for custom malloc */
+ void* const ptr = customMem.customAlloc(customMem.opaque, size);
+ memset(ptr, 0, size);
+ return ptr;
+ }
+ return calloc(1, size);
+}
+
+void ZSTD_free(void* ptr, ZSTD_customMem customMem)
+{
+ if (ptr!=NULL) {
+ if (customMem.customFree)
+ customMem.customFree(customMem.opaque, ptr);
+ else
+ free(ptr);
+ }
+}
diff --git a/Utilities/cmzstd/lib/common/zstd_errors.h b/Utilities/cmzstd/lib/common/zstd_errors.h
new file mode 100644
index 000000000..92a343389
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/zstd_errors.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#ifndef ZSTD_ERRORS_H_398273423
+#define ZSTD_ERRORS_H_398273423
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+/*===== dependency =====*/
+#include <stddef.h> /* size_t */
+
+
+/* ===== ZSTDERRORLIB_API : control library symbols visibility ===== */
+#ifndef ZSTDERRORLIB_VISIBILITY
+# if defined(__GNUC__) && (__GNUC__ >= 4)
+# define ZSTDERRORLIB_VISIBILITY __attribute__ ((visibility ("default")))
+# else
+# define ZSTDERRORLIB_VISIBILITY
+# endif
+#endif
+#if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1)
+# define ZSTDERRORLIB_API __declspec(dllexport) ZSTDERRORLIB_VISIBILITY
+#elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1)
+# define ZSTDERRORLIB_API __declspec(dllimport) ZSTDERRORLIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
+#else
+# define ZSTDERRORLIB_API ZSTDERRORLIB_VISIBILITY
+#endif
+
+/*-*********************************************
+ * Error codes list
+ *-*********************************************
+ * Error codes _values_ are pinned down since v1.3.1 only.
+ * Therefore, don't rely on values if you may link to any version < v1.3.1.
+ *
+ * Only values < 100 are considered stable.
+ *
+ * note 1 : this API shall be used with static linking only.
+ * dynamic linking is not yet officially supported.
+ * note 2 : Prefer relying on the enum than on its value whenever possible
+ * This is the only supported way to use the error list < v1.3.1
+ * note 3 : ZSTD_isError() is always correct, whatever the library version.
+ **********************************************/
+typedef enum {
+ ZSTD_error_no_error = 0,
+ ZSTD_error_GENERIC = 1,
+ ZSTD_error_prefix_unknown = 10,
+ ZSTD_error_version_unsupported = 12,
+ ZSTD_error_frameParameter_unsupported = 14,
+ ZSTD_error_frameParameter_windowTooLarge = 16,
+ ZSTD_error_corruption_detected = 20,
+ ZSTD_error_checksum_wrong = 22,
+ ZSTD_error_dictionary_corrupted = 30,
+ ZSTD_error_dictionary_wrong = 32,
+ ZSTD_error_dictionaryCreation_failed = 34,
+ ZSTD_error_parameter_unsupported = 40,
+ ZSTD_error_parameter_outOfBound = 42,
+ ZSTD_error_tableLog_tooLarge = 44,
+ ZSTD_error_maxSymbolValue_tooLarge = 46,
+ ZSTD_error_maxSymbolValue_tooSmall = 48,
+ ZSTD_error_stage_wrong = 60,
+ ZSTD_error_init_missing = 62,
+ ZSTD_error_memory_allocation = 64,
+ ZSTD_error_workSpace_tooSmall= 66,
+ ZSTD_error_dstSize_tooSmall = 70,
+ ZSTD_error_srcSize_wrong = 72,
+ ZSTD_error_dstBuffer_null = 74,
+ /* following error codes are __NOT STABLE__, they can be removed or changed in future versions */
+ ZSTD_error_frameIndex_tooLarge = 100,
+ ZSTD_error_seekableIO = 102,
+ ZSTD_error_maxCode = 120 /* never EVER use this value directly, it can change in future versions! Use ZSTD_isError() instead */
+} ZSTD_ErrorCode;
+
+/*! ZSTD_getErrorCode() :
+ convert a `size_t` function result into a `ZSTD_ErrorCode` enum type,
+ which can be used to compare with enum list published above */
+ZSTDERRORLIB_API ZSTD_ErrorCode ZSTD_getErrorCode(size_t functionResult);
+ZSTDERRORLIB_API const char* ZSTD_getErrorString(ZSTD_ErrorCode code); /**< Same as ZSTD_getErrorName, but using a `ZSTD_ErrorCode` enum argument */
+
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* ZSTD_ERRORS_H_398273423 */
diff --git a/Utilities/cmzstd/lib/common/zstd_internal.h b/Utilities/cmzstd/lib/common/zstd_internal.h
new file mode 100644
index 000000000..edeb74b9c
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/zstd_internal.h
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#ifndef ZSTD_CCOMMON_H_MODULE
+#define ZSTD_CCOMMON_H_MODULE
+
+/* this module contains definitions which must be identical
+ * across compression, decompression and dictBuilder.
+ * It also contains a few functions useful to at least 2 of them
+ * and which benefit from being inlined */
+
+/*-*************************************
+* Dependencies
+***************************************/
+#include "compiler.h"
+#include "mem.h"
+#include "debug.h" /* assert, DEBUGLOG, RAWLOG, g_debuglevel */
+#include "error_private.h"
+#define ZSTD_STATIC_LINKING_ONLY
+#include "zstd.h"
+#define FSE_STATIC_LINKING_ONLY
+#include "fse.h"
+#define HUF_STATIC_LINKING_ONLY
+#include "huf.h"
+#ifndef XXH_STATIC_LINKING_ONLY
+# define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */
+#endif
+#include "xxhash.h" /* XXH_reset, update, digest */
+
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+/* ---- static assert (debug) --- */
+#define ZSTD_STATIC_ASSERT(c) DEBUG_STATIC_ASSERT(c)
+#define ZSTD_isError ERR_isError /* for inlining */
+#define FSE_isError ERR_isError
+#define HUF_isError ERR_isError
+
+
+/*-*************************************
+* shared macros
+***************************************/
+#undef MIN
+#undef MAX
+#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 */
+
+
+/*-*************************************
+* Common constants
+***************************************/
+#define ZSTD_OPT_NUM (1<<12)
+
+#define ZSTD_REP_NUM 3 /* number of repcodes */
+#define ZSTD_REP_MOVE (ZSTD_REP_NUM-1)
+static const U32 repStartValue[ZSTD_REP_NUM] = { 1, 4, 8 };
+
+#define KB *(1 <<10)
+#define MB *(1 <<20)
+#define GB *(1U<<30)
+
+#define BIT7 128
+#define BIT6 64
+#define BIT5 32
+#define BIT4 16
+#define BIT1 2
+#define BIT0 1
+
+#define ZSTD_WINDOWLOG_ABSOLUTEMIN 10
+static const size_t ZSTD_fcs_fieldSize[4] = { 0, 2, 4, 8 };
+static const size_t ZSTD_did_fieldSize[4] = { 0, 1, 2, 4 };
+
+#define ZSTD_FRAMEIDSIZE 4 /* magic number size */
+
+#define ZSTD_BLOCKHEADERSIZE 3 /* C standard doesn't allow `static const` variable to be init using another `static const` variable */
+static const size_t ZSTD_blockHeaderSize = ZSTD_BLOCKHEADERSIZE;
+typedef enum { bt_raw, bt_rle, bt_compressed, bt_reserved } blockType_e;
+
+#define MIN_SEQUENCES_SIZE 1 /* nbSeq==0 */
+#define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */ + MIN_SEQUENCES_SIZE /* nbSeq==0 */) /* for a non-null block */
+
+#define HufLog 12
+typedef enum { set_basic, set_rle, set_compressed, set_repeat } symbolEncodingType_e;
+
+#define LONGNBSEQ 0x7F00
+
+#define MINMATCH 3
+
+#define Litbits 8
+#define MaxLit ((1<<Litbits) - 1)
+#define MaxML 52
+#define MaxLL 35
+#define DefaultMaxOff 28
+#define MaxOff 31
+#define MaxSeq MAX(MaxLL, MaxML) /* Assumption : MaxOff < MaxLL,MaxML */
+#define MLFSELog 9
+#define LLFSELog 9
+#define OffFSELog 8
+#define MaxFSELog MAX(MAX(MLFSELog, LLFSELog), OffFSELog)
+
+static const U32 LL_bits[MaxLL+1] = { 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 2, 2, 3, 3,
+ 4, 6, 7, 8, 9,10,11,12,
+ 13,14,15,16 };
+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 };
+#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,
+ 1, 1, 1, 1, 2, 2, 3, 3,
+ 4, 4, 5, 7, 8, 9,10,11,
+ 12,13,14,15,16 };
+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,-1,-1,-1,-1 };
+#define ML_DEFAULTNORMLOG 6 /* for static allocation */
+static const U32 ML_defaultNormLog = ML_DEFAULTNORMLOG;
+
+static const S16 OF_defaultNorm[DefaultMaxOff+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 };
+#define OF_DEFAULTNORMLOG 5 /* for static allocation */
+static const U32 OF_defaultNormLog = OF_DEFAULTNORMLOG;
+
+
+/*-*******************************************
+* Shared functions to include for inlining
+*********************************************/
+static void ZSTD_copy8(void* dst, const void* src) { memcpy(dst, src, 8); }
+#define COPY8(d,s) { ZSTD_copy8(d,s); d+=8; s+=8; }
+
+/*! ZSTD_wildcopy() :
+ * custom version of memcpy(), can overwrite up to WILDCOPY_OVERLENGTH bytes (if length==0) */
+#define WILDCOPY_OVERLENGTH 8
+MEM_STATIC void ZSTD_wildcopy(void* dst, const void* src, ptrdiff_t length)
+{
+ const BYTE* ip = (const BYTE*)src;
+ BYTE* op = (BYTE*)dst;
+ BYTE* const oend = op + length;
+ do
+ COPY8(op, ip)
+ while (op < oend);
+}
+
+MEM_STATIC void ZSTD_wildcopy_e(void* dst, const void* src, void* dstEnd) /* should be faster for decoding, but strangely, not verified on all platform */
+{
+ const BYTE* ip = (const BYTE*)src;
+ BYTE* op = (BYTE*)dst;
+ BYTE* const oend = (BYTE*)dstEnd;
+ do
+ COPY8(op, ip)
+ while (op < oend);
+}
+
+
+/*-*******************************************
+* Private declarations
+*********************************************/
+typedef struct seqDef_s {
+ U32 offset;
+ U16 litLength;
+ U16 matchLength;
+} seqDef;
+
+typedef struct {
+ seqDef* sequencesStart;
+ seqDef* sequences;
+ BYTE* litStart;
+ BYTE* lit;
+ BYTE* llCode;
+ BYTE* mlCode;
+ BYTE* ofCode;
+ size_t maxNbSeq;
+ size_t maxNbLit;
+ U32 longLengthID; /* 0 == no longLength; 1 == Lit.longLength; 2 == Match.longLength; */
+ U32 longLengthPos;
+} seqStore_t;
+
+const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx); /* compress & dictBuilder */
+void ZSTD_seqToCodes(const seqStore_t* seqStorePtr); /* compress, dictBuilder, decodeCorpus (shouldn't get its definition from here) */
+
+/* custom memory allocation functions */
+void* ZSTD_malloc(size_t size, ZSTD_customMem customMem);
+void* ZSTD_calloc(size_t size, ZSTD_customMem customMem);
+void ZSTD_free(void* ptr, ZSTD_customMem customMem);
+
+
+MEM_STATIC U32 ZSTD_highbit32(U32 val) /* compress, dictBuilder, decodeCorpus */
+{
+ assert(val != 0);
+ {
+# if defined(_MSC_VER) /* Visual */
+ unsigned long r=0;
+ _BitScanReverse(&r, val);
+ return (unsigned)r;
+# elif defined(__GNUC__) && (__GNUC__ >= 3) /* GCC Intrinsic */
+ return 31 - __builtin_clz(val);
+# else /* Software version */
+ static const U32 DeBruijnClz[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 };
+ U32 v = val;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ return DeBruijnClz[(v * 0x07C4ACDDU) >> 27];
+# endif
+ }
+}
+
+
+/* ZSTD_invalidateRepCodes() :
+ * ensures next compression will not use repcodes from previous block.
+ * Note : only works with regular variant;
+ * do not use with extDict variant ! */
+void ZSTD_invalidateRepCodes(ZSTD_CCtx* cctx); /* zstdmt, adaptive_compression (shouldn't get this definition from here) */
+
+
+typedef struct {
+ blockType_e blockType;
+ U32 lastBlock;
+ U32 origSize;
+} blockProperties_t; /* declared here for decompress and fullbench */
+
+/*! ZSTD_getcBlockSize() :
+ * Provides the size of compressed block from block header `src` */
+/* Used by: decompress, fullbench (does not get its definition from here) */
+size_t ZSTD_getcBlockSize(const void* src, size_t srcSize,
+ blockProperties_t* bpPtr);
+
+/*! ZSTD_decodeSeqHeaders() :
+ * decode sequence header from src */
+/* Used by: decompress, fullbench (does not get its definition from here) */
+size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr,
+ const void* src, size_t srcSize);
+
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* ZSTD_CCOMMON_H_MODULE */
diff --git a/Utilities/cmzstd/lib/compress/fse_compress.c b/Utilities/cmzstd/lib/compress/fse_compress.c
new file mode 100644
index 000000000..60f357bbd
--- /dev/null
+++ b/Utilities/cmzstd/lib/compress/fse_compress.c
@@ -0,0 +1,721 @@
+/* ******************************************************************
+ FSE : Finite State Entropy encoder
+ Copyright (C) 2013-present, 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 :
+ - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy
+ - Public forum : https://groups.google.com/forum/#!forum/lz4c
+****************************************************************** */
+
+/* **************************************************************
+* Includes
+****************************************************************/
+#include <stdlib.h> /* malloc, free, qsort */
+#include <string.h> /* memcpy, memset */
+#include "compiler.h"
+#include "mem.h" /* U32, U16, etc. */
+#include "debug.h" /* assert, DEBUGLOG */
+#include "hist.h" /* HIST_count_wksp */
+#include "bitstream.h"
+#define FSE_STATIC_LINKING_ONLY
+#include "fse.h"
+#include "error_private.h"
+
+
+/* **************************************************************
+* Error Management
+****************************************************************/
+#define FSE_isError ERR_isError
+
+
+/* **************************************************************
+* Templates
+****************************************************************/
+/*
+ designed to be included
+ for type-specific functions (template emulation in C)
+ Objective is to write these functions only once, for improved maintenance
+*/
+
+/* safety checks */
+#ifndef FSE_FUNCTION_EXTENSION
+# error "FSE_FUNCTION_EXTENSION must be defined"
+#endif
+#ifndef FSE_FUNCTION_TYPE
+# error "FSE_FUNCTION_TYPE must be defined"
+#endif
+
+/* Function names */
+#define FSE_CAT(X,Y) X##Y
+#define FSE_FUNCTION_NAME(X,Y) FSE_CAT(X,Y)
+#define FSE_TYPE_NAME(X,Y) FSE_CAT(X,Y)
+
+
+/* Function templates */
+
+/* FSE_buildCTable_wksp() :
+ * Same as FSE_buildCTable(), but using an externally allocated scratch buffer (`workSpace`).
+ * wkspSize should be sized to handle worst case situation, which is `1<<max_tableLog * sizeof(FSE_FUNCTION_TYPE)`
+ * workSpace must also be properly aligned with FSE_FUNCTION_TYPE requirements
+ */
+size_t FSE_buildCTable_wksp(FSE_CTable* ct,
+ const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog,
+ void* workSpace, size_t wkspSize)
+{
+ U32 const tableSize = 1 << tableLog;
+ U32 const tableMask = tableSize - 1;
+ void* const ptr = ct;
+ U16* const tableU16 = ( (U16*) ptr) + 2;
+ void* const FSCT = ((U32*)ptr) + 1 /* header */ + (tableLog ? tableSize>>1 : 1) ;
+ FSE_symbolCompressionTransform* const symbolTT = (FSE_symbolCompressionTransform*) (FSCT);
+ U32 const step = FSE_TABLESTEP(tableSize);
+ U32 cumul[FSE_MAX_SYMBOL_VALUE+2];
+
+ FSE_FUNCTION_TYPE* const tableSymbol = (FSE_FUNCTION_TYPE*)workSpace;
+ U32 highThreshold = tableSize-1;
+
+ /* CTable header */
+ if (((size_t)1 << tableLog) * sizeof(FSE_FUNCTION_TYPE) > wkspSize) return ERROR(tableLog_tooLarge);
+ tableU16[-2] = (U16) tableLog;
+ tableU16[-1] = (U16) maxSymbolValue;
+ assert(tableLog < 16); /* required for threshold strategy to work */
+
+ /* For explanations on how to distribute symbol values over the table :
+ * http://fastcompression.blogspot.fr/2014/02/fse-distributing-symbol-values.html */
+
+ #ifdef __clang_analyzer__
+ memset(tableSymbol, 0, sizeof(*tableSymbol) * tableSize); /* useless initialization, just to keep scan-build happy */
+ #endif
+
+ /* symbol start positions */
+ { U32 u;
+ cumul[0] = 0;
+ for (u=1; u <= maxSymbolValue+1; u++) {
+ if (normalizedCounter[u-1]==-1) { /* Low proba symbol */
+ cumul[u] = cumul[u-1] + 1;
+ tableSymbol[highThreshold--] = (FSE_FUNCTION_TYPE)(u-1);
+ } else {
+ cumul[u] = cumul[u-1] + normalizedCounter[u-1];
+ } }
+ cumul[maxSymbolValue+1] = tableSize+1;
+ }
+
+ /* Spread symbols */
+ { U32 position = 0;
+ U32 symbol;
+ for (symbol=0; symbol<=maxSymbolValue; symbol++) {
+ int nbOccurences;
+ int const freq = normalizedCounter[symbol];
+ for (nbOccurences=0; nbOccurences<freq; nbOccurences++) {
+ tableSymbol[position] = (FSE_FUNCTION_TYPE)symbol;
+ position = (position + step) & tableMask;
+ while (position > highThreshold)
+ position = (position + step) & tableMask; /* Low proba area */
+ } }
+
+ assert(position==0); /* Must have initialized all positions */
+ }
+
+ /* Build table */
+ { U32 u; for (u=0; u<tableSize; u++) {
+ FSE_FUNCTION_TYPE s = tableSymbol[u]; /* note : static analyzer may not understand tableSymbol is properly initialized */
+ tableU16[cumul[s]++] = (U16) (tableSize+u); /* TableU16 : sorted by symbol order; gives next state value */
+ } }
+
+ /* Build Symbol Transformation Table */
+ { unsigned total = 0;
+ unsigned s;
+ for (s=0; s<=maxSymbolValue; s++) {
+ switch (normalizedCounter[s])
+ {
+ case 0:
+ /* filling nonetheless, for compatibility with FSE_getMaxNbBits() */
+ symbolTT[s].deltaNbBits = ((tableLog+1) << 16) - (1<<tableLog);
+ break;
+
+ case -1:
+ case 1:
+ symbolTT[s].deltaNbBits = (tableLog << 16) - (1<<tableLog);
+ symbolTT[s].deltaFindState = total - 1;
+ total ++;
+ break;
+ default :
+ {
+ U32 const maxBitsOut = tableLog - BIT_highbit32 (normalizedCounter[s]-1);
+ U32 const minStatePlus = normalizedCounter[s] << maxBitsOut;
+ symbolTT[s].deltaNbBits = (maxBitsOut << 16) - minStatePlus;
+ symbolTT[s].deltaFindState = total - normalizedCounter[s];
+ total += normalizedCounter[s];
+ } } } }
+
+#if 0 /* debug : symbol costs */
+ DEBUGLOG(5, "\n --- table statistics : ");
+ { U32 symbol;
+ for (symbol=0; symbol<=maxSymbolValue; symbol++) {
+ DEBUGLOG(5, "%3u: w=%3i, maxBits=%u, fracBits=%.2f",
+ symbol, normalizedCounter[symbol],
+ FSE_getMaxNbBits(symbolTT, symbol),
+ (double)FSE_bitCost(symbolTT, tableLog, symbol, 8) / 256);
+ }
+ }
+#endif
+
+ return 0;
+}
+
+
+size_t FSE_buildCTable(FSE_CTable* ct, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog)
+{
+ FSE_FUNCTION_TYPE tableSymbol[FSE_MAX_TABLESIZE]; /* memset() is not necessary, even if static analyzer complain about it */
+ return FSE_buildCTable_wksp(ct, normalizedCounter, maxSymbolValue, tableLog, tableSymbol, sizeof(tableSymbol));
+}
+
+
+
+#ifndef FSE_COMMONDEFS_ONLY
+
+
+/*-**************************************************************
+* FSE NCount encoding
+****************************************************************/
+size_t FSE_NCountWriteBound(unsigned maxSymbolValue, unsigned tableLog)
+{
+ size_t const maxHeaderSize = (((maxSymbolValue+1) * tableLog) >> 3) + 3;
+ return maxSymbolValue ? maxHeaderSize : FSE_NCOUNTBOUND; /* maxSymbolValue==0 ? use default */
+}
+
+static size_t
+FSE_writeNCount_generic (void* header, size_t headerBufferSize,
+ const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog,
+ unsigned writeIsSafe)
+{
+ BYTE* const ostart = (BYTE*) header;
+ BYTE* out = ostart;
+ BYTE* const oend = ostart + headerBufferSize;
+ int nbBits;
+ const int tableSize = 1 << tableLog;
+ int remaining;
+ int threshold;
+ U32 bitStream = 0;
+ int bitCount = 0;
+ unsigned symbol = 0;
+ unsigned const alphabetSize = maxSymbolValue + 1;
+ int previousIs0 = 0;
+
+ /* Table Size */
+ bitStream += (tableLog-FSE_MIN_TABLELOG) << bitCount;
+ bitCount += 4;
+
+ /* Init */
+ remaining = tableSize+1; /* +1 for extra accuracy */
+ threshold = tableSize;
+ nbBits = tableLog+1;
+
+ while ((symbol < alphabetSize) && (remaining>1)) { /* stops at 1 */
+ if (previousIs0) {
+ unsigned start = symbol;
+ while ((symbol < alphabetSize) && !normalizedCounter[symbol]) symbol++;
+ if (symbol == alphabetSize) break; /* incorrect distribution */
+ while (symbol >= start+24) {
+ start+=24;
+ bitStream += 0xFFFFU << bitCount;
+ if ((!writeIsSafe) && (out > oend-2))
+ return ERROR(dstSize_tooSmall); /* Buffer overflow */
+ out[0] = (BYTE) bitStream;
+ out[1] = (BYTE)(bitStream>>8);
+ out+=2;
+ bitStream>>=16;
+ }
+ while (symbol >= start+3) {
+ start+=3;
+ bitStream += 3 << bitCount;
+ bitCount += 2;
+ }
+ bitStream += (symbol-start) << bitCount;
+ bitCount += 2;
+ if (bitCount>16) {
+ if ((!writeIsSafe) && (out > oend - 2))
+ return ERROR(dstSize_tooSmall); /* Buffer overflow */
+ out[0] = (BYTE)bitStream;
+ out[1] = (BYTE)(bitStream>>8);
+ out += 2;
+ bitStream >>= 16;
+ bitCount -= 16;
+ } }
+ { int count = normalizedCounter[symbol++];
+ int const max = (2*threshold-1) - remaining;
+ remaining -= count < 0 ? -count : count;
+ count++; /* +1 for extra accuracy */
+ if (count>=threshold)
+ count += max; /* [0..max[ [max..threshold[ (...) [threshold+max 2*threshold[ */
+ bitStream += count << bitCount;
+ bitCount += nbBits;
+ bitCount -= (count<max);
+ previousIs0 = (count==1);
+ if (remaining<1) return ERROR(GENERIC);
+ while (remaining<threshold) { nbBits--; threshold>>=1; }
+ }
+ if (bitCount>16) {
+ if ((!writeIsSafe) && (out > oend - 2))
+ return ERROR(dstSize_tooSmall); /* Buffer overflow */
+ out[0] = (BYTE)bitStream;
+ out[1] = (BYTE)(bitStream>>8);
+ out += 2;
+ bitStream >>= 16;
+ bitCount -= 16;
+ } }
+
+ if (remaining != 1)
+ return ERROR(GENERIC); /* incorrect normalized distribution */
+ assert(symbol <= alphabetSize);
+
+ /* flush remaining bitStream */
+ if ((!writeIsSafe) && (out > oend - 2))
+ return ERROR(dstSize_tooSmall); /* Buffer overflow */
+ out[0] = (BYTE)bitStream;
+ out[1] = (BYTE)(bitStream>>8);
+ out+= (bitCount+7) /8;
+
+ return (out-ostart);
+}
+
+
+size_t FSE_writeNCount (void* buffer, size_t bufferSize,
+ const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog)
+{
+ if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge); /* Unsupported */
+ if (tableLog < FSE_MIN_TABLELOG) return ERROR(GENERIC); /* Unsupported */
+
+ if (bufferSize < FSE_NCountWriteBound(maxSymbolValue, tableLog))
+ return FSE_writeNCount_generic(buffer, bufferSize, normalizedCounter, maxSymbolValue, tableLog, 0);
+
+ return FSE_writeNCount_generic(buffer, bufferSize, normalizedCounter, maxSymbolValue, tableLog, 1 /* write in buffer is safe */);
+}
+
+
+/*-**************************************************************
+* FSE Compression Code
+****************************************************************/
+
+FSE_CTable* FSE_createCTable (unsigned maxSymbolValue, unsigned tableLog)
+{
+ size_t size;
+ if (tableLog > FSE_TABLELOG_ABSOLUTE_MAX) tableLog = FSE_TABLELOG_ABSOLUTE_MAX;
+ size = FSE_CTABLE_SIZE_U32 (tableLog, maxSymbolValue) * sizeof(U32);
+ return (FSE_CTable*)malloc(size);
+}
+
+void FSE_freeCTable (FSE_CTable* ct) { free(ct); }
+
+/* provides the minimum logSize to safely represent a distribution */
+static unsigned FSE_minTableLog(size_t srcSize, unsigned maxSymbolValue)
+{
+ U32 minBitsSrc = BIT_highbit32((U32)(srcSize)) + 1;
+ U32 minBitsSymbols = BIT_highbit32(maxSymbolValue) + 2;
+ U32 minBits = minBitsSrc < minBitsSymbols ? minBitsSrc : minBitsSymbols;
+ assert(srcSize > 1); /* Not supported, RLE should be used instead */
+ return minBits;
+}
+
+unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus)
+{
+ U32 maxBitsSrc = BIT_highbit32((U32)(srcSize - 1)) - minus;
+ U32 tableLog = maxTableLog;
+ U32 minBits = FSE_minTableLog(srcSize, maxSymbolValue);
+ assert(srcSize > 1); /* Not supported, RLE should be used instead */
+ if (tableLog==0) tableLog = FSE_DEFAULT_TABLELOG;
+ if (maxBitsSrc < tableLog) tableLog = maxBitsSrc; /* Accuracy can be reduced */
+ if (minBits > tableLog) tableLog = minBits; /* Need a minimum to safely represent all symbol values */
+ if (tableLog < FSE_MIN_TABLELOG) tableLog = FSE_MIN_TABLELOG;
+ if (tableLog > FSE_MAX_TABLELOG) tableLog = FSE_MAX_TABLELOG;
+ return tableLog;
+}
+
+unsigned FSE_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue)
+{
+ return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 2);
+}
+
+
+/* Secondary normalization method.
+ To be used when primary method fails. */
+
+static size_t FSE_normalizeM2(short* norm, U32 tableLog, const unsigned* count, size_t total, U32 maxSymbolValue)
+{
+ short const NOT_YET_ASSIGNED = -2;
+ U32 s;
+ U32 distributed = 0;
+ U32 ToDistribute;
+
+ /* Init */
+ U32 const lowThreshold = (U32)(total >> tableLog);
+ U32 lowOne = (U32)((total * 3) >> (tableLog + 1));
+
+ for (s=0; s<=maxSymbolValue; s++) {
+ if (count[s] == 0) {
+ norm[s]=0;
+ continue;
+ }
+ if (count[s] <= lowThreshold) {
+ norm[s] = -1;
+ distributed++;
+ total -= count[s];
+ continue;
+ }
+ if (count[s] <= lowOne) {
+ norm[s] = 1;
+ distributed++;
+ total -= count[s];
+ continue;
+ }
+
+ norm[s]=NOT_YET_ASSIGNED;
+ }
+ ToDistribute = (1 << tableLog) - distributed;
+
+ if (ToDistribute == 0)
+ return 0;
+
+ if ((total / ToDistribute) > lowOne) {
+ /* risk of rounding to zero */
+ lowOne = (U32)((total * 3) / (ToDistribute * 2));
+ for (s=0; s<=maxSymbolValue; s++) {
+ if ((norm[s] == NOT_YET_ASSIGNED) && (count[s] <= lowOne)) {
+ norm[s] = 1;
+ distributed++;
+ total -= count[s];
+ continue;
+ } }
+ ToDistribute = (1 << tableLog) - distributed;
+ }
+
+ if (distributed == maxSymbolValue+1) {
+ /* all values are pretty poor;
+ probably incompressible data (should have already been detected);
+ find max, then give all remaining points to max */
+ U32 maxV = 0, maxC = 0;
+ for (s=0; s<=maxSymbolValue; s++)
+ if (count[s] > maxC) { maxV=s; maxC=count[s]; }
+ norm[maxV] += (short)ToDistribute;
+ return 0;
+ }
+
+ if (total == 0) {
+ /* all of the symbols were low enough for the lowOne or lowThreshold */
+ for (s=0; ToDistribute > 0; s = (s+1)%(maxSymbolValue+1))
+ if (norm[s] > 0) { ToDistribute--; norm[s]++; }
+ return 0;
+ }
+
+ { U64 const vStepLog = 62 - tableLog;
+ U64 const mid = (1ULL << (vStepLog-1)) - 1;
+ U64 const rStep = ((((U64)1<<vStepLog) * ToDistribute) + mid) / total; /* scale on remaining */
+ U64 tmpTotal = mid;
+ for (s=0; s<=maxSymbolValue; s++) {
+ if (norm[s]==NOT_YET_ASSIGNED) {
+ U64 const end = tmpTotal + (count[s] * rStep);
+ U32 const sStart = (U32)(tmpTotal >> vStepLog);
+ U32 const sEnd = (U32)(end >> vStepLog);
+ U32 const weight = sEnd - sStart;
+ if (weight < 1)
+ return ERROR(GENERIC);
+ norm[s] = (short)weight;
+ tmpTotal = end;
+ } } }
+
+ return 0;
+}
+
+
+size_t FSE_normalizeCount (short* normalizedCounter, unsigned tableLog,
+ const unsigned* count, size_t total,
+ unsigned maxSymbolValue)
+{
+ /* Sanity checks */
+ if (tableLog==0) tableLog = FSE_DEFAULT_TABLELOG;
+ if (tableLog < FSE_MIN_TABLELOG) return ERROR(GENERIC); /* Unsupported size */
+ if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge); /* Unsupported size */
+ if (tableLog < FSE_minTableLog(total, maxSymbolValue)) return ERROR(GENERIC); /* Too small tableLog, compression potentially impossible */
+
+ { static U32 const rtbTable[] = { 0, 473195, 504333, 520860, 550000, 700000, 750000, 830000 };
+ U64 const scale = 62 - tableLog;
+ U64 const step = ((U64)1<<62) / total; /* <== here, one division ! */
+ U64 const vStep = 1ULL<<(scale-20);
+ int stillToDistribute = 1<<tableLog;
+ unsigned s;
+ unsigned largest=0;
+ short largestP=0;
+ U32 lowThreshold = (U32)(total >> tableLog);
+
+ for (s=0; s<=maxSymbolValue; s++) {
+ if (count[s] == total) return 0; /* rle special case */
+ if (count[s] == 0) { normalizedCounter[s]=0; continue; }
+ if (count[s] <= lowThreshold) {
+ normalizedCounter[s] = -1;
+ stillToDistribute--;
+ } else {
+ short proba = (short)((count[s]*step) >> scale);
+ if (proba<8) {
+ U64 restToBeat = vStep * rtbTable[proba];
+ proba += (count[s]*step) - ((U64)proba<<scale) > restToBeat;
+ }
+ if (proba > largestP) { largestP=proba; largest=s; }
+ normalizedCounter[s] = proba;
+ stillToDistribute -= proba;
+ } }
+ if (-stillToDistribute >= (normalizedCounter[largest] >> 1)) {
+ /* corner case, need another normalization method */
+ size_t const errorCode = FSE_normalizeM2(normalizedCounter, tableLog, count, total, maxSymbolValue);
+ if (FSE_isError(errorCode)) return errorCode;
+ }
+ else normalizedCounter[largest] += (short)stillToDistribute;
+ }
+
+#if 0
+ { /* Print Table (debug) */
+ U32 s;
+ U32 nTotal = 0;
+ for (s=0; s<=maxSymbolValue; s++)
+ RAWLOG(2, "%3i: %4i \n", s, normalizedCounter[s]);
+ for (s=0; s<=maxSymbolValue; s++)
+ nTotal += abs(normalizedCounter[s]);
+ if (nTotal != (1U<<tableLog))
+ RAWLOG(2, "Warning !!! Total == %u != %u !!!", nTotal, 1U<<tableLog);
+ getchar();
+ }
+#endif
+
+ return tableLog;
+}
+
+
+/* fake FSE_CTable, for raw (uncompressed) input */
+size_t FSE_buildCTable_raw (FSE_CTable* ct, unsigned nbBits)
+{
+ const unsigned tableSize = 1 << nbBits;
+ const unsigned tableMask = tableSize - 1;
+ const unsigned maxSymbolValue = tableMask;
+ void* const ptr = ct;
+ U16* const tableU16 = ( (U16*) ptr) + 2;
+ void* const FSCT = ((U32*)ptr) + 1 /* header */ + (tableSize>>1); /* assumption : tableLog >= 1 */
+ FSE_symbolCompressionTransform* const symbolTT = (FSE_symbolCompressionTransform*) (FSCT);
+ unsigned s;
+
+ /* Sanity checks */
+ if (nbBits < 1) return ERROR(GENERIC); /* min size */
+
+ /* header */
+ tableU16[-2] = (U16) nbBits;
+ tableU16[-1] = (U16) maxSymbolValue;
+
+ /* Build table */
+ for (s=0; s<tableSize; s++)
+ tableU16[s] = (U16)(tableSize + s);
+
+ /* Build Symbol Transformation Table */
+ { const U32 deltaNbBits = (nbBits << 16) - (1 << nbBits);
+ for (s=0; s<=maxSymbolValue; s++) {
+ symbolTT[s].deltaNbBits = deltaNbBits;
+ symbolTT[s].deltaFindState = s-1;
+ } }
+
+ return 0;
+}
+
+/* fake FSE_CTable, for rle input (always same symbol) */
+size_t FSE_buildCTable_rle (FSE_CTable* ct, BYTE symbolValue)
+{
+ void* ptr = ct;
+ U16* tableU16 = ( (U16*) ptr) + 2;
+ void* FSCTptr = (U32*)ptr + 2;
+ FSE_symbolCompressionTransform* symbolTT = (FSE_symbolCompressionTransform*) FSCTptr;
+
+ /* header */
+ tableU16[-2] = (U16) 0;
+ tableU16[-1] = (U16) symbolValue;
+
+ /* Build table */
+ tableU16[0] = 0;
+ tableU16[1] = 0; /* just in case */
+
+ /* Build Symbol Transformation Table */
+ symbolTT[symbolValue].deltaNbBits = 0;
+ symbolTT[symbolValue].deltaFindState = 0;
+
+ return 0;
+}
+
+
+static size_t FSE_compress_usingCTable_generic (void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ const FSE_CTable* ct, const unsigned fast)
+{
+ const BYTE* const istart = (const BYTE*) src;
+ const BYTE* const iend = istart + srcSize;
+ const BYTE* ip=iend;
+
+ BIT_CStream_t bitC;
+ FSE_CState_t CState1, CState2;
+
+ /* init */
+ if (srcSize <= 2) return 0;
+ { size_t const initError = BIT_initCStream(&bitC, dst, dstSize);
+ if (FSE_isError(initError)) return 0; /* not enough space available to write a bitstream */ }
+
+#define FSE_FLUSHBITS(s) (fast ? BIT_flushBitsFast(s) : BIT_flushBits(s))
+
+ if (srcSize & 1) {
+ FSE_initCState2(&CState1, ct, *--ip);
+ FSE_initCState2(&CState2, ct, *--ip);
+ FSE_encodeSymbol(&bitC, &CState1, *--ip);
+ FSE_FLUSHBITS(&bitC);
+ } else {
+ FSE_initCState2(&CState2, ct, *--ip);
+ FSE_initCState2(&CState1, ct, *--ip);
+ }
+
+ /* join to mod 4 */
+ srcSize -= 2;
+ if ((sizeof(bitC.bitContainer)*8 > FSE_MAX_TABLELOG*4+7 ) && (srcSize & 2)) { /* test bit 2 */
+ FSE_encodeSymbol(&bitC, &CState2, *--ip);
+ FSE_encodeSymbol(&bitC, &CState1, *--ip);
+ FSE_FLUSHBITS(&bitC);
+ }
+
+ /* 2 or 4 encoding per loop */
+ while ( ip>istart ) {
+
+ FSE_encodeSymbol(&bitC, &CState2, *--ip);
+
+ if (sizeof(bitC.bitContainer)*8 < FSE_MAX_TABLELOG*2+7 ) /* this test must be static */
+ FSE_FLUSHBITS(&bitC);
+
+ FSE_encodeSymbol(&bitC, &CState1, *--ip);
+
+ if (sizeof(bitC.bitContainer)*8 > FSE_MAX_TABLELOG*4+7 ) { /* this test must be static */
+ FSE_encodeSymbol(&bitC, &CState2, *--ip);
+ FSE_encodeSymbol(&bitC, &CState1, *--ip);
+ }
+
+ FSE_FLUSHBITS(&bitC);
+ }
+
+ FSE_flushCState(&bitC, &CState2);
+ FSE_flushCState(&bitC, &CState1);
+ return BIT_closeCStream(&bitC);
+}
+
+size_t FSE_compress_usingCTable (void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ const FSE_CTable* ct)
+{
+ unsigned const fast = (dstSize >= FSE_BLOCKBOUND(srcSize));
+
+ if (fast)
+ return FSE_compress_usingCTable_generic(dst, dstSize, src, srcSize, ct, 1);
+ else
+ return FSE_compress_usingCTable_generic(dst, dstSize, src, srcSize, ct, 0);
+}
+
+
+size_t FSE_compressBound(size_t size) { return FSE_COMPRESSBOUND(size); }
+
+#define CHECK_V_F(e, f) size_t const e = f; if (ERR_isError(e)) return e
+#define CHECK_F(f) { CHECK_V_F(_var_err__, f); }
+
+/* FSE_compress_wksp() :
+ * Same as FSE_compress2(), but using an externally allocated scratch buffer (`workSpace`).
+ * `wkspSize` size must be `(1<<tableLog)`.
+ */
+size_t FSE_compress_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize)
+{
+ BYTE* const ostart = (BYTE*) dst;
+ BYTE* op = ostart;
+ BYTE* const oend = ostart + dstSize;
+
+ unsigned count[FSE_MAX_SYMBOL_VALUE+1];
+ S16 norm[FSE_MAX_SYMBOL_VALUE+1];
+ FSE_CTable* CTable = (FSE_CTable*)workSpace;
+ size_t const CTableSize = FSE_CTABLE_SIZE_U32(tableLog, maxSymbolValue);
+ void* scratchBuffer = (void*)(CTable + CTableSize);
+ size_t const scratchBufferSize = wkspSize - (CTableSize * sizeof(FSE_CTable));
+
+ /* init conditions */
+ if (wkspSize < FSE_WKSP_SIZE_U32(tableLog, maxSymbolValue)) return ERROR(tableLog_tooLarge);
+ if (srcSize <= 1) return 0; /* Not compressible */
+ if (!maxSymbolValue) maxSymbolValue = FSE_MAX_SYMBOL_VALUE;
+ if (!tableLog) tableLog = FSE_DEFAULT_TABLELOG;
+
+ /* Scan input and build symbol stats */
+ { CHECK_V_F(maxCount, HIST_count_wksp(count, &maxSymbolValue, src, srcSize, scratchBuffer, scratchBufferSize) );
+ if (maxCount == srcSize) return 1; /* only a single symbol in src : rle */
+ if (maxCount == 1) return 0; /* each symbol present maximum once => not compressible */
+ if (maxCount < (srcSize >> 7)) return 0; /* Heuristic : not compressible enough */
+ }
+
+ tableLog = FSE_optimalTableLog(tableLog, srcSize, maxSymbolValue);
+ CHECK_F( FSE_normalizeCount(norm, tableLog, count, srcSize, maxSymbolValue) );
+
+ /* Write table description header */
+ { CHECK_V_F(nc_err, FSE_writeNCount(op, oend-op, norm, maxSymbolValue, tableLog) );
+ op += nc_err;
+ }
+
+ /* Compress */
+ CHECK_F( FSE_buildCTable_wksp(CTable, norm, maxSymbolValue, tableLog, scratchBuffer, scratchBufferSize) );
+ { CHECK_V_F(cSize, FSE_compress_usingCTable(op, oend - op, src, srcSize, CTable) );
+ if (cSize == 0) return 0; /* not enough space for compressed data */
+ op += cSize;
+ }
+
+ /* check compressibility */
+ if ( (size_t)(op-ostart) >= srcSize-1 ) return 0;
+
+ return op-ostart;
+}
+
+typedef struct {
+ FSE_CTable CTable_max[FSE_CTABLE_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE)];
+ BYTE scratchBuffer[1 << FSE_MAX_TABLELOG];
+} fseWkspMax_t;
+
+size_t FSE_compress2 (void* dst, size_t dstCapacity, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog)
+{
+ fseWkspMax_t scratchBuffer;
+ DEBUG_STATIC_ASSERT(sizeof(scratchBuffer) >= FSE_WKSP_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE)); /* compilation failures here means scratchBuffer is not large enough */
+ if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge);
+ return FSE_compress_wksp(dst, dstCapacity, src, srcSize, maxSymbolValue, tableLog, &scratchBuffer, sizeof(scratchBuffer));
+}
+
+size_t FSE_compress (void* dst, size_t dstCapacity, const void* src, size_t srcSize)
+{
+ return FSE_compress2(dst, dstCapacity, src, srcSize, FSE_MAX_SYMBOL_VALUE, FSE_DEFAULT_TABLELOG);
+}
+
+
+#endif /* FSE_COMMONDEFS_ONLY */
diff --git a/Utilities/cmzstd/lib/compress/hist.c b/Utilities/cmzstd/lib/compress/hist.c
new file mode 100644
index 000000000..45b7babc1
--- /dev/null
+++ b/Utilities/cmzstd/lib/compress/hist.c
@@ -0,0 +1,203 @@
+/* ******************************************************************
+ hist : Histogram functions
+ part of Finite State Entropy project
+ Copyright (C) 2013-present, 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 :
+ - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy
+ - Public forum : https://groups.google.com/forum/#!forum/lz4c
+****************************************************************** */
+
+/* --- dependencies --- */
+#include "mem.h" /* U32, BYTE, etc. */
+#include "debug.h" /* assert, DEBUGLOG */
+#include "error_private.h" /* ERROR */
+#include "hist.h"
+
+
+/* --- Error management --- */
+unsigned HIST_isError(size_t code) { return ERR_isError(code); }
+
+/*-**************************************************************
+ * Histogram functions
+ ****************************************************************/
+unsigned HIST_count_simple(unsigned* count, unsigned* maxSymbolValuePtr,
+ const void* src, size_t srcSize)
+{
+ const BYTE* ip = (const BYTE*)src;
+ const BYTE* const end = ip + srcSize;
+ unsigned maxSymbolValue = *maxSymbolValuePtr;
+ unsigned largestCount=0;
+
+ memset(count, 0, (maxSymbolValue+1) * sizeof(*count));
+ if (srcSize==0) { *maxSymbolValuePtr = 0; return 0; }
+
+ while (ip<end) {
+ assert(*ip <= maxSymbolValue);
+ count[*ip++]++;
+ }
+
+ while (!count[maxSymbolValue]) maxSymbolValue--;
+ *maxSymbolValuePtr = maxSymbolValue;
+
+ { U32 s;
+ for (s=0; s<=maxSymbolValue; s++)
+ if (count[s] > largestCount) largestCount = count[s];
+ }
+
+ return largestCount;
+}
+
+typedef enum { trustInput, checkMaxSymbolValue } HIST_checkInput_e;
+
+/* HIST_count_parallel_wksp() :
+ * store histogram into 4 intermediate tables, recombined at the end.
+ * this design makes better use of OoO cpus,
+ * and is noticeably faster when some values are heavily repeated.
+ * But it needs some additional workspace for intermediate tables.
+ * `workSpace` size must be a table of size >= HIST_WKSP_SIZE_U32.
+ * @return : largest histogram frequency,
+ * or an error code (notably when histogram would be larger than *maxSymbolValuePtr). */
+static size_t HIST_count_parallel_wksp(
+ unsigned* count, unsigned* maxSymbolValuePtr,
+ const void* source, size_t sourceSize,
+ HIST_checkInput_e check,
+ U32* const workSpace)
+{
+ const BYTE* ip = (const BYTE*)source;
+ const BYTE* const iend = ip+sourceSize;
+ unsigned maxSymbolValue = *maxSymbolValuePtr;
+ unsigned max=0;
+ U32* const Counting1 = workSpace;
+ U32* const Counting2 = Counting1 + 256;
+ U32* const Counting3 = Counting2 + 256;
+ U32* const Counting4 = Counting3 + 256;
+
+ memset(workSpace, 0, 4*256*sizeof(unsigned));
+
+ /* safety checks */
+ if (!sourceSize) {
+ memset(count, 0, maxSymbolValue + 1);
+ *maxSymbolValuePtr = 0;
+ return 0;
+ }
+ if (!maxSymbolValue) maxSymbolValue = 255; /* 0 == default */
+
+ /* by stripes of 16 bytes */
+ { U32 cached = MEM_read32(ip); ip += 4;
+ while (ip < iend-15) {
+ U32 c = cached; cached = MEM_read32(ip); ip += 4;
+ Counting1[(BYTE) c ]++;
+ Counting2[(BYTE)(c>>8) ]++;
+ Counting3[(BYTE)(c>>16)]++;
+ Counting4[ c>>24 ]++;
+ c = cached; cached = MEM_read32(ip); ip += 4;
+ Counting1[(BYTE) c ]++;
+ Counting2[(BYTE)(c>>8) ]++;
+ Counting3[(BYTE)(c>>16)]++;
+ Counting4[ c>>24 ]++;
+ c = cached; cached = MEM_read32(ip); ip += 4;
+ Counting1[(BYTE) c ]++;
+ Counting2[(BYTE)(c>>8) ]++;
+ Counting3[(BYTE)(c>>16)]++;
+ Counting4[ c>>24 ]++;
+ c = cached; cached = MEM_read32(ip); ip += 4;
+ Counting1[(BYTE) c ]++;
+ Counting2[(BYTE)(c>>8) ]++;
+ Counting3[(BYTE)(c>>16)]++;
+ Counting4[ c>>24 ]++;
+ }
+ ip-=4;
+ }
+
+ /* finish last symbols */
+ while (ip<iend) Counting1[*ip++]++;
+
+ if (check) { /* verify stats will fit into destination table */
+ U32 s; for (s=255; s>maxSymbolValue; s--) {
+ Counting1[s] += Counting2[s] + Counting3[s] + Counting4[s];
+ if (Counting1[s]) return ERROR(maxSymbolValue_tooSmall);
+ } }
+
+ { U32 s;
+ if (maxSymbolValue > 255) maxSymbolValue = 255;
+ for (s=0; s<=maxSymbolValue; s++) {
+ count[s] = Counting1[s] + Counting2[s] + Counting3[s] + Counting4[s];
+ if (count[s] > max) max = count[s];
+ } }
+
+ while (!count[maxSymbolValue]) maxSymbolValue--;
+ *maxSymbolValuePtr = maxSymbolValue;
+ return (size_t)max;
+}
+
+/* HIST_countFast_wksp() :
+ * Same as HIST_countFast(), but using an externally provided scratch buffer.
+ * `workSpace` is a writable buffer which must be 4-bytes aligned,
+ * `workSpaceSize` must be >= HIST_WKSP_SIZE
+ */
+size_t HIST_countFast_wksp(unsigned* count, unsigned* maxSymbolValuePtr,
+ const void* source, size_t sourceSize,
+ void* workSpace, size_t workSpaceSize)
+{
+ if (sourceSize < 1500) /* heuristic threshold */
+ return HIST_count_simple(count, maxSymbolValuePtr, source, sourceSize);
+ if ((size_t)workSpace & 3) return ERROR(GENERIC); /* must be aligned on 4-bytes boundaries */
+ if (workSpaceSize < HIST_WKSP_SIZE) return ERROR(workSpace_tooSmall);
+ return HIST_count_parallel_wksp(count, maxSymbolValuePtr, source, sourceSize, trustInput, (U32*)workSpace);
+}
+
+/* fast variant (unsafe : won't check if src contains values beyond count[] limit) */
+size_t HIST_countFast(unsigned* count, unsigned* maxSymbolValuePtr,
+ const void* source, size_t sourceSize)
+{
+ unsigned tmpCounters[HIST_WKSP_SIZE_U32];
+ return HIST_countFast_wksp(count, maxSymbolValuePtr, source, sourceSize, tmpCounters, sizeof(tmpCounters));
+}
+
+/* HIST_count_wksp() :
+ * Same as HIST_count(), but using an externally provided scratch buffer.
+ * `workSpace` size must be table of >= HIST_WKSP_SIZE_U32 unsigned */
+size_t HIST_count_wksp(unsigned* count, unsigned* maxSymbolValuePtr,
+ const void* source, size_t sourceSize,
+ void* workSpace, size_t workSpaceSize)
+{
+ if ((size_t)workSpace & 3) return ERROR(GENERIC); /* must be aligned on 4-bytes boundaries */
+ if (workSpaceSize < HIST_WKSP_SIZE) return ERROR(workSpace_tooSmall);
+ if (*maxSymbolValuePtr < 255)
+ return HIST_count_parallel_wksp(count, maxSymbolValuePtr, source, sourceSize, checkMaxSymbolValue, (U32*)workSpace);
+ *maxSymbolValuePtr = 255;
+ return HIST_countFast_wksp(count, maxSymbolValuePtr, source, sourceSize, workSpace, workSpaceSize);
+}
+
+size_t HIST_count(unsigned* count, unsigned* maxSymbolValuePtr,
+ const void* src, size_t srcSize)
+{
+ unsigned tmpCounters[HIST_WKSP_SIZE_U32];
+ return HIST_count_wksp(count, maxSymbolValuePtr, src, srcSize, tmpCounters, sizeof(tmpCounters));
+}
diff --git a/Utilities/cmzstd/lib/compress/hist.h b/Utilities/cmzstd/lib/compress/hist.h
new file mode 100644
index 000000000..8b389358d
--- /dev/null
+++ b/Utilities/cmzstd/lib/compress/hist.h
@@ -0,0 +1,95 @@
+/* ******************************************************************
+ hist : Histogram functions
+ part of Finite State Entropy project
+ Copyright (C) 2013-present, 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 :
+ - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy
+ - Public forum : https://groups.google.com/forum/#!forum/lz4c
+****************************************************************** */
+
+/* --- dependencies --- */
+#include <stddef.h> /* size_t */
+
+
+/* --- simple histogram functions --- */
+
+/*! HIST_count():
+ * Provides the precise count of each byte within a table 'count'.
+ * 'count' is a table of unsigned int, of minimum size (*maxSymbolValuePtr+1).
+ * Updates *maxSymbolValuePtr with actual largest symbol value detected.
+ * @return : count of the most frequent symbol (which isn't identified).
+ * or an error code, which can be tested using HIST_isError().
+ * note : if return == srcSize, there is only one symbol.
+ */
+size_t HIST_count(unsigned* count, unsigned* maxSymbolValuePtr,
+ const void* src, size_t srcSize);
+
+unsigned HIST_isError(size_t code); /**< tells if a return value is an error code */
+
+
+/* --- advanced histogram functions --- */
+
+#define HIST_WKSP_SIZE_U32 1024
+#define HIST_WKSP_SIZE (HIST_WKSP_SIZE_U32 * sizeof(unsigned))
+/** HIST_count_wksp() :
+ * Same as HIST_count(), but using an externally provided scratch buffer.
+ * Benefit is this function will use very little stack space.
+ * `workSpace` is a writable buffer which must be 4-bytes aligned,
+ * `workSpaceSize` must be >= HIST_WKSP_SIZE
+ */
+size_t HIST_count_wksp(unsigned* count, unsigned* maxSymbolValuePtr,
+ const void* src, size_t srcSize,
+ void* workSpace, size_t workSpaceSize);
+
+/** HIST_countFast() :
+ * same as HIST_count(), but blindly trusts that all byte values within src are <= *maxSymbolValuePtr.
+ * This function is unsafe, and will segfault if any value within `src` is `> *maxSymbolValuePtr`
+ */
+size_t HIST_countFast(unsigned* count, unsigned* maxSymbolValuePtr,
+ const void* src, size_t srcSize);
+
+/** HIST_countFast_wksp() :
+ * Same as HIST_countFast(), but using an externally provided scratch buffer.
+ * `workSpace` is a writable buffer which must be 4-bytes aligned,
+ * `workSpaceSize` must be >= HIST_WKSP_SIZE
+ */
+size_t HIST_countFast_wksp(unsigned* count, unsigned* maxSymbolValuePtr,
+ const void* src, size_t srcSize,
+ void* workSpace, size_t workSpaceSize);
+
+/*! HIST_count_simple() :
+ * Same as HIST_countFast(), this function is unsafe,
+ * and will segfault if any value within `src` is `> *maxSymbolValuePtr`.
+ * It is also a bit slower for large inputs.
+ * However, it does not need any additional memory (not even on stack).
+ * @return : count of the most frequent symbol.
+ * Note this function doesn't produce any error (i.e. it must succeed).
+ */
+unsigned HIST_count_simple(unsigned* count, unsigned* maxSymbolValuePtr,
+ const void* src, size_t srcSize);
diff --git a/Utilities/cmzstd/lib/compress/huf_compress.c b/Utilities/cmzstd/lib/compress/huf_compress.c
new file mode 100644
index 000000000..f074f1e0a
--- /dev/null
+++ b/Utilities/cmzstd/lib/compress/huf_compress.c
@@ -0,0 +1,798 @@
+/* ******************************************************************
+ Huffman encoder, part of New Generation Entropy library
+ 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 :
+ - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy
+ - Public forum : https://groups.google.com/forum/#!forum/lz4c
+****************************************************************** */
+
+/* **************************************************************
+* Compiler specifics
+****************************************************************/
+#ifdef _MSC_VER /* Visual Studio */
+# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
+#endif
+
+
+/* **************************************************************
+* Includes
+****************************************************************/
+#include <string.h> /* memcpy, memset */
+#include <stdio.h> /* printf (debug) */
+#include "compiler.h"
+#include "bitstream.h"
+#include "hist.h"
+#define FSE_STATIC_LINKING_ONLY /* FSE_optimalTableLog_internal */
+#include "fse.h" /* header compression */
+#define HUF_STATIC_LINKING_ONLY
+#include "huf.h"
+#include "error_private.h"
+
+
+/* **************************************************************
+* Error Management
+****************************************************************/
+#define HUF_isError ERR_isError
+#define HUF_STATIC_ASSERT(c) DEBUG_STATIC_ASSERT(c) /* use only *after* variable declarations */
+#define CHECK_V_F(e, f) size_t const e = f; if (ERR_isError(e)) return e
+#define CHECK_F(f) { CHECK_V_F(_var_err__, f); }
+
+
+/* **************************************************************
+* Utils
+****************************************************************/
+unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue)
+{
+ return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 1);
+}
+
+
+/* *******************************************************
+* HUF : Huffman block compression
+*********************************************************/
+/* HUF_compressWeights() :
+ * Same as FSE_compress(), but dedicated to huff0's weights compression.
+ * The use case needs much less stack memory.
+ * Note : all elements within weightTable are supposed to be <= HUF_TABLELOG_MAX.
+ */
+#define MAX_FSE_TABLELOG_FOR_HUFF_HEADER 6
+static size_t HUF_compressWeights (void* dst, size_t dstSize, const void* weightTable, size_t wtSize)
+{
+ BYTE* const ostart = (BYTE*) dst;
+ BYTE* op = ostart;
+ BYTE* const oend = ostart + dstSize;
+
+ unsigned maxSymbolValue = HUF_TABLELOG_MAX;
+ U32 tableLog = MAX_FSE_TABLELOG_FOR_HUFF_HEADER;
+
+ FSE_CTable CTable[FSE_CTABLE_SIZE_U32(MAX_FSE_TABLELOG_FOR_HUFF_HEADER, HUF_TABLELOG_MAX)];
+ BYTE scratchBuffer[1<<MAX_FSE_TABLELOG_FOR_HUFF_HEADER];
+
+ unsigned count[HUF_TABLELOG_MAX+1];
+ S16 norm[HUF_TABLELOG_MAX+1];
+
+ /* init conditions */
+ if (wtSize <= 1) return 0; /* Not compressible */
+
+ /* Scan input and build symbol stats */
+ { unsigned const maxCount = HIST_count_simple(count, &maxSymbolValue, weightTable, wtSize); /* never fails */
+ if (maxCount == wtSize) return 1; /* only a single symbol in src : rle */
+ if (maxCount == 1) return 0; /* each symbol present maximum once => not compressible */
+ }
+
+ tableLog = FSE_optimalTableLog(tableLog, wtSize, maxSymbolValue);
+ CHECK_F( FSE_normalizeCount(norm, tableLog, count, wtSize, maxSymbolValue) );
+
+ /* Write table description header */
+ { CHECK_V_F(hSize, FSE_writeNCount(op, oend-op, norm, maxSymbolValue, tableLog) );
+ op += hSize;
+ }
+
+ /* Compress */
+ CHECK_F( FSE_buildCTable_wksp(CTable, norm, maxSymbolValue, tableLog, scratchBuffer, sizeof(scratchBuffer)) );
+ { CHECK_V_F(cSize, FSE_compress_usingCTable(op, oend - op, weightTable, wtSize, CTable) );
+ if (cSize == 0) return 0; /* not enough space for compressed data */
+ op += cSize;
+ }
+
+ return op-ostart;
+}
+
+
+struct HUF_CElt_s {
+ U16 val;
+ BYTE nbBits;
+}; /* typedef'd to HUF_CElt within "huf.h" */
+
+/*! HUF_writeCTable() :
+ `CTable` : Huffman tree to save, using huf representation.
+ @return : size of saved CTable */
+size_t HUF_writeCTable (void* dst, size_t maxDstSize,
+ const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog)
+{
+ BYTE bitsToWeight[HUF_TABLELOG_MAX + 1]; /* precomputed conversion table */
+ BYTE huffWeight[HUF_SYMBOLVALUE_MAX];
+ BYTE* op = (BYTE*)dst;
+ U32 n;
+
+ /* check conditions */
+ if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR(maxSymbolValue_tooLarge);
+
+ /* convert to weight */
+ bitsToWeight[0] = 0;
+ for (n=1; n<huffLog+1; n++)
+ bitsToWeight[n] = (BYTE)(huffLog + 1 - n);
+ for (n=0; n<maxSymbolValue; n++)
+ huffWeight[n] = bitsToWeight[CTable[n].nbBits];
+
+ /* attempt weights compression by FSE */
+ { CHECK_V_F(hSize, HUF_compressWeights(op+1, maxDstSize-1, huffWeight, maxSymbolValue) );
+ if ((hSize>1) & (hSize < maxSymbolValue/2)) { /* FSE compressed */
+ op[0] = (BYTE)hSize;
+ return hSize+1;
+ } }
+
+ /* write raw values as 4-bits (max : 15) */
+ if (maxSymbolValue > (256-128)) return ERROR(GENERIC); /* should not happen : likely means source cannot be compressed */
+ if (((maxSymbolValue+1)/2) + 1 > maxDstSize) return ERROR(dstSize_tooSmall); /* not enough space within dst buffer */
+ op[0] = (BYTE)(128 /*special case*/ + (maxSymbolValue-1));
+ huffWeight[maxSymbolValue] = 0; /* to be sure it doesn't cause msan issue in final combination */
+ for (n=0; n<maxSymbolValue; n+=2)
+ op[(n/2)+1] = (BYTE)((huffWeight[n] << 4) + huffWeight[n+1]);
+ return ((maxSymbolValue+1)/2) + 1;
+}
+
+
+size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize)
+{
+ BYTE huffWeight[HUF_SYMBOLVALUE_MAX + 1]; /* init not required, even though some static analyzer may complain */
+ U32 rankVal[HUF_TABLELOG_ABSOLUTEMAX + 1]; /* large enough for values from 0 to 16 */
+ U32 tableLog = 0;
+ U32 nbSymbols = 0;
+
+ /* get symbol weights */
+ CHECK_V_F(readSize, HUF_readStats(huffWeight, HUF_SYMBOLVALUE_MAX+1, rankVal, &nbSymbols, &tableLog, src, srcSize));
+
+ /* check result */
+ if (tableLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge);
+ if (nbSymbols > *maxSymbolValuePtr+1) return ERROR(maxSymbolValue_tooSmall);
+
+ /* Prepare base value per rank */
+ { U32 n, nextRankStart = 0;
+ for (n=1; n<=tableLog; n++) {
+ U32 current = nextRankStart;
+ nextRankStart += (rankVal[n] << (n-1));
+ rankVal[n] = current;
+ } }
+
+ /* fill nbBits */
+ { U32 n; for (n=0; n<nbSymbols; n++) {
+ const U32 w = huffWeight[n];
+ CTable[n].nbBits = (BYTE)(tableLog + 1 - w);
+ } }
+
+ /* fill val */
+ { U16 nbPerRank[HUF_TABLELOG_MAX+2] = {0}; /* support w=0=>n=tableLog+1 */
+ U16 valPerRank[HUF_TABLELOG_MAX+2] = {0};
+ { U32 n; for (n=0; n<nbSymbols; n++) nbPerRank[CTable[n].nbBits]++; }
+ /* determine stating value per rank */
+ valPerRank[tableLog+1] = 0; /* for w==0 */
+ { U16 min = 0;
+ U32 n; for (n=tableLog; n>0; n--) { /* start at n=tablelog <-> w=1 */
+ valPerRank[n] = min; /* get starting value within each rank */
+ min += nbPerRank[n];
+ min >>= 1;
+ } }
+ /* assign value within rank, symbol order */
+ { U32 n; for (n=0; n<nbSymbols; n++) CTable[n].val = valPerRank[CTable[n].nbBits]++; }
+ }
+
+ *maxSymbolValuePtr = nbSymbols - 1;
+ return readSize;
+}
+
+U32 HUF_getNbBits(const void* symbolTable, U32 symbolValue)
+{
+ const HUF_CElt* table = (const HUF_CElt*)symbolTable;
+ assert(symbolValue <= HUF_SYMBOLVALUE_MAX);
+ return table[symbolValue].nbBits;
+}
+
+
+typedef struct nodeElt_s {
+ U32 count;
+ U16 parent;
+ BYTE byte;
+ BYTE nbBits;
+} nodeElt;
+
+static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 maxNbBits)
+{
+ const U32 largestBits = huffNode[lastNonNull].nbBits;
+ if (largestBits <= maxNbBits) return largestBits; /* early exit : no elt > maxNbBits */
+
+ /* there are several too large elements (at least >= 2) */
+ { int totalCost = 0;
+ const U32 baseCost = 1 << (largestBits - maxNbBits);
+ U32 n = lastNonNull;
+
+ while (huffNode[n].nbBits > maxNbBits) {
+ totalCost += baseCost - (1 << (largestBits - huffNode[n].nbBits));
+ huffNode[n].nbBits = (BYTE)maxNbBits;
+ n --;
+ } /* n stops at huffNode[n].nbBits <= maxNbBits */
+ while (huffNode[n].nbBits == maxNbBits) n--; /* n end at index of smallest symbol using < maxNbBits */
+
+ /* renorm totalCost */
+ totalCost >>= (largestBits - maxNbBits); /* note : totalCost is necessarily a multiple of baseCost */
+
+ /* repay normalized cost */
+ { U32 const noSymbol = 0xF0F0F0F0;
+ U32 rankLast[HUF_TABLELOG_MAX+2];
+ int pos;
+
+ /* Get pos of last (smallest) symbol per rank */
+ memset(rankLast, 0xF0, sizeof(rankLast));
+ { U32 currentNbBits = maxNbBits;
+ for (pos=n ; pos >= 0; pos--) {
+ if (huffNode[pos].nbBits >= currentNbBits) continue;
+ currentNbBits = huffNode[pos].nbBits; /* < maxNbBits */
+ rankLast[maxNbBits-currentNbBits] = pos;
+ } }
+
+ while (totalCost > 0) {
+ U32 nBitsToDecrease = BIT_highbit32(totalCost) + 1;
+ for ( ; nBitsToDecrease > 1; nBitsToDecrease--) {
+ U32 highPos = rankLast[nBitsToDecrease];
+ U32 lowPos = rankLast[nBitsToDecrease-1];
+ if (highPos == noSymbol) continue;
+ if (lowPos == noSymbol) break;
+ { U32 const highTotal = huffNode[highPos].count;
+ U32 const lowTotal = 2 * huffNode[lowPos].count;
+ if (highTotal <= lowTotal) break;
+ } }
+ /* only triggered when no more rank 1 symbol left => find closest one (note : there is necessarily at least one !) */
+ /* HUF_MAX_TABLELOG test just to please gcc 5+; but it should not be necessary */
+ while ((nBitsToDecrease<=HUF_TABLELOG_MAX) && (rankLast[nBitsToDecrease] == noSymbol))
+ nBitsToDecrease ++;
+ totalCost -= 1 << (nBitsToDecrease-1);
+ if (rankLast[nBitsToDecrease-1] == noSymbol)
+ rankLast[nBitsToDecrease-1] = rankLast[nBitsToDecrease]; /* this rank is no longer empty */
+ huffNode[rankLast[nBitsToDecrease]].nbBits ++;
+ if (rankLast[nBitsToDecrease] == 0) /* special case, reached largest symbol */
+ rankLast[nBitsToDecrease] = noSymbol;
+ else {
+ rankLast[nBitsToDecrease]--;
+ if (huffNode[rankLast[nBitsToDecrease]].nbBits != maxNbBits-nBitsToDecrease)
+ rankLast[nBitsToDecrease] = noSymbol; /* this rank is now empty */
+ } } /* while (totalCost > 0) */
+
+ while (totalCost < 0) { /* Sometimes, cost correction overshoot */
+ if (rankLast[1] == noSymbol) { /* special case : no rank 1 symbol (using maxNbBits-1); let's create one from largest rank 0 (using maxNbBits) */
+ while (huffNode[n].nbBits == maxNbBits) n--;
+ huffNode[n+1].nbBits--;
+ rankLast[1] = n+1;
+ totalCost++;
+ continue;
+ }
+ huffNode[ rankLast[1] + 1 ].nbBits--;
+ rankLast[1]++;
+ totalCost ++;
+ } } } /* there are several too large elements (at least >= 2) */
+
+ return maxNbBits;
+}
+
+
+typedef struct {
+ U32 base;
+ U32 current;
+} rankPos;
+
+static void HUF_sort(nodeElt* huffNode, const unsigned* count, U32 maxSymbolValue)
+{
+ rankPos rank[32];
+ U32 n;
+
+ memset(rank, 0, sizeof(rank));
+ for (n=0; n<=maxSymbolValue; n++) {
+ U32 r = BIT_highbit32(count[n] + 1);
+ rank[r].base ++;
+ }
+ for (n=30; n>0; n--) rank[n-1].base += rank[n].base;
+ for (n=0; n<32; n++) rank[n].current = rank[n].base;
+ for (n=0; n<=maxSymbolValue; n++) {
+ U32 const c = count[n];
+ U32 const r = BIT_highbit32(c+1) + 1;
+ U32 pos = rank[r].current++;
+ while ((pos > rank[r].base) && (c > huffNode[pos-1].count)) {
+ huffNode[pos] = huffNode[pos-1];
+ pos--;
+ }
+ huffNode[pos].count = c;
+ huffNode[pos].byte = (BYTE)n;
+ }
+}
+
+
+/** HUF_buildCTable_wksp() :
+ * Same as HUF_buildCTable(), but using externally allocated scratch buffer.
+ * `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as a table of HUF_CTABLE_WORKSPACE_SIZE_U32 unsigned.
+ */
+#define STARTNODE (HUF_SYMBOLVALUE_MAX+1)
+typedef nodeElt huffNodeTable[HUF_CTABLE_WORKSPACE_SIZE_U32];
+size_t HUF_buildCTable_wksp (HUF_CElt* tree, const unsigned* count, U32 maxSymbolValue, U32 maxNbBits, void* workSpace, size_t wkspSize)
+{
+ nodeElt* const huffNode0 = (nodeElt*)workSpace;
+ nodeElt* const huffNode = huffNode0+1;
+ U32 n, nonNullRank;
+ int lowS, lowN;
+ U16 nodeNb = STARTNODE;
+ U32 nodeRoot;
+
+ /* safety checks */
+ if (((size_t)workSpace & 3) != 0) return ERROR(GENERIC); /* must be aligned on 4-bytes boundaries */
+ if (wkspSize < sizeof(huffNodeTable)) return ERROR(workSpace_tooSmall);
+ if (maxNbBits == 0) maxNbBits = HUF_TABLELOG_DEFAULT;
+ if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR(maxSymbolValue_tooLarge);
+ memset(huffNode0, 0, sizeof(huffNodeTable));
+
+ /* sort, decreasing order */
+ HUF_sort(huffNode, count, maxSymbolValue);
+
+ /* init for parents */
+ nonNullRank = maxSymbolValue;
+ while(huffNode[nonNullRank].count == 0) nonNullRank--;
+ lowS = nonNullRank; nodeRoot = nodeNb + lowS - 1; lowN = nodeNb;
+ huffNode[nodeNb].count = huffNode[lowS].count + huffNode[lowS-1].count;
+ huffNode[lowS].parent = huffNode[lowS-1].parent = nodeNb;
+ nodeNb++; lowS-=2;
+ for (n=nodeNb; n<=nodeRoot; n++) huffNode[n].count = (U32)(1U<<30);
+ huffNode0[0].count = (U32)(1U<<31); /* fake entry, strong barrier */
+
+ /* create parents */
+ while (nodeNb <= nodeRoot) {
+ U32 n1 = (huffNode[lowS].count < huffNode[lowN].count) ? lowS-- : lowN++;
+ U32 n2 = (huffNode[lowS].count < huffNode[lowN].count) ? lowS-- : lowN++;
+ huffNode[nodeNb].count = huffNode[n1].count + huffNode[n2].count;
+ huffNode[n1].parent = huffNode[n2].parent = nodeNb;
+ nodeNb++;
+ }
+
+ /* distribute weights (unlimited tree height) */
+ huffNode[nodeRoot].nbBits = 0;
+ for (n=nodeRoot-1; n>=STARTNODE; n--)
+ huffNode[n].nbBits = huffNode[ huffNode[n].parent ].nbBits + 1;
+ for (n=0; n<=nonNullRank; n++)
+ huffNode[n].nbBits = huffNode[ huffNode[n].parent ].nbBits + 1;
+
+ /* enforce maxTableLog */
+ maxNbBits = HUF_setMaxHeight(huffNode, nonNullRank, maxNbBits);
+
+ /* fill result into tree (val, nbBits) */
+ { U16 nbPerRank[HUF_TABLELOG_MAX+1] = {0};
+ U16 valPerRank[HUF_TABLELOG_MAX+1] = {0};
+ if (maxNbBits > HUF_TABLELOG_MAX) return ERROR(GENERIC); /* check fit into table */
+ for (n=0; n<=nonNullRank; n++)
+ nbPerRank[huffNode[n].nbBits]++;
+ /* determine stating value per rank */
+ { U16 min = 0;
+ for (n=maxNbBits; n>0; n--) {
+ valPerRank[n] = min; /* get starting value within each rank */
+ min += nbPerRank[n];
+ min >>= 1;
+ } }
+ for (n=0; n<=maxSymbolValue; n++)
+ tree[huffNode[n].byte].nbBits = huffNode[n].nbBits; /* push nbBits per symbol, symbol order */
+ for (n=0; n<=maxSymbolValue; n++)
+ tree[n].val = valPerRank[tree[n].nbBits]++; /* assign value within rank, symbol order */
+ }
+
+ return maxNbBits;
+}
+
+/** HUF_buildCTable() :
+ * @return : maxNbBits
+ * Note : count is used before tree is written, so they can safely overlap
+ */
+size_t HUF_buildCTable (HUF_CElt* tree, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits)
+{
+ huffNodeTable nodeTable;
+ return HUF_buildCTable_wksp(tree, count, maxSymbolValue, maxNbBits, nodeTable, sizeof(nodeTable));
+}
+
+static size_t HUF_estimateCompressedSize(HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue)
+{
+ size_t nbBits = 0;
+ int s;
+ for (s = 0; s <= (int)maxSymbolValue; ++s) {
+ nbBits += CTable[s].nbBits * count[s];
+ }
+ return nbBits >> 3;
+}
+
+static int HUF_validateCTable(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue) {
+ int bad = 0;
+ int s;
+ for (s = 0; s <= (int)maxSymbolValue; ++s) {
+ bad |= (count[s] != 0) & (CTable[s].nbBits == 0);
+ }
+ return !bad;
+}
+
+size_t HUF_compressBound(size_t size) { return HUF_COMPRESSBOUND(size); }
+
+FORCE_INLINE_TEMPLATE void
+HUF_encodeSymbol(BIT_CStream_t* bitCPtr, U32 symbol, const HUF_CElt* CTable)
+{
+ BIT_addBitsFast(bitCPtr, CTable[symbol].val, CTable[symbol].nbBits);
+}
+
+#define HUF_FLUSHBITS(s) BIT_flushBits(s)
+
+#define HUF_FLUSHBITS_1(stream) \
+ if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*2+7) HUF_FLUSHBITS(stream)
+
+#define HUF_FLUSHBITS_2(stream) \
+ if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*4+7) HUF_FLUSHBITS(stream)
+
+FORCE_INLINE_TEMPLATE size_t
+HUF_compress1X_usingCTable_internal_body(void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ const HUF_CElt* CTable)
+{
+ const BYTE* ip = (const BYTE*) src;
+ BYTE* const ostart = (BYTE*)dst;
+ BYTE* const oend = ostart + dstSize;
+ BYTE* op = ostart;
+ size_t n;
+ BIT_CStream_t bitC;
+
+ /* init */
+ if (dstSize < 8) return 0; /* not enough space to compress */
+ { size_t const initErr = BIT_initCStream(&bitC, op, oend-op);
+ if (HUF_isError(initErr)) return 0; }
+
+ n = srcSize & ~3; /* join to mod 4 */
+ switch (srcSize & 3)
+ {
+ case 3 : HUF_encodeSymbol(&bitC, ip[n+ 2], CTable);
+ HUF_FLUSHBITS_2(&bitC);
+ /* fall-through */
+ case 2 : HUF_encodeSymbol(&bitC, ip[n+ 1], CTable);
+ HUF_FLUSHBITS_1(&bitC);
+ /* fall-through */
+ case 1 : HUF_encodeSymbol(&bitC, ip[n+ 0], CTable);
+ HUF_FLUSHBITS(&bitC);
+ /* fall-through */
+ case 0 : /* fall-through */
+ default: break;
+ }
+
+ for (; n>0; n-=4) { /* note : n&3==0 at this stage */
+ HUF_encodeSymbol(&bitC, ip[n- 1], CTable);
+ HUF_FLUSHBITS_1(&bitC);
+ HUF_encodeSymbol(&bitC, ip[n- 2], CTable);
+ HUF_FLUSHBITS_2(&bitC);
+ HUF_encodeSymbol(&bitC, ip[n- 3], CTable);
+ HUF_FLUSHBITS_1(&bitC);
+ HUF_encodeSymbol(&bitC, ip[n- 4], CTable);
+ HUF_FLUSHBITS(&bitC);
+ }
+
+ return BIT_closeCStream(&bitC);
+}
+
+#if DYNAMIC_BMI2
+
+static TARGET_ATTRIBUTE("bmi2") size_t
+HUF_compress1X_usingCTable_internal_bmi2(void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ const HUF_CElt* CTable)
+{
+ return HUF_compress1X_usingCTable_internal_body(dst, dstSize, src, srcSize, CTable);
+}
+
+static size_t
+HUF_compress1X_usingCTable_internal_default(void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ const HUF_CElt* CTable)
+{
+ return HUF_compress1X_usingCTable_internal_body(dst, dstSize, src, srcSize, CTable);
+}
+
+static size_t
+HUF_compress1X_usingCTable_internal(void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ const HUF_CElt* CTable, const int bmi2)
+{
+ if (bmi2) {
+ return HUF_compress1X_usingCTable_internal_bmi2(dst, dstSize, src, srcSize, CTable);
+ }
+ return HUF_compress1X_usingCTable_internal_default(dst, dstSize, src, srcSize, CTable);
+}
+
+#else
+
+static size_t
+HUF_compress1X_usingCTable_internal(void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ const HUF_CElt* CTable, const int bmi2)
+{
+ (void)bmi2;
+ return HUF_compress1X_usingCTable_internal_body(dst, dstSize, src, srcSize, CTable);
+}
+
+#endif
+
+size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable)
+{
+ return HUF_compress1X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, /* bmi2 */ 0);
+}
+
+
+static size_t
+HUF_compress4X_usingCTable_internal(void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ const HUF_CElt* CTable, int bmi2)
+{
+ size_t const segmentSize = (srcSize+3)/4; /* first 3 segments */
+ const BYTE* ip = (const BYTE*) src;
+ const BYTE* const iend = ip + srcSize;
+ BYTE* const ostart = (BYTE*) dst;
+ BYTE* const oend = ostart + dstSize;
+ BYTE* op = ostart;
+
+ if (dstSize < 6 + 1 + 1 + 1 + 8) return 0; /* minimum space to compress successfully */
+ if (srcSize < 12) return 0; /* no saving possible : too small input */
+ op += 6; /* jumpTable */
+
+ { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, oend-op, ip, segmentSize, CTable, bmi2) );
+ if (cSize==0) return 0;
+ assert(cSize <= 65535);
+ MEM_writeLE16(ostart, (U16)cSize);
+ op += cSize;
+ }
+
+ ip += segmentSize;
+ { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, oend-op, ip, segmentSize, CTable, bmi2) );
+ if (cSize==0) return 0;
+ assert(cSize <= 65535);
+ MEM_writeLE16(ostart+2, (U16)cSize);
+ op += cSize;
+ }
+
+ ip += segmentSize;
+ { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, oend-op, ip, segmentSize, CTable, bmi2) );
+ if (cSize==0) return 0;
+ assert(cSize <= 65535);
+ MEM_writeLE16(ostart+4, (U16)cSize);
+ op += cSize;
+ }
+
+ ip += segmentSize;
+ { CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, oend-op, ip, iend-ip, CTable, bmi2) );
+ if (cSize==0) return 0;
+ op += cSize;
+ }
+
+ return op-ostart;
+}
+
+size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable)
+{
+ return HUF_compress4X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, /* bmi2 */ 0);
+}
+
+typedef enum { HUF_singleStream, HUF_fourStreams } HUF_nbStreams_e;
+
+static size_t HUF_compressCTable_internal(
+ BYTE* const ostart, BYTE* op, BYTE* const oend,
+ const void* src, size_t srcSize,
+ HUF_nbStreams_e nbStreams, const HUF_CElt* CTable, const int bmi2)
+{
+ size_t const cSize = (nbStreams==HUF_singleStream) ?
+ HUF_compress1X_usingCTable_internal(op, oend - op, src, srcSize, CTable, bmi2) :
+ HUF_compress4X_usingCTable_internal(op, oend - op, src, srcSize, CTable, bmi2);
+ if (HUF_isError(cSize)) { return cSize; }
+ if (cSize==0) { return 0; } /* uncompressible */
+ op += cSize;
+ /* check compressibility */
+ if ((size_t)(op-ostart) >= srcSize-1) { return 0; }
+ return op-ostart;
+}
+
+typedef struct {
+ unsigned count[HUF_SYMBOLVALUE_MAX + 1];
+ HUF_CElt CTable[HUF_SYMBOLVALUE_MAX + 1];
+ huffNodeTable nodeTable;
+} HUF_compress_tables_t;
+
+/* HUF_compress_internal() :
+ * `workSpace` must a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */
+static size_t
+HUF_compress_internal (void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ unsigned maxSymbolValue, unsigned huffLog,
+ HUF_nbStreams_e nbStreams,
+ void* workSpace, size_t wkspSize,
+ HUF_CElt* oldHufTable, HUF_repeat* repeat, int preferRepeat,
+ const int bmi2)
+{
+ HUF_compress_tables_t* const table = (HUF_compress_tables_t*)workSpace;
+ BYTE* const ostart = (BYTE*)dst;
+ BYTE* const oend = ostart + dstSize;
+ BYTE* op = ostart;
+
+ /* checks & inits */
+ if (((size_t)workSpace & 3) != 0) return ERROR(GENERIC); /* must be aligned on 4-bytes boundaries */
+ if (wkspSize < HUF_WORKSPACE_SIZE) return ERROR(workSpace_tooSmall);
+ if (!srcSize) return 0; /* Uncompressed */
+ if (!dstSize) return 0; /* cannot fit anything within dst budget */
+ if (srcSize > HUF_BLOCKSIZE_MAX) return ERROR(srcSize_wrong); /* current block size limit */
+ if (huffLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge);
+ if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR(maxSymbolValue_tooLarge);
+ if (!maxSymbolValue) maxSymbolValue = HUF_SYMBOLVALUE_MAX;
+ if (!huffLog) huffLog = HUF_TABLELOG_DEFAULT;
+
+ /* Heuristic : If old table is valid, use it for small inputs */
+ if (preferRepeat && repeat && *repeat == HUF_repeat_valid) {
+ return HUF_compressCTable_internal(ostart, op, oend,
+ src, srcSize,
+ nbStreams, oldHufTable, bmi2);
+ }
+
+ /* Scan input and build symbol stats */
+ { CHECK_V_F(largest, HIST_count_wksp (table->count, &maxSymbolValue, (const BYTE*)src, srcSize, workSpace, wkspSize) );
+ if (largest == srcSize) { *ostart = ((const BYTE*)src)[0]; return 1; } /* single symbol, rle */
+ if (largest <= (srcSize >> 7)+4) return 0; /* heuristic : probably not compressible enough */
+ }
+
+ /* Check validity of previous table */
+ if ( repeat
+ && *repeat == HUF_repeat_check
+ && !HUF_validateCTable(oldHufTable, table->count, maxSymbolValue)) {
+ *repeat = HUF_repeat_none;
+ }
+ /* Heuristic : use existing table for small inputs */
+ if (preferRepeat && repeat && *repeat != HUF_repeat_none) {
+ return HUF_compressCTable_internal(ostart, op, oend,
+ src, srcSize,
+ nbStreams, oldHufTable, bmi2);
+ }
+
+ /* Build Huffman Tree */
+ huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue);
+ { size_t const maxBits = HUF_buildCTable_wksp(table->CTable, table->count,
+ maxSymbolValue, huffLog,
+ table->nodeTable, sizeof(table->nodeTable));
+ CHECK_F(maxBits);
+ huffLog = (U32)maxBits;
+ /* Zero unused symbols in CTable, so we can check it for validity */
+ memset(table->CTable + (maxSymbolValue + 1), 0,
+ sizeof(table->CTable) - ((maxSymbolValue + 1) * sizeof(HUF_CElt)));
+ }
+
+ /* Write table description header */
+ { CHECK_V_F(hSize, HUF_writeCTable (op, dstSize, table->CTable, maxSymbolValue, huffLog) );
+ /* Check if using previous huffman table is beneficial */
+ if (repeat && *repeat != HUF_repeat_none) {
+ size_t const oldSize = HUF_estimateCompressedSize(oldHufTable, table->count, maxSymbolValue);
+ size_t const newSize = HUF_estimateCompressedSize(table->CTable, table->count, maxSymbolValue);
+ if (oldSize <= hSize + newSize || hSize + 12 >= srcSize) {
+ return HUF_compressCTable_internal(ostart, op, oend,
+ src, srcSize,
+ nbStreams, oldHufTable, bmi2);
+ } }
+
+ /* Use the new huffman table */
+ if (hSize + 12ul >= srcSize) { return 0; }
+ op += hSize;
+ if (repeat) { *repeat = HUF_repeat_none; }
+ if (oldHufTable)
+ memcpy(oldHufTable, table->CTable, sizeof(table->CTable)); /* Save new table */
+ }
+ return HUF_compressCTable_internal(ostart, op, oend,
+ src, srcSize,
+ nbStreams, table->CTable, bmi2);
+}
+
+
+size_t HUF_compress1X_wksp (void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ unsigned maxSymbolValue, unsigned huffLog,
+ void* workSpace, size_t wkspSize)
+{
+ return HUF_compress_internal(dst, dstSize, src, srcSize,
+ maxSymbolValue, huffLog, HUF_singleStream,
+ workSpace, wkspSize,
+ NULL, NULL, 0, 0 /*bmi2*/);
+}
+
+size_t HUF_compress1X_repeat (void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ unsigned maxSymbolValue, unsigned huffLog,
+ void* workSpace, size_t wkspSize,
+ HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2)
+{
+ return HUF_compress_internal(dst, dstSize, src, srcSize,
+ maxSymbolValue, huffLog, HUF_singleStream,
+ workSpace, wkspSize, hufTable,
+ repeat, preferRepeat, bmi2);
+}
+
+size_t HUF_compress1X (void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ unsigned maxSymbolValue, unsigned huffLog)
+{
+ unsigned workSpace[HUF_WORKSPACE_SIZE_U32];
+ return HUF_compress1X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace));
+}
+
+/* HUF_compress4X_repeat():
+ * compress input using 4 streams.
+ * provide workspace to generate compression tables */
+size_t HUF_compress4X_wksp (void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ unsigned maxSymbolValue, unsigned huffLog,
+ void* workSpace, size_t wkspSize)
+{
+ return HUF_compress_internal(dst, dstSize, src, srcSize,
+ maxSymbolValue, huffLog, HUF_fourStreams,
+ workSpace, wkspSize,
+ NULL, NULL, 0, 0 /*bmi2*/);
+}
+
+/* HUF_compress4X_repeat():
+ * compress input using 4 streams.
+ * re-use an existing huffman compression table */
+size_t HUF_compress4X_repeat (void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ unsigned maxSymbolValue, unsigned huffLog,
+ void* workSpace, size_t wkspSize,
+ HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2)
+{
+ return HUF_compress_internal(dst, dstSize, src, srcSize,
+ maxSymbolValue, huffLog, HUF_fourStreams,
+ workSpace, wkspSize,
+ hufTable, repeat, preferRepeat, bmi2);
+}
+
+size_t HUF_compress2 (void* dst, size_t dstSize,
+ const void* src, size_t srcSize,
+ unsigned maxSymbolValue, unsigned huffLog)
+{
+ unsigned workSpace[HUF_WORKSPACE_SIZE_U32];
+ return HUF_compress4X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace));
+}
+
+size_t HUF_compress (void* dst, size_t maxDstSize, const void* src, size_t srcSize)
+{
+ return HUF_compress2(dst, maxDstSize, src, srcSize, 255, HUF_TABLELOG_DEFAULT);
+}
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress.c b/Utilities/cmzstd/lib/compress/zstd_compress.c
new file mode 100644
index 000000000..c2c9d3bc5
--- /dev/null
+++ b/Utilities/cmzstd/lib/compress/zstd_compress.c
@@ -0,0 +1,4290 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+/*-*************************************
+* Dependencies
+***************************************/
+#include <limits.h> /* INT_MAX */
+#include <string.h> /* memset */
+#include "cpu.h"
+#include "mem.h"
+#include "hist.h" /* HIST_countFast_wksp */
+#define FSE_STATIC_LINKING_ONLY /* FSE_encodeSymbol */
+#include "fse.h"
+#define HUF_STATIC_LINKING_ONLY
+#include "huf.h"
+#include "zstd_compress_internal.h"
+#include "zstd_fast.h"
+#include "zstd_double_fast.h"
+#include "zstd_lazy.h"
+#include "zstd_opt.h"
+#include "zstd_ldm.h"
+
+
+/*-*************************************
+* Helper functions
+***************************************/
+size_t ZSTD_compressBound(size_t srcSize) {
+ return ZSTD_COMPRESSBOUND(srcSize);
+}
+
+
+/*-*************************************
+* Context memory management
+***************************************/
+struct ZSTD_CDict_s {
+ void* dictBuffer;
+ const void* dictContent;
+ size_t dictContentSize;
+ void* workspace;
+ size_t workspaceSize;
+ ZSTD_matchState_t matchState;
+ ZSTD_compressedBlockState_t cBlockState;
+ ZSTD_customMem customMem;
+ U32 dictID;
+}; /* typedef'd to ZSTD_CDict within "zstd.h" */
+
+ZSTD_CCtx* ZSTD_createCCtx(void)
+{
+ return ZSTD_createCCtx_advanced(ZSTD_defaultCMem);
+}
+
+static void ZSTD_initCCtx(ZSTD_CCtx* cctx, ZSTD_customMem memManager)
+{
+ assert(cctx != NULL);
+ memset(cctx, 0, sizeof(*cctx));
+ cctx->customMem = memManager;
+ cctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid());
+ { size_t const err = ZSTD_CCtx_reset(cctx, ZSTD_reset_parameters);
+ assert(!ZSTD_isError(err));
+ (void)err;
+ }
+}
+
+ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem)
+{
+ ZSTD_STATIC_ASSERT(zcss_init==0);
+ ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_UNKNOWN==(0ULL - 1));
+ if (!customMem.customAlloc ^ !customMem.customFree) return NULL;
+ { ZSTD_CCtx* const cctx = (ZSTD_CCtx*)ZSTD_malloc(sizeof(ZSTD_CCtx), customMem);
+ if (!cctx) return NULL;
+ ZSTD_initCCtx(cctx, customMem);
+ return cctx;
+ }
+}
+
+ZSTD_CCtx* ZSTD_initStaticCCtx(void *workspace, size_t workspaceSize)
+{
+ ZSTD_CCtx* const cctx = (ZSTD_CCtx*) workspace;
+ if (workspaceSize <= sizeof(ZSTD_CCtx)) return NULL; /* minimum size */
+ if ((size_t)workspace & 7) return NULL; /* must be 8-aligned */
+ memset(workspace, 0, workspaceSize); /* may be a bit generous, could memset be smaller ? */
+ cctx->staticSize = workspaceSize;
+ cctx->workSpace = (void*)(cctx+1);
+ cctx->workSpaceSize = workspaceSize - sizeof(ZSTD_CCtx);
+
+ /* statically sized space. entropyWorkspace never moves (but prev/next block swap places) */
+ if (cctx->workSpaceSize < HUF_WORKSPACE_SIZE + 2 * sizeof(ZSTD_compressedBlockState_t)) return NULL;
+ assert(((size_t)cctx->workSpace & (sizeof(void*)-1)) == 0); /* ensure correct alignment */
+ cctx->blockState.prevCBlock = (ZSTD_compressedBlockState_t*)cctx->workSpace;
+ cctx->blockState.nextCBlock = cctx->blockState.prevCBlock + 1;
+ {
+ void* const ptr = cctx->blockState.nextCBlock + 1;
+ cctx->entropyWorkspace = (U32*)ptr;
+ }
+ cctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid());
+ return cctx;
+}
+
+static void ZSTD_freeCCtxContent(ZSTD_CCtx* cctx)
+{
+ assert(cctx != NULL);
+ assert(cctx->staticSize == 0);
+ ZSTD_free(cctx->workSpace, cctx->customMem); cctx->workSpace = NULL;
+ ZSTD_freeCDict(cctx->cdictLocal); cctx->cdictLocal = NULL;
+#ifdef ZSTD_MULTITHREAD
+ ZSTDMT_freeCCtx(cctx->mtctx); cctx->mtctx = NULL;
+#endif
+}
+
+size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx)
+{
+ if (cctx==NULL) return 0; /* support free on NULL */
+ if (cctx->staticSize) return ERROR(memory_allocation); /* not compatible with static CCtx */
+ ZSTD_freeCCtxContent(cctx);
+ ZSTD_free(cctx, cctx->customMem);
+ return 0;
+}
+
+
+static size_t ZSTD_sizeof_mtctx(const ZSTD_CCtx* cctx)
+{
+#ifdef ZSTD_MULTITHREAD
+ return ZSTDMT_sizeof_CCtx(cctx->mtctx);
+#else
+ (void)cctx;
+ return 0;
+#endif
+}
+
+
+size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx)
+{
+ if (cctx==NULL) return 0; /* support sizeof on NULL */
+ return sizeof(*cctx) + cctx->workSpaceSize
+ + ZSTD_sizeof_CDict(cctx->cdictLocal)
+ + ZSTD_sizeof_mtctx(cctx);
+}
+
+size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs)
+{
+ return ZSTD_sizeof_CCtx(zcs); /* same object */
+}
+
+/* private API call, for dictBuilder only */
+const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx) { return &(ctx->seqStore); }
+
+static ZSTD_CCtx_params ZSTD_makeCCtxParamsFromCParams(
+ ZSTD_compressionParameters cParams)
+{
+ ZSTD_CCtx_params cctxParams;
+ memset(&cctxParams, 0, sizeof(cctxParams));
+ cctxParams.cParams = cParams;
+ cctxParams.compressionLevel = ZSTD_CLEVEL_DEFAULT; /* should not matter, as all cParams are presumed properly defined */
+ assert(!ZSTD_checkCParams(cParams));
+ cctxParams.fParams.contentSizeFlag = 1;
+ return cctxParams;
+}
+
+static ZSTD_CCtx_params* ZSTD_createCCtxParams_advanced(
+ ZSTD_customMem customMem)
+{
+ ZSTD_CCtx_params* params;
+ if (!customMem.customAlloc ^ !customMem.customFree) return NULL;
+ params = (ZSTD_CCtx_params*)ZSTD_calloc(
+ sizeof(ZSTD_CCtx_params), customMem);
+ if (!params) { return NULL; }
+ params->customMem = customMem;
+ params->compressionLevel = ZSTD_CLEVEL_DEFAULT;
+ params->fParams.contentSizeFlag = 1;
+ return params;
+}
+
+ZSTD_CCtx_params* ZSTD_createCCtxParams(void)
+{
+ return ZSTD_createCCtxParams_advanced(ZSTD_defaultCMem);
+}
+
+size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params)
+{
+ if (params == NULL) { return 0; }
+ ZSTD_free(params, params->customMem);
+ return 0;
+}
+
+size_t ZSTD_CCtxParams_reset(ZSTD_CCtx_params* params)
+{
+ return ZSTD_CCtxParams_init(params, ZSTD_CLEVEL_DEFAULT);
+}
+
+size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel) {
+ if (!cctxParams) { return ERROR(GENERIC); }
+ memset(cctxParams, 0, sizeof(*cctxParams));
+ cctxParams->compressionLevel = compressionLevel;
+ cctxParams->fParams.contentSizeFlag = 1;
+ return 0;
+}
+
+size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params)
+{
+ if (!cctxParams) { return ERROR(GENERIC); }
+ CHECK_F( ZSTD_checkCParams(params.cParams) );
+ memset(cctxParams, 0, sizeof(*cctxParams));
+ cctxParams->cParams = params.cParams;
+ cctxParams->fParams = params.fParams;
+ cctxParams->compressionLevel = ZSTD_CLEVEL_DEFAULT; /* should not matter, as all cParams are presumed properly defined */
+ assert(!ZSTD_checkCParams(params.cParams));
+ return 0;
+}
+
+/* ZSTD_assignParamsToCCtxParams() :
+ * params is presumed valid at this stage */
+static ZSTD_CCtx_params ZSTD_assignParamsToCCtxParams(
+ ZSTD_CCtx_params cctxParams, ZSTD_parameters params)
+{
+ ZSTD_CCtx_params ret = cctxParams;
+ ret.cParams = params.cParams;
+ ret.fParams = params.fParams;
+ ret.compressionLevel = ZSTD_CLEVEL_DEFAULT; /* should not matter, as all cParams are presumed properly defined */
+ assert(!ZSTD_checkCParams(params.cParams));
+ return ret;
+}
+
+ZSTD_bounds ZSTD_cParam_getBounds(ZSTD_cParameter param)
+{
+ ZSTD_bounds bounds = { 0, 0, 0 };
+
+ switch(param)
+ {
+ case ZSTD_c_compressionLevel:
+ bounds.lowerBound = ZSTD_minCLevel();
+ bounds.upperBound = ZSTD_maxCLevel();
+ return bounds;
+
+ case ZSTD_c_windowLog:
+ bounds.lowerBound = ZSTD_WINDOWLOG_MIN;
+ bounds.upperBound = ZSTD_WINDOWLOG_MAX;
+ return bounds;
+
+ case ZSTD_c_hashLog:
+ bounds.lowerBound = ZSTD_HASHLOG_MIN;
+ bounds.upperBound = ZSTD_HASHLOG_MAX;
+ return bounds;
+
+ case ZSTD_c_chainLog:
+ bounds.lowerBound = ZSTD_CHAINLOG_MIN;
+ bounds.upperBound = ZSTD_CHAINLOG_MAX;
+ return bounds;
+
+ case ZSTD_c_searchLog:
+ bounds.lowerBound = ZSTD_SEARCHLOG_MIN;
+ bounds.upperBound = ZSTD_SEARCHLOG_MAX;
+ return bounds;
+
+ case ZSTD_c_minMatch:
+ bounds.lowerBound = ZSTD_MINMATCH_MIN;
+ bounds.upperBound = ZSTD_MINMATCH_MAX;
+ return bounds;
+
+ case ZSTD_c_targetLength:
+ bounds.lowerBound = ZSTD_TARGETLENGTH_MIN;
+ bounds.upperBound = ZSTD_TARGETLENGTH_MAX;
+ return bounds;
+
+ case ZSTD_c_strategy:
+ bounds.lowerBound = ZSTD_STRATEGY_MIN;
+ bounds.upperBound = ZSTD_STRATEGY_MAX;
+ return bounds;
+
+ case ZSTD_c_contentSizeFlag:
+ bounds.lowerBound = 0;
+ bounds.upperBound = 1;
+ return bounds;
+
+ case ZSTD_c_checksumFlag:
+ bounds.lowerBound = 0;
+ bounds.upperBound = 1;
+ return bounds;
+
+ case ZSTD_c_dictIDFlag:
+ bounds.lowerBound = 0;
+ bounds.upperBound = 1;
+ return bounds;
+
+ case ZSTD_c_nbWorkers:
+ bounds.lowerBound = 0;
+#ifdef ZSTD_MULTITHREAD
+ bounds.upperBound = ZSTDMT_NBWORKERS_MAX;
+#else
+ bounds.upperBound = 0;
+#endif
+ return bounds;
+
+ case ZSTD_c_jobSize:
+ bounds.lowerBound = 0;
+#ifdef ZSTD_MULTITHREAD
+ bounds.upperBound = ZSTDMT_JOBSIZE_MAX;
+#else
+ bounds.upperBound = 0;
+#endif
+ return bounds;
+
+ case ZSTD_c_overlapLog:
+ bounds.lowerBound = ZSTD_OVERLAPLOG_MIN;
+ bounds.upperBound = ZSTD_OVERLAPLOG_MAX;
+ return bounds;
+
+ case ZSTD_c_enableLongDistanceMatching:
+ bounds.lowerBound = 0;
+ bounds.upperBound = 1;
+ return bounds;
+
+ case ZSTD_c_ldmHashLog:
+ bounds.lowerBound = ZSTD_LDM_HASHLOG_MIN;
+ bounds.upperBound = ZSTD_LDM_HASHLOG_MAX;
+ return bounds;
+
+ case ZSTD_c_ldmMinMatch:
+ bounds.lowerBound = ZSTD_LDM_MINMATCH_MIN;
+ bounds.upperBound = ZSTD_LDM_MINMATCH_MAX;
+ return bounds;
+
+ case ZSTD_c_ldmBucketSizeLog:
+ bounds.lowerBound = ZSTD_LDM_BUCKETSIZELOG_MIN;
+ bounds.upperBound = ZSTD_LDM_BUCKETSIZELOG_MAX;
+ return bounds;
+
+ case ZSTD_c_ldmHashRateLog:
+ bounds.lowerBound = ZSTD_LDM_HASHRATELOG_MIN;
+ bounds.upperBound = ZSTD_LDM_HASHRATELOG_MAX;
+ return bounds;
+
+ /* experimental parameters */
+ case ZSTD_c_rsyncable:
+ bounds.lowerBound = 0;
+ bounds.upperBound = 1;
+ return bounds;
+
+ case ZSTD_c_forceMaxWindow :
+ bounds.lowerBound = 0;
+ bounds.upperBound = 1;
+ return bounds;
+
+ case ZSTD_c_format:
+ ZSTD_STATIC_ASSERT(ZSTD_f_zstd1 < ZSTD_f_zstd1_magicless);
+ bounds.lowerBound = ZSTD_f_zstd1;
+ bounds.upperBound = ZSTD_f_zstd1_magicless; /* note : how to ensure at compile time that this is the highest value enum ? */
+ return bounds;
+
+ case ZSTD_c_forceAttachDict:
+ ZSTD_STATIC_ASSERT(ZSTD_dictDefaultAttach < ZSTD_dictForceCopy);
+ bounds.lowerBound = ZSTD_dictDefaultAttach;
+ bounds.upperBound = ZSTD_dictForceCopy; /* note : how to ensure at compile time that this is the highest value enum ? */
+ return bounds;
+
+ default:
+ { ZSTD_bounds const boundError = { ERROR(parameter_unsupported), 0, 0 };
+ return boundError;
+ }
+ }
+}
+
+/* ZSTD_cParam_withinBounds:
+ * @return 1 if value is within cParam bounds,
+ * 0 otherwise */
+static int ZSTD_cParam_withinBounds(ZSTD_cParameter cParam, int value)
+{
+ ZSTD_bounds const bounds = ZSTD_cParam_getBounds(cParam);
+ if (ZSTD_isError(bounds.error)) return 0;
+ if (value < bounds.lowerBound) return 0;
+ if (value > bounds.upperBound) return 0;
+ return 1;
+}
+
+#define BOUNDCHECK(cParam, val) { \
+ if (!ZSTD_cParam_withinBounds(cParam,val)) { \
+ return ERROR(parameter_outOfBound); \
+} }
+
+
+static int ZSTD_isUpdateAuthorized(ZSTD_cParameter param)
+{
+ switch(param)
+ {
+ case ZSTD_c_compressionLevel:
+ case ZSTD_c_hashLog:
+ case ZSTD_c_chainLog:
+ case ZSTD_c_searchLog:
+ case ZSTD_c_minMatch:
+ case ZSTD_c_targetLength:
+ case ZSTD_c_strategy:
+ return 1;
+
+ case ZSTD_c_format:
+ case ZSTD_c_windowLog:
+ case ZSTD_c_contentSizeFlag:
+ case ZSTD_c_checksumFlag:
+ case ZSTD_c_dictIDFlag:
+ case ZSTD_c_forceMaxWindow :
+ case ZSTD_c_nbWorkers:
+ case ZSTD_c_jobSize:
+ case ZSTD_c_overlapLog:
+ case ZSTD_c_rsyncable:
+ case ZSTD_c_enableLongDistanceMatching:
+ case ZSTD_c_ldmHashLog:
+ case ZSTD_c_ldmMinMatch:
+ case ZSTD_c_ldmBucketSizeLog:
+ case ZSTD_c_ldmHashRateLog:
+ case ZSTD_c_forceAttachDict:
+ default:
+ return 0;
+ }
+}
+
+size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int value)
+{
+ DEBUGLOG(4, "ZSTD_CCtx_setParameter (%i, %i)", (int)param, value);
+ if (cctx->streamStage != zcss_init) {
+ if (ZSTD_isUpdateAuthorized(param)) {
+ cctx->cParamsChanged = 1;
+ } else {
+ return ERROR(stage_wrong);
+ } }
+
+ switch(param)
+ {
+ case ZSTD_c_format :
+ return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value);
+
+ case ZSTD_c_compressionLevel:
+ if (cctx->cdict) return ERROR(stage_wrong);
+ return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value);
+
+ case ZSTD_c_windowLog:
+ case ZSTD_c_hashLog:
+ case ZSTD_c_chainLog:
+ case ZSTD_c_searchLog:
+ case ZSTD_c_minMatch:
+ case ZSTD_c_targetLength:
+ case ZSTD_c_strategy:
+ if (cctx->cdict) return ERROR(stage_wrong);
+ return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value);
+
+ case ZSTD_c_contentSizeFlag:
+ case ZSTD_c_checksumFlag:
+ case ZSTD_c_dictIDFlag:
+ return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value);
+
+ case ZSTD_c_forceMaxWindow : /* Force back-references to remain < windowSize,
+ * even when referencing into Dictionary content.
+ * default : 0 when using a CDict, 1 when using a Prefix */
+ return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value);
+
+ case ZSTD_c_forceAttachDict:
+ return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value);
+
+ case ZSTD_c_nbWorkers:
+ if ((value!=0) && cctx->staticSize) {
+ return ERROR(parameter_unsupported); /* MT not compatible with static alloc */
+ }
+ return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value);
+
+ case ZSTD_c_jobSize:
+ case ZSTD_c_overlapLog:
+ case ZSTD_c_rsyncable:
+ return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value);
+
+ case ZSTD_c_enableLongDistanceMatching:
+ case ZSTD_c_ldmHashLog:
+ case ZSTD_c_ldmMinMatch:
+ case ZSTD_c_ldmBucketSizeLog:
+ case ZSTD_c_ldmHashRateLog:
+ if (cctx->cdict) return ERROR(stage_wrong);
+ return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value);
+
+ default: return ERROR(parameter_unsupported);
+ }
+}
+
+size_t ZSTD_CCtxParam_setParameter(ZSTD_CCtx_params* CCtxParams,
+ ZSTD_cParameter param, int value)
+{
+ DEBUGLOG(4, "ZSTD_CCtxParam_setParameter (%i, %i)", (int)param, value);
+ switch(param)
+ {
+ case ZSTD_c_format :
+ BOUNDCHECK(ZSTD_c_format, value);
+ CCtxParams->format = (ZSTD_format_e)value;
+ return (size_t)CCtxParams->format;
+
+ case ZSTD_c_compressionLevel : {
+ int cLevel = value;
+ if (cLevel > ZSTD_maxCLevel()) cLevel = ZSTD_maxCLevel();
+ if (cLevel < ZSTD_minCLevel()) cLevel = ZSTD_minCLevel();
+ if (cLevel) { /* 0 : does not change current level */
+ CCtxParams->compressionLevel = cLevel;
+ }
+ if (CCtxParams->compressionLevel >= 0) return CCtxParams->compressionLevel;
+ return 0; /* return type (size_t) cannot represent negative values */
+ }
+
+ case ZSTD_c_windowLog :
+ if (value!=0) /* 0 => use default */
+ BOUNDCHECK(ZSTD_c_windowLog, value);
+ CCtxParams->cParams.windowLog = value;
+ return CCtxParams->cParams.windowLog;
+
+ case ZSTD_c_hashLog :
+ if (value!=0) /* 0 => use default */
+ BOUNDCHECK(ZSTD_c_hashLog, value);
+ CCtxParams->cParams.hashLog = value;
+ return CCtxParams->cParams.hashLog;
+
+ case ZSTD_c_chainLog :
+ if (value!=0) /* 0 => use default */
+ BOUNDCHECK(ZSTD_c_chainLog, value);
+ CCtxParams->cParams.chainLog = value;
+ return CCtxParams->cParams.chainLog;
+
+ case ZSTD_c_searchLog :
+ if (value!=0) /* 0 => use default */
+ BOUNDCHECK(ZSTD_c_searchLog, value);
+ CCtxParams->cParams.searchLog = value;
+ return value;
+
+ case ZSTD_c_minMatch :
+ if (value!=0) /* 0 => use default */
+ BOUNDCHECK(ZSTD_c_minMatch, value);
+ CCtxParams->cParams.minMatch = value;
+ return CCtxParams->cParams.minMatch;
+
+ case ZSTD_c_targetLength :
+ BOUNDCHECK(ZSTD_c_targetLength, value);
+ CCtxParams->cParams.targetLength = value;
+ return CCtxParams->cParams.targetLength;
+
+ case ZSTD_c_strategy :
+ if (value!=0) /* 0 => use default */
+ BOUNDCHECK(ZSTD_c_strategy, value);
+ CCtxParams->cParams.strategy = (ZSTD_strategy)value;
+ return (size_t)CCtxParams->cParams.strategy;
+
+ case ZSTD_c_contentSizeFlag :
+ /* Content size written in frame header _when known_ (default:1) */
+ DEBUGLOG(4, "set content size flag = %u", (value!=0));
+ CCtxParams->fParams.contentSizeFlag = value != 0;
+ return CCtxParams->fParams.contentSizeFlag;
+
+ case ZSTD_c_checksumFlag :
+ /* A 32-bits content checksum will be calculated and written at end of frame (default:0) */
+ CCtxParams->fParams.checksumFlag = value != 0;
+ return CCtxParams->fParams.checksumFlag;
+
+ case ZSTD_c_dictIDFlag : /* When applicable, dictionary's dictID is provided in frame header (default:1) */
+ DEBUGLOG(4, "set dictIDFlag = %u", (value!=0));
+ CCtxParams->fParams.noDictIDFlag = !value;
+ return !CCtxParams->fParams.noDictIDFlag;
+
+ case ZSTD_c_forceMaxWindow :
+ CCtxParams->forceWindow = (value != 0);
+ return CCtxParams->forceWindow;
+
+ case ZSTD_c_forceAttachDict : {
+ const ZSTD_dictAttachPref_e pref = (ZSTD_dictAttachPref_e)value;
+ BOUNDCHECK(ZSTD_c_forceAttachDict, pref);
+ CCtxParams->attachDictPref = pref;
+ return CCtxParams->attachDictPref;
+ }
+
+ case ZSTD_c_nbWorkers :
+#ifndef ZSTD_MULTITHREAD
+ if (value!=0) return ERROR(parameter_unsupported);
+ return 0;
+#else
+ return ZSTDMT_CCtxParam_setNbWorkers(CCtxParams, value);
+#endif
+
+ case ZSTD_c_jobSize :
+#ifndef ZSTD_MULTITHREAD
+ return ERROR(parameter_unsupported);
+#else
+ return ZSTDMT_CCtxParam_setMTCtxParameter(CCtxParams, ZSTDMT_p_jobSize, value);
+#endif
+
+ case ZSTD_c_overlapLog :
+#ifndef ZSTD_MULTITHREAD
+ return ERROR(parameter_unsupported);
+#else
+ return ZSTDMT_CCtxParam_setMTCtxParameter(CCtxParams, ZSTDMT_p_overlapLog, value);
+#endif
+
+ case ZSTD_c_rsyncable :
+#ifndef ZSTD_MULTITHREAD
+ return ERROR(parameter_unsupported);
+#else
+ return ZSTDMT_CCtxParam_setMTCtxParameter(CCtxParams, ZSTDMT_p_rsyncable, value);
+#endif
+
+ case ZSTD_c_enableLongDistanceMatching :
+ CCtxParams->ldmParams.enableLdm = (value!=0);
+ return CCtxParams->ldmParams.enableLdm;
+
+ case ZSTD_c_ldmHashLog :
+ if (value!=0) /* 0 ==> auto */
+ BOUNDCHECK(ZSTD_c_ldmHashLog, value);
+ CCtxParams->ldmParams.hashLog = value;
+ return CCtxParams->ldmParams.hashLog;
+
+ case ZSTD_c_ldmMinMatch :
+ if (value!=0) /* 0 ==> default */
+ BOUNDCHECK(ZSTD_c_ldmMinMatch, value);
+ CCtxParams->ldmParams.minMatchLength = value;
+ return CCtxParams->ldmParams.minMatchLength;
+
+ case ZSTD_c_ldmBucketSizeLog :
+ if (value!=0) /* 0 ==> default */
+ BOUNDCHECK(ZSTD_c_ldmBucketSizeLog, value);
+ CCtxParams->ldmParams.bucketSizeLog = value;
+ return CCtxParams->ldmParams.bucketSizeLog;
+
+ case ZSTD_c_ldmHashRateLog :
+ if (value > ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN)
+ return ERROR(parameter_outOfBound);
+ CCtxParams->ldmParams.hashRateLog = value;
+ return CCtxParams->ldmParams.hashRateLog;
+
+ default: return ERROR(parameter_unsupported);
+ }
+}
+
+size_t ZSTD_CCtx_getParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int* value)
+{
+ return ZSTD_CCtxParam_getParameter(&cctx->requestedParams, param, value);
+}
+
+size_t ZSTD_CCtxParam_getParameter(
+ ZSTD_CCtx_params* CCtxParams, ZSTD_cParameter param, int* value)
+{
+ switch(param)
+ {
+ case ZSTD_c_format :
+ *value = CCtxParams->format;
+ break;
+ case ZSTD_c_compressionLevel :
+ *value = CCtxParams->compressionLevel;
+ break;
+ case ZSTD_c_windowLog :
+ *value = CCtxParams->cParams.windowLog;
+ break;
+ case ZSTD_c_hashLog :
+ *value = CCtxParams->cParams.hashLog;
+ break;
+ case ZSTD_c_chainLog :
+ *value = CCtxParams->cParams.chainLog;
+ break;
+ case ZSTD_c_searchLog :
+ *value = CCtxParams->cParams.searchLog;
+ break;
+ case ZSTD_c_minMatch :
+ *value = CCtxParams->cParams.minMatch;
+ break;
+ case ZSTD_c_targetLength :
+ *value = CCtxParams->cParams.targetLength;
+ break;
+ case ZSTD_c_strategy :
+ *value = (unsigned)CCtxParams->cParams.strategy;
+ break;
+ case ZSTD_c_contentSizeFlag :
+ *value = CCtxParams->fParams.contentSizeFlag;
+ break;
+ case ZSTD_c_checksumFlag :
+ *value = CCtxParams->fParams.checksumFlag;
+ break;
+ case ZSTD_c_dictIDFlag :
+ *value = !CCtxParams->fParams.noDictIDFlag;
+ break;
+ case ZSTD_c_forceMaxWindow :
+ *value = CCtxParams->forceWindow;
+ break;
+ case ZSTD_c_forceAttachDict :
+ *value = CCtxParams->attachDictPref;
+ break;
+ case ZSTD_c_nbWorkers :
+#ifndef ZSTD_MULTITHREAD
+ assert(CCtxParams->nbWorkers == 0);
+#endif
+ *value = CCtxParams->nbWorkers;
+ break;
+ case ZSTD_c_jobSize :
+#ifndef ZSTD_MULTITHREAD
+ return ERROR(parameter_unsupported);
+#else
+ assert(CCtxParams->jobSize <= INT_MAX);
+ *value = (int)CCtxParams->jobSize;
+ break;
+#endif
+ case ZSTD_c_overlapLog :
+#ifndef ZSTD_MULTITHREAD
+ return ERROR(parameter_unsupported);
+#else
+ *value = CCtxParams->overlapLog;
+ break;
+#endif
+ case ZSTD_c_rsyncable :
+#ifndef ZSTD_MULTITHREAD
+ return ERROR(parameter_unsupported);
+#else
+ *value = CCtxParams->rsyncable;
+ break;
+#endif
+ case ZSTD_c_enableLongDistanceMatching :
+ *value = CCtxParams->ldmParams.enableLdm;
+ break;
+ case ZSTD_c_ldmHashLog :
+ *value = CCtxParams->ldmParams.hashLog;
+ break;
+ case ZSTD_c_ldmMinMatch :
+ *value = CCtxParams->ldmParams.minMatchLength;
+ break;
+ case ZSTD_c_ldmBucketSizeLog :
+ *value = CCtxParams->ldmParams.bucketSizeLog;
+ break;
+ case ZSTD_c_ldmHashRateLog :
+ *value = CCtxParams->ldmParams.hashRateLog;
+ break;
+ default: return ERROR(parameter_unsupported);
+ }
+ return 0;
+}
+
+/** ZSTD_CCtx_setParametersUsingCCtxParams() :
+ * just applies `params` into `cctx`
+ * no action is performed, parameters are merely stored.
+ * If ZSTDMT is enabled, parameters are pushed to cctx->mtctx.
+ * This is possible even if a compression is ongoing.
+ * In which case, new parameters will be applied on the fly, starting with next compression job.
+ */
+size_t ZSTD_CCtx_setParametersUsingCCtxParams(
+ ZSTD_CCtx* cctx, const ZSTD_CCtx_params* params)
+{
+ DEBUGLOG(4, "ZSTD_CCtx_setParametersUsingCCtxParams");
+ if (cctx->streamStage != zcss_init) return ERROR(stage_wrong);
+ if (cctx->cdict) return ERROR(stage_wrong);
+
+ cctx->requestedParams = *params;
+ return 0;
+}
+
+ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize)
+{
+ DEBUGLOG(4, "ZSTD_CCtx_setPledgedSrcSize to %u bytes", (U32)pledgedSrcSize);
+ if (cctx->streamStage != zcss_init) return ERROR(stage_wrong);
+ cctx->pledgedSrcSizePlusOne = pledgedSrcSize+1;
+ return 0;
+}
+
+size_t ZSTD_CCtx_loadDictionary_advanced(
+ ZSTD_CCtx* cctx, const void* dict, size_t dictSize,
+ ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType)
+{
+ if (cctx->streamStage != zcss_init) return ERROR(stage_wrong);
+ if (cctx->staticSize) return ERROR(memory_allocation); /* no malloc for static CCtx */
+ DEBUGLOG(4, "ZSTD_CCtx_loadDictionary_advanced (size: %u)", (U32)dictSize);
+ ZSTD_freeCDict(cctx->cdictLocal); /* in case one already exists */
+ if (dict==NULL || dictSize==0) { /* no dictionary mode */
+ cctx->cdictLocal = NULL;
+ cctx->cdict = NULL;
+ } else {
+ ZSTD_compressionParameters const cParams =
+ ZSTD_getCParamsFromCCtxParams(&cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, dictSize);
+ cctx->cdictLocal = ZSTD_createCDict_advanced(
+ dict, dictSize,
+ dictLoadMethod, dictContentType,
+ cParams, cctx->customMem);
+ cctx->cdict = cctx->cdictLocal;
+ if (cctx->cdictLocal == NULL)
+ return ERROR(memory_allocation);
+ }
+ return 0;
+}
+
+ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_byReference(
+ ZSTD_CCtx* cctx, const void* dict, size_t dictSize)
+{
+ return ZSTD_CCtx_loadDictionary_advanced(
+ cctx, dict, dictSize, ZSTD_dlm_byRef, ZSTD_dct_auto);
+}
+
+ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize)
+{
+ return ZSTD_CCtx_loadDictionary_advanced(
+ cctx, dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto);
+}
+
+
+size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict)
+{
+ if (cctx->streamStage != zcss_init) return ERROR(stage_wrong);
+ cctx->cdict = cdict;
+ memset(&cctx->prefixDict, 0, sizeof(cctx->prefixDict)); /* exclusive */
+ return 0;
+}
+
+size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize)
+{
+ return ZSTD_CCtx_refPrefix_advanced(cctx, prefix, prefixSize, ZSTD_dct_rawContent);
+}
+
+size_t ZSTD_CCtx_refPrefix_advanced(
+ ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType)
+{
+ if (cctx->streamStage != zcss_init) return ERROR(stage_wrong);
+ cctx->cdict = NULL; /* prefix discards any prior cdict */
+ cctx->prefixDict.dict = prefix;
+ cctx->prefixDict.dictSize = prefixSize;
+ cctx->prefixDict.dictContentType = dictContentType;
+ return 0;
+}
+
+/*! ZSTD_CCtx_reset() :
+ * Also dumps dictionary */
+size_t ZSTD_CCtx_reset(ZSTD_CCtx* cctx, ZSTD_ResetDirective reset)
+{
+ if ( (reset == ZSTD_reset_session_only)
+ || (reset == ZSTD_reset_session_and_parameters) ) {
+ cctx->streamStage = zcss_init;
+ cctx->pledgedSrcSizePlusOne = 0;
+ }
+ if ( (reset == ZSTD_reset_parameters)
+ || (reset == ZSTD_reset_session_and_parameters) ) {
+ if (cctx->streamStage != zcss_init) return ERROR(stage_wrong);
+ cctx->cdict = NULL;
+ return ZSTD_CCtxParams_reset(&cctx->requestedParams);
+ }
+ return 0;
+}
+
+
+/** ZSTD_checkCParams() :
+ control CParam 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)
+{
+ BOUNDCHECK(ZSTD_c_windowLog, cParams.windowLog);
+ BOUNDCHECK(ZSTD_c_chainLog, cParams.chainLog);
+ BOUNDCHECK(ZSTD_c_hashLog, cParams.hashLog);
+ BOUNDCHECK(ZSTD_c_searchLog, cParams.searchLog);
+ BOUNDCHECK(ZSTD_c_minMatch, cParams.minMatch);
+ BOUNDCHECK(ZSTD_c_targetLength,cParams.targetLength);
+ BOUNDCHECK(ZSTD_c_strategy, cParams.strategy);
+ return 0;
+}
+
+/** ZSTD_clampCParams() :
+ * make CParam values within valid range.
+ * @return : valid CParams */
+static ZSTD_compressionParameters
+ZSTD_clampCParams(ZSTD_compressionParameters cParams)
+{
+# define CLAMP_TYPE(cParam, val, type) { \
+ ZSTD_bounds const bounds = ZSTD_cParam_getBounds(cParam); \
+ if ((int)val<bounds.lowerBound) val=(type)bounds.lowerBound; \
+ else if ((int)val>bounds.upperBound) val=(type)bounds.upperBound; \
+ }
+# define CLAMP(cParam, val) CLAMP_TYPE(cParam, val, int)
+ CLAMP(ZSTD_c_windowLog, cParams.windowLog);
+ CLAMP(ZSTD_c_chainLog, cParams.chainLog);
+ CLAMP(ZSTD_c_hashLog, cParams.hashLog);
+ CLAMP(ZSTD_c_searchLog, cParams.searchLog);
+ CLAMP(ZSTD_c_minMatch, cParams.minMatch);
+ CLAMP(ZSTD_c_targetLength,cParams.targetLength);
+ CLAMP_TYPE(ZSTD_c_strategy,cParams.strategy, ZSTD_strategy);
+ return cParams;
+}
+
+/** ZSTD_cycleLog() :
+ * condition for correct operation : hashLog > 1 */
+static U32 ZSTD_cycleLog(U32 hashLog, ZSTD_strategy strat)
+{
+ U32 const btScale = ((U32)strat >= (U32)ZSTD_btlazy2);
+ return hashLog - btScale;
+}
+
+/** ZSTD_adjustCParams_internal() :
+ optimize `cPar` for a given input (`srcSize` and `dictSize`).
+ mostly downsizing to reduce memory consumption and initialization latency.
+ Both `srcSize` and `dictSize` are optional (use 0 if unknown).
+ Note : cPar is assumed validated. Use ZSTD_checkCParams() to ensure this condition. */
+static ZSTD_compressionParameters
+ZSTD_adjustCParams_internal(ZSTD_compressionParameters cPar,
+ unsigned long long srcSize,
+ size_t dictSize)
+{
+ static const U64 minSrcSize = 513; /* (1<<9) + 1 */
+ static const U64 maxWindowResize = 1ULL << (ZSTD_WINDOWLOG_MAX-1);
+ assert(ZSTD_checkCParams(cPar)==0);
+
+ if (dictSize && (srcSize+1<2) /* srcSize unknown */ )
+ srcSize = minSrcSize; /* presumed small when there is a dictionary */
+ else if (srcSize == 0)
+ srcSize = ZSTD_CONTENTSIZE_UNKNOWN; /* 0 == unknown : presumed large */
+
+ /* resize windowLog if input is small enough, to use less memory */
+ if ( (srcSize < maxWindowResize)
+ && (dictSize < maxWindowResize) ) {
+ U32 const tSize = (U32)(srcSize + dictSize);
+ static U32 const hashSizeMin = 1 << ZSTD_HASHLOG_MIN;
+ U32 const srcLog = (tSize < hashSizeMin) ? ZSTD_HASHLOG_MIN :
+ ZSTD_highbit32(tSize-1) + 1;
+ if (cPar.windowLog > srcLog) cPar.windowLog = srcLog;
+ }
+ if (cPar.hashLog > cPar.windowLog+1) cPar.hashLog = cPar.windowLog+1;
+ { U32 const cycleLog = ZSTD_cycleLog(cPar.chainLog, cPar.strategy);
+ if (cycleLog > cPar.windowLog)
+ cPar.chainLog -= (cycleLog - cPar.windowLog);
+ }
+
+ if (cPar.windowLog < ZSTD_WINDOWLOG_ABSOLUTEMIN)
+ cPar.windowLog = ZSTD_WINDOWLOG_ABSOLUTEMIN; /* required for frame header */
+
+ return cPar;
+}
+
+ZSTD_compressionParameters
+ZSTD_adjustCParams(ZSTD_compressionParameters cPar,
+ unsigned long long srcSize,
+ size_t dictSize)
+{
+ cPar = ZSTD_clampCParams(cPar);
+ return ZSTD_adjustCParams_internal(cPar, srcSize, dictSize);
+}
+
+ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams(
+ const ZSTD_CCtx_params* CCtxParams, U64 srcSizeHint, size_t dictSize)
+{
+ ZSTD_compressionParameters cParams = ZSTD_getCParams(CCtxParams->compressionLevel, srcSizeHint, dictSize);
+ if (CCtxParams->ldmParams.enableLdm) cParams.windowLog = ZSTD_LDM_DEFAULT_WINDOW_LOG;
+ if (CCtxParams->cParams.windowLog) cParams.windowLog = CCtxParams->cParams.windowLog;
+ if (CCtxParams->cParams.hashLog) cParams.hashLog = CCtxParams->cParams.hashLog;
+ if (CCtxParams->cParams.chainLog) cParams.chainLog = CCtxParams->cParams.chainLog;
+ if (CCtxParams->cParams.searchLog) cParams.searchLog = CCtxParams->cParams.searchLog;
+ if (CCtxParams->cParams.minMatch) cParams.minMatch = CCtxParams->cParams.minMatch;
+ if (CCtxParams->cParams.targetLength) cParams.targetLength = CCtxParams->cParams.targetLength;
+ if (CCtxParams->cParams.strategy) cParams.strategy = CCtxParams->cParams.strategy;
+ assert(!ZSTD_checkCParams(cParams));
+ return ZSTD_adjustCParams_internal(cParams, srcSizeHint, dictSize);
+}
+
+static size_t
+ZSTD_sizeof_matchState(const ZSTD_compressionParameters* const cParams,
+ const U32 forCCtx)
+{
+ size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog);
+ size_t const hSize = ((size_t)1) << cParams->hashLog;
+ U32 const hashLog3 = (forCCtx && cParams->minMatch==3) ? MIN(ZSTD_HASHLOG3_MAX, cParams->windowLog) : 0;
+ size_t const h3Size = ((size_t)1) << hashLog3;
+ size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32);
+ size_t const optPotentialSpace = ((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 optSpace = (forCCtx && (cParams->strategy >= ZSTD_btopt))
+ ? optPotentialSpace
+ : 0;
+ DEBUGLOG(4, "chainSize: %u - hSize: %u - h3Size: %u",
+ (U32)chainSize, (U32)hSize, (U32)h3Size);
+ return tableSpace + optSpace;
+}
+
+size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params)
+{
+ /* Estimate CCtx size is supported for single-threaded compression only. */
+ if (params->nbWorkers > 0) { return ERROR(GENERIC); }
+ { ZSTD_compressionParameters const cParams =
+ ZSTD_getCParamsFromCCtxParams(params, 0, 0);
+ size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << cParams.windowLog);
+ U32 const divider = (cParams.minMatch==3) ? 3 : 4;
+ size_t const maxNbSeq = blockSize / divider;
+ size_t const tokenSpace = WILDCOPY_OVERLENGTH + blockSize + 11*maxNbSeq;
+ size_t const entropySpace = HUF_WORKSPACE_SIZE;
+ size_t const blockStateSpace = 2 * sizeof(ZSTD_compressedBlockState_t);
+ size_t const matchStateSize = ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 1);
+
+ size_t const ldmSpace = ZSTD_ldm_getTableSize(params->ldmParams);
+ size_t const ldmSeqSpace = ZSTD_ldm_getMaxNbSeq(params->ldmParams, blockSize) * sizeof(rawSeq);
+
+ size_t const neededSpace = entropySpace + blockStateSpace + tokenSpace +
+ matchStateSize + ldmSpace + ldmSeqSpace;
+
+ DEBUGLOG(5, "sizeof(ZSTD_CCtx) : %u", (U32)sizeof(ZSTD_CCtx));
+ DEBUGLOG(5, "estimate workSpace : %u", (U32)neededSpace);
+ return sizeof(ZSTD_CCtx) + neededSpace;
+ }
+}
+
+size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams)
+{
+ ZSTD_CCtx_params const params = ZSTD_makeCCtxParamsFromCParams(cParams);
+ return ZSTD_estimateCCtxSize_usingCCtxParams(&params);
+}
+
+static size_t ZSTD_estimateCCtxSize_internal(int compressionLevel)
+{
+ ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, 0, 0);
+ return ZSTD_estimateCCtxSize_usingCParams(cParams);
+}
+
+size_t ZSTD_estimateCCtxSize(int compressionLevel)
+{
+ int level;
+ size_t memBudget = 0;
+ for (level=MIN(compressionLevel, 1); level<=compressionLevel; level++) {
+ size_t const newMB = ZSTD_estimateCCtxSize_internal(level);
+ if (newMB > memBudget) memBudget = newMB;
+ }
+ return memBudget;
+}
+
+size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_params* params)
+{
+ if (params->nbWorkers > 0) { return ERROR(GENERIC); }
+ { size_t const CCtxSize = ZSTD_estimateCCtxSize_usingCCtxParams(params);
+ size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << params->cParams.windowLog);
+ size_t const inBuffSize = ((size_t)1 << params->cParams.windowLog) + blockSize;
+ size_t const outBuffSize = ZSTD_compressBound(blockSize) + 1;
+ size_t const streamingSize = inBuffSize + outBuffSize;
+
+ return CCtxSize + streamingSize;
+ }
+}
+
+size_t ZSTD_estimateCStreamSize_usingCParams(ZSTD_compressionParameters cParams)
+{
+ ZSTD_CCtx_params const params = ZSTD_makeCCtxParamsFromCParams(cParams);
+ return ZSTD_estimateCStreamSize_usingCCtxParams(&params);
+}
+
+static size_t ZSTD_estimateCStreamSize_internal(int compressionLevel)
+{
+ ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, 0, 0);
+ return ZSTD_estimateCStreamSize_usingCParams(cParams);
+}
+
+size_t ZSTD_estimateCStreamSize(int compressionLevel)
+{
+ int level;
+ size_t memBudget = 0;
+ for (level=MIN(compressionLevel, 1); level<=compressionLevel; level++) {
+ size_t const newMB = ZSTD_estimateCStreamSize_internal(level);
+ if (newMB > memBudget) memBudget = newMB;
+ }
+ return memBudget;
+}
+
+/* ZSTD_getFrameProgression():
+ * tells how much data has been consumed (input) and produced (output) for current frame.
+ * able to count progression inside worker threads (non-blocking mode).
+ */
+ZSTD_frameProgression ZSTD_getFrameProgression(const ZSTD_CCtx* cctx)
+{
+#ifdef ZSTD_MULTITHREAD
+ if (cctx->appliedParams.nbWorkers > 0) {
+ return ZSTDMT_getFrameProgression(cctx->mtctx);
+ }
+#endif
+ { ZSTD_frameProgression fp;
+ size_t const buffered = (cctx->inBuff == NULL) ? 0 :
+ cctx->inBuffPos - cctx->inToCompress;
+ if (buffered) assert(cctx->inBuffPos >= cctx->inToCompress);
+ assert(buffered <= ZSTD_BLOCKSIZE_MAX);
+ fp.ingested = cctx->consumedSrcSize + buffered;
+ fp.consumed = cctx->consumedSrcSize;
+ fp.produced = cctx->producedCSize;
+ fp.flushed = cctx->producedCSize; /* simplified; some data might still be left within streaming output buffer */
+ fp.currentJobID = 0;
+ fp.nbActiveWorkers = 0;
+ return fp;
+} }
+
+/*! ZSTD_toFlushNow()
+ * Only useful for multithreading scenarios currently (nbWorkers >= 1).
+ */
+size_t ZSTD_toFlushNow(ZSTD_CCtx* cctx)
+{
+#ifdef ZSTD_MULTITHREAD
+ if (cctx->appliedParams.nbWorkers > 0) {
+ return ZSTDMT_toFlushNow(cctx->mtctx);
+ }
+#endif
+ (void)cctx;
+ return 0; /* over-simplification; could also check if context is currently running in streaming mode, and in which case, report how many bytes are left to be flushed within output buffer */
+}
+
+
+
+static U32 ZSTD_equivalentCParams(ZSTD_compressionParameters cParams1,
+ ZSTD_compressionParameters cParams2)
+{
+ return (cParams1.hashLog == cParams2.hashLog)
+ & (cParams1.chainLog == cParams2.chainLog)
+ & (cParams1.strategy == cParams2.strategy) /* opt parser space */
+ & ((cParams1.minMatch==3) == (cParams2.minMatch==3)); /* hashlog3 space */
+}
+
+static void ZSTD_assertEqualCParams(ZSTD_compressionParameters cParams1,
+ ZSTD_compressionParameters cParams2)
+{
+ (void)cParams1;
+ (void)cParams2;
+ assert(cParams1.windowLog == cParams2.windowLog);
+ assert(cParams1.chainLog == cParams2.chainLog);
+ assert(cParams1.hashLog == cParams2.hashLog);
+ assert(cParams1.searchLog == cParams2.searchLog);
+ assert(cParams1.minMatch == cParams2.minMatch);
+ assert(cParams1.targetLength == cParams2.targetLength);
+ assert(cParams1.strategy == cParams2.strategy);
+}
+
+/** The parameters are equivalent if ldm is not enabled in both sets or
+ * all the parameters are equivalent. */
+static U32 ZSTD_equivalentLdmParams(ldmParams_t ldmParams1,
+ ldmParams_t ldmParams2)
+{
+ return (!ldmParams1.enableLdm && !ldmParams2.enableLdm) ||
+ (ldmParams1.enableLdm == ldmParams2.enableLdm &&
+ ldmParams1.hashLog == ldmParams2.hashLog &&
+ ldmParams1.bucketSizeLog == ldmParams2.bucketSizeLog &&
+ ldmParams1.minMatchLength == ldmParams2.minMatchLength &&
+ ldmParams1.hashRateLog == ldmParams2.hashRateLog);
+}
+
+typedef enum { ZSTDb_not_buffered, ZSTDb_buffered } ZSTD_buffered_policy_e;
+
+/* ZSTD_sufficientBuff() :
+ * check internal buffers exist for streaming if buffPol == ZSTDb_buffered .
+ * Note : they are assumed to be correctly sized if ZSTD_equivalentCParams()==1 */
+static U32 ZSTD_sufficientBuff(size_t bufferSize1, size_t maxNbSeq1,
+ size_t maxNbLit1,
+ ZSTD_buffered_policy_e buffPol2,
+ ZSTD_compressionParameters cParams2,
+ U64 pledgedSrcSize)
+{
+ size_t const windowSize2 = MAX(1, (size_t)MIN(((U64)1 << cParams2.windowLog), pledgedSrcSize));
+ size_t const blockSize2 = MIN(ZSTD_BLOCKSIZE_MAX, windowSize2);
+ size_t const maxNbSeq2 = blockSize2 / ((cParams2.minMatch == 3) ? 3 : 4);
+ size_t const maxNbLit2 = blockSize2;
+ size_t const neededBufferSize2 = (buffPol2==ZSTDb_buffered) ? windowSize2 + blockSize2 : 0;
+ DEBUGLOG(4, "ZSTD_sufficientBuff: is neededBufferSize2=%u <= bufferSize1=%u",
+ (U32)neededBufferSize2, (U32)bufferSize1);
+ DEBUGLOG(4, "ZSTD_sufficientBuff: is maxNbSeq2=%u <= maxNbSeq1=%u",
+ (U32)maxNbSeq2, (U32)maxNbSeq1);
+ DEBUGLOG(4, "ZSTD_sufficientBuff: is maxNbLit2=%u <= maxNbLit1=%u",
+ (U32)maxNbLit2, (U32)maxNbLit1);
+ return (maxNbLit2 <= maxNbLit1)
+ & (maxNbSeq2 <= maxNbSeq1)
+ & (neededBufferSize2 <= bufferSize1);
+}
+
+/** Equivalence for resetCCtx purposes */
+static U32 ZSTD_equivalentParams(ZSTD_CCtx_params params1,
+ ZSTD_CCtx_params params2,
+ size_t buffSize1,
+ size_t maxNbSeq1, size_t maxNbLit1,
+ ZSTD_buffered_policy_e buffPol2,
+ U64 pledgedSrcSize)
+{
+ DEBUGLOG(4, "ZSTD_equivalentParams: pledgedSrcSize=%u", (U32)pledgedSrcSize);
+ if (!ZSTD_equivalentCParams(params1.cParams, params2.cParams)) {
+ DEBUGLOG(4, "ZSTD_equivalentCParams() == 0");
+ return 0;
+ }
+ if (!ZSTD_equivalentLdmParams(params1.ldmParams, params2.ldmParams)) {
+ DEBUGLOG(4, "ZSTD_equivalentLdmParams() == 0");
+ return 0;
+ }
+ if (!ZSTD_sufficientBuff(buffSize1, maxNbSeq1, maxNbLit1, buffPol2,
+ params2.cParams, pledgedSrcSize)) {
+ DEBUGLOG(4, "ZSTD_sufficientBuff() == 0");
+ return 0;
+ }
+ return 1;
+}
+
+static void ZSTD_reset_compressedBlockState(ZSTD_compressedBlockState_t* bs)
+{
+ int i;
+ for (i = 0; i < ZSTD_REP_NUM; ++i)
+ bs->rep[i] = repStartValue[i];
+ bs->entropy.huf.repeatMode = HUF_repeat_none;
+ bs->entropy.fse.offcode_repeatMode = FSE_repeat_none;
+ bs->entropy.fse.matchlength_repeatMode = FSE_repeat_none;
+ bs->entropy.fse.litlength_repeatMode = FSE_repeat_none;
+}
+
+/*! ZSTD_invalidateMatchState()
+ * Invalidate all the matches in the match finder tables.
+ * Requires nextSrc and base to be set (can be NULL).
+ */
+static void ZSTD_invalidateMatchState(ZSTD_matchState_t* ms)
+{
+ ZSTD_window_clear(&ms->window);
+
+ ms->nextToUpdate = ms->window.dictLimit;
+ ms->nextToUpdate3 = ms->window.dictLimit;
+ ms->loadedDictEnd = 0;
+ ms->opt.litLengthSum = 0; /* force reset of btopt stats */
+ ms->dictMatchState = NULL;
+}
+
+/*! ZSTD_continueCCtx() :
+ * reuse CCtx without reset (note : requires no dictionary) */
+static size_t ZSTD_continueCCtx(ZSTD_CCtx* cctx, ZSTD_CCtx_params params, U64 pledgedSrcSize)
+{
+ size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << params.cParams.windowLog), pledgedSrcSize));
+ size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, windowSize);
+ DEBUGLOG(4, "ZSTD_continueCCtx: re-use context in place");
+
+ cctx->blockSize = blockSize; /* previous block size could be different even for same windowLog, due to pledgedSrcSize */
+ cctx->appliedParams = params;
+ cctx->blockState.matchState.cParams = params.cParams;
+ cctx->pledgedSrcSizePlusOne = pledgedSrcSize+1;
+ cctx->consumedSrcSize = 0;
+ cctx->producedCSize = 0;
+ if (pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN)
+ cctx->appliedParams.fParams.contentSizeFlag = 0;
+ DEBUGLOG(4, "pledged content size : %u ; flag : %u",
+ (U32)pledgedSrcSize, cctx->appliedParams.fParams.contentSizeFlag);
+ cctx->stage = ZSTDcs_init;
+ cctx->dictID = 0;
+ if (params.ldmParams.enableLdm)
+ ZSTD_window_clear(&cctx->ldmState.window);
+ ZSTD_referenceExternalSequences(cctx, NULL, 0);
+ ZSTD_invalidateMatchState(&cctx->blockState.matchState);
+ ZSTD_reset_compressedBlockState(cctx->blockState.prevCBlock);
+ XXH64_reset(&cctx->xxhState, 0);
+ return 0;
+}
+
+typedef enum { ZSTDcrp_continue, ZSTDcrp_noMemset } ZSTD_compResetPolicy_e;
+
+static void*
+ZSTD_reset_matchState(ZSTD_matchState_t* ms,
+ void* ptr,
+ const ZSTD_compressionParameters* cParams,
+ ZSTD_compResetPolicy_e const crp, U32 const forCCtx)
+{
+ size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog);
+ size_t const hSize = ((size_t)1) << cParams->hashLog;
+ U32 const hashLog3 = (forCCtx && cParams->minMatch==3) ? MIN(ZSTD_HASHLOG3_MAX, cParams->windowLog) : 0;
+ size_t const h3Size = ((size_t)1) << hashLog3;
+ size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32);
+
+ assert(((size_t)ptr & 3) == 0);
+
+ ms->hashLog3 = hashLog3;
+ memset(&ms->window, 0, sizeof(ms->window));
+ ms->window.dictLimit = 1; /* start from 1, so that 1st position is valid */
+ ms->window.lowLimit = 1; /* it ensures first and later CCtx usages compress the same */
+ ms->window.nextSrc = ms->window.base + 1; /* see issue #1241 */
+ ZSTD_invalidateMatchState(ms);
+
+ /* opt parser space */
+ if (forCCtx && (cParams->strategy >= ZSTD_btopt)) {
+ DEBUGLOG(4, "reserving optimal parser space");
+ ms->opt.litFreq = (unsigned*)ptr;
+ ms->opt.litLengthFreq = ms->opt.litFreq + (1<<Litbits);
+ ms->opt.matchLengthFreq = ms->opt.litLengthFreq + (MaxLL+1);
+ ms->opt.offCodeFreq = ms->opt.matchLengthFreq + (MaxML+1);
+ ptr = ms->opt.offCodeFreq + (MaxOff+1);
+ ms->opt.matchTable = (ZSTD_match_t*)ptr;
+ ptr = ms->opt.matchTable + ZSTD_OPT_NUM+1;
+ ms->opt.priceTable = (ZSTD_optimal_t*)ptr;
+ ptr = ms->opt.priceTable + ZSTD_OPT_NUM+1;
+ }
+
+ /* table Space */
+ DEBUGLOG(4, "reset table : %u", crp!=ZSTDcrp_noMemset);
+ assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */
+ if (crp!=ZSTDcrp_noMemset) memset(ptr, 0, tableSpace); /* reset tables only */
+ ms->hashTable = (U32*)(ptr);
+ ms->chainTable = ms->hashTable + hSize;
+ ms->hashTable3 = ms->chainTable + chainSize;
+ ptr = ms->hashTable3 + h3Size;
+
+ ms->cParams = *cParams;
+
+ assert(((size_t)ptr & 3) == 0);
+ return ptr;
+}
+
+#define ZSTD_WORKSPACETOOLARGE_FACTOR 3 /* define "workspace is too large" as this number of times larger than needed */
+#define ZSTD_WORKSPACETOOLARGE_MAXDURATION 128 /* when workspace is continuously too large
+ * during at least this number of times,
+ * context's memory usage is considered wasteful,
+ * because it's sized to handle a worst case scenario which rarely happens.
+ * In which case, resize it down to free some memory */
+
+/*! ZSTD_resetCCtx_internal() :
+ note : `params` are assumed fully validated at this stage */
+static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
+ ZSTD_CCtx_params params,
+ U64 pledgedSrcSize,
+ ZSTD_compResetPolicy_e const crp,
+ ZSTD_buffered_policy_e const zbuff)
+{
+ DEBUGLOG(4, "ZSTD_resetCCtx_internal: pledgedSrcSize=%u, wlog=%u",
+ (U32)pledgedSrcSize, params.cParams.windowLog);
+ assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));
+
+ if (crp == ZSTDcrp_continue) {
+ if (ZSTD_equivalentParams(zc->appliedParams, params,
+ zc->inBuffSize,
+ zc->seqStore.maxNbSeq, zc->seqStore.maxNbLit,
+ zbuff, pledgedSrcSize)) {
+ DEBUGLOG(4, "ZSTD_equivalentParams()==1 -> continue mode (wLog1=%u, blockSize1=%zu)",
+ zc->appliedParams.cParams.windowLog, zc->blockSize);
+ zc->workSpaceOversizedDuration += (zc->workSpaceOversizedDuration > 0); /* if it was too large, it still is */
+ if (zc->workSpaceOversizedDuration <= ZSTD_WORKSPACETOOLARGE_MAXDURATION)
+ return ZSTD_continueCCtx(zc, params, pledgedSrcSize);
+ } }
+ DEBUGLOG(4, "ZSTD_equivalentParams()==0 -> reset CCtx");
+
+ if (params.ldmParams.enableLdm) {
+ /* Adjust long distance matching parameters */
+ ZSTD_ldm_adjustParameters(&params.ldmParams, &params.cParams);
+ assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog);
+ assert(params.ldmParams.hashRateLog < 32);
+ zc->ldmState.hashPower = ZSTD_rollingHash_primePower(params.ldmParams.minMatchLength);
+ }
+
+ { size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << params.cParams.windowLog), pledgedSrcSize));
+ size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, windowSize);
+ U32 const divider = (params.cParams.minMatch==3) ? 3 : 4;
+ size_t const maxNbSeq = blockSize / divider;
+ size_t const tokenSpace = WILDCOPY_OVERLENGTH + blockSize + 11*maxNbSeq;
+ size_t const buffOutSize = (zbuff==ZSTDb_buffered) ? ZSTD_compressBound(blockSize)+1 : 0;
+ size_t const buffInSize = (zbuff==ZSTDb_buffered) ? windowSize + blockSize : 0;
+ size_t const matchStateSize = ZSTD_sizeof_matchState(&params.cParams, /* forCCtx */ 1);
+ size_t const maxNbLdmSeq = ZSTD_ldm_getMaxNbSeq(params.ldmParams, blockSize);
+ void* ptr; /* used to partition workSpace */
+
+ /* Check if workSpace is large enough, alloc a new one if needed */
+ { size_t const entropySpace = HUF_WORKSPACE_SIZE;
+ size_t const blockStateSpace = 2 * sizeof(ZSTD_compressedBlockState_t);
+ size_t const bufferSpace = buffInSize + buffOutSize;
+ size_t const ldmSpace = ZSTD_ldm_getTableSize(params.ldmParams);
+ size_t const ldmSeqSpace = maxNbLdmSeq * sizeof(rawSeq);
+
+ size_t const neededSpace = entropySpace + blockStateSpace + ldmSpace +
+ ldmSeqSpace + matchStateSize + tokenSpace +
+ bufferSpace;
+
+ int const workSpaceTooSmall = zc->workSpaceSize < neededSpace;
+ int const workSpaceTooLarge = zc->workSpaceSize > ZSTD_WORKSPACETOOLARGE_FACTOR * neededSpace;
+ int const workSpaceWasteful = workSpaceTooLarge && (zc->workSpaceOversizedDuration > ZSTD_WORKSPACETOOLARGE_MAXDURATION);
+ zc->workSpaceOversizedDuration = workSpaceTooLarge ? zc->workSpaceOversizedDuration+1 : 0;
+
+ DEBUGLOG(4, "Need %zuKB workspace, including %zuKB for match state, and %zuKB for buffers",
+ neededSpace>>10, matchStateSize>>10, bufferSpace>>10);
+ DEBUGLOG(4, "windowSize: %zu - blockSize: %zu", windowSize, blockSize);
+
+ if (workSpaceTooSmall || workSpaceWasteful) {
+ DEBUGLOG(4, "Need to resize workSpaceSize from %zuKB to %zuKB",
+ zc->workSpaceSize >> 10,
+ neededSpace >> 10);
+ /* static cctx : no resize, error out */
+ if (zc->staticSize) return ERROR(memory_allocation);
+
+ zc->workSpaceSize = 0;
+ ZSTD_free(zc->workSpace, zc->customMem);
+ zc->workSpace = ZSTD_malloc(neededSpace, zc->customMem);
+ if (zc->workSpace == NULL) return ERROR(memory_allocation);
+ zc->workSpaceSize = neededSpace;
+ zc->workSpaceOversizedDuration = 0;
+
+ /* Statically sized space.
+ * entropyWorkspace never moves,
+ * though prev/next block swap places */
+ assert(((size_t)zc->workSpace & 3) == 0); /* ensure correct alignment */
+ assert(zc->workSpaceSize >= 2 * sizeof(ZSTD_compressedBlockState_t));
+ zc->blockState.prevCBlock = (ZSTD_compressedBlockState_t*)zc->workSpace;
+ zc->blockState.nextCBlock = zc->blockState.prevCBlock + 1;
+ ptr = zc->blockState.nextCBlock + 1;
+ zc->entropyWorkspace = (U32*)ptr;
+ } }
+
+ /* init params */
+ zc->appliedParams = params;
+ zc->blockState.matchState.cParams = params.cParams;
+ zc->pledgedSrcSizePlusOne = pledgedSrcSize+1;
+ zc->consumedSrcSize = 0;
+ zc->producedCSize = 0;
+ if (pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN)
+ zc->appliedParams.fParams.contentSizeFlag = 0;
+ DEBUGLOG(4, "pledged content size : %u ; flag : %u",
+ (unsigned)pledgedSrcSize, zc->appliedParams.fParams.contentSizeFlag);
+ zc->blockSize = blockSize;
+
+ XXH64_reset(&zc->xxhState, 0);
+ zc->stage = ZSTDcs_init;
+ zc->dictID = 0;
+
+ ZSTD_reset_compressedBlockState(zc->blockState.prevCBlock);
+
+ ptr = zc->entropyWorkspace + HUF_WORKSPACE_SIZE_U32;
+
+ /* ldm hash table */
+ /* initialize bucketOffsets table later for pointer alignment */
+ if (params.ldmParams.enableLdm) {
+ size_t const ldmHSize = ((size_t)1) << params.ldmParams.hashLog;
+ memset(ptr, 0, ldmHSize * sizeof(ldmEntry_t));
+ assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */
+ zc->ldmState.hashTable = (ldmEntry_t*)ptr;
+ ptr = zc->ldmState.hashTable + ldmHSize;
+ zc->ldmSequences = (rawSeq*)ptr;
+ ptr = zc->ldmSequences + maxNbLdmSeq;
+ zc->maxNbLdmSequences = maxNbLdmSeq;
+
+ memset(&zc->ldmState.window, 0, sizeof(zc->ldmState.window));
+ }
+ assert(((size_t)ptr & 3) == 0); /* ensure ptr is properly aligned */
+
+ ptr = ZSTD_reset_matchState(&zc->blockState.matchState, ptr, &params.cParams, crp, /* forCCtx */ 1);
+
+ /* sequences storage */
+ zc->seqStore.maxNbSeq = maxNbSeq;
+ zc->seqStore.sequencesStart = (seqDef*)ptr;
+ ptr = zc->seqStore.sequencesStart + maxNbSeq;
+ zc->seqStore.llCode = (BYTE*) ptr;
+ zc->seqStore.mlCode = zc->seqStore.llCode + maxNbSeq;
+ zc->seqStore.ofCode = zc->seqStore.mlCode + maxNbSeq;
+ zc->seqStore.litStart = zc->seqStore.ofCode + maxNbSeq;
+ /* ZSTD_wildcopy() is used to copy into the literals buffer,
+ * so we have to oversize the buffer by WILDCOPY_OVERLENGTH bytes.
+ */
+ zc->seqStore.maxNbLit = blockSize;
+ ptr = zc->seqStore.litStart + blockSize + WILDCOPY_OVERLENGTH;
+
+ /* ldm bucketOffsets table */
+ if (params.ldmParams.enableLdm) {
+ size_t const ldmBucketSize =
+ ((size_t)1) << (params.ldmParams.hashLog -
+ params.ldmParams.bucketSizeLog);
+ memset(ptr, 0, ldmBucketSize);
+ zc->ldmState.bucketOffsets = (BYTE*)ptr;
+ ptr = zc->ldmState.bucketOffsets + ldmBucketSize;
+ ZSTD_window_clear(&zc->ldmState.window);
+ }
+ ZSTD_referenceExternalSequences(zc, NULL, 0);
+
+ /* buffers */
+ zc->inBuffSize = buffInSize;
+ zc->inBuff = (char*)ptr;
+ zc->outBuffSize = buffOutSize;
+ zc->outBuff = zc->inBuff + buffInSize;
+
+ return 0;
+ }
+}
+
+/* ZSTD_invalidateRepCodes() :
+ * ensures next compression will not use repcodes from previous block.
+ * Note : only works with regular variant;
+ * do not use with extDict variant ! */
+void ZSTD_invalidateRepCodes(ZSTD_CCtx* cctx) {
+ int i;
+ for (i=0; i<ZSTD_REP_NUM; i++) cctx->blockState.prevCBlock->rep[i] = 0;
+ assert(!ZSTD_window_hasExtDict(cctx->blockState.matchState.window));
+}
+
+/* These are the approximate sizes for each strategy past which copying the
+ * dictionary tables into the working context is faster than using them
+ * in-place.
+ */
+static const size_t attachDictSizeCutoffs[ZSTD_STRATEGY_MAX+1] = {
+ 8 KB, /* unused */
+ 8 KB, /* ZSTD_fast */
+ 16 KB, /* ZSTD_dfast */
+ 32 KB, /* ZSTD_greedy */
+ 32 KB, /* ZSTD_lazy */
+ 32 KB, /* ZSTD_lazy2 */
+ 32 KB, /* ZSTD_btlazy2 */
+ 32 KB, /* ZSTD_btopt */
+ 8 KB, /* ZSTD_btultra */
+ 8 KB /* ZSTD_btultra2 */
+};
+
+static int ZSTD_shouldAttachDict(const ZSTD_CDict* cdict,
+ ZSTD_CCtx_params params,
+ U64 pledgedSrcSize)
+{
+ size_t cutoff = attachDictSizeCutoffs[cdict->matchState.cParams.strategy];
+ return ( pledgedSrcSize <= cutoff
+ || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN
+ || params.attachDictPref == ZSTD_dictForceAttach )
+ && params.attachDictPref != ZSTD_dictForceCopy
+ && !params.forceWindow; /* dictMatchState isn't correctly
+ * handled in _enforceMaxDist */
+}
+
+static size_t ZSTD_resetCCtx_byAttachingCDict(
+ ZSTD_CCtx* cctx,
+ const ZSTD_CDict* cdict,
+ ZSTD_CCtx_params params,
+ U64 pledgedSrcSize,
+ ZSTD_buffered_policy_e zbuff)
+{
+ {
+ const ZSTD_compressionParameters *cdict_cParams = &cdict->matchState.cParams;
+ unsigned const windowLog = params.cParams.windowLog;
+ assert(windowLog != 0);
+ /* Resize working context table params for input only, since the dict
+ * has its own tables. */
+ params.cParams = ZSTD_adjustCParams_internal(*cdict_cParams, pledgedSrcSize, 0);
+ params.cParams.windowLog = windowLog;
+ ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize,
+ ZSTDcrp_continue, zbuff);
+ assert(cctx->appliedParams.cParams.strategy == cdict_cParams->strategy);
+ }
+
+ {
+ const U32 cdictEnd = (U32)( cdict->matchState.window.nextSrc
+ - cdict->matchState.window.base);
+ const U32 cdictLen = cdictEnd - cdict->matchState.window.dictLimit;
+ if (cdictLen == 0) {
+ /* don't even attach dictionaries with no contents */
+ DEBUGLOG(4, "skipping attaching empty dictionary");
+ } else {
+ DEBUGLOG(4, "attaching dictionary into context");
+ cctx->blockState.matchState.dictMatchState = &cdict->matchState;
+
+ /* prep working match state so dict matches never have negative indices
+ * when they are translated to the working context's index space. */
+ if (cctx->blockState.matchState.window.dictLimit < cdictEnd) {
+ cctx->blockState.matchState.window.nextSrc =
+ cctx->blockState.matchState.window.base + cdictEnd;
+ ZSTD_window_clear(&cctx->blockState.matchState.window);
+ }
+ cctx->blockState.matchState.loadedDictEnd = cctx->blockState.matchState.window.dictLimit;
+ }
+ }
+
+ cctx->dictID = cdict->dictID;
+
+ /* copy block state */
+ memcpy(cctx->blockState.prevCBlock, &cdict->cBlockState, sizeof(cdict->cBlockState));
+
+ return 0;
+}
+
+static size_t ZSTD_resetCCtx_byCopyingCDict(ZSTD_CCtx* cctx,
+ const ZSTD_CDict* cdict,
+ ZSTD_CCtx_params params,
+ U64 pledgedSrcSize,
+ ZSTD_buffered_policy_e zbuff)
+{
+ const ZSTD_compressionParameters *cdict_cParams = &cdict->matchState.cParams;
+
+ DEBUGLOG(4, "copying dictionary into context");
+
+ { unsigned const windowLog = params.cParams.windowLog;
+ assert(windowLog != 0);
+ /* Copy only compression parameters related to tables. */
+ params.cParams = *cdict_cParams;
+ params.cParams.windowLog = windowLog;
+ ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize,
+ ZSTDcrp_noMemset, zbuff);
+ assert(cctx->appliedParams.cParams.strategy == cdict_cParams->strategy);
+ assert(cctx->appliedParams.cParams.hashLog == cdict_cParams->hashLog);
+ assert(cctx->appliedParams.cParams.chainLog == cdict_cParams->chainLog);
+ }
+
+ /* copy tables */
+ { size_t const chainSize = (cdict_cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cdict_cParams->chainLog);
+ size_t const hSize = (size_t)1 << cdict_cParams->hashLog;
+ size_t const tableSpace = (chainSize + hSize) * sizeof(U32);
+ assert((U32*)cctx->blockState.matchState.chainTable == (U32*)cctx->blockState.matchState.hashTable + hSize); /* chainTable must follow hashTable */
+ assert((U32*)cctx->blockState.matchState.hashTable3 == (U32*)cctx->blockState.matchState.chainTable + chainSize);
+ assert((U32*)cdict->matchState.chainTable == (U32*)cdict->matchState.hashTable + hSize); /* chainTable must follow hashTable */
+ assert((U32*)cdict->matchState.hashTable3 == (U32*)cdict->matchState.chainTable + chainSize);
+ memcpy(cctx->blockState.matchState.hashTable, cdict->matchState.hashTable, tableSpace); /* presumes all tables follow each other */
+ }
+
+ /* Zero the hashTable3, since the cdict never fills it */
+ { size_t const h3Size = (size_t)1 << cctx->blockState.matchState.hashLog3;
+ assert(cdict->matchState.hashLog3 == 0);
+ memset(cctx->blockState.matchState.hashTable3, 0, h3Size * sizeof(U32));
+ }
+
+ /* copy dictionary offsets */
+ { ZSTD_matchState_t const* srcMatchState = &cdict->matchState;
+ ZSTD_matchState_t* dstMatchState = &cctx->blockState.matchState;
+ dstMatchState->window = srcMatchState->window;
+ dstMatchState->nextToUpdate = srcMatchState->nextToUpdate;
+ dstMatchState->nextToUpdate3= srcMatchState->nextToUpdate3;
+ dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd;
+ }
+
+ cctx->dictID = cdict->dictID;
+
+ /* copy block state */
+ memcpy(cctx->blockState.prevCBlock, &cdict->cBlockState, sizeof(cdict->cBlockState));
+
+ return 0;
+}
+
+/* We have a choice between copying the dictionary context into the working
+ * context, or referencing the dictionary context from the working context
+ * in-place. We decide here which strategy to use. */
+static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx,
+ const ZSTD_CDict* cdict,
+ ZSTD_CCtx_params params,
+ U64 pledgedSrcSize,
+ ZSTD_buffered_policy_e zbuff)
+{
+
+ DEBUGLOG(4, "ZSTD_resetCCtx_usingCDict (pledgedSrcSize=%u)",
+ (unsigned)pledgedSrcSize);
+
+ if (ZSTD_shouldAttachDict(cdict, params, pledgedSrcSize)) {
+ return ZSTD_resetCCtx_byAttachingCDict(
+ cctx, cdict, params, pledgedSrcSize, zbuff);
+ } else {
+ return ZSTD_resetCCtx_byCopyingCDict(
+ cctx, cdict, params, pledgedSrcSize, zbuff);
+ }
+}
+
+/*! ZSTD_copyCCtx_internal() :
+ * 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()).
+ * The "context", in this case, refers to the hash and chain tables,
+ * entropy tables, and dictionary references.
+ * `windowLog` value is enforced if != 0, otherwise value is copied from srcCCtx.
+ * @return : 0, or an error code */
+static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx,
+ const ZSTD_CCtx* srcCCtx,
+ ZSTD_frameParameters fParams,
+ U64 pledgedSrcSize,
+ ZSTD_buffered_policy_e zbuff)
+{
+ DEBUGLOG(5, "ZSTD_copyCCtx_internal");
+ if (srcCCtx->stage!=ZSTDcs_init) return ERROR(stage_wrong);
+
+ memcpy(&dstCCtx->customMem, &srcCCtx->customMem, sizeof(ZSTD_customMem));
+ { ZSTD_CCtx_params params = dstCCtx->requestedParams;
+ /* Copy only compression parameters related to tables. */
+ params.cParams = srcCCtx->appliedParams.cParams;
+ params.fParams = fParams;
+ ZSTD_resetCCtx_internal(dstCCtx, params, pledgedSrcSize,
+ ZSTDcrp_noMemset, zbuff);
+ assert(dstCCtx->appliedParams.cParams.windowLog == srcCCtx->appliedParams.cParams.windowLog);
+ assert(dstCCtx->appliedParams.cParams.strategy == srcCCtx->appliedParams.cParams.strategy);
+ assert(dstCCtx->appliedParams.cParams.hashLog == srcCCtx->appliedParams.cParams.hashLog);
+ assert(dstCCtx->appliedParams.cParams.chainLog == srcCCtx->appliedParams.cParams.chainLog);
+ assert(dstCCtx->blockState.matchState.hashLog3 == srcCCtx->blockState.matchState.hashLog3);
+ }
+
+ /* copy tables */
+ { size_t const chainSize = (srcCCtx->appliedParams.cParams.strategy == ZSTD_fast) ? 0 : ((size_t)1 << srcCCtx->appliedParams.cParams.chainLog);
+ size_t const hSize = (size_t)1 << srcCCtx->appliedParams.cParams.hashLog;
+ size_t const h3Size = (size_t)1 << srcCCtx->blockState.matchState.hashLog3;
+ size_t const tableSpace = (chainSize + hSize + h3Size) * sizeof(U32);
+ assert((U32*)dstCCtx->blockState.matchState.chainTable == (U32*)dstCCtx->blockState.matchState.hashTable + hSize); /* chainTable must follow hashTable */
+ assert((U32*)dstCCtx->blockState.matchState.hashTable3 == (U32*)dstCCtx->blockState.matchState.chainTable + chainSize);
+ memcpy(dstCCtx->blockState.matchState.hashTable, srcCCtx->blockState.matchState.hashTable, tableSpace); /* presumes all tables follow each other */
+ }
+
+ /* copy dictionary offsets */
+ {
+ const ZSTD_matchState_t* srcMatchState = &srcCCtx->blockState.matchState;
+ ZSTD_matchState_t* dstMatchState = &dstCCtx->blockState.matchState;
+ dstMatchState->window = srcMatchState->window;
+ dstMatchState->nextToUpdate = srcMatchState->nextToUpdate;
+ dstMatchState->nextToUpdate3= srcMatchState->nextToUpdate3;
+ dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd;
+ }
+ dstCCtx->dictID = srcCCtx->dictID;
+
+ /* copy block state */
+ memcpy(dstCCtx->blockState.prevCBlock, srcCCtx->blockState.prevCBlock, sizeof(*srcCCtx->blockState.prevCBlock));
+
+ return 0;
+}
+
+/*! ZSTD_copyCCtx() :
+ * 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()).
+ * pledgedSrcSize==0 means "unknown".
+* @return : 0, or an error code */
+size_t ZSTD_copyCCtx(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, unsigned long long pledgedSrcSize)
+{
+ ZSTD_frameParameters fParams = { 1 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ };
+ ZSTD_buffered_policy_e const zbuff = (ZSTD_buffered_policy_e)(srcCCtx->inBuffSize>0);
+ ZSTD_STATIC_ASSERT((U32)ZSTDb_buffered==1);
+ if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;
+ fParams.contentSizeFlag = (pledgedSrcSize != ZSTD_CONTENTSIZE_UNKNOWN);
+
+ return ZSTD_copyCCtx_internal(dstCCtx, srcCCtx,
+ fParams, pledgedSrcSize,
+ zbuff);
+}
+
+
+#define ZSTD_ROWSIZE 16
+/*! ZSTD_reduceTable() :
+ * reduce table indexes by `reducerValue`, or squash to zero.
+ * PreserveMark preserves "unsorted mark" for btlazy2 strategy.
+ * It must be set to a clear 0/1 value, to remove branch during inlining.
+ * Presume table size is a multiple of ZSTD_ROWSIZE
+ * to help auto-vectorization */
+FORCE_INLINE_TEMPLATE void
+ZSTD_reduceTable_internal (U32* const table, U32 const size, U32 const reducerValue, int const preserveMark)
+{
+ int const nbRows = (int)size / ZSTD_ROWSIZE;
+ int cellNb = 0;
+ int rowNb;
+ assert((size & (ZSTD_ROWSIZE-1)) == 0); /* multiple of ZSTD_ROWSIZE */
+ assert(size < (1U<<31)); /* can be casted to int */
+ for (rowNb=0 ; rowNb < nbRows ; rowNb++) {
+ int column;
+ for (column=0; column<ZSTD_ROWSIZE; column++) {
+ if (preserveMark) {
+ U32 const adder = (table[cellNb] == ZSTD_DUBT_UNSORTED_MARK) ? reducerValue : 0;
+ table[cellNb] += adder;
+ }
+ if (table[cellNb] < reducerValue) table[cellNb] = 0;
+ else table[cellNb] -= reducerValue;
+ cellNb++;
+ } }
+}
+
+static void ZSTD_reduceTable(U32* const table, U32 const size, U32 const reducerValue)
+{
+ ZSTD_reduceTable_internal(table, size, reducerValue, 0);
+}
+
+static void ZSTD_reduceTable_btlazy2(U32* const table, U32 const size, U32 const reducerValue)
+{
+ ZSTD_reduceTable_internal(table, size, reducerValue, 1);
+}
+
+/*! ZSTD_reduceIndex() :
+* rescale all indexes to avoid future overflow (indexes are U32) */
+static void ZSTD_reduceIndex (ZSTD_CCtx* zc, const U32 reducerValue)
+{
+ ZSTD_matchState_t* const ms = &zc->blockState.matchState;
+ { U32 const hSize = (U32)1 << zc->appliedParams.cParams.hashLog;
+ ZSTD_reduceTable(ms->hashTable, hSize, reducerValue);
+ }
+
+ if (zc->appliedParams.cParams.strategy != ZSTD_fast) {
+ U32 const chainSize = (U32)1 << zc->appliedParams.cParams.chainLog;
+ if (zc->appliedParams.cParams.strategy == ZSTD_btlazy2)
+ ZSTD_reduceTable_btlazy2(ms->chainTable, chainSize, reducerValue);
+ else
+ ZSTD_reduceTable(ms->chainTable, chainSize, reducerValue);
+ }
+
+ if (ms->hashLog3) {
+ U32 const h3Size = (U32)1 << ms->hashLog3;
+ ZSTD_reduceTable(ms->hashTable3, h3Size, reducerValue);
+ }
+}
+
+
+/*-*******************************************************
+* Block entropic compression
+*********************************************************/
+
+/* See doc/zstd_compression_format.md for detailed format description */
+
+static size_t ZSTD_noCompressBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 lastBlock)
+{
+ U32 const cBlockHeader24 = lastBlock + (((U32)bt_raw)<<1) + (U32)(srcSize << 3);
+ if (srcSize + ZSTD_blockHeaderSize > dstCapacity) return ERROR(dstSize_tooSmall);
+ MEM_writeLE24(dst, cBlockHeader24);
+ memcpy((BYTE*)dst + ZSTD_blockHeaderSize, src, srcSize);
+ return ZSTD_blockHeaderSize + srcSize;
+}
+
+static size_t ZSTD_noCompressLiterals (void* dst, size_t dstCapacity, const void* src, size_t srcSize)
+{
+ BYTE* const ostart = (BYTE* const)dst;
+ U32 const flSize = 1 + (srcSize>31) + (srcSize>4095);
+
+ if (srcSize + flSize > dstCapacity) return ERROR(dstSize_tooSmall);
+
+ switch(flSize)
+ {
+ case 1: /* 2 - 1 - 5 */
+ ostart[0] = (BYTE)((U32)set_basic + (srcSize<<3));
+ break;
+ case 2: /* 2 - 2 - 12 */
+ MEM_writeLE16(ostart, (U16)((U32)set_basic + (1<<2) + (srcSize<<4)));
+ break;
+ case 3: /* 2 - 2 - 20 */
+ MEM_writeLE32(ostart, (U32)((U32)set_basic + (3<<2) + (srcSize<<4)));
+ break;
+ default: /* not necessary : flSize is {1,2,3} */
+ assert(0);
+ }
+
+ memcpy(ostart + flSize, src, srcSize);
+ return srcSize + flSize;
+}
+
+static size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize)
+{
+ BYTE* const ostart = (BYTE* const)dst;
+ U32 const flSize = 1 + (srcSize>31) + (srcSize>4095);
+
+ (void)dstCapacity; /* dstCapacity already guaranteed to be >=4, hence large enough */
+
+ switch(flSize)
+ {
+ case 1: /* 2 - 1 - 5 */
+ ostart[0] = (BYTE)((U32)set_rle + (srcSize<<3));
+ break;
+ case 2: /* 2 - 2 - 12 */
+ MEM_writeLE16(ostart, (U16)((U32)set_rle + (1<<2) + (srcSize<<4)));
+ break;
+ case 3: /* 2 - 2 - 20 */
+ MEM_writeLE32(ostart, (U32)((U32)set_rle + (3<<2) + (srcSize<<4)));
+ break;
+ default: /* not necessary : flSize is {1,2,3} */
+ assert(0);
+ }
+
+ ostart[flSize] = *(const BYTE*)src;
+ return flSize+1;
+}
+
+
+/* ZSTD_minGain() :
+ * minimum compression required
+ * to generate a compress block or a compressed literals section.
+ * note : use same formula for both situations */
+static size_t ZSTD_minGain(size_t srcSize, ZSTD_strategy strat)
+{
+ U32 const minlog = (strat>=ZSTD_btultra) ? (U32)(strat) - 1 : 6;
+ ZSTD_STATIC_ASSERT(ZSTD_btultra == 8);
+ assert(ZSTD_cParam_withinBounds(ZSTD_c_strategy, strat));
+ return (srcSize >> minlog) + 2;
+}
+
+static size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf,
+ ZSTD_hufCTables_t* nextHuf,
+ ZSTD_strategy strategy, int disableLiteralCompression,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ void* workspace, size_t wkspSize,
+ const int bmi2)
+{
+ size_t const minGain = ZSTD_minGain(srcSize, strategy);
+ size_t const lhSize = 3 + (srcSize >= 1 KB) + (srcSize >= 16 KB);
+ BYTE* const ostart = (BYTE*)dst;
+ U32 singleStream = srcSize < 256;
+ symbolEncodingType_e hType = set_compressed;
+ size_t cLitSize;
+
+ DEBUGLOG(5,"ZSTD_compressLiterals (disableLiteralCompression=%i)",
+ disableLiteralCompression);
+
+ /* Prepare nextEntropy assuming reusing the existing table */
+ memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
+
+ if (disableLiteralCompression)
+ return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize);
+
+ /* small ? don't even attempt compression (speed opt) */
+# define COMPRESS_LITERALS_SIZE_MIN 63
+ { size_t const minLitSize = (prevHuf->repeatMode == HUF_repeat_valid) ? 6 : COMPRESS_LITERALS_SIZE_MIN;
+ if (srcSize <= minLitSize) return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize);
+ }
+
+ if (dstCapacity < lhSize+1) return ERROR(dstSize_tooSmall); /* not enough space for compression */
+ { HUF_repeat repeat = prevHuf->repeatMode;
+ int const preferRepeat = strategy < ZSTD_lazy ? srcSize <= 1024 : 0;
+ if (repeat == HUF_repeat_valid && lhSize == 3) singleStream = 1;
+ cLitSize = singleStream ? HUF_compress1X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11,
+ workspace, wkspSize, (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2)
+ : HUF_compress4X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11,
+ workspace, wkspSize, (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2);
+ if (repeat != HUF_repeat_none) {
+ /* reused the existing table */
+ hType = set_repeat;
+ }
+ }
+
+ if ((cLitSize==0) | (cLitSize >= srcSize - minGain) | ERR_isError(cLitSize)) {
+ memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
+ return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize);
+ }
+ if (cLitSize==1) {
+ memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
+ return ZSTD_compressRleLiteralsBlock(dst, dstCapacity, src, srcSize);
+ }
+
+ if (hType == set_compressed) {
+ /* using a newly constructed table */
+ nextHuf->repeatMode = HUF_repeat_check;
+ }
+
+ /* Build header */
+ switch(lhSize)
+ {
+ case 3: /* 2 - 2 - 10 - 10 */
+ { U32 const lhc = hType + ((!singleStream) << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<14);
+ MEM_writeLE24(ostart, lhc);
+ break;
+ }
+ case 4: /* 2 - 2 - 14 - 14 */
+ { U32 const lhc = hType + (2 << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<18);
+ MEM_writeLE32(ostart, lhc);
+ break;
+ }
+ case 5: /* 2 - 2 - 18 - 18 */
+ { U32 const lhc = hType + (3 << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<22);
+ MEM_writeLE32(ostart, lhc);
+ ostart[4] = (BYTE)(cLitSize >> 10);
+ break;
+ }
+ default: /* not possible : lhSize is {3,4,5} */
+ assert(0);
+ }
+ return lhSize+cLitSize;
+}
+
+
+void ZSTD_seqToCodes(const seqStore_t* seqStorePtr)
+{
+ const seqDef* const sequences = seqStorePtr->sequencesStart;
+ BYTE* const llCodeTable = seqStorePtr->llCode;
+ BYTE* const ofCodeTable = seqStorePtr->ofCode;
+ BYTE* const mlCodeTable = seqStorePtr->mlCode;
+ U32 const nbSeq = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart);
+ U32 u;
+ assert(nbSeq <= seqStorePtr->maxNbSeq);
+ for (u=0; u<nbSeq; u++) {
+ U32 const llv = sequences[u].litLength;
+ U32 const mlv = sequences[u].matchLength;
+ llCodeTable[u] = (BYTE)ZSTD_LLcode(llv);
+ ofCodeTable[u] = (BYTE)ZSTD_highbit32(sequences[u].offset);
+ mlCodeTable[u] = (BYTE)ZSTD_MLcode(mlv);
+ }
+ if (seqStorePtr->longLengthID==1)
+ llCodeTable[seqStorePtr->longLengthPos] = MaxLL;
+ if (seqStorePtr->longLengthID==2)
+ mlCodeTable[seqStorePtr->longLengthPos] = MaxML;
+}
+
+
+/**
+ * -log2(x / 256) lookup table for x in [0, 256).
+ * If x == 0: Return 0
+ * Else: Return floor(-log2(x / 256) * 256)
+ */
+static unsigned const kInverseProbabiltyLog256[256] = {
+ 0, 2048, 1792, 1642, 1536, 1453, 1386, 1329, 1280, 1236, 1197, 1162,
+ 1130, 1100, 1073, 1047, 1024, 1001, 980, 960, 941, 923, 906, 889,
+ 874, 859, 844, 830, 817, 804, 791, 779, 768, 756, 745, 734,
+ 724, 714, 704, 694, 685, 676, 667, 658, 650, 642, 633, 626,
+ 618, 610, 603, 595, 588, 581, 574, 567, 561, 554, 548, 542,
+ 535, 529, 523, 517, 512, 506, 500, 495, 489, 484, 478, 473,
+ 468, 463, 458, 453, 448, 443, 438, 434, 429, 424, 420, 415,
+ 411, 407, 402, 398, 394, 390, 386, 382, 377, 373, 370, 366,
+ 362, 358, 354, 350, 347, 343, 339, 336, 332, 329, 325, 322,
+ 318, 315, 311, 308, 305, 302, 298, 295, 292, 289, 286, 282,
+ 279, 276, 273, 270, 267, 264, 261, 258, 256, 253, 250, 247,
+ 244, 241, 239, 236, 233, 230, 228, 225, 222, 220, 217, 215,
+ 212, 209, 207, 204, 202, 199, 197, 194, 192, 190, 187, 185,
+ 182, 180, 178, 175, 173, 171, 168, 166, 164, 162, 159, 157,
+ 155, 153, 151, 149, 146, 144, 142, 140, 138, 136, 134, 132,
+ 130, 128, 126, 123, 121, 119, 117, 115, 114, 112, 110, 108,
+ 106, 104, 102, 100, 98, 96, 94, 93, 91, 89, 87, 85,
+ 83, 82, 80, 78, 76, 74, 73, 71, 69, 67, 66, 64,
+ 62, 61, 59, 57, 55, 54, 52, 50, 49, 47, 46, 44,
+ 42, 41, 39, 37, 36, 34, 33, 31, 30, 28, 26, 25,
+ 23, 22, 20, 19, 17, 16, 14, 13, 11, 10, 8, 7,
+ 5, 4, 2, 1,
+};
+
+
+/**
+ * Returns the cost in bits of encoding the distribution described by count
+ * using the entropy bound.
+ */
+static size_t ZSTD_entropyCost(unsigned const* count, unsigned const max, size_t const total)
+{
+ unsigned cost = 0;
+ unsigned s;
+ for (s = 0; s <= max; ++s) {
+ unsigned norm = (unsigned)((256 * count[s]) / total);
+ if (count[s] != 0 && norm == 0)
+ norm = 1;
+ assert(count[s] < total);
+ cost += count[s] * kInverseProbabiltyLog256[norm];
+ }
+ return cost >> 8;
+}
+
+
+/**
+ * Returns the cost in bits of encoding the distribution in count using the
+ * table described by norm. The max symbol support by norm is assumed >= max.
+ * norm must be valid for every symbol with non-zero probability in count.
+ */
+static size_t ZSTD_crossEntropyCost(short const* norm, unsigned accuracyLog,
+ unsigned const* count, unsigned const max)
+{
+ unsigned const shift = 8 - accuracyLog;
+ size_t cost = 0;
+ unsigned s;
+ assert(accuracyLog <= 8);
+ for (s = 0; s <= max; ++s) {
+ unsigned const normAcc = norm[s] != -1 ? norm[s] : 1;
+ unsigned const norm256 = normAcc << shift;
+ assert(norm256 > 0);
+ assert(norm256 < 256);
+ cost += count[s] * kInverseProbabiltyLog256[norm256];
+ }
+ return cost >> 8;
+}
+
+
+static unsigned ZSTD_getFSEMaxSymbolValue(FSE_CTable const* ctable) {
+ void const* ptr = ctable;
+ U16 const* u16ptr = (U16 const*)ptr;
+ U32 const maxSymbolValue = MEM_read16(u16ptr + 1);
+ return maxSymbolValue;
+}
+
+
+/**
+ * Returns the cost in bits of encoding the distribution in count using ctable.
+ * Returns an error if ctable cannot represent all the symbols in count.
+ */
+static size_t ZSTD_fseBitCost(
+ FSE_CTable const* ctable,
+ unsigned const* count,
+ unsigned const max)
+{
+ unsigned const kAccuracyLog = 8;
+ size_t cost = 0;
+ unsigned s;
+ FSE_CState_t cstate;
+ FSE_initCState(&cstate, ctable);
+ if (ZSTD_getFSEMaxSymbolValue(ctable) < max) {
+ DEBUGLOG(5, "Repeat FSE_CTable has maxSymbolValue %u < %u",
+ ZSTD_getFSEMaxSymbolValue(ctable), max);
+ return ERROR(GENERIC);
+ }
+ for (s = 0; s <= max; ++s) {
+ unsigned const tableLog = cstate.stateLog;
+ unsigned const badCost = (tableLog + 1) << kAccuracyLog;
+ unsigned const bitCost = FSE_bitCost(cstate.symbolTT, tableLog, s, kAccuracyLog);
+ if (count[s] == 0)
+ continue;
+ if (bitCost >= badCost) {
+ DEBUGLOG(5, "Repeat FSE_CTable has Prob[%u] == 0", s);
+ return ERROR(GENERIC);
+ }
+ cost += count[s] * bitCost;
+ }
+ return cost >> kAccuracyLog;
+}
+
+/**
+ * Returns the cost in bytes of encoding the normalized count header.
+ * Returns an error if any of the helper functions return an error.
+ */
+static size_t ZSTD_NCountCost(unsigned const* count, unsigned const max,
+ size_t const nbSeq, unsigned const FSELog)
+{
+ BYTE wksp[FSE_NCOUNTBOUND];
+ S16 norm[MaxSeq + 1];
+ const U32 tableLog = FSE_optimalTableLog(FSELog, nbSeq, max);
+ CHECK_F(FSE_normalizeCount(norm, tableLog, count, nbSeq, max));
+ return FSE_writeNCount(wksp, sizeof(wksp), norm, max, tableLog);
+}
+
+
+typedef enum {
+ ZSTD_defaultDisallowed = 0,
+ ZSTD_defaultAllowed = 1
+} ZSTD_defaultPolicy_e;
+
+MEM_STATIC symbolEncodingType_e
+ZSTD_selectEncodingType(
+ FSE_repeat* repeatMode, unsigned const* count, unsigned const max,
+ size_t const mostFrequent, size_t nbSeq, unsigned const FSELog,
+ FSE_CTable const* prevCTable,
+ short const* defaultNorm, U32 defaultNormLog,
+ ZSTD_defaultPolicy_e const isDefaultAllowed,
+ ZSTD_strategy const strategy)
+{
+ ZSTD_STATIC_ASSERT(ZSTD_defaultDisallowed == 0 && ZSTD_defaultAllowed != 0);
+ if (mostFrequent == nbSeq) {
+ *repeatMode = FSE_repeat_none;
+ if (isDefaultAllowed && nbSeq <= 2) {
+ /* Prefer set_basic over set_rle when there are 2 or less symbols,
+ * since RLE uses 1 byte, but set_basic uses 5-6 bits per symbol.
+ * If basic encoding isn't possible, always choose RLE.
+ */
+ DEBUGLOG(5, "Selected set_basic");
+ return set_basic;
+ }
+ DEBUGLOG(5, "Selected set_rle");
+ return set_rle;
+ }
+ if (strategy < ZSTD_lazy) {
+ if (isDefaultAllowed) {
+ size_t const staticFse_nbSeq_max = 1000;
+ size_t const mult = 10 - strategy;
+ size_t const baseLog = 3;
+ size_t const dynamicFse_nbSeq_min = (((size_t)1 << defaultNormLog) * mult) >> baseLog; /* 28-36 for offset, 56-72 for lengths */
+ assert(defaultNormLog >= 5 && defaultNormLog <= 6); /* xx_DEFAULTNORMLOG */
+ assert(mult <= 9 && mult >= 7);
+ if ( (*repeatMode == FSE_repeat_valid)
+ && (nbSeq < staticFse_nbSeq_max) ) {
+ DEBUGLOG(5, "Selected set_repeat");
+ return set_repeat;
+ }
+ if ( (nbSeq < dynamicFse_nbSeq_min)
+ || (mostFrequent < (nbSeq >> (defaultNormLog-1))) ) {
+ DEBUGLOG(5, "Selected set_basic");
+ /* The format allows default tables to be repeated, but it isn't useful.
+ * When using simple heuristics to select encoding type, we don't want
+ * to confuse these tables with dictionaries. When running more careful
+ * analysis, we don't need to waste time checking both repeating tables
+ * and default tables.
+ */
+ *repeatMode = FSE_repeat_none;
+ return set_basic;
+ }
+ }
+ } else {
+ size_t const basicCost = isDefaultAllowed ? ZSTD_crossEntropyCost(defaultNorm, defaultNormLog, count, max) : ERROR(GENERIC);
+ size_t const repeatCost = *repeatMode != FSE_repeat_none ? ZSTD_fseBitCost(prevCTable, count, max) : ERROR(GENERIC);
+ size_t const NCountCost = ZSTD_NCountCost(count, max, nbSeq, FSELog);
+ size_t const compressedCost = (NCountCost << 3) + ZSTD_entropyCost(count, max, nbSeq);
+
+ if (isDefaultAllowed) {
+ assert(!ZSTD_isError(basicCost));
+ assert(!(*repeatMode == FSE_repeat_valid && ZSTD_isError(repeatCost)));
+ }
+ assert(!ZSTD_isError(NCountCost));
+ assert(compressedCost < ERROR(maxCode));
+ DEBUGLOG(5, "Estimated bit costs: basic=%u\trepeat=%u\tcompressed=%u",
+ (unsigned)basicCost, (unsigned)repeatCost, (unsigned)compressedCost);
+ if (basicCost <= repeatCost && basicCost <= compressedCost) {
+ DEBUGLOG(5, "Selected set_basic");
+ assert(isDefaultAllowed);
+ *repeatMode = FSE_repeat_none;
+ return set_basic;
+ }
+ if (repeatCost <= compressedCost) {
+ DEBUGLOG(5, "Selected set_repeat");
+ assert(!ZSTD_isError(repeatCost));
+ return set_repeat;
+ }
+ assert(compressedCost < basicCost && compressedCost < repeatCost);
+ }
+ DEBUGLOG(5, "Selected set_compressed");
+ *repeatMode = FSE_repeat_check;
+ return set_compressed;
+}
+
+MEM_STATIC size_t
+ZSTD_buildCTable(void* dst, size_t dstCapacity,
+ FSE_CTable* nextCTable, U32 FSELog, symbolEncodingType_e type,
+ unsigned* count, U32 max,
+ const BYTE* codeTable, size_t nbSeq,
+ const S16* defaultNorm, U32 defaultNormLog, U32 defaultMax,
+ const FSE_CTable* prevCTable, size_t prevCTableSize,
+ void* workspace, size_t workspaceSize)
+{
+ BYTE* op = (BYTE*)dst;
+ const BYTE* const oend = op + dstCapacity;
+ DEBUGLOG(6, "ZSTD_buildCTable (dstCapacity=%u)", (unsigned)dstCapacity);
+
+ switch (type) {
+ case set_rle:
+ CHECK_F(FSE_buildCTable_rle(nextCTable, (BYTE)max));
+ if (dstCapacity==0) return ERROR(dstSize_tooSmall);
+ *op = codeTable[0];
+ return 1;
+ case set_repeat:
+ memcpy(nextCTable, prevCTable, prevCTableSize);
+ return 0;
+ case set_basic:
+ CHECK_F(FSE_buildCTable_wksp(nextCTable, defaultNorm, defaultMax, defaultNormLog, workspace, workspaceSize)); /* note : could be pre-calculated */
+ return 0;
+ case set_compressed: {
+ S16 norm[MaxSeq + 1];
+ size_t nbSeq_1 = nbSeq;
+ const U32 tableLog = FSE_optimalTableLog(FSELog, nbSeq, max);
+ if (count[codeTable[nbSeq-1]] > 1) {
+ count[codeTable[nbSeq-1]]--;
+ nbSeq_1--;
+ }
+ assert(nbSeq_1 > 1);
+ CHECK_F(FSE_normalizeCount(norm, tableLog, count, nbSeq_1, max));
+ { size_t const NCountSize = FSE_writeNCount(op, oend - op, norm, max, tableLog); /* overflow protected */
+ if (FSE_isError(NCountSize)) return NCountSize;
+ CHECK_F(FSE_buildCTable_wksp(nextCTable, norm, max, tableLog, workspace, workspaceSize));
+ return NCountSize;
+ }
+ }
+ default: return assert(0), ERROR(GENERIC);
+ }
+}
+
+FORCE_INLINE_TEMPLATE size_t
+ZSTD_encodeSequences_body(
+ void* dst, size_t dstCapacity,
+ FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable,
+ FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable,
+ FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable,
+ seqDef const* sequences, size_t nbSeq, int longOffsets)
+{
+ BIT_CStream_t blockStream;
+ FSE_CState_t stateMatchLength;
+ FSE_CState_t stateOffsetBits;
+ FSE_CState_t stateLitLength;
+
+ CHECK_E(BIT_initCStream(&blockStream, dst, dstCapacity), dstSize_tooSmall); /* not enough space remaining */
+ DEBUGLOG(6, "available space for bitstream : %i (dstCapacity=%u)",
+ (int)(blockStream.endPtr - blockStream.startPtr),
+ (unsigned)dstCapacity);
+
+ /* first symbols */
+ FSE_initCState2(&stateMatchLength, CTable_MatchLength, mlCodeTable[nbSeq-1]);
+ FSE_initCState2(&stateOffsetBits, CTable_OffsetBits, ofCodeTable[nbSeq-1]);
+ FSE_initCState2(&stateLitLength, CTable_LitLength, llCodeTable[nbSeq-1]);
+ BIT_addBits(&blockStream, sequences[nbSeq-1].litLength, LL_bits[llCodeTable[nbSeq-1]]);
+ if (MEM_32bits()) BIT_flushBits(&blockStream);
+ BIT_addBits(&blockStream, sequences[nbSeq-1].matchLength, ML_bits[mlCodeTable[nbSeq-1]]);
+ if (MEM_32bits()) BIT_flushBits(&blockStream);
+ if (longOffsets) {
+ U32 const ofBits = ofCodeTable[nbSeq-1];
+ int const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1);
+ if (extraBits) {
+ BIT_addBits(&blockStream, sequences[nbSeq-1].offset, extraBits);
+ BIT_flushBits(&blockStream);
+ }
+ BIT_addBits(&blockStream, sequences[nbSeq-1].offset >> extraBits,
+ ofBits - extraBits);
+ } else {
+ BIT_addBits(&blockStream, sequences[nbSeq-1].offset, ofCodeTable[nbSeq-1]);
+ }
+ BIT_flushBits(&blockStream);
+
+ { size_t n;
+ for (n=nbSeq-2 ; n<nbSeq ; n--) { /* intentional underflow */
+ BYTE const llCode = llCodeTable[n];
+ BYTE const ofCode = ofCodeTable[n];
+ BYTE const mlCode = mlCodeTable[n];
+ U32 const llBits = LL_bits[llCode];
+ U32 const ofBits = ofCode;
+ U32 const mlBits = ML_bits[mlCode];
+ DEBUGLOG(6, "encoding: litlen:%2u - matchlen:%2u - offCode:%7u",
+ (unsigned)sequences[n].litLength,
+ (unsigned)sequences[n].matchLength + MINMATCH,
+ (unsigned)sequences[n].offset);
+ /* 32b*/ /* 64b*/
+ /* (7)*/ /* (7)*/
+ FSE_encodeSymbol(&blockStream, &stateOffsetBits, ofCode); /* 15 */ /* 15 */
+ FSE_encodeSymbol(&blockStream, &stateMatchLength, mlCode); /* 24 */ /* 24 */
+ if (MEM_32bits()) BIT_flushBits(&blockStream); /* (7)*/
+ FSE_encodeSymbol(&blockStream, &stateLitLength, llCode); /* 16 */ /* 33 */
+ if (MEM_32bits() || (ofBits+mlBits+llBits >= 64-7-(LLFSELog+MLFSELog+OffFSELog)))
+ BIT_flushBits(&blockStream); /* (7)*/
+ BIT_addBits(&blockStream, sequences[n].litLength, llBits);
+ if (MEM_32bits() && ((llBits+mlBits)>24)) BIT_flushBits(&blockStream);
+ BIT_addBits(&blockStream, sequences[n].matchLength, mlBits);
+ if (MEM_32bits() || (ofBits+mlBits+llBits > 56)) BIT_flushBits(&blockStream);
+ if (longOffsets) {
+ int const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1);
+ if (extraBits) {
+ BIT_addBits(&blockStream, sequences[n].offset, extraBits);
+ BIT_flushBits(&blockStream); /* (7)*/
+ }
+ BIT_addBits(&blockStream, sequences[n].offset >> extraBits,
+ ofBits - extraBits); /* 31 */
+ } else {
+ BIT_addBits(&blockStream, sequences[n].offset, ofBits); /* 31 */
+ }
+ BIT_flushBits(&blockStream); /* (7)*/
+ DEBUGLOG(7, "remaining space : %i", (int)(blockStream.endPtr - blockStream.ptr));
+ } }
+
+ DEBUGLOG(6, "ZSTD_encodeSequences: flushing ML state with %u bits", stateMatchLength.stateLog);
+ FSE_flushCState(&blockStream, &stateMatchLength);
+ DEBUGLOG(6, "ZSTD_encodeSequences: flushing Off state with %u bits", stateOffsetBits.stateLog);
+ FSE_flushCState(&blockStream, &stateOffsetBits);
+ DEBUGLOG(6, "ZSTD_encodeSequences: flushing LL state with %u bits", stateLitLength.stateLog);
+ FSE_flushCState(&blockStream, &stateLitLength);
+
+ { size_t const streamSize = BIT_closeCStream(&blockStream);
+ if (streamSize==0) return ERROR(dstSize_tooSmall); /* not enough space */
+ return streamSize;
+ }
+}
+
+static size_t
+ZSTD_encodeSequences_default(
+ void* dst, size_t dstCapacity,
+ FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable,
+ FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable,
+ FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable,
+ seqDef const* sequences, size_t nbSeq, int longOffsets)
+{
+ return ZSTD_encodeSequences_body(dst, dstCapacity,
+ CTable_MatchLength, mlCodeTable,
+ CTable_OffsetBits, ofCodeTable,
+ CTable_LitLength, llCodeTable,
+ sequences, nbSeq, longOffsets);
+}
+
+
+#if DYNAMIC_BMI2
+
+static TARGET_ATTRIBUTE("bmi2") size_t
+ZSTD_encodeSequences_bmi2(
+ void* dst, size_t dstCapacity,
+ FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable,
+ FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable,
+ FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable,
+ seqDef const* sequences, size_t nbSeq, int longOffsets)
+{
+ return ZSTD_encodeSequences_body(dst, dstCapacity,
+ CTable_MatchLength, mlCodeTable,
+ CTable_OffsetBits, ofCodeTable,
+ CTable_LitLength, llCodeTable,
+ sequences, nbSeq, longOffsets);
+}
+
+#endif
+
+static size_t ZSTD_encodeSequences(
+ void* dst, size_t dstCapacity,
+ FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable,
+ FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable,
+ FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable,
+ seqDef const* sequences, size_t nbSeq, int longOffsets, int bmi2)
+{
+ DEBUGLOG(5, "ZSTD_encodeSequences: dstCapacity = %u", (unsigned)dstCapacity);
+#if DYNAMIC_BMI2
+ if (bmi2) {
+ return ZSTD_encodeSequences_bmi2(dst, dstCapacity,
+ CTable_MatchLength, mlCodeTable,
+ CTable_OffsetBits, ofCodeTable,
+ CTable_LitLength, llCodeTable,
+ sequences, nbSeq, longOffsets);
+ }
+#endif
+ (void)bmi2;
+ return ZSTD_encodeSequences_default(dst, dstCapacity,
+ CTable_MatchLength, mlCodeTable,
+ CTable_OffsetBits, ofCodeTable,
+ CTable_LitLength, llCodeTable,
+ sequences, nbSeq, longOffsets);
+}
+
+/* ZSTD_compressSequences_internal():
+ * actually compresses both literals and sequences */
+MEM_STATIC size_t
+ZSTD_compressSequences_internal(seqStore_t* seqStorePtr,
+ const ZSTD_entropyCTables_t* prevEntropy,
+ ZSTD_entropyCTables_t* nextEntropy,
+ const ZSTD_CCtx_params* cctxParams,
+ void* dst, size_t dstCapacity,
+ void* workspace, size_t wkspSize,
+ const int bmi2)
+{
+ const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN;
+ ZSTD_strategy const strategy = cctxParams->cParams.strategy;
+ unsigned count[MaxSeq+1];
+ FSE_CTable* CTable_LitLength = nextEntropy->fse.litlengthCTable;
+ FSE_CTable* CTable_OffsetBits = nextEntropy->fse.offcodeCTable;
+ FSE_CTable* CTable_MatchLength = nextEntropy->fse.matchlengthCTable;
+ U32 LLtype, Offtype, MLtype; /* compressed, raw or rle */
+ const seqDef* const sequences = seqStorePtr->sequencesStart;
+ const BYTE* const ofCodeTable = seqStorePtr->ofCode;
+ const BYTE* const llCodeTable = seqStorePtr->llCode;
+ const BYTE* const mlCodeTable = seqStorePtr->mlCode;
+ BYTE* const ostart = (BYTE*)dst;
+ BYTE* const oend = ostart + dstCapacity;
+ BYTE* op = ostart;
+ size_t const nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart;
+ BYTE* seqHead;
+ BYTE* lastNCount = NULL;
+
+ ZSTD_STATIC_ASSERT(HUF_WORKSPACE_SIZE >= (1<<MAX(MLFSELog,LLFSELog)));
+ DEBUGLOG(5, "ZSTD_compressSequences_internal");
+
+ /* Compress literals */
+ { const BYTE* const literals = seqStorePtr->litStart;
+ size_t const litSize = seqStorePtr->lit - literals;
+ int const disableLiteralCompression = (cctxParams->cParams.strategy == ZSTD_fast) && (cctxParams->cParams.targetLength > 0);
+ size_t const cSize = ZSTD_compressLiterals(
+ &prevEntropy->huf, &nextEntropy->huf,
+ cctxParams->cParams.strategy, disableLiteralCompression,
+ op, dstCapacity,
+ literals, litSize,
+ workspace, wkspSize,
+ bmi2);
+ if (ZSTD_isError(cSize))
+ return cSize;
+ assert(cSize <= dstCapacity);
+ op += cSize;
+ }
+
+ /* Sequences Header */
+ if ((oend-op) < 3 /*max nbSeq Size*/ + 1 /*seqHead*/) return ERROR(dstSize_tooSmall);
+ if (nbSeq < 0x7F)
+ *op++ = (BYTE)nbSeq;
+ else if (nbSeq < LONGNBSEQ)
+ op[0] = (BYTE)((nbSeq>>8) + 0x80), op[1] = (BYTE)nbSeq, op+=2;
+ else
+ op[0]=0xFF, MEM_writeLE16(op+1, (U16)(nbSeq - LONGNBSEQ)), op+=3;
+ if (nbSeq==0) {
+ /* Copy the old tables over as if we repeated them */
+ memcpy(&nextEntropy->fse, &prevEntropy->fse, sizeof(prevEntropy->fse));
+ return op - ostart;
+ }
+
+ /* seqHead : flags for FSE encoding type */
+ seqHead = op++;
+
+ /* convert length/distances into codes */
+ ZSTD_seqToCodes(seqStorePtr);
+ /* build CTable for Literal Lengths */
+ { unsigned max = MaxLL;
+ size_t const mostFrequent = HIST_countFast_wksp(count, &max, llCodeTable, nbSeq, workspace, wkspSize); /* can't fail */
+ DEBUGLOG(5, "Building LL table");
+ nextEntropy->fse.litlength_repeatMode = prevEntropy->fse.litlength_repeatMode;
+ LLtype = ZSTD_selectEncodingType(&nextEntropy->fse.litlength_repeatMode,
+ count, max, mostFrequent, nbSeq,
+ LLFSELog, prevEntropy->fse.litlengthCTable,
+ LL_defaultNorm, LL_defaultNormLog,
+ ZSTD_defaultAllowed, strategy);
+ assert(set_basic < set_compressed && set_rle < set_compressed);
+ assert(!(LLtype < set_compressed && nextEntropy->fse.litlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */
+ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_LitLength, LLFSELog, (symbolEncodingType_e)LLtype,
+ count, max, llCodeTable, nbSeq, LL_defaultNorm, LL_defaultNormLog, MaxLL,
+ prevEntropy->fse.litlengthCTable, sizeof(prevEntropy->fse.litlengthCTable),
+ workspace, wkspSize);
+ if (ZSTD_isError(countSize)) return countSize;
+ if (LLtype == set_compressed)
+ lastNCount = op;
+ op += countSize;
+ } }
+ /* build CTable for Offsets */
+ { unsigned max = MaxOff;
+ size_t const mostFrequent = HIST_countFast_wksp(count, &max, ofCodeTable, nbSeq, workspace, wkspSize); /* can't fail */
+ /* We can only use the basic table if max <= DefaultMaxOff, otherwise the offsets are too large */
+ ZSTD_defaultPolicy_e const defaultPolicy = (max <= DefaultMaxOff) ? ZSTD_defaultAllowed : ZSTD_defaultDisallowed;
+ DEBUGLOG(5, "Building OF table");
+ nextEntropy->fse.offcode_repeatMode = prevEntropy->fse.offcode_repeatMode;
+ Offtype = ZSTD_selectEncodingType(&nextEntropy->fse.offcode_repeatMode,
+ count, max, mostFrequent, nbSeq,
+ OffFSELog, prevEntropy->fse.offcodeCTable,
+ OF_defaultNorm, OF_defaultNormLog,
+ defaultPolicy, strategy);
+ assert(!(Offtype < set_compressed && nextEntropy->fse.offcode_repeatMode != FSE_repeat_none)); /* We don't copy tables */
+ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)Offtype,
+ count, max, ofCodeTable, nbSeq, OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff,
+ prevEntropy->fse.offcodeCTable, sizeof(prevEntropy->fse.offcodeCTable),
+ workspace, wkspSize);
+ if (ZSTD_isError(countSize)) return countSize;
+ if (Offtype == set_compressed)
+ lastNCount = op;
+ op += countSize;
+ } }
+ /* build CTable for MatchLengths */
+ { unsigned max = MaxML;
+ size_t const mostFrequent = HIST_countFast_wksp(count, &max, mlCodeTable, nbSeq, workspace, wkspSize); /* can't fail */
+ DEBUGLOG(5, "Building ML table (remaining space : %i)", (int)(oend-op));
+ nextEntropy->fse.matchlength_repeatMode = prevEntropy->fse.matchlength_repeatMode;
+ MLtype = ZSTD_selectEncodingType(&nextEntropy->fse.matchlength_repeatMode,
+ count, max, mostFrequent, nbSeq,
+ MLFSELog, prevEntropy->fse.matchlengthCTable,
+ ML_defaultNorm, ML_defaultNormLog,
+ ZSTD_defaultAllowed, strategy);
+ assert(!(MLtype < set_compressed && nextEntropy->fse.matchlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */
+ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_MatchLength, MLFSELog, (symbolEncodingType_e)MLtype,
+ count, max, mlCodeTable, nbSeq, ML_defaultNorm, ML_defaultNormLog, MaxML,
+ prevEntropy->fse.matchlengthCTable, sizeof(prevEntropy->fse.matchlengthCTable),
+ workspace, wkspSize);
+ if (ZSTD_isError(countSize)) return countSize;
+ if (MLtype == set_compressed)
+ lastNCount = op;
+ op += countSize;
+ } }
+
+ *seqHead = (BYTE)((LLtype<<6) + (Offtype<<4) + (MLtype<<2));
+
+ { size_t const bitstreamSize = ZSTD_encodeSequences(
+ op, oend - op,
+ CTable_MatchLength, mlCodeTable,
+ CTable_OffsetBits, ofCodeTable,
+ CTable_LitLength, llCodeTable,
+ sequences, nbSeq,
+ longOffsets, bmi2);
+ if (ZSTD_isError(bitstreamSize)) return bitstreamSize;
+ op += bitstreamSize;
+ /* zstd versions <= 1.3.4 mistakenly report corruption when
+ * FSE_readNCount() recieves a buffer < 4 bytes.
+ * Fixed by https://github.com/facebook/zstd/pull/1146.
+ * This can happen when the last set_compressed table present is 2
+ * bytes and the bitstream is only one byte.
+ * In this exceedingly rare case, we will simply emit an uncompressed
+ * block, since it isn't worth optimizing.
+ */
+ if (lastNCount && (op - lastNCount) < 4) {
+ /* NCountSize >= 2 && bitstreamSize > 0 ==> lastCountSize == 3 */
+ assert(op - lastNCount == 3);
+ DEBUGLOG(5, "Avoiding bug in zstd decoder in versions <= 1.3.4 by "
+ "emitting an uncompressed block.");
+ return 0;
+ }
+ }
+
+ DEBUGLOG(5, "compressed block size : %u", (unsigned)(op - ostart));
+ return op - ostart;
+}
+
+MEM_STATIC size_t
+ZSTD_compressSequences(seqStore_t* seqStorePtr,
+ const ZSTD_entropyCTables_t* prevEntropy,
+ ZSTD_entropyCTables_t* nextEntropy,
+ const ZSTD_CCtx_params* cctxParams,
+ void* dst, size_t dstCapacity,
+ size_t srcSize,
+ void* workspace, size_t wkspSize,
+ int bmi2)
+{
+ size_t const cSize = ZSTD_compressSequences_internal(
+ seqStorePtr, prevEntropy, nextEntropy, cctxParams,
+ dst, dstCapacity,
+ workspace, wkspSize, bmi2);
+ if (cSize == 0) return 0;
+ /* When srcSize <= dstCapacity, there is enough space to write a raw uncompressed block.
+ * Since we ran out of space, block must be not compressible, so fall back to raw uncompressed block.
+ */
+ if ((cSize == ERROR(dstSize_tooSmall)) & (srcSize <= dstCapacity))
+ return 0; /* block not compressed */
+ if (ZSTD_isError(cSize)) return cSize;
+
+ /* Check compressibility */
+ { size_t const maxCSize = srcSize - ZSTD_minGain(srcSize, cctxParams->cParams.strategy);
+ if (cSize >= maxCSize) return 0; /* block not compressed */
+ }
+
+ return cSize;
+}
+
+/* ZSTD_selectBlockCompressor() :
+ * Not static, but internal use only (used by long distance matcher)
+ * assumption : strat is a valid strategy */
+ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_dictMode_e dictMode)
+{
+ static const ZSTD_blockCompressor blockCompressor[3][ZSTD_STRATEGY_MAX+1] = {
+ { ZSTD_compressBlock_fast /* default for 0 */,
+ ZSTD_compressBlock_fast,
+ ZSTD_compressBlock_doubleFast,
+ ZSTD_compressBlock_greedy,
+ ZSTD_compressBlock_lazy,
+ ZSTD_compressBlock_lazy2,
+ ZSTD_compressBlock_btlazy2,
+ ZSTD_compressBlock_btopt,
+ ZSTD_compressBlock_btultra,
+ ZSTD_compressBlock_btultra2 },
+ { ZSTD_compressBlock_fast_extDict /* default for 0 */,
+ ZSTD_compressBlock_fast_extDict,
+ ZSTD_compressBlock_doubleFast_extDict,
+ ZSTD_compressBlock_greedy_extDict,
+ ZSTD_compressBlock_lazy_extDict,
+ ZSTD_compressBlock_lazy2_extDict,
+ ZSTD_compressBlock_btlazy2_extDict,
+ ZSTD_compressBlock_btopt_extDict,
+ ZSTD_compressBlock_btultra_extDict,
+ ZSTD_compressBlock_btultra_extDict },
+ { ZSTD_compressBlock_fast_dictMatchState /* default for 0 */,
+ ZSTD_compressBlock_fast_dictMatchState,
+ ZSTD_compressBlock_doubleFast_dictMatchState,
+ ZSTD_compressBlock_greedy_dictMatchState,
+ ZSTD_compressBlock_lazy_dictMatchState,
+ ZSTD_compressBlock_lazy2_dictMatchState,
+ ZSTD_compressBlock_btlazy2_dictMatchState,
+ ZSTD_compressBlock_btopt_dictMatchState,
+ ZSTD_compressBlock_btultra_dictMatchState,
+ ZSTD_compressBlock_btultra_dictMatchState }
+ };
+ ZSTD_blockCompressor selectedCompressor;
+ ZSTD_STATIC_ASSERT((unsigned)ZSTD_fast == 1);
+
+ assert(ZSTD_cParam_withinBounds(ZSTD_c_strategy, strat));
+ selectedCompressor = blockCompressor[(int)dictMode][(int)strat];
+ assert(selectedCompressor != NULL);
+ return selectedCompressor;
+}
+
+static void ZSTD_storeLastLiterals(seqStore_t* seqStorePtr,
+ const BYTE* anchor, size_t lastLLSize)
+{
+ memcpy(seqStorePtr->lit, anchor, lastLLSize);
+ seqStorePtr->lit += lastLLSize;
+}
+
+void ZSTD_resetSeqStore(seqStore_t* ssPtr)
+{
+ ssPtr->lit = ssPtr->litStart;
+ ssPtr->sequences = ssPtr->sequencesStart;
+ ssPtr->longLengthID = 0;
+}
+
+static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize)
+{
+ ZSTD_matchState_t* const ms = &zc->blockState.matchState;
+ size_t cSize;
+ DEBUGLOG(5, "ZSTD_compressBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)",
+ (unsigned)dstCapacity, (unsigned)ms->window.dictLimit, (unsigned)ms->nextToUpdate);
+ assert(srcSize <= ZSTD_BLOCKSIZE_MAX);
+
+ /* Assert that we have correctly flushed the ctx params into the ms's copy */
+ ZSTD_assertEqualCParams(zc->appliedParams.cParams, ms->cParams);
+
+ if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) {
+ ZSTD_ldm_skipSequences(&zc->externSeqStore, srcSize, zc->appliedParams.cParams.minMatch);
+ cSize = 0;
+ goto out; /* don't even attempt compression below a certain srcSize */
+ }
+ ZSTD_resetSeqStore(&(zc->seqStore));
+ ms->opt.symbolCosts = &zc->blockState.prevCBlock->entropy; /* required for optimal parser to read stats from dictionary */
+
+ /* a gap between an attached dict and the current window is not safe,
+ * they must remain adjacent,
+ * and when that stops being the case, the dict must be unset */
+ assert(ms->dictMatchState == NULL || ms->loadedDictEnd == ms->window.dictLimit);
+
+ /* limited update after a very long match */
+ { const BYTE* const base = ms->window.base;
+ const BYTE* const istart = (const BYTE*)src;
+ const U32 current = (U32)(istart-base);
+ if (sizeof(ptrdiff_t)==8) assert(istart - base < (ptrdiff_t)(U32)(-1)); /* ensure no overflow */
+ if (current > ms->nextToUpdate + 384)
+ ms->nextToUpdate = current - MIN(192, (U32)(current - ms->nextToUpdate - 384));
+ }
+
+ /* select and store sequences */
+ { ZSTD_dictMode_e const dictMode = ZSTD_matchState_dictMode(ms);
+ size_t lastLLSize;
+ { int i;
+ for (i = 0; i < ZSTD_REP_NUM; ++i)
+ zc->blockState.nextCBlock->rep[i] = zc->blockState.prevCBlock->rep[i];
+ }
+ if (zc->externSeqStore.pos < zc->externSeqStore.size) {
+ assert(!zc->appliedParams.ldmParams.enableLdm);
+ /* Updates ldmSeqStore.pos */
+ lastLLSize =
+ ZSTD_ldm_blockCompress(&zc->externSeqStore,
+ ms, &zc->seqStore,
+ zc->blockState.nextCBlock->rep,
+ src, srcSize);
+ assert(zc->externSeqStore.pos <= zc->externSeqStore.size);
+ } else if (zc->appliedParams.ldmParams.enableLdm) {
+ rawSeqStore_t ldmSeqStore = {NULL, 0, 0, 0};
+
+ ldmSeqStore.seq = zc->ldmSequences;
+ ldmSeqStore.capacity = zc->maxNbLdmSequences;
+ /* Updates ldmSeqStore.size */
+ CHECK_F(ZSTD_ldm_generateSequences(&zc->ldmState, &ldmSeqStore,
+ &zc->appliedParams.ldmParams,
+ src, srcSize));
+ /* Updates ldmSeqStore.pos */
+ lastLLSize =
+ ZSTD_ldm_blockCompress(&ldmSeqStore,
+ ms, &zc->seqStore,
+ zc->blockState.nextCBlock->rep,
+ src, srcSize);
+ assert(ldmSeqStore.pos == ldmSeqStore.size);
+ } else { /* not long range mode */
+ ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, dictMode);
+ lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, src, srcSize);
+ }
+ { const BYTE* const lastLiterals = (const BYTE*)src + srcSize - lastLLSize;
+ ZSTD_storeLastLiterals(&zc->seqStore, lastLiterals, lastLLSize);
+ } }
+
+ /* encode sequences and literals */
+ cSize = ZSTD_compressSequences(&zc->seqStore,
+ &zc->blockState.prevCBlock->entropy, &zc->blockState.nextCBlock->entropy,
+ &zc->appliedParams,
+ dst, dstCapacity,
+ srcSize,
+ zc->entropyWorkspace, HUF_WORKSPACE_SIZE /* statically allocated in resetCCtx */,
+ zc->bmi2);
+
+out:
+ if (!ZSTD_isError(cSize) && cSize != 0) {
+ /* confirm repcodes and entropy tables when emitting a compressed block */
+ ZSTD_compressedBlockState_t* const tmp = zc->blockState.prevCBlock;
+ zc->blockState.prevCBlock = zc->blockState.nextCBlock;
+ zc->blockState.nextCBlock = tmp;
+ }
+ /* We check that dictionaries have offset codes available for the first
+ * block. After the first block, the offcode table might not have large
+ * enough codes to represent the offsets in the data.
+ */
+ if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid)
+ zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check;
+
+ return cSize;
+}
+
+
+/*! ZSTD_compress_frameChunk() :
+* Compress a chunk of data into one or multiple blocks.
+* All blocks will be terminated, all input will be consumed.
+* Function will issue an error if there is not enough `dstCapacity` to hold the compressed content.
+* Frame is supposed already started (header already produced)
+* @return : compressed size, or an error code
+*/
+static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ U32 lastFrameChunk)
+{
+ size_t blockSize = cctx->blockSize;
+ size_t remaining = srcSize;
+ const BYTE* ip = (const BYTE*)src;
+ BYTE* const ostart = (BYTE*)dst;
+ BYTE* op = ostart;
+ U32 const maxDist = (U32)1 << cctx->appliedParams.cParams.windowLog;
+ assert(cctx->appliedParams.cParams.windowLog <= 31);
+
+ DEBUGLOG(5, "ZSTD_compress_frameChunk (blockSize=%u)", (unsigned)blockSize);
+ if (cctx->appliedParams.fParams.checksumFlag && srcSize)
+ XXH64_update(&cctx->xxhState, src, srcSize);
+
+ while (remaining) {
+ ZSTD_matchState_t* const ms = &cctx->blockState.matchState;
+ U32 const lastBlock = lastFrameChunk & (blockSize >= remaining);
+
+ if (dstCapacity < ZSTD_blockHeaderSize + MIN_CBLOCK_SIZE)
+ return ERROR(dstSize_tooSmall); /* not enough space to store compressed block */
+ if (remaining < blockSize) blockSize = remaining;
+
+ if (ZSTD_window_needOverflowCorrection(ms->window, ip + blockSize)) {
+ U32 const cycleLog = ZSTD_cycleLog(cctx->appliedParams.cParams.chainLog, cctx->appliedParams.cParams.strategy);
+ U32 const correction = ZSTD_window_correctOverflow(&ms->window, cycleLog, maxDist, ip);
+ ZSTD_STATIC_ASSERT(ZSTD_CHAINLOG_MAX <= 30);
+ ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX_32 <= 30);
+ ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX <= 31);
+ ZSTD_reduceIndex(cctx, correction);
+ if (ms->nextToUpdate < correction) ms->nextToUpdate = 0;
+ else ms->nextToUpdate -= correction;
+ ms->loadedDictEnd = 0;
+ ms->dictMatchState = NULL;
+ }
+ ZSTD_window_enforceMaxDist(&ms->window, ip + blockSize, maxDist, &ms->loadedDictEnd, &ms->dictMatchState);
+ if (ms->nextToUpdate < ms->window.lowLimit) ms->nextToUpdate = ms->window.lowLimit;
+
+ { size_t cSize = ZSTD_compressBlock_internal(cctx,
+ op+ZSTD_blockHeaderSize, dstCapacity-ZSTD_blockHeaderSize,
+ ip, blockSize);
+ if (ZSTD_isError(cSize)) return cSize;
+
+ if (cSize == 0) { /* block is not compressible */
+ cSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize, lastBlock);
+ if (ZSTD_isError(cSize)) return cSize;
+ } else {
+ U32 const cBlockHeader24 = lastBlock + (((U32)bt_compressed)<<1) + (U32)(cSize << 3);
+ MEM_writeLE24(op, cBlockHeader24);
+ cSize += ZSTD_blockHeaderSize;
+ }
+
+ ip += blockSize;
+ assert(remaining >= blockSize);
+ remaining -= blockSize;
+ op += cSize;
+ assert(dstCapacity >= cSize);
+ dstCapacity -= cSize;
+ DEBUGLOG(5, "ZSTD_compress_frameChunk: adding a block of size %u",
+ (unsigned)cSize);
+ } }
+
+ if (lastFrameChunk && (op>ostart)) cctx->stage = ZSTDcs_ending;
+ return op-ostart;
+}
+
+
+static size_t ZSTD_writeFrameHeader(void* dst, size_t dstCapacity,
+ ZSTD_CCtx_params params, U64 pledgedSrcSize, U32 dictID)
+{ BYTE* const op = (BYTE*)dst;
+ U32 const dictIDSizeCodeLength = (dictID>0) + (dictID>=256) + (dictID>=65536); /* 0-3 */
+ U32 const dictIDSizeCode = params.fParams.noDictIDFlag ? 0 : dictIDSizeCodeLength; /* 0-3 */
+ U32 const checksumFlag = params.fParams.checksumFlag>0;
+ U32 const windowSize = (U32)1 << params.cParams.windowLog;
+ U32 const singleSegment = params.fParams.contentSizeFlag && (windowSize >= pledgedSrcSize);
+ BYTE const windowLogByte = (BYTE)((params.cParams.windowLog - ZSTD_WINDOWLOG_ABSOLUTEMIN) << 3);
+ U32 const fcsCode = params.fParams.contentSizeFlag ?
+ (pledgedSrcSize>=256) + (pledgedSrcSize>=65536+256) + (pledgedSrcSize>=0xFFFFFFFFU) : 0; /* 0-3 */
+ BYTE const frameHeaderDecriptionByte = (BYTE)(dictIDSizeCode + (checksumFlag<<2) + (singleSegment<<5) + (fcsCode<<6) );
+ size_t pos=0;
+
+ assert(!(params.fParams.contentSizeFlag && pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN));
+ if (dstCapacity < ZSTD_FRAMEHEADERSIZE_MAX) return ERROR(dstSize_tooSmall);
+ DEBUGLOG(4, "ZSTD_writeFrameHeader : dictIDFlag : %u ; dictID : %u ; dictIDSizeCode : %u",
+ !params.fParams.noDictIDFlag, (unsigned)dictID, (unsigned)dictIDSizeCode);
+
+ if (params.format == ZSTD_f_zstd1) {
+ MEM_writeLE32(dst, ZSTD_MAGICNUMBER);
+ pos = 4;
+ }
+ op[pos++] = frameHeaderDecriptionByte;
+ if (!singleSegment) op[pos++] = windowLogByte;
+ switch(dictIDSizeCode)
+ {
+ default: assert(0); /* impossible */
+ case 0 : break;
+ case 1 : op[pos] = (BYTE)(dictID); pos++; break;
+ case 2 : MEM_writeLE16(op+pos, (U16)dictID); pos+=2; break;
+ case 3 : MEM_writeLE32(op+pos, dictID); pos+=4; break;
+ }
+ switch(fcsCode)
+ {
+ default: assert(0); /* impossible */
+ case 0 : if (singleSegment) op[pos++] = (BYTE)(pledgedSrcSize); break;
+ case 1 : MEM_writeLE16(op+pos, (U16)(pledgedSrcSize-256)); pos+=2; break;
+ case 2 : MEM_writeLE32(op+pos, (U32)(pledgedSrcSize)); pos+=4; break;
+ case 3 : MEM_writeLE64(op+pos, (U64)(pledgedSrcSize)); pos+=8; break;
+ }
+ return pos;
+}
+
+/* ZSTD_writeLastEmptyBlock() :
+ * output an empty Block with end-of-frame mark to complete a frame
+ * @return : size of data written into `dst` (== ZSTD_blockHeaderSize (defined in zstd_internal.h))
+ * or an error code if `dstCapcity` is too small (<ZSTD_blockHeaderSize)
+ */
+size_t ZSTD_writeLastEmptyBlock(void* dst, size_t dstCapacity)
+{
+ if (dstCapacity < ZSTD_blockHeaderSize) return ERROR(dstSize_tooSmall);
+ { U32 const cBlockHeader24 = 1 /*lastBlock*/ + (((U32)bt_raw)<<1); /* 0 size */
+ MEM_writeLE24(dst, cBlockHeader24);
+ return ZSTD_blockHeaderSize;
+ }
+}
+
+size_t ZSTD_referenceExternalSequences(ZSTD_CCtx* cctx, rawSeq* seq, size_t nbSeq)
+{
+ if (cctx->stage != ZSTDcs_init)
+ return ERROR(stage_wrong);
+ if (cctx->appliedParams.ldmParams.enableLdm)
+ return ERROR(parameter_unsupported);
+ cctx->externSeqStore.seq = seq;
+ cctx->externSeqStore.size = nbSeq;
+ cctx->externSeqStore.capacity = nbSeq;
+ cctx->externSeqStore.pos = 0;
+ return 0;
+}
+
+
+static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ U32 frame, U32 lastFrameChunk)
+{
+ ZSTD_matchState_t* const ms = &cctx->blockState.matchState;
+ size_t fhSize = 0;
+
+ DEBUGLOG(5, "ZSTD_compressContinue_internal, stage: %u, srcSize: %u",
+ cctx->stage, (unsigned)srcSize);
+ if (cctx->stage==ZSTDcs_created) return ERROR(stage_wrong); /* missing init (ZSTD_compressBegin) */
+
+ if (frame && (cctx->stage==ZSTDcs_init)) {
+ fhSize = ZSTD_writeFrameHeader(dst, dstCapacity, cctx->appliedParams,
+ cctx->pledgedSrcSizePlusOne-1, cctx->dictID);
+ if (ZSTD_isError(fhSize)) return fhSize;
+ dstCapacity -= fhSize;
+ dst = (char*)dst + fhSize;
+ cctx->stage = ZSTDcs_ongoing;
+ }
+
+ if (!srcSize) return fhSize; /* do not generate an empty block if no input */
+
+ if (!ZSTD_window_update(&ms->window, src, srcSize)) {
+ ms->nextToUpdate = ms->window.dictLimit;
+ }
+ if (cctx->appliedParams.ldmParams.enableLdm) {
+ ZSTD_window_update(&cctx->ldmState.window, src, srcSize);
+ }
+
+ if (!frame) {
+ /* overflow check and correction for block mode */
+ if (ZSTD_window_needOverflowCorrection(ms->window, (const char*)src + srcSize)) {
+ U32 const cycleLog = ZSTD_cycleLog(cctx->appliedParams.cParams.chainLog, cctx->appliedParams.cParams.strategy);
+ U32 const correction = ZSTD_window_correctOverflow(&ms->window, cycleLog, 1 << cctx->appliedParams.cParams.windowLog, src);
+ ZSTD_STATIC_ASSERT(ZSTD_CHAINLOG_MAX <= 30);
+ ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX_32 <= 30);
+ ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX <= 31);
+ ZSTD_reduceIndex(cctx, correction);
+ if (ms->nextToUpdate < correction) ms->nextToUpdate = 0;
+ else ms->nextToUpdate -= correction;
+ ms->loadedDictEnd = 0;
+ ms->dictMatchState = NULL;
+ }
+ }
+
+ DEBUGLOG(5, "ZSTD_compressContinue_internal (blockSize=%u)", (unsigned)cctx->blockSize);
+ { size_t const cSize = frame ?
+ ZSTD_compress_frameChunk (cctx, dst, dstCapacity, src, srcSize, lastFrameChunk) :
+ ZSTD_compressBlock_internal (cctx, dst, dstCapacity, src, srcSize);
+ if (ZSTD_isError(cSize)) return cSize;
+ cctx->consumedSrcSize += srcSize;
+ cctx->producedCSize += (cSize + fhSize);
+ assert(!(cctx->appliedParams.fParams.contentSizeFlag && cctx->pledgedSrcSizePlusOne == 0));
+ if (cctx->pledgedSrcSizePlusOne != 0) { /* control src size */
+ ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_UNKNOWN == (unsigned long long)-1);
+ if (cctx->consumedSrcSize+1 > cctx->pledgedSrcSizePlusOne) {
+ DEBUGLOG(4, "error : pledgedSrcSize = %u, while realSrcSize >= %u",
+ (unsigned)cctx->pledgedSrcSizePlusOne-1, (unsigned)cctx->consumedSrcSize);
+ return ERROR(srcSize_wrong);
+ }
+ }
+ return cSize + fhSize;
+ }
+}
+
+size_t ZSTD_compressContinue (ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize)
+{
+ DEBUGLOG(5, "ZSTD_compressContinue (srcSize=%u)", (unsigned)srcSize);
+ return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 1 /* frame mode */, 0 /* last chunk */);
+}
+
+
+size_t ZSTD_getBlockSize(const ZSTD_CCtx* cctx)
+{
+ ZSTD_compressionParameters const cParams = cctx->appliedParams.cParams;
+ assert(!ZSTD_checkCParams(cParams));
+ return MIN (ZSTD_BLOCKSIZE_MAX, (U32)1 << cParams.windowLog);
+}
+
+size_t ZSTD_compressBlock(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize)
+{
+ size_t const blockSizeMax = ZSTD_getBlockSize(cctx);
+ if (srcSize > blockSizeMax) return ERROR(srcSize_wrong);
+
+ return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 0 /* frame mode */, 0 /* last chunk */);
+}
+
+/*! ZSTD_loadDictionaryContent() :
+ * @return : 0, or an error code
+ */
+static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms,
+ ZSTD_CCtx_params const* params,
+ const void* src, size_t srcSize,
+ ZSTD_dictTableLoadMethod_e dtlm)
+{
+ const BYTE* const ip = (const BYTE*) src;
+ const BYTE* const iend = ip + srcSize;
+
+ ZSTD_window_update(&ms->window, src, srcSize);
+ ms->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ms->window.base);
+
+ /* Assert that we the ms params match the params we're being given */
+ ZSTD_assertEqualCParams(params->cParams, ms->cParams);
+
+ if (srcSize <= HASH_READ_SIZE) return 0;
+
+ switch(params->cParams.strategy)
+ {
+ case ZSTD_fast:
+ ZSTD_fillHashTable(ms, iend, dtlm);
+ break;
+ case ZSTD_dfast:
+ ZSTD_fillDoubleHashTable(ms, iend, dtlm);
+ break;
+
+ case ZSTD_greedy:
+ case ZSTD_lazy:
+ case ZSTD_lazy2:
+ if (srcSize >= HASH_READ_SIZE)
+ ZSTD_insertAndFindFirstIndex(ms, iend-HASH_READ_SIZE);
+ break;
+
+ case ZSTD_btlazy2: /* we want the dictionary table fully sorted */
+ case ZSTD_btopt:
+ case ZSTD_btultra:
+ case ZSTD_btultra2:
+ if (srcSize >= HASH_READ_SIZE)
+ ZSTD_updateTree(ms, iend-HASH_READ_SIZE, iend);
+ break;
+
+ default:
+ assert(0); /* not possible : not a valid strategy id */
+ }
+
+ ms->nextToUpdate = (U32)(iend - ms->window.base);
+ return 0;
+}
+
+
+/* Dictionaries that assign zero probability to symbols that show up causes problems
+ when FSE encoding. Refuse dictionaries that assign zero probability to symbols
+ that we may encounter during compression.
+ NOTE: This behavior is not standard and could be improved in the future. */
+static size_t ZSTD_checkDictNCount(short* normalizedCounter, unsigned dictMaxSymbolValue, unsigned maxSymbolValue) {
+ U32 s;
+ if (dictMaxSymbolValue < maxSymbolValue) return ERROR(dictionary_corrupted);
+ for (s = 0; s <= maxSymbolValue; ++s) {
+ if (normalizedCounter[s] == 0) return ERROR(dictionary_corrupted);
+ }
+ return 0;
+}
+
+
+/* Dictionary format :
+ * See :
+ * https://github.com/facebook/zstd/blob/master/doc/zstd_compression_format.md#dictionary-format
+ */
+/*! ZSTD_loadZstdDictionary() :
+ * @return : dictID, or an error code
+ * assumptions : magic number supposed already checked
+ * dictSize supposed > 8
+ */
+static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs,
+ ZSTD_matchState_t* ms,
+ ZSTD_CCtx_params const* params,
+ const void* dict, size_t dictSize,
+ ZSTD_dictTableLoadMethod_e dtlm,
+ void* workspace)
+{
+ const BYTE* dictPtr = (const BYTE*)dict;
+ const BYTE* const dictEnd = dictPtr + dictSize;
+ short offcodeNCount[MaxOff+1];
+ unsigned offcodeMaxValue = MaxOff;
+ size_t dictID;
+
+ ZSTD_STATIC_ASSERT(HUF_WORKSPACE_SIZE >= (1<<MAX(MLFSELog,LLFSELog)));
+ assert(dictSize > 8);
+ assert(MEM_readLE32(dictPtr) == ZSTD_MAGIC_DICTIONARY);
+
+ dictPtr += 4; /* skip magic number */
+ dictID = params->fParams.noDictIDFlag ? 0 : MEM_readLE32(dictPtr);
+ dictPtr += 4;
+
+ { unsigned maxSymbolValue = 255;
+ size_t const hufHeaderSize = HUF_readCTable((HUF_CElt*)bs->entropy.huf.CTable, &maxSymbolValue, dictPtr, dictEnd-dictPtr);
+ if (HUF_isError(hufHeaderSize)) return ERROR(dictionary_corrupted);
+ if (maxSymbolValue < 255) return ERROR(dictionary_corrupted);
+ dictPtr += hufHeaderSize;
+ }
+
+ { unsigned offcodeLog;
+ size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, &offcodeMaxValue, &offcodeLog, dictPtr, dictEnd-dictPtr);
+ if (FSE_isError(offcodeHeaderSize)) return ERROR(dictionary_corrupted);
+ if (offcodeLog > OffFSELog) return ERROR(dictionary_corrupted);
+ /* Defer checking offcodeMaxValue because we need to know the size of the dictionary content */
+ /* fill all offset symbols to avoid garbage at end of table */
+ CHECK_E( FSE_buildCTable_wksp(bs->entropy.fse.offcodeCTable,
+ offcodeNCount, MaxOff, offcodeLog,
+ workspace, HUF_WORKSPACE_SIZE),
+ dictionary_corrupted);
+ dictPtr += offcodeHeaderSize;
+ }
+
+ { short matchlengthNCount[MaxML+1];
+ unsigned matchlengthMaxValue = MaxML, matchlengthLog;
+ size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, dictEnd-dictPtr);
+ if (FSE_isError(matchlengthHeaderSize)) return ERROR(dictionary_corrupted);
+ if (matchlengthLog > MLFSELog) return ERROR(dictionary_corrupted);
+ /* Every match length code must have non-zero probability */
+ CHECK_F( ZSTD_checkDictNCount(matchlengthNCount, matchlengthMaxValue, MaxML));
+ CHECK_E( FSE_buildCTable_wksp(bs->entropy.fse.matchlengthCTable,
+ matchlengthNCount, matchlengthMaxValue, matchlengthLog,
+ workspace, HUF_WORKSPACE_SIZE),
+ dictionary_corrupted);
+ dictPtr += matchlengthHeaderSize;
+ }
+
+ { short litlengthNCount[MaxLL+1];
+ unsigned litlengthMaxValue = MaxLL, litlengthLog;
+ size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, dictEnd-dictPtr);
+ if (FSE_isError(litlengthHeaderSize)) return ERROR(dictionary_corrupted);
+ if (litlengthLog > LLFSELog) return ERROR(dictionary_corrupted);
+ /* Every literal length code must have non-zero probability */
+ CHECK_F( ZSTD_checkDictNCount(litlengthNCount, litlengthMaxValue, MaxLL));
+ CHECK_E( FSE_buildCTable_wksp(bs->entropy.fse.litlengthCTable,
+ litlengthNCount, litlengthMaxValue, litlengthLog,
+ workspace, HUF_WORKSPACE_SIZE),
+ dictionary_corrupted);
+ dictPtr += litlengthHeaderSize;
+ }
+
+ if (dictPtr+12 > dictEnd) return ERROR(dictionary_corrupted);
+ bs->rep[0] = MEM_readLE32(dictPtr+0);
+ bs->rep[1] = MEM_readLE32(dictPtr+4);
+ bs->rep[2] = MEM_readLE32(dictPtr+8);
+ dictPtr += 12;
+
+ { size_t const dictContentSize = (size_t)(dictEnd - dictPtr);
+ U32 offcodeMax = MaxOff;
+ if (dictContentSize <= ((U32)-1) - 128 KB) {
+ U32 const maxOffset = (U32)dictContentSize + 128 KB; /* The maximum offset that must be supported */
+ offcodeMax = ZSTD_highbit32(maxOffset); /* Calculate minimum offset code required to represent maxOffset */
+ }
+ /* All offset values <= dictContentSize + 128 KB must be representable */
+ CHECK_F (ZSTD_checkDictNCount(offcodeNCount, offcodeMaxValue, MIN(offcodeMax, MaxOff)));
+ /* All repCodes must be <= dictContentSize and != 0*/
+ { U32 u;
+ for (u=0; u<3; u++) {
+ if (bs->rep[u] == 0) return ERROR(dictionary_corrupted);
+ if (bs->rep[u] > dictContentSize) return ERROR(dictionary_corrupted);
+ } }
+
+ bs->entropy.huf.repeatMode = HUF_repeat_valid;
+ bs->entropy.fse.offcode_repeatMode = FSE_repeat_valid;
+ bs->entropy.fse.matchlength_repeatMode = FSE_repeat_valid;
+ bs->entropy.fse.litlength_repeatMode = FSE_repeat_valid;
+ CHECK_F(ZSTD_loadDictionaryContent(ms, params, dictPtr, dictContentSize, dtlm));
+ return dictID;
+ }
+}
+
+/** ZSTD_compress_insertDictionary() :
+* @return : dictID, or an error code */
+static size_t
+ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs,
+ ZSTD_matchState_t* ms,
+ const ZSTD_CCtx_params* params,
+ const void* dict, size_t dictSize,
+ ZSTD_dictContentType_e dictContentType,
+ ZSTD_dictTableLoadMethod_e dtlm,
+ void* workspace)
+{
+ DEBUGLOG(4, "ZSTD_compress_insertDictionary (dictSize=%u)", (U32)dictSize);
+ if ((dict==NULL) || (dictSize<=8)) return 0;
+
+ ZSTD_reset_compressedBlockState(bs);
+
+ /* dict restricted modes */
+ if (dictContentType == ZSTD_dct_rawContent)
+ return ZSTD_loadDictionaryContent(ms, params, dict, dictSize, dtlm);
+
+ if (MEM_readLE32(dict) != ZSTD_MAGIC_DICTIONARY) {
+ if (dictContentType == ZSTD_dct_auto) {
+ DEBUGLOG(4, "raw content dictionary detected");
+ return ZSTD_loadDictionaryContent(ms, params, dict, dictSize, dtlm);
+ }
+ if (dictContentType == ZSTD_dct_fullDict)
+ return ERROR(dictionary_wrong);
+ assert(0); /* impossible */
+ }
+
+ /* dict as full zstd dictionary */
+ return ZSTD_loadZstdDictionary(bs, ms, params, dict, dictSize, dtlm, workspace);
+}
+
+/*! ZSTD_compressBegin_internal() :
+ * @return : 0, or an error code */
+static size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx,
+ const void* dict, size_t dictSize,
+ ZSTD_dictContentType_e dictContentType,
+ ZSTD_dictTableLoadMethod_e dtlm,
+ const ZSTD_CDict* cdict,
+ ZSTD_CCtx_params params, U64 pledgedSrcSize,
+ ZSTD_buffered_policy_e zbuff)
+{
+ DEBUGLOG(4, "ZSTD_compressBegin_internal: wlog=%u", params.cParams.windowLog);
+ /* params are supposed to be fully validated at this point */
+ assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));
+ assert(!((dict) && (cdict))); /* either dict or cdict, not both */
+
+ if (cdict && cdict->dictContentSize>0) {
+ return ZSTD_resetCCtx_usingCDict(cctx, cdict, params, pledgedSrcSize, zbuff);
+ }
+
+ CHECK_F( ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize,
+ ZSTDcrp_continue, zbuff) );
+ {
+ size_t const dictID = ZSTD_compress_insertDictionary(
+ cctx->blockState.prevCBlock, &cctx->blockState.matchState,
+ &params, dict, dictSize, dictContentType, dtlm, cctx->entropyWorkspace);
+ if (ZSTD_isError(dictID)) return dictID;
+ assert(dictID <= (size_t)(U32)-1);
+ cctx->dictID = (U32)dictID;
+ }
+ return 0;
+}
+
+size_t ZSTD_compressBegin_advanced_internal(ZSTD_CCtx* cctx,
+ const void* dict, size_t dictSize,
+ ZSTD_dictContentType_e dictContentType,
+ ZSTD_dictTableLoadMethod_e dtlm,
+ const ZSTD_CDict* cdict,
+ ZSTD_CCtx_params params,
+ unsigned long long pledgedSrcSize)
+{
+ DEBUGLOG(4, "ZSTD_compressBegin_advanced_internal: wlog=%u", params.cParams.windowLog);
+ /* compression parameters verification and optimization */
+ CHECK_F( ZSTD_checkCParams(params.cParams) );
+ return ZSTD_compressBegin_internal(cctx,
+ dict, dictSize, dictContentType, dtlm,
+ cdict,
+ params, pledgedSrcSize,
+ ZSTDb_not_buffered);
+}
+
+/*! ZSTD_compressBegin_advanced() :
+* @return : 0, or an error code */
+size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx,
+ const void* dict, size_t dictSize,
+ ZSTD_parameters params, unsigned long long pledgedSrcSize)
+{
+ ZSTD_CCtx_params const cctxParams =
+ ZSTD_assignParamsToCCtxParams(cctx->requestedParams, params);
+ return ZSTD_compressBegin_advanced_internal(cctx,
+ dict, dictSize, ZSTD_dct_auto, ZSTD_dtlm_fast,
+ NULL /*cdict*/,
+ cctxParams, pledgedSrcSize);
+}
+
+size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel)
+{
+ ZSTD_parameters const params = ZSTD_getParams(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize);
+ ZSTD_CCtx_params const cctxParams =
+ ZSTD_assignParamsToCCtxParams(cctx->requestedParams, params);
+ DEBUGLOG(4, "ZSTD_compressBegin_usingDict (dictSize=%u)", (unsigned)dictSize);
+ return ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dct_auto, ZSTD_dtlm_fast, NULL,
+ cctxParams, ZSTD_CONTENTSIZE_UNKNOWN, ZSTDb_not_buffered);
+}
+
+size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel)
+{
+ return ZSTD_compressBegin_usingDict(cctx, NULL, 0, compressionLevel);
+}
+
+
+/*! ZSTD_writeEpilogue() :
+* Ends a frame.
+* @return : nb of bytes written into dst (or an error code) */
+static size_t ZSTD_writeEpilogue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity)
+{
+ BYTE* const ostart = (BYTE*)dst;
+ BYTE* op = ostart;
+ size_t fhSize = 0;
+
+ DEBUGLOG(4, "ZSTD_writeEpilogue");
+ if (cctx->stage == ZSTDcs_created) return ERROR(stage_wrong); /* init missing */
+
+ /* special case : empty frame */
+ if (cctx->stage == ZSTDcs_init) {
+ fhSize = ZSTD_writeFrameHeader(dst, dstCapacity, cctx->appliedParams, 0, 0);
+ if (ZSTD_isError(fhSize)) return fhSize;
+ dstCapacity -= fhSize;
+ op += fhSize;
+ cctx->stage = ZSTDcs_ongoing;
+ }
+
+ if (cctx->stage != ZSTDcs_ending) {
+ /* write one last empty block, make it the "last" block */
+ U32 const cBlockHeader24 = 1 /* last block */ + (((U32)bt_raw)<<1) + 0;
+ if (dstCapacity<4) return ERROR(dstSize_tooSmall);
+ MEM_writeLE32(op, cBlockHeader24);
+ op += ZSTD_blockHeaderSize;
+ dstCapacity -= ZSTD_blockHeaderSize;
+ }
+
+ if (cctx->appliedParams.fParams.checksumFlag) {
+ U32 const checksum = (U32) XXH64_digest(&cctx->xxhState);
+ if (dstCapacity<4) return ERROR(dstSize_tooSmall);
+ DEBUGLOG(4, "ZSTD_writeEpilogue: write checksum : %08X", (unsigned)checksum);
+ MEM_writeLE32(op, checksum);
+ op += 4;
+ }
+
+ cctx->stage = ZSTDcs_created; /* return to "created but no init" status */
+ return op-ostart;
+}
+
+size_t ZSTD_compressEnd (ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize)
+{
+ size_t endResult;
+ size_t const cSize = ZSTD_compressContinue_internal(cctx,
+ dst, dstCapacity, src, srcSize,
+ 1 /* frame mode */, 1 /* last chunk */);
+ if (ZSTD_isError(cSize)) return cSize;
+ endResult = ZSTD_writeEpilogue(cctx, (char*)dst + cSize, dstCapacity-cSize);
+ if (ZSTD_isError(endResult)) return endResult;
+ assert(!(cctx->appliedParams.fParams.contentSizeFlag && cctx->pledgedSrcSizePlusOne == 0));
+ if (cctx->pledgedSrcSizePlusOne != 0) { /* control src size */
+ ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_UNKNOWN == (unsigned long long)-1);
+ DEBUGLOG(4, "end of frame : controlling src size");
+ if (cctx->pledgedSrcSizePlusOne != cctx->consumedSrcSize+1) {
+ DEBUGLOG(4, "error : pledgedSrcSize = %u, while realSrcSize = %u",
+ (unsigned)cctx->pledgedSrcSizePlusOne-1, (unsigned)cctx->consumedSrcSize);
+ return ERROR(srcSize_wrong);
+ } }
+ return cSize + endResult;
+}
+
+
+static size_t ZSTD_compress_internal (ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ const void* dict,size_t dictSize,
+ ZSTD_parameters params)
+{
+ ZSTD_CCtx_params const cctxParams =
+ ZSTD_assignParamsToCCtxParams(cctx->requestedParams, params);
+ DEBUGLOG(4, "ZSTD_compress_internal");
+ return ZSTD_compress_advanced_internal(cctx,
+ dst, dstCapacity,
+ src, srcSize,
+ dict, dictSize,
+ cctxParams);
+}
+
+size_t ZSTD_compress_advanced (ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ const void* dict,size_t dictSize,
+ ZSTD_parameters params)
+{
+ DEBUGLOG(4, "ZSTD_compress_advanced");
+ CHECK_F(ZSTD_checkCParams(params.cParams));
+ return ZSTD_compress_internal(cctx,
+ dst, dstCapacity,
+ src, srcSize,
+ dict, dictSize,
+ params);
+}
+
+/* Internal */
+size_t ZSTD_compress_advanced_internal(
+ ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ const void* dict,size_t dictSize,
+ ZSTD_CCtx_params params)
+{
+ DEBUGLOG(4, "ZSTD_compress_advanced_internal (srcSize:%u)", (unsigned)srcSize);
+ CHECK_F( ZSTD_compressBegin_internal(cctx,
+ dict, dictSize, ZSTD_dct_auto, ZSTD_dtlm_fast, NULL,
+ params, srcSize, ZSTDb_not_buffered) );
+ return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize);
+}
+
+size_t ZSTD_compress_usingDict(ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ const void* dict, size_t dictSize,
+ int compressionLevel)
+{
+ ZSTD_parameters const params = ZSTD_getParams(compressionLevel, srcSize + (!srcSize), dict ? dictSize : 0);
+ ZSTD_CCtx_params cctxParams = ZSTD_assignParamsToCCtxParams(cctx->requestedParams, params);
+ assert(params.fParams.contentSizeFlag == 1);
+ return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, dict, dictSize, cctxParams);
+}
+
+size_t ZSTD_compressCCtx(ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ int compressionLevel)
+{
+ DEBUGLOG(4, "ZSTD_compressCCtx (srcSize=%u)", (unsigned)srcSize);
+ assert(cctx != NULL);
+ return ZSTD_compress_usingDict(cctx, dst, dstCapacity, src, srcSize, NULL, 0, compressionLevel);
+}
+
+size_t ZSTD_compress(void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ int compressionLevel)
+{
+ size_t result;
+ ZSTD_CCtx ctxBody;
+ ZSTD_initCCtx(&ctxBody, ZSTD_defaultCMem);
+ result = ZSTD_compressCCtx(&ctxBody, dst, dstCapacity, src, srcSize, compressionLevel);
+ ZSTD_freeCCtxContent(&ctxBody); /* can't free ctxBody itself, as it's on stack; free only heap content */
+ return result;
+}
+
+
+/* ===== Dictionary API ===== */
+
+/*! ZSTD_estimateCDictSize_advanced() :
+ * Estimate amount of memory that will be needed to create a dictionary with following arguments */
+size_t ZSTD_estimateCDictSize_advanced(
+ size_t dictSize, ZSTD_compressionParameters cParams,
+ ZSTD_dictLoadMethod_e dictLoadMethod)
+{
+ DEBUGLOG(5, "sizeof(ZSTD_CDict) : %u", (unsigned)sizeof(ZSTD_CDict));
+ return sizeof(ZSTD_CDict) + HUF_WORKSPACE_SIZE + ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0)
+ + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : dictSize);
+}
+
+size_t ZSTD_estimateCDictSize(size_t dictSize, int compressionLevel)
+{
+ ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, 0, dictSize);
+ return ZSTD_estimateCDictSize_advanced(dictSize, cParams, ZSTD_dlm_byCopy);
+}
+
+size_t ZSTD_sizeof_CDict(const ZSTD_CDict* cdict)
+{
+ if (cdict==NULL) return 0; /* support sizeof on NULL */
+ DEBUGLOG(5, "sizeof(*cdict) : %u", (unsigned)sizeof(*cdict));
+ return cdict->workspaceSize + (cdict->dictBuffer ? cdict->dictContentSize : 0) + sizeof(*cdict);
+}
+
+static size_t ZSTD_initCDict_internal(
+ ZSTD_CDict* cdict,
+ const void* dictBuffer, size_t dictSize,
+ ZSTD_dictLoadMethod_e dictLoadMethod,
+ ZSTD_dictContentType_e dictContentType,
+ ZSTD_compressionParameters cParams)
+{
+ DEBUGLOG(3, "ZSTD_initCDict_internal (dictContentType:%u)", (unsigned)dictContentType);
+ assert(!ZSTD_checkCParams(cParams));
+ cdict->matchState.cParams = cParams;
+ if ((dictLoadMethod == ZSTD_dlm_byRef) || (!dictBuffer) || (!dictSize)) {
+ cdict->dictBuffer = NULL;
+ cdict->dictContent = dictBuffer;
+ } else {
+ void* const internalBuffer = ZSTD_malloc(dictSize, cdict->customMem);
+ cdict->dictBuffer = internalBuffer;
+ cdict->dictContent = internalBuffer;
+ if (!internalBuffer) return ERROR(memory_allocation);
+ memcpy(internalBuffer, dictBuffer, dictSize);
+ }
+ cdict->dictContentSize = dictSize;
+
+ /* Reset the state to no dictionary */
+ ZSTD_reset_compressedBlockState(&cdict->cBlockState);
+ { void* const end = ZSTD_reset_matchState(
+ &cdict->matchState,
+ (U32*)cdict->workspace + HUF_WORKSPACE_SIZE_U32,
+ &cParams, ZSTDcrp_continue, /* forCCtx */ 0);
+ assert(end == (char*)cdict->workspace + cdict->workspaceSize);
+ (void)end;
+ }
+ /* (Maybe) load the dictionary
+ * Skips loading the dictionary if it is <= 8 bytes.
+ */
+ { ZSTD_CCtx_params params;
+ memset(&params, 0, sizeof(params));
+ params.compressionLevel = ZSTD_CLEVEL_DEFAULT;
+ params.fParams.contentSizeFlag = 1;
+ params.cParams = cParams;
+ { size_t const dictID = ZSTD_compress_insertDictionary(
+ &cdict->cBlockState, &cdict->matchState, &params,
+ cdict->dictContent, cdict->dictContentSize,
+ dictContentType, ZSTD_dtlm_full, cdict->workspace);
+ if (ZSTD_isError(dictID)) return dictID;
+ assert(dictID <= (size_t)(U32)-1);
+ cdict->dictID = (U32)dictID;
+ }
+ }
+
+ return 0;
+}
+
+ZSTD_CDict* ZSTD_createCDict_advanced(const void* dictBuffer, size_t dictSize,
+ ZSTD_dictLoadMethod_e dictLoadMethod,
+ ZSTD_dictContentType_e dictContentType,
+ ZSTD_compressionParameters cParams, ZSTD_customMem customMem)
+{
+ DEBUGLOG(3, "ZSTD_createCDict_advanced, mode %u", (unsigned)dictContentType);
+ if (!customMem.customAlloc ^ !customMem.customFree) return NULL;
+
+ { ZSTD_CDict* const cdict = (ZSTD_CDict*)ZSTD_malloc(sizeof(ZSTD_CDict), customMem);
+ size_t const workspaceSize = HUF_WORKSPACE_SIZE + ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0);
+ void* const workspace = ZSTD_malloc(workspaceSize, customMem);
+
+ if (!cdict || !workspace) {
+ ZSTD_free(cdict, customMem);
+ ZSTD_free(workspace, customMem);
+ return NULL;
+ }
+ cdict->customMem = customMem;
+ cdict->workspace = workspace;
+ cdict->workspaceSize = workspaceSize;
+ if (ZSTD_isError( ZSTD_initCDict_internal(cdict,
+ dictBuffer, dictSize,
+ dictLoadMethod, dictContentType,
+ cParams) )) {
+ ZSTD_freeCDict(cdict);
+ return NULL;
+ }
+
+ return cdict;
+ }
+}
+
+ZSTD_CDict* ZSTD_createCDict(const void* dict, size_t dictSize, int compressionLevel)
+{
+ ZSTD_compressionParameters cParams = ZSTD_getCParams(compressionLevel, 0, dictSize);
+ return ZSTD_createCDict_advanced(dict, dictSize,
+ ZSTD_dlm_byCopy, ZSTD_dct_auto,
+ cParams, ZSTD_defaultCMem);
+}
+
+ZSTD_CDict* ZSTD_createCDict_byReference(const void* dict, size_t dictSize, int compressionLevel)
+{
+ ZSTD_compressionParameters cParams = ZSTD_getCParams(compressionLevel, 0, dictSize);
+ return ZSTD_createCDict_advanced(dict, dictSize,
+ ZSTD_dlm_byRef, ZSTD_dct_auto,
+ cParams, ZSTD_defaultCMem);
+}
+
+size_t ZSTD_freeCDict(ZSTD_CDict* cdict)
+{
+ if (cdict==NULL) return 0; /* support free on NULL */
+ { ZSTD_customMem const cMem = cdict->customMem;
+ ZSTD_free(cdict->workspace, cMem);
+ ZSTD_free(cdict->dictBuffer, cMem);
+ ZSTD_free(cdict, cMem);
+ return 0;
+ }
+}
+
+/*! ZSTD_initStaticCDict_advanced() :
+ * Generate a digested dictionary in provided memory area.
+ * workspace: The memory area to emplace the dictionary into.
+ * Provided pointer must 8-bytes aligned.
+ * It must outlive dictionary usage.
+ * workspaceSize: Use ZSTD_estimateCDictSize()
+ * to determine how large workspace must be.
+ * cParams : use ZSTD_getCParams() to transform a compression level
+ * into its relevants cParams.
+ * @return : pointer to ZSTD_CDict*, or NULL if error (size too small)
+ * Note : there is no corresponding "free" function.
+ * Since workspace was allocated externally, it must be freed externally.
+ */
+const ZSTD_CDict* ZSTD_initStaticCDict(
+ void* workspace, size_t workspaceSize,
+ const void* dict, size_t dictSize,
+ ZSTD_dictLoadMethod_e dictLoadMethod,
+ ZSTD_dictContentType_e dictContentType,
+ ZSTD_compressionParameters cParams)
+{
+ size_t const matchStateSize = ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0);
+ size_t const neededSize = sizeof(ZSTD_CDict) + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : dictSize)
+ + HUF_WORKSPACE_SIZE + matchStateSize;
+ ZSTD_CDict* const cdict = (ZSTD_CDict*) workspace;
+ void* ptr;
+ if ((size_t)workspace & 7) return NULL; /* 8-aligned */
+ DEBUGLOG(4, "(workspaceSize < neededSize) : (%u < %u) => %u",
+ (unsigned)workspaceSize, (unsigned)neededSize, (unsigned)(workspaceSize < neededSize));
+ if (workspaceSize < neededSize) return NULL;
+
+ if (dictLoadMethod == ZSTD_dlm_byCopy) {
+ memcpy(cdict+1, dict, dictSize);
+ dict = cdict+1;
+ ptr = (char*)workspace + sizeof(ZSTD_CDict) + dictSize;
+ } else {
+ ptr = cdict+1;
+ }
+ cdict->workspace = ptr;
+ cdict->workspaceSize = HUF_WORKSPACE_SIZE + matchStateSize;
+
+ if (ZSTD_isError( ZSTD_initCDict_internal(cdict,
+ dict, dictSize,
+ ZSTD_dlm_byRef, dictContentType,
+ cParams) ))
+ return NULL;
+
+ return cdict;
+}
+
+ZSTD_compressionParameters ZSTD_getCParamsFromCDict(const ZSTD_CDict* cdict)
+{
+ assert(cdict != NULL);
+ return cdict->matchState.cParams;
+}
+
+/* ZSTD_compressBegin_usingCDict_advanced() :
+ * cdict must be != NULL */
+size_t ZSTD_compressBegin_usingCDict_advanced(
+ ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict,
+ ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize)
+{
+ DEBUGLOG(4, "ZSTD_compressBegin_usingCDict_advanced");
+ if (cdict==NULL) return ERROR(dictionary_wrong);
+ { ZSTD_CCtx_params params = cctx->requestedParams;
+ params.cParams = ZSTD_getCParamsFromCDict(cdict);
+ /* Increase window log to fit the entire dictionary and source if the
+ * source size is known. Limit the increase to 19, which is the
+ * window log for compression level 1 with the largest source size.
+ */
+ if (pledgedSrcSize != ZSTD_CONTENTSIZE_UNKNOWN) {
+ U32 const limitedSrcSize = (U32)MIN(pledgedSrcSize, 1U << 19);
+ U32 const limitedSrcLog = limitedSrcSize > 1 ? ZSTD_highbit32(limitedSrcSize - 1) + 1 : 1;
+ params.cParams.windowLog = MAX(params.cParams.windowLog, limitedSrcLog);
+ }
+ params.fParams = fParams;
+ return ZSTD_compressBegin_internal(cctx,
+ NULL, 0, ZSTD_dct_auto, ZSTD_dtlm_fast,
+ cdict,
+ params, pledgedSrcSize,
+ ZSTDb_not_buffered);
+ }
+}
+
+/* ZSTD_compressBegin_usingCDict() :
+ * pledgedSrcSize=0 means "unknown"
+ * if pledgedSrcSize>0, it will enable contentSizeFlag */
+size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict)
+{
+ ZSTD_frameParameters const fParams = { 0 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ };
+ DEBUGLOG(4, "ZSTD_compressBegin_usingCDict : dictIDFlag == %u", !fParams.noDictIDFlag);
+ return ZSTD_compressBegin_usingCDict_advanced(cctx, cdict, fParams, ZSTD_CONTENTSIZE_UNKNOWN);
+}
+
+size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ const ZSTD_CDict* cdict, ZSTD_frameParameters fParams)
+{
+ CHECK_F (ZSTD_compressBegin_usingCDict_advanced(cctx, cdict, fParams, srcSize)); /* will check if cdict != NULL */
+ return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize);
+}
+
+/*! 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 parameters are decided at CDict creation time
+ * while frame parameters are hardcoded */
+size_t ZSTD_compress_usingCDict(ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ const ZSTD_CDict* cdict)
+{
+ ZSTD_frameParameters const fParams = { 1 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ };
+ return ZSTD_compress_usingCDict_advanced(cctx, dst, dstCapacity, src, srcSize, cdict, fParams);
+}
+
+
+
+/* ******************************************************************
+* Streaming
+********************************************************************/
+
+ZSTD_CStream* ZSTD_createCStream(void)
+{
+ DEBUGLOG(3, "ZSTD_createCStream");
+ return ZSTD_createCStream_advanced(ZSTD_defaultCMem);
+}
+
+ZSTD_CStream* ZSTD_initStaticCStream(void *workspace, size_t workspaceSize)
+{
+ return ZSTD_initStaticCCtx(workspace, workspaceSize);
+}
+
+ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem)
+{ /* CStream and CCtx are now same object */
+ return ZSTD_createCCtx_advanced(customMem);
+}
+
+size_t ZSTD_freeCStream(ZSTD_CStream* zcs)
+{
+ return ZSTD_freeCCtx(zcs); /* same object */
+}
+
+
+
+/*====== Initialization ======*/
+
+size_t ZSTD_CStreamInSize(void) { return ZSTD_BLOCKSIZE_MAX; }
+
+size_t ZSTD_CStreamOutSize(void)
+{
+ return ZSTD_compressBound(ZSTD_BLOCKSIZE_MAX) + ZSTD_blockHeaderSize + 4 /* 32-bits hash */ ;
+}
+
+static size_t ZSTD_resetCStream_internal(ZSTD_CStream* cctx,
+ const void* const dict, size_t const dictSize, ZSTD_dictContentType_e const dictContentType,
+ const ZSTD_CDict* const cdict,
+ ZSTD_CCtx_params params, unsigned long long const pledgedSrcSize)
+{
+ DEBUGLOG(4, "ZSTD_resetCStream_internal");
+ /* Finalize the compression parameters */
+ params.cParams = ZSTD_getCParamsFromCCtxParams(&params, pledgedSrcSize, dictSize);
+ /* params are supposed to be fully validated at this point */
+ assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));
+ assert(!((dict) && (cdict))); /* either dict or cdict, not both */
+
+ CHECK_F( ZSTD_compressBegin_internal(cctx,
+ dict, dictSize, dictContentType, ZSTD_dtlm_fast,
+ cdict,
+ params, pledgedSrcSize,
+ ZSTDb_buffered) );
+
+ cctx->inToCompress = 0;
+ cctx->inBuffPos = 0;
+ cctx->inBuffTarget = cctx->blockSize
+ + (cctx->blockSize == pledgedSrcSize); /* for small input: avoid automatic flush on reaching end of block, since it would require to add a 3-bytes null block to end frame */
+ cctx->outBuffContentSize = cctx->outBuffFlushedSize = 0;
+ cctx->streamStage = zcss_load;
+ cctx->frameEnded = 0;
+ return 0; /* ready to go */
+}
+
+/* ZSTD_resetCStream():
+ * pledgedSrcSize == 0 means "unknown" */
+size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize)
+{
+ ZSTD_CCtx_params params = zcs->requestedParams;
+ DEBUGLOG(4, "ZSTD_resetCStream: pledgedSrcSize = %u", (unsigned)pledgedSrcSize);
+ if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;
+ params.fParams.contentSizeFlag = 1;
+ return ZSTD_resetCStream_internal(zcs, NULL, 0, ZSTD_dct_auto, zcs->cdict, params, pledgedSrcSize);
+}
+
+/*! ZSTD_initCStream_internal() :
+ * Note : for lib/compress only. Used by zstdmt_compress.c.
+ * Assumption 1 : params are valid
+ * Assumption 2 : either dict, or cdict, is defined, not both */
+size_t ZSTD_initCStream_internal(ZSTD_CStream* zcs,
+ const void* dict, size_t dictSize, const ZSTD_CDict* cdict,
+ ZSTD_CCtx_params params, unsigned long long pledgedSrcSize)
+{
+ DEBUGLOG(4, "ZSTD_initCStream_internal");
+ params.cParams = ZSTD_getCParamsFromCCtxParams(&params, pledgedSrcSize, dictSize);
+ assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));
+ assert(!((dict) && (cdict))); /* either dict or cdict, not both */
+
+ if (dict && dictSize >= 8) {
+ DEBUGLOG(4, "loading dictionary of size %u", (unsigned)dictSize);
+ if (zcs->staticSize) { /* static CCtx : never uses malloc */
+ /* incompatible with internal cdict creation */
+ return ERROR(memory_allocation);
+ }
+ ZSTD_freeCDict(zcs->cdictLocal);
+ zcs->cdictLocal = ZSTD_createCDict_advanced(dict, dictSize,
+ ZSTD_dlm_byCopy, ZSTD_dct_auto,
+ params.cParams, zcs->customMem);
+ zcs->cdict = zcs->cdictLocal;
+ if (zcs->cdictLocal == NULL) return ERROR(memory_allocation);
+ } else {
+ if (cdict) {
+ params.cParams = ZSTD_getCParamsFromCDict(cdict); /* cParams are enforced from cdict; it includes windowLog */
+ }
+ ZSTD_freeCDict(zcs->cdictLocal);
+ zcs->cdictLocal = NULL;
+ zcs->cdict = cdict;
+ }
+
+ return ZSTD_resetCStream_internal(zcs, NULL, 0, ZSTD_dct_auto, zcs->cdict, params, pledgedSrcSize);
+}
+
+/* ZSTD_initCStream_usingCDict_advanced() :
+ * same as ZSTD_initCStream_usingCDict(), with control over frame parameters */
+size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs,
+ const ZSTD_CDict* cdict,
+ ZSTD_frameParameters fParams,
+ unsigned long long pledgedSrcSize)
+{
+ DEBUGLOG(4, "ZSTD_initCStream_usingCDict_advanced");
+ if (!cdict) return ERROR(dictionary_wrong); /* cannot handle NULL cdict (does not know what to do) */
+ { ZSTD_CCtx_params params = zcs->requestedParams;
+ params.cParams = ZSTD_getCParamsFromCDict(cdict);
+ params.fParams = fParams;
+ return ZSTD_initCStream_internal(zcs,
+ NULL, 0, cdict,
+ params, pledgedSrcSize);
+ }
+}
+
+/* note : cdict must outlive compression session */
+size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict)
+{
+ ZSTD_frameParameters const fParams = { 0 /* contentSizeFlag */, 0 /* checksum */, 0 /* hideDictID */ };
+ DEBUGLOG(4, "ZSTD_initCStream_usingCDict");
+ return ZSTD_initCStream_usingCDict_advanced(zcs, cdict, fParams, ZSTD_CONTENTSIZE_UNKNOWN); /* note : will check that cdict != NULL */
+}
+
+
+/* ZSTD_initCStream_advanced() :
+ * pledgedSrcSize must be exact.
+ * if srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN.
+ * dict is loaded with default parameters ZSTD_dm_auto and ZSTD_dlm_byCopy. */
+size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs,
+ const void* dict, size_t dictSize,
+ ZSTD_parameters params, unsigned long long pledgedSrcSize)
+{
+ DEBUGLOG(4, "ZSTD_initCStream_advanced: pledgedSrcSize=%u, flag=%u",
+ (unsigned)pledgedSrcSize, params.fParams.contentSizeFlag);
+ CHECK_F( ZSTD_checkCParams(params.cParams) );
+ if ((pledgedSrcSize==0) && (params.fParams.contentSizeFlag==0)) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; /* for compatibility with older programs relying on this behavior. Users should now specify ZSTD_CONTENTSIZE_UNKNOWN. This line will be removed in the future. */
+ zcs->requestedParams = ZSTD_assignParamsToCCtxParams(zcs->requestedParams, params);
+ return ZSTD_initCStream_internal(zcs, dict, dictSize, NULL /*cdict*/, zcs->requestedParams, pledgedSrcSize);
+}
+
+size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel)
+{
+ ZSTD_CCtxParams_init(&zcs->requestedParams, compressionLevel);
+ return ZSTD_initCStream_internal(zcs, dict, dictSize, NULL, zcs->requestedParams, ZSTD_CONTENTSIZE_UNKNOWN);
+}
+
+size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pss)
+{
+ U64 const pledgedSrcSize = (pss==0) ? ZSTD_CONTENTSIZE_UNKNOWN : pss; /* temporary : 0 interpreted as "unknown" during transition period. Users willing to specify "unknown" **must** use ZSTD_CONTENTSIZE_UNKNOWN. `0` will be interpreted as "empty" in the future */
+ ZSTD_CCtxParams_init(&zcs->requestedParams, compressionLevel);
+ return ZSTD_initCStream_internal(zcs, NULL, 0, NULL, zcs->requestedParams, pledgedSrcSize);
+}
+
+size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel)
+{
+ DEBUGLOG(4, "ZSTD_initCStream");
+ return ZSTD_initCStream_srcSize(zcs, compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN);
+}
+
+/*====== Compression ======*/
+
+static size_t ZSTD_nextInputSizeHint(const ZSTD_CCtx* cctx)
+{
+ size_t hintInSize = cctx->inBuffTarget - cctx->inBuffPos;
+ if (hintInSize==0) hintInSize = cctx->blockSize;
+ return hintInSize;
+}
+
+static size_t ZSTD_limitCopy(void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize)
+{
+ size_t const length = MIN(dstCapacity, srcSize);
+ if (length) memcpy(dst, src, length);
+ return length;
+}
+
+/** ZSTD_compressStream_generic():
+ * internal function for all *compressStream*() variants
+ * non-static, because can be called from zstdmt_compress.c
+ * @return : hint size for next input */
+size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs,
+ ZSTD_outBuffer* output,
+ ZSTD_inBuffer* input,
+ ZSTD_EndDirective const flushMode)
+{
+ const char* const istart = (const char*)input->src;
+ const char* const iend = istart + input->size;
+ const char* ip = istart + input->pos;
+ char* const ostart = (char*)output->dst;
+ char* const oend = ostart + output->size;
+ char* op = ostart + output->pos;
+ U32 someMoreWork = 1;
+
+ /* check expectations */
+ DEBUGLOG(5, "ZSTD_compressStream_generic, flush=%u", (unsigned)flushMode);
+ assert(zcs->inBuff != NULL);
+ assert(zcs->inBuffSize > 0);
+ assert(zcs->outBuff != NULL);
+ assert(zcs->outBuffSize > 0);
+ assert(output->pos <= output->size);
+ assert(input->pos <= input->size);
+
+ while (someMoreWork) {
+ switch(zcs->streamStage)
+ {
+ case zcss_init:
+ /* call ZSTD_initCStream() first ! */
+ return ERROR(init_missing);
+
+ case zcss_load:
+ if ( (flushMode == ZSTD_e_end)
+ && ((size_t)(oend-op) >= ZSTD_compressBound(iend-ip)) /* enough dstCapacity */
+ && (zcs->inBuffPos == 0) ) {
+ /* shortcut to compression pass directly into output buffer */
+ size_t const cSize = ZSTD_compressEnd(zcs,
+ op, oend-op, ip, iend-ip);
+ DEBUGLOG(4, "ZSTD_compressEnd : cSize=%u", (unsigned)cSize);
+ if (ZSTD_isError(cSize)) return cSize;
+ ip = iend;
+ op += cSize;
+ zcs->frameEnded = 1;
+ ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
+ someMoreWork = 0; break;
+ }
+ /* complete loading into inBuffer */
+ { size_t const toLoad = zcs->inBuffTarget - zcs->inBuffPos;
+ size_t const loaded = ZSTD_limitCopy(
+ zcs->inBuff + zcs->inBuffPos, toLoad,
+ ip, iend-ip);
+ zcs->inBuffPos += loaded;
+ ip += loaded;
+ if ( (flushMode == ZSTD_e_continue)
+ && (zcs->inBuffPos < zcs->inBuffTarget) ) {
+ /* not enough input to fill full block : stop here */
+ someMoreWork = 0; break;
+ }
+ if ( (flushMode == ZSTD_e_flush)
+ && (zcs->inBuffPos == zcs->inToCompress) ) {
+ /* empty */
+ someMoreWork = 0; break;
+ }
+ }
+ /* compress current block (note : this stage cannot be stopped in the middle) */
+ DEBUGLOG(5, "stream compression stage (flushMode==%u)", flushMode);
+ { void* cDst;
+ size_t cSize;
+ size_t const iSize = zcs->inBuffPos - zcs->inToCompress;
+ size_t oSize = oend-op;
+ unsigned const lastBlock = (flushMode == ZSTD_e_end) && (ip==iend);
+ if (oSize >= ZSTD_compressBound(iSize))
+ cDst = op; /* compress into output buffer, to skip flush stage */
+ else
+ cDst = zcs->outBuff, oSize = zcs->outBuffSize;
+ cSize = lastBlock ?
+ ZSTD_compressEnd(zcs, cDst, oSize,
+ zcs->inBuff + zcs->inToCompress, iSize) :
+ ZSTD_compressContinue(zcs, cDst, oSize,
+ zcs->inBuff + zcs->inToCompress, iSize);
+ if (ZSTD_isError(cSize)) return cSize;
+ zcs->frameEnded = lastBlock;
+ /* prepare next block */
+ zcs->inBuffTarget = zcs->inBuffPos + zcs->blockSize;
+ if (zcs->inBuffTarget > zcs->inBuffSize)
+ zcs->inBuffPos = 0, zcs->inBuffTarget = zcs->blockSize;
+ DEBUGLOG(5, "inBuffTarget:%u / inBuffSize:%u",
+ (unsigned)zcs->inBuffTarget, (unsigned)zcs->inBuffSize);
+ if (!lastBlock)
+ assert(zcs->inBuffTarget <= zcs->inBuffSize);
+ zcs->inToCompress = zcs->inBuffPos;
+ if (cDst == op) { /* no need to flush */
+ op += cSize;
+ if (zcs->frameEnded) {
+ DEBUGLOG(5, "Frame completed directly in outBuffer");
+ someMoreWork = 0;
+ ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
+ }
+ break;
+ }
+ zcs->outBuffContentSize = cSize;
+ zcs->outBuffFlushedSize = 0;
+ zcs->streamStage = zcss_flush; /* pass-through to flush stage */
+ }
+ /* fall-through */
+ case zcss_flush:
+ DEBUGLOG(5, "flush stage");
+ { size_t const toFlush = zcs->outBuffContentSize - zcs->outBuffFlushedSize;
+ size_t const flushed = ZSTD_limitCopy(op, oend-op,
+ zcs->outBuff + zcs->outBuffFlushedSize, toFlush);
+ DEBUGLOG(5, "toFlush: %u into %u ==> flushed: %u",
+ (unsigned)toFlush, (unsigned)(oend-op), (unsigned)flushed);
+ op += flushed;
+ zcs->outBuffFlushedSize += flushed;
+ if (toFlush!=flushed) {
+ /* flush not fully completed, presumably because dst is too small */
+ assert(op==oend);
+ someMoreWork = 0;
+ break;
+ }
+ zcs->outBuffContentSize = zcs->outBuffFlushedSize = 0;
+ if (zcs->frameEnded) {
+ DEBUGLOG(5, "Frame completed on flush");
+ someMoreWork = 0;
+ ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
+ break;
+ }
+ zcs->streamStage = zcss_load;
+ break;
+ }
+
+ default: /* impossible */
+ assert(0);
+ }
+ }
+
+ input->pos = ip - istart;
+ output->pos = op - ostart;
+ if (zcs->frameEnded) return 0;
+ return ZSTD_nextInputSizeHint(zcs);
+}
+
+static size_t ZSTD_nextInputSizeHint_MTorST(const ZSTD_CCtx* cctx)
+{
+#ifdef ZSTD_MULTITHREAD
+ if (cctx->appliedParams.nbWorkers >= 1) {
+ assert(cctx->mtctx != NULL);
+ return ZSTDMT_nextInputSizeHint(cctx->mtctx);
+ }
+#endif
+ return ZSTD_nextInputSizeHint(cctx);
+
+}
+
+size_t ZSTD_compressStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input)
+{
+ CHECK_F( ZSTD_compressStream2(zcs, output, input, ZSTD_e_continue) );
+ return ZSTD_nextInputSizeHint_MTorST(zcs);
+}
+
+
+size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,
+ ZSTD_outBuffer* output,
+ ZSTD_inBuffer* input,
+ ZSTD_EndDirective endOp)
+{
+ DEBUGLOG(5, "ZSTD_compressStream2, endOp=%u ", (unsigned)endOp);
+ /* check conditions */
+ if (output->pos > output->size) return ERROR(GENERIC);
+ if (input->pos > input->size) return ERROR(GENERIC);
+ assert(cctx!=NULL);
+
+ /* transparent initialization stage */
+ if (cctx->streamStage == zcss_init) {
+ ZSTD_CCtx_params params = cctx->requestedParams;
+ ZSTD_prefixDict const prefixDict = cctx->prefixDict;
+ memset(&cctx->prefixDict, 0, sizeof(cctx->prefixDict)); /* single usage */
+ assert(prefixDict.dict==NULL || cctx->cdict==NULL); /* only one can be set */
+ DEBUGLOG(4, "ZSTD_compressStream2 : transparent init stage");
+ if (endOp == ZSTD_e_end) cctx->pledgedSrcSizePlusOne = input->size + 1; /* auto-fix pledgedSrcSize */
+ params.cParams = ZSTD_getCParamsFromCCtxParams(
+ &cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, 0 /*dictSize*/);
+
+
+#ifdef ZSTD_MULTITHREAD
+ if ((cctx->pledgedSrcSizePlusOne-1) <= ZSTDMT_JOBSIZE_MIN) {
+ params.nbWorkers = 0; /* do not invoke multi-threading when src size is too small */
+ }
+ if (params.nbWorkers > 0) {
+ /* mt context creation */
+ if (cctx->mtctx == NULL) {
+ DEBUGLOG(4, "ZSTD_compressStream2: creating new mtctx for nbWorkers=%u",
+ params.nbWorkers);
+ cctx->mtctx = ZSTDMT_createCCtx_advanced(params.nbWorkers, cctx->customMem);
+ if (cctx->mtctx == NULL) return ERROR(memory_allocation);
+ }
+ /* mt compression */
+ DEBUGLOG(4, "call ZSTDMT_initCStream_internal as nbWorkers=%u", params.nbWorkers);
+ CHECK_F( ZSTDMT_initCStream_internal(
+ cctx->mtctx,
+ prefixDict.dict, prefixDict.dictSize, ZSTD_dct_rawContent,
+ cctx->cdict, params, cctx->pledgedSrcSizePlusOne-1) );
+ cctx->streamStage = zcss_load;
+ cctx->appliedParams.nbWorkers = params.nbWorkers;
+ } else
+#endif
+ { CHECK_F( ZSTD_resetCStream_internal(cctx,
+ prefixDict.dict, prefixDict.dictSize, prefixDict.dictContentType,
+ cctx->cdict,
+ params, cctx->pledgedSrcSizePlusOne-1) );
+ assert(cctx->streamStage == zcss_load);
+ assert(cctx->appliedParams.nbWorkers == 0);
+ } }
+ /* end of transparent initialization stage */
+
+ /* compression stage */
+#ifdef ZSTD_MULTITHREAD
+ if (cctx->appliedParams.nbWorkers > 0) {
+ if (cctx->cParamsChanged) {
+ ZSTDMT_updateCParams_whileCompressing(cctx->mtctx, &cctx->requestedParams);
+ cctx->cParamsChanged = 0;
+ }
+ { size_t const flushMin = ZSTDMT_compressStream_generic(cctx->mtctx, output, input, endOp);
+ if ( ZSTD_isError(flushMin)
+ || (endOp == ZSTD_e_end && flushMin == 0) ) { /* compression completed */
+ ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);
+ }
+ DEBUGLOG(5, "completed ZSTD_compressStream2 delegating to ZSTDMT_compressStream_generic");
+ return flushMin;
+ } }
+#endif
+ CHECK_F( ZSTD_compressStream_generic(cctx, output, input, endOp) );
+ DEBUGLOG(5, "completed ZSTD_compressStream2");
+ return cctx->outBuffContentSize - cctx->outBuffFlushedSize; /* remaining to flush */
+}
+
+size_t ZSTD_compressStream2_simpleArgs (
+ ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity, size_t* dstPos,
+ const void* src, size_t srcSize, size_t* srcPos,
+ ZSTD_EndDirective endOp)
+{
+ ZSTD_outBuffer output = { dst, dstCapacity, *dstPos };
+ ZSTD_inBuffer input = { src, srcSize, *srcPos };
+ /* ZSTD_compressStream2() will check validity of dstPos and srcPos */
+ size_t const cErr = ZSTD_compressStream2(cctx, &output, &input, endOp);
+ *dstPos = output.pos;
+ *srcPos = input.pos;
+ return cErr;
+}
+
+size_t ZSTD_compress2(ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize)
+{
+ ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);
+ { size_t oPos = 0;
+ size_t iPos = 0;
+ size_t const result = ZSTD_compressStream2_simpleArgs(cctx,
+ dst, dstCapacity, &oPos,
+ src, srcSize, &iPos,
+ ZSTD_e_end);
+ if (ZSTD_isError(result)) return result;
+ if (result != 0) { /* compression not completed, due to lack of output space */
+ assert(oPos == dstCapacity);
+ return ERROR(dstSize_tooSmall);
+ }
+ assert(iPos == srcSize); /* all input is expected consumed */
+ return oPos;
+ }
+}
+
+/*====== Finalize ======*/
+
+/*! ZSTD_flushStream() :
+ * @return : amount of data remaining to flush */
+size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output)
+{
+ ZSTD_inBuffer input = { NULL, 0, 0 };
+ return ZSTD_compressStream2(zcs, output, &input, ZSTD_e_flush);
+}
+
+
+size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output)
+{
+ ZSTD_inBuffer input = { NULL, 0, 0 };
+ size_t const remainingToFlush = ZSTD_compressStream2(zcs, output, &input, ZSTD_e_end);
+ CHECK_F( remainingToFlush );
+ if (zcs->appliedParams.nbWorkers > 0) return remainingToFlush; /* minimal estimation */
+ /* single thread mode : attempt to calculate remaining to flush more precisely */
+ { size_t const lastBlockSize = zcs->frameEnded ? 0 : ZSTD_BLOCKHEADERSIZE;
+ size_t const checksumSize = zcs->frameEnded ? 0 : zcs->appliedParams.fParams.checksumFlag * 4;
+ size_t const toFlush = remainingToFlush + lastBlockSize + checksumSize;
+ DEBUGLOG(4, "ZSTD_endStream : remaining to flush : %u", (unsigned)toFlush);
+ return toFlush;
+ }
+}
+
+
+/*-===== Pre-defined compression levels =====-*/
+
+#define ZSTD_MAX_CLEVEL 22
+int ZSTD_maxCLevel(void) { return ZSTD_MAX_CLEVEL; }
+int ZSTD_minCLevel(void) { return (int)-ZSTD_TARGETLENGTH_MAX; }
+
+static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEVEL+1] = {
+{ /* "default" - guarantees a monotonically increasing memory budget */
+ /* W, C, H, S, L, TL, strat */
+ { 19, 12, 13, 1, 6, 1, ZSTD_fast }, /* base for negative levels */
+ { 19, 13, 14, 1, 7, 0, ZSTD_fast }, /* level 1 */
+ { 20, 15, 16, 1, 6, 0, ZSTD_fast }, /* level 2 */
+ { 21, 16, 17, 1, 5, 1, ZSTD_dfast }, /* level 3 */
+ { 21, 18, 18, 1, 5, 1, ZSTD_dfast }, /* level 4 */
+ { 21, 18, 19, 2, 5, 2, ZSTD_greedy }, /* level 5 */
+ { 21, 19, 19, 3, 5, 4, ZSTD_greedy }, /* level 6 */
+ { 21, 19, 19, 3, 5, 8, ZSTD_lazy }, /* level 7 */
+ { 21, 19, 19, 3, 5, 16, ZSTD_lazy2 }, /* level 8 */
+ { 21, 19, 20, 4, 5, 16, ZSTD_lazy2 }, /* level 9 */
+ { 22, 20, 21, 4, 5, 16, ZSTD_lazy2 }, /* level 10 */
+ { 22, 21, 22, 4, 5, 16, ZSTD_lazy2 }, /* level 11 */
+ { 22, 21, 22, 5, 5, 16, ZSTD_lazy2 }, /* level 12 */
+ { 22, 21, 22, 5, 5, 32, ZSTD_btlazy2 }, /* level 13 */
+ { 22, 22, 23, 5, 5, 32, ZSTD_btlazy2 }, /* level 14 */
+ { 22, 23, 23, 6, 5, 32, ZSTD_btlazy2 }, /* level 15 */
+ { 22, 22, 22, 5, 5, 48, ZSTD_btopt }, /* level 16 */
+ { 23, 23, 22, 5, 4, 64, ZSTD_btopt }, /* level 17 */
+ { 23, 23, 22, 6, 3, 64, ZSTD_btultra }, /* level 18 */
+ { 23, 24, 22, 7, 3,256, ZSTD_btultra2}, /* level 19 */
+ { 25, 25, 23, 7, 3,256, ZSTD_btultra2}, /* level 20 */
+ { 26, 26, 24, 7, 3,512, ZSTD_btultra2}, /* level 21 */
+ { 27, 27, 25, 9, 3,999, ZSTD_btultra2}, /* level 22 */
+},
+{ /* for srcSize <= 256 KB */
+ /* W, C, H, S, L, T, strat */
+ { 18, 12, 13, 1, 5, 1, ZSTD_fast }, /* base for negative levels */
+ { 18, 13, 14, 1, 6, 0, ZSTD_fast }, /* level 1 */
+ { 18, 14, 14, 1, 5, 1, ZSTD_dfast }, /* level 2 */
+ { 18, 16, 16, 1, 4, 1, ZSTD_dfast }, /* level 3 */
+ { 18, 16, 17, 2, 5, 2, ZSTD_greedy }, /* level 4.*/
+ { 18, 18, 18, 3, 5, 2, ZSTD_greedy }, /* level 5.*/
+ { 18, 18, 19, 3, 5, 4, ZSTD_lazy }, /* level 6.*/
+ { 18, 18, 19, 4, 4, 4, ZSTD_lazy }, /* level 7 */
+ { 18, 18, 19, 4, 4, 8, ZSTD_lazy2 }, /* level 8 */
+ { 18, 18, 19, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */
+ { 18, 18, 19, 6, 4, 8, ZSTD_lazy2 }, /* level 10 */
+ { 18, 18, 19, 5, 4, 12, ZSTD_btlazy2 }, /* level 11.*/
+ { 18, 19, 19, 7, 4, 12, ZSTD_btlazy2 }, /* level 12.*/
+ { 18, 18, 19, 4, 4, 16, ZSTD_btopt }, /* level 13 */
+ { 18, 18, 19, 4, 3, 32, ZSTD_btopt }, /* level 14.*/
+ { 18, 18, 19, 6, 3,128, ZSTD_btopt }, /* level 15.*/
+ { 18, 19, 19, 6, 3,128, ZSTD_btultra }, /* level 16.*/
+ { 18, 19, 19, 8, 3,256, ZSTD_btultra }, /* level 17.*/
+ { 18, 19, 19, 6, 3,128, ZSTD_btultra2}, /* level 18.*/
+ { 18, 19, 19, 8, 3,256, ZSTD_btultra2}, /* level 19.*/
+ { 18, 19, 19, 10, 3,512, ZSTD_btultra2}, /* level 20.*/
+ { 18, 19, 19, 12, 3,512, ZSTD_btultra2}, /* level 21.*/
+ { 18, 19, 19, 13, 3,999, ZSTD_btultra2}, /* level 22.*/
+},
+{ /* for srcSize <= 128 KB */
+ /* W, C, H, S, L, T, strat */
+ { 17, 12, 12, 1, 5, 1, ZSTD_fast }, /* base for negative levels */
+ { 17, 12, 13, 1, 6, 0, ZSTD_fast }, /* level 1 */
+ { 17, 13, 15, 1, 5, 0, ZSTD_fast }, /* level 2 */
+ { 17, 15, 16, 2, 5, 1, ZSTD_dfast }, /* level 3 */
+ { 17, 17, 17, 2, 4, 1, ZSTD_dfast }, /* level 4 */
+ { 17, 16, 17, 3, 4, 2, ZSTD_greedy }, /* level 5 */
+ { 17, 17, 17, 3, 4, 4, ZSTD_lazy }, /* level 6 */
+ { 17, 17, 17, 3, 4, 8, ZSTD_lazy2 }, /* level 7 */
+ { 17, 17, 17, 4, 4, 8, ZSTD_lazy2 }, /* level 8 */
+ { 17, 17, 17, 5, 4, 8, ZSTD_lazy2 }, /* level 9 */
+ { 17, 17, 17, 6, 4, 8, ZSTD_lazy2 }, /* level 10 */
+ { 17, 17, 17, 5, 4, 8, ZSTD_btlazy2 }, /* level 11 */
+ { 17, 18, 17, 7, 4, 12, ZSTD_btlazy2 }, /* level 12 */
+ { 17, 18, 17, 3, 4, 12, ZSTD_btopt }, /* level 13.*/
+ { 17, 18, 17, 4, 3, 32, ZSTD_btopt }, /* level 14.*/
+ { 17, 18, 17, 6, 3,256, ZSTD_btopt }, /* level 15.*/
+ { 17, 18, 17, 6, 3,128, ZSTD_btultra }, /* level 16.*/
+ { 17, 18, 17, 8, 3,256, ZSTD_btultra }, /* level 17.*/
+ { 17, 18, 17, 10, 3,512, ZSTD_btultra }, /* level 18.*/
+ { 17, 18, 17, 5, 3,256, ZSTD_btultra2}, /* level 19.*/
+ { 17, 18, 17, 7, 3,512, ZSTD_btultra2}, /* level 20.*/
+ { 17, 18, 17, 9, 3,512, ZSTD_btultra2}, /* level 21.*/
+ { 17, 18, 17, 11, 3,999, ZSTD_btultra2}, /* level 22.*/
+},
+{ /* for srcSize <= 16 KB */
+ /* W, C, H, S, L, T, strat */
+ { 14, 12, 13, 1, 5, 1, ZSTD_fast }, /* base for negative levels */
+ { 14, 14, 15, 1, 5, 0, ZSTD_fast }, /* level 1 */
+ { 14, 14, 15, 1, 4, 0, ZSTD_fast }, /* level 2 */
+ { 14, 14, 15, 2, 4, 1, ZSTD_dfast }, /* level 3 */
+ { 14, 14, 14, 4, 4, 2, ZSTD_greedy }, /* level 4 */
+ { 14, 14, 14, 3, 4, 4, ZSTD_lazy }, /* level 5.*/
+ { 14, 14, 14, 4, 4, 8, ZSTD_lazy2 }, /* level 6 */
+ { 14, 14, 14, 6, 4, 8, ZSTD_lazy2 }, /* level 7 */
+ { 14, 14, 14, 8, 4, 8, ZSTD_lazy2 }, /* level 8.*/
+ { 14, 15, 14, 5, 4, 8, ZSTD_btlazy2 }, /* level 9.*/
+ { 14, 15, 14, 9, 4, 8, ZSTD_btlazy2 }, /* level 10.*/
+ { 14, 15, 14, 3, 4, 12, ZSTD_btopt }, /* level 11.*/
+ { 14, 15, 14, 4, 3, 24, ZSTD_btopt }, /* level 12.*/
+ { 14, 15, 14, 5, 3, 32, ZSTD_btultra }, /* level 13.*/
+ { 14, 15, 15, 6, 3, 64, ZSTD_btultra }, /* level 14.*/
+ { 14, 15, 15, 7, 3,256, ZSTD_btultra }, /* level 15.*/
+ { 14, 15, 15, 5, 3, 48, ZSTD_btultra2}, /* level 16.*/
+ { 14, 15, 15, 6, 3,128, ZSTD_btultra2}, /* level 17.*/
+ { 14, 15, 15, 7, 3,256, ZSTD_btultra2}, /* level 18.*/
+ { 14, 15, 15, 8, 3,256, ZSTD_btultra2}, /* level 19.*/
+ { 14, 15, 15, 8, 3,512, ZSTD_btultra2}, /* level 20.*/
+ { 14, 15, 15, 9, 3,512, ZSTD_btultra2}, /* level 21.*/
+ { 14, 15, 15, 10, 3,999, ZSTD_btultra2}, /* level 22.*/
+},
+};
+
+/*! ZSTD_getCParams() :
+* @return ZSTD_compressionParameters structure for a selected compression level, srcSize and dictSize.
+* Size values are optional, provide 0 if not known or unused */
+ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize)
+{
+ size_t const addedSize = srcSizeHint ? 0 : 500;
+ U64 const rSize = srcSizeHint+dictSize ? srcSizeHint+dictSize+addedSize : (U64)-1;
+ U32 const tableID = (rSize <= 256 KB) + (rSize <= 128 KB) + (rSize <= 16 KB); /* intentional underflow for srcSizeHint == 0 */
+ int row = compressionLevel;
+ DEBUGLOG(5, "ZSTD_getCParams (cLevel=%i)", compressionLevel);
+ if (compressionLevel == 0) row = ZSTD_CLEVEL_DEFAULT; /* 0 == default */
+ if (compressionLevel < 0) row = 0; /* entry 0 is baseline for fast mode */
+ if (compressionLevel > ZSTD_MAX_CLEVEL) row = ZSTD_MAX_CLEVEL;
+ { ZSTD_compressionParameters cp = ZSTD_defaultCParameters[tableID][row];
+ if (compressionLevel < 0) cp.targetLength = (unsigned)(-compressionLevel); /* acceleration factor */
+ return ZSTD_adjustCParams_internal(cp, srcSizeHint, dictSize);
+ }
+}
+
+/*! ZSTD_getParams() :
+* same as ZSTD_getCParams(), but @return a `ZSTD_parameters` object (instead of `ZSTD_compressionParameters`).
+* All fields of `ZSTD_frameParameters` are set to default (0) */
+ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize) {
+ ZSTD_parameters params;
+ ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, srcSizeHint, dictSize);
+ DEBUGLOG(5, "ZSTD_getParams (cLevel=%i)", compressionLevel);
+ memset(&params, 0, sizeof(params));
+ params.cParams = cParams;
+ params.fParams.contentSizeFlag = 1;
+ return params;
+}
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_internal.h b/Utilities/cmzstd/lib/compress/zstd_compress_internal.h
new file mode 100644
index 000000000..29bca5985
--- /dev/null
+++ b/Utilities/cmzstd/lib/compress/zstd_compress_internal.h
@@ -0,0 +1,860 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+/* This header contains definitions
+ * that shall **only** be used by modules within lib/compress.
+ */
+
+#ifndef ZSTD_COMPRESS_H
+#define ZSTD_COMPRESS_H
+
+/*-*************************************
+* Dependencies
+***************************************/
+#include "zstd_internal.h"
+#ifdef ZSTD_MULTITHREAD
+# include "zstdmt_compress.h"
+#endif
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+
+/*-*************************************
+* Constants
+***************************************/
+#define kSearchStrength 8
+#define HASH_READ_SIZE 8
+#define ZSTD_DUBT_UNSORTED_MARK 1 /* For btlazy2 strategy, index 1 now means "unsorted".
+ It could be confused for a real successor at index "1", if sorted as larger than its predecessor.
+ It's not a big deal though : candidate will just be sorted again.
+ Additionnally, candidate position 1 will be lost.
+ But candidate 1 cannot hide a large tree of candidates, so it's a minimal loss.
+ The benefit is that ZSTD_DUBT_UNSORTED_MARK cannot be misdhandled after table re-use with a different strategy
+ Constant required by ZSTD_compressBlock_btlazy2() and ZSTD_reduceTable_internal() */
+
+
+/*-*************************************
+* Context memory management
+***************************************/
+typedef enum { ZSTDcs_created=0, ZSTDcs_init, ZSTDcs_ongoing, ZSTDcs_ending } ZSTD_compressionStage_e;
+typedef enum { zcss_init=0, zcss_load, zcss_flush } ZSTD_cStreamStage;
+
+typedef struct ZSTD_prefixDict_s {
+ const void* dict;
+ size_t dictSize;
+ ZSTD_dictContentType_e dictContentType;
+} ZSTD_prefixDict;
+
+typedef struct {
+ U32 CTable[HUF_CTABLE_SIZE_U32(255)];
+ HUF_repeat repeatMode;
+} ZSTD_hufCTables_t;
+
+typedef struct {
+ FSE_CTable offcodeCTable[FSE_CTABLE_SIZE_U32(OffFSELog, MaxOff)];
+ FSE_CTable matchlengthCTable[FSE_CTABLE_SIZE_U32(MLFSELog, MaxML)];
+ FSE_CTable litlengthCTable[FSE_CTABLE_SIZE_U32(LLFSELog, MaxLL)];
+ FSE_repeat offcode_repeatMode;
+ FSE_repeat matchlength_repeatMode;
+ FSE_repeat litlength_repeatMode;
+} ZSTD_fseCTables_t;
+
+typedef struct {
+ ZSTD_hufCTables_t huf;
+ ZSTD_fseCTables_t fse;
+} ZSTD_entropyCTables_t;
+
+typedef struct {
+ U32 off;
+ U32 len;
+} ZSTD_match_t;
+
+typedef struct {
+ int price;
+ U32 off;
+ U32 mlen;
+ U32 litlen;
+ U32 rep[ZSTD_REP_NUM];
+} ZSTD_optimal_t;
+
+typedef enum { zop_dynamic=0, zop_predef } ZSTD_OptPrice_e;
+
+typedef struct {
+ /* All tables are allocated inside cctx->workspace by ZSTD_resetCCtx_internal() */
+ unsigned* litFreq; /* table of literals statistics, of size 256 */
+ unsigned* litLengthFreq; /* table of litLength statistics, of size (MaxLL+1) */
+ unsigned* matchLengthFreq; /* table of matchLength statistics, of size (MaxML+1) */
+ unsigned* offCodeFreq; /* table of offCode statistics, of size (MaxOff+1) */
+ ZSTD_match_t* matchTable; /* list of found matches, of size ZSTD_OPT_NUM+1 */
+ ZSTD_optimal_t* priceTable; /* All positions tracked by optimal parser, of size ZSTD_OPT_NUM+1 */
+
+ U32 litSum; /* nb of literals */
+ U32 litLengthSum; /* nb of litLength codes */
+ U32 matchLengthSum; /* nb of matchLength codes */
+ U32 offCodeSum; /* nb of offset codes */
+ U32 litSumBasePrice; /* to compare to log2(litfreq) */
+ U32 litLengthSumBasePrice; /* to compare to log2(llfreq) */
+ U32 matchLengthSumBasePrice;/* to compare to log2(mlfreq) */
+ U32 offCodeSumBasePrice; /* to compare to log2(offreq) */
+ ZSTD_OptPrice_e priceType; /* prices can be determined dynamically, or follow a pre-defined cost structure */
+ const ZSTD_entropyCTables_t* symbolCosts; /* pre-calculated dictionary statistics */
+} optState_t;
+
+typedef struct {
+ ZSTD_entropyCTables_t entropy;
+ U32 rep[ZSTD_REP_NUM];
+} ZSTD_compressedBlockState_t;
+
+typedef struct {
+ BYTE const* nextSrc; /* next block here to continue on current prefix */
+ BYTE const* base; /* All regular indexes relative to this position */
+ BYTE const* dictBase; /* extDict indexes relative to this position */
+ U32 dictLimit; /* below that point, need extDict */
+ U32 lowLimit; /* below that point, no more data */
+} ZSTD_window_t;
+
+typedef struct ZSTD_matchState_t ZSTD_matchState_t;
+struct ZSTD_matchState_t {
+ ZSTD_window_t window; /* State for window round buffer management */
+ U32 loadedDictEnd; /* index of end of dictionary */
+ U32 nextToUpdate; /* index from which to continue table update */
+ U32 nextToUpdate3; /* index from which to continue table update */
+ U32 hashLog3; /* dispatch table : larger == faster, more memory */
+ U32* hashTable;
+ U32* hashTable3;
+ U32* chainTable;
+ optState_t opt; /* optimal parser state */
+ const ZSTD_matchState_t * dictMatchState;
+ ZSTD_compressionParameters cParams;
+};
+
+typedef struct {
+ ZSTD_compressedBlockState_t* prevCBlock;
+ ZSTD_compressedBlockState_t* nextCBlock;
+ ZSTD_matchState_t matchState;
+} ZSTD_blockState_t;
+
+typedef struct {
+ U32 offset;
+ U32 checksum;
+} ldmEntry_t;
+
+typedef struct {
+ ZSTD_window_t window; /* State for the window round buffer management */
+ ldmEntry_t* hashTable;
+ BYTE* bucketOffsets; /* Next position in bucket to insert entry */
+ U64 hashPower; /* Used to compute the rolling hash.
+ * Depends on ldmParams.minMatchLength */
+} ldmState_t;
+
+typedef struct {
+ U32 enableLdm; /* 1 if enable long distance matching */
+ U32 hashLog; /* Log size of hashTable */
+ U32 bucketSizeLog; /* Log bucket size for collision resolution, at most 8 */
+ U32 minMatchLength; /* Minimum match length */
+ U32 hashRateLog; /* Log number of entries to skip */
+ U32 windowLog; /* Window log for the LDM */
+} ldmParams_t;
+
+typedef struct {
+ U32 offset;
+ U32 litLength;
+ U32 matchLength;
+} rawSeq;
+
+typedef struct {
+ rawSeq* seq; /* The start of the sequences */
+ size_t pos; /* The position where reading stopped. <= size. */
+ size_t size; /* The number of sequences. <= capacity. */
+ size_t capacity; /* The capacity starting from `seq` pointer */
+} rawSeqStore_t;
+
+struct ZSTD_CCtx_params_s {
+ ZSTD_format_e format;
+ ZSTD_compressionParameters cParams;
+ ZSTD_frameParameters fParams;
+
+ int compressionLevel;
+ int forceWindow; /* force back-references to respect limit of
+ * 1<<wLog, even for dictionary */
+
+ ZSTD_dictAttachPref_e attachDictPref;
+
+ /* Multithreading: used to pass parameters to mtctx */
+ int nbWorkers;
+ size_t jobSize;
+ int overlapLog;
+ int rsyncable;
+
+ /* Long distance matching parameters */
+ ldmParams_t ldmParams;
+
+ /* Internal use, for createCCtxParams() and freeCCtxParams() only */
+ ZSTD_customMem customMem;
+}; /* typedef'd to ZSTD_CCtx_params within "zstd.h" */
+
+struct ZSTD_CCtx_s {
+ ZSTD_compressionStage_e stage;
+ int cParamsChanged; /* == 1 if cParams(except wlog) or compression level are changed in requestedParams. Triggers transmission of new params to ZSTDMT (if available) then reset to 0. */
+ int bmi2; /* == 1 if the CPU supports BMI2 and 0 otherwise. CPU support is determined dynamically once per context lifetime. */
+ ZSTD_CCtx_params requestedParams;
+ ZSTD_CCtx_params appliedParams;
+ U32 dictID;
+
+ int workSpaceOversizedDuration;
+ void* workSpace;
+ size_t workSpaceSize;
+ size_t blockSize;
+ unsigned long long pledgedSrcSizePlusOne; /* this way, 0 (default) == unknown */
+ unsigned long long consumedSrcSize;
+ unsigned long long producedCSize;
+ XXH64_state_t xxhState;
+ ZSTD_customMem customMem;
+ size_t staticSize;
+
+ seqStore_t seqStore; /* sequences storage ptrs */
+ ldmState_t ldmState; /* long distance matching state */
+ rawSeq* ldmSequences; /* Storage for the ldm output sequences */
+ size_t maxNbLdmSequences;
+ rawSeqStore_t externSeqStore; /* Mutable reference to external sequences */
+ ZSTD_blockState_t blockState;
+ U32* entropyWorkspace; /* entropy workspace of HUF_WORKSPACE_SIZE bytes */
+
+ /* streaming */
+ char* inBuff;
+ size_t inBuffSize;
+ size_t inToCompress;
+ size_t inBuffPos;
+ size_t inBuffTarget;
+ char* outBuff;
+ size_t outBuffSize;
+ size_t outBuffContentSize;
+ size_t outBuffFlushedSize;
+ ZSTD_cStreamStage streamStage;
+ U32 frameEnded;
+
+ /* Dictionary */
+ ZSTD_CDict* cdictLocal;
+ const ZSTD_CDict* cdict;
+ ZSTD_prefixDict prefixDict; /* single-usage dictionary */
+
+ /* Multi-threading */
+#ifdef ZSTD_MULTITHREAD
+ ZSTDMT_CCtx* mtctx;
+#endif
+};
+
+typedef enum { ZSTD_dtlm_fast, ZSTD_dtlm_full } ZSTD_dictTableLoadMethod_e;
+
+typedef enum { ZSTD_noDict = 0, ZSTD_extDict = 1, ZSTD_dictMatchState = 2 } ZSTD_dictMode_e;
+
+
+typedef size_t (*ZSTD_blockCompressor) (
+ ZSTD_matchState_t* bs, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_dictMode_e dictMode);
+
+
+MEM_STATIC U32 ZSTD_LLcode(U32 litLength)
+{
+ static const BYTE LL_Code[64] = { 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 16, 17, 17, 18, 18, 19, 19,
+ 20, 20, 20, 20, 21, 21, 21, 21,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 23, 23, 23, 23, 23, 23, 23, 23,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24 };
+ static const U32 LL_deltaCode = 19;
+ return (litLength > 63) ? ZSTD_highbit32(litLength) + LL_deltaCode : LL_Code[litLength];
+}
+
+/* ZSTD_MLcode() :
+ * note : mlBase = matchLength - MINMATCH;
+ * because it's the format it's stored in seqStore->sequences */
+MEM_STATIC U32 ZSTD_MLcode(U32 mlBase)
+{
+ static const BYTE ML_Code[128] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37,
+ 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39,
+ 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
+ 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 };
+ static const U32 ML_deltaCode = 36;
+ return (mlBase > 127) ? ZSTD_highbit32(mlBase) + ML_deltaCode : ML_Code[mlBase];
+}
+
+/*! ZSTD_storeSeq() :
+ * Store a sequence (literal length, literals, offset code and match length code) into seqStore_t.
+ * `offsetCode` : distance to match + 3 (values 1-3 are repCodes).
+ * `mlBase` : matchLength - MINMATCH
+*/
+MEM_STATIC void ZSTD_storeSeq(seqStore_t* seqStorePtr, size_t litLength, const void* literals, U32 offsetCode, size_t mlBase)
+{
+#if defined(DEBUGLEVEL) && (DEBUGLEVEL >= 6)
+ static const BYTE* g_start = NULL;
+ if (g_start==NULL) g_start = (const BYTE*)literals; /* note : index only works for compression within a single segment */
+ { U32 const pos = (U32)((const BYTE*)literals - g_start);
+ DEBUGLOG(6, "Cpos%7u :%3u literals, match%4u bytes at offCode%7u",
+ pos, (U32)litLength, (U32)mlBase+MINMATCH, (U32)offsetCode);
+ }
+#endif
+ assert((size_t)(seqStorePtr->sequences - seqStorePtr->sequencesStart) < seqStorePtr->maxNbSeq);
+ /* copy Literals */
+ assert(seqStorePtr->maxNbLit <= 128 KB);
+ assert(seqStorePtr->lit + litLength <= seqStorePtr->litStart + seqStorePtr->maxNbLit);
+ ZSTD_wildcopy(seqStorePtr->lit, literals, litLength);
+ seqStorePtr->lit += litLength;
+
+ /* literal Length */
+ if (litLength>0xFFFF) {
+ assert(seqStorePtr->longLengthID == 0); /* there can only be a single long length */
+ seqStorePtr->longLengthID = 1;
+ seqStorePtr->longLengthPos = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart);
+ }
+ seqStorePtr->sequences[0].litLength = (U16)litLength;
+
+ /* match offset */
+ seqStorePtr->sequences[0].offset = offsetCode + 1;
+
+ /* match Length */
+ if (mlBase>0xFFFF) {
+ assert(seqStorePtr->longLengthID == 0); /* there can only be a single long length */
+ seqStorePtr->longLengthID = 2;
+ seqStorePtr->longLengthPos = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart);
+ }
+ seqStorePtr->sequences[0].matchLength = (U16)mlBase;
+
+ seqStorePtr->sequences++;
+}
+
+
+/*-*************************************
+* Match length counter
+***************************************/
+static unsigned ZSTD_NbCommonBytes (size_t val)
+{
+ if (MEM_isLittleEndian()) {
+ if (MEM_64bits()) {
+# if defined(_MSC_VER) && defined(_WIN64)
+ unsigned long r = 0;
+ _BitScanForward64( &r, (U64)val );
+ return (unsigned)(r>>3);
+# elif defined(__GNUC__) && (__GNUC__ >= 4)
+ return (__builtin_ctzll((U64)val) >> 3);
+# else
+ static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2,
+ 0, 3, 1, 3, 1, 4, 2, 7,
+ 0, 2, 3, 6, 1, 5, 3, 5,
+ 1, 3, 4, 4, 2, 5, 6, 7,
+ 7, 0, 1, 2, 3, 3, 4, 6,
+ 2, 6, 5, 5, 3, 4, 5, 6,
+ 7, 1, 2, 4, 6, 4, 4, 5,
+ 7, 2, 6, 5, 7, 6, 7, 7 };
+ return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
+# endif
+ } else { /* 32 bits */
+# if defined(_MSC_VER)
+ unsigned long r=0;
+ _BitScanForward( &r, (U32)val );
+ return (unsigned)(r>>3);
+# elif defined(__GNUC__) && (__GNUC__ >= 3)
+ return (__builtin_ctz((U32)val) >> 3);
+# else
+ static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0,
+ 3, 2, 2, 1, 3, 2, 0, 1,
+ 3, 3, 1, 2, 2, 2, 2, 0,
+ 3, 1, 2, 0, 1, 0, 1, 1 };
+ return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
+# endif
+ }
+ } else { /* Big Endian CPU */
+ if (MEM_64bits()) {
+# if defined(_MSC_VER) && defined(_WIN64)
+ unsigned long r = 0;
+ _BitScanReverse64( &r, val );
+ return (unsigned)(r>>3);
+# elif defined(__GNUC__) && (__GNUC__ >= 4)
+ return (__builtin_clzll(val) >> 3);
+# else
+ unsigned r;
+ const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */
+ if (!(val>>n32)) { r=4; } else { r=0; val>>=n32; }
+ if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
+ r += (!val);
+ return r;
+# endif
+ } else { /* 32 bits */
+# if defined(_MSC_VER)
+ unsigned long r = 0;
+ _BitScanReverse( &r, (unsigned long)val );
+ return (unsigned)(r>>3);
+# elif defined(__GNUC__) && (__GNUC__ >= 3)
+ return (__builtin_clz((U32)val) >> 3);
+# else
+ unsigned r;
+ if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
+ r += (!val);
+ return r;
+# endif
+ } }
+}
+
+
+MEM_STATIC size_t ZSTD_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* const pInLimit)
+{
+ const BYTE* const pStart = pIn;
+ const BYTE* const pInLoopLimit = pInLimit - (sizeof(size_t)-1);
+
+ if (pIn < pInLoopLimit) {
+ { size_t const diff = MEM_readST(pMatch) ^ MEM_readST(pIn);
+ if (diff) return ZSTD_NbCommonBytes(diff); }
+ pIn+=sizeof(size_t); pMatch+=sizeof(size_t);
+ while (pIn < pInLoopLimit) {
+ size_t const diff = MEM_readST(pMatch) ^ MEM_readST(pIn);
+ if (!diff) { pIn+=sizeof(size_t); pMatch+=sizeof(size_t); continue; }
+ pIn += ZSTD_NbCommonBytes(diff);
+ return (size_t)(pIn - pStart);
+ } }
+ if (MEM_64bits() && (pIn<(pInLimit-3)) && (MEM_read32(pMatch) == MEM_read32(pIn))) { pIn+=4; pMatch+=4; }
+ if ((pIn<(pInLimit-1)) && (MEM_read16(pMatch) == MEM_read16(pIn))) { pIn+=2; pMatch+=2; }
+ if ((pIn<pInLimit) && (*pMatch == *pIn)) pIn++;
+ return (size_t)(pIn - pStart);
+}
+
+/** ZSTD_count_2segments() :
+ * can count match length with `ip` & `match` in 2 different segments.
+ * convention : on reaching mEnd, match count continue starting from iStart
+ */
+MEM_STATIC size_t
+ZSTD_count_2segments(const BYTE* ip, const BYTE* match,
+ const BYTE* iEnd, const BYTE* mEnd, const BYTE* iStart)
+{
+ const BYTE* const vEnd = MIN( ip + (mEnd - match), iEnd);
+ size_t const matchLength = ZSTD_count(ip, match, vEnd);
+ if (match + matchLength != mEnd) return matchLength;
+ DEBUGLOG(7, "ZSTD_count_2segments: found a 2-parts match (current length==%zu)", matchLength);
+ DEBUGLOG(7, "distance from match beginning to end dictionary = %zi", mEnd - match);
+ DEBUGLOG(7, "distance from current pos to end buffer = %zi", iEnd - ip);
+ DEBUGLOG(7, "next byte : ip==%02X, istart==%02X", ip[matchLength], *iStart);
+ DEBUGLOG(7, "final match length = %zu", matchLength + ZSTD_count(ip+matchLength, iStart, iEnd));
+ return matchLength + ZSTD_count(ip+matchLength, iStart, iEnd);
+}
+
+
+/*-*************************************
+ * Hashes
+ ***************************************/
+static const U32 prime3bytes = 506832829U;
+static U32 ZSTD_hash3(U32 u, U32 h) { return ((u << (32-24)) * prime3bytes) >> (32-h) ; }
+MEM_STATIC size_t ZSTD_hash3Ptr(const void* ptr, U32 h) { return ZSTD_hash3(MEM_readLE32(ptr), h); } /* only in zstd_opt.h */
+
+static const U32 prime4bytes = 2654435761U;
+static U32 ZSTD_hash4(U32 u, U32 h) { return (u * prime4bytes) >> (32-h) ; }
+static size_t ZSTD_hash4Ptr(const void* ptr, U32 h) { return ZSTD_hash4(MEM_read32(ptr), h); }
+
+static const U64 prime5bytes = 889523592379ULL;
+static size_t ZSTD_hash5(U64 u, U32 h) { return (size_t)(((u << (64-40)) * prime5bytes) >> (64-h)) ; }
+static size_t ZSTD_hash5Ptr(const void* p, U32 h) { return ZSTD_hash5(MEM_readLE64(p), h); }
+
+static const U64 prime6bytes = 227718039650203ULL;
+static size_t ZSTD_hash6(U64 u, U32 h) { return (size_t)(((u << (64-48)) * prime6bytes) >> (64-h)) ; }
+static size_t ZSTD_hash6Ptr(const void* p, U32 h) { return ZSTD_hash6(MEM_readLE64(p), h); }
+
+static const U64 prime7bytes = 58295818150454627ULL;
+static size_t ZSTD_hash7(U64 u, U32 h) { return (size_t)(((u << (64-56)) * prime7bytes) >> (64-h)) ; }
+static size_t ZSTD_hash7Ptr(const void* p, U32 h) { return ZSTD_hash7(MEM_readLE64(p), h); }
+
+static const U64 prime8bytes = 0xCF1BBCDCB7A56463ULL;
+static size_t ZSTD_hash8(U64 u, U32 h) { return (size_t)(((u) * prime8bytes) >> (64-h)) ; }
+static size_t ZSTD_hash8Ptr(const void* p, U32 h) { return ZSTD_hash8(MEM_readLE64(p), h); }
+
+MEM_STATIC size_t ZSTD_hashPtr(const void* p, U32 hBits, U32 mls)
+{
+ switch(mls)
+ {
+ default:
+ case 4: return ZSTD_hash4Ptr(p, hBits);
+ case 5: return ZSTD_hash5Ptr(p, hBits);
+ case 6: return ZSTD_hash6Ptr(p, hBits);
+ case 7: return ZSTD_hash7Ptr(p, hBits);
+ case 8: return ZSTD_hash8Ptr(p, hBits);
+ }
+}
+
+/** ZSTD_ipow() :
+ * Return base^exponent.
+ */
+static U64 ZSTD_ipow(U64 base, U64 exponent)
+{
+ U64 power = 1;
+ while (exponent) {
+ if (exponent & 1) power *= base;
+ exponent >>= 1;
+ base *= base;
+ }
+ return power;
+}
+
+#define ZSTD_ROLL_HASH_CHAR_OFFSET 10
+
+/** ZSTD_rollingHash_append() :
+ * Add the buffer to the hash value.
+ */
+static U64 ZSTD_rollingHash_append(U64 hash, void const* buf, size_t size)
+{
+ BYTE const* istart = (BYTE const*)buf;
+ size_t pos;
+ for (pos = 0; pos < size; ++pos) {
+ hash *= prime8bytes;
+ hash += istart[pos] + ZSTD_ROLL_HASH_CHAR_OFFSET;
+ }
+ return hash;
+}
+
+/** ZSTD_rollingHash_compute() :
+ * Compute the rolling hash value of the buffer.
+ */
+MEM_STATIC U64 ZSTD_rollingHash_compute(void const* buf, size_t size)
+{
+ return ZSTD_rollingHash_append(0, buf, size);
+}
+
+/** ZSTD_rollingHash_primePower() :
+ * Compute the primePower to be passed to ZSTD_rollingHash_rotate() for a hash
+ * over a window of length bytes.
+ */
+MEM_STATIC U64 ZSTD_rollingHash_primePower(U32 length)
+{
+ return ZSTD_ipow(prime8bytes, length - 1);
+}
+
+/** ZSTD_rollingHash_rotate() :
+ * Rotate the rolling hash by one byte.
+ */
+MEM_STATIC U64 ZSTD_rollingHash_rotate(U64 hash, BYTE toRemove, BYTE toAdd, U64 primePower)
+{
+ hash -= (toRemove + ZSTD_ROLL_HASH_CHAR_OFFSET) * primePower;
+ hash *= prime8bytes;
+ hash += toAdd + ZSTD_ROLL_HASH_CHAR_OFFSET;
+ return hash;
+}
+
+/*-*************************************
+* Round buffer management
+***************************************/
+/* Max current allowed */
+#define ZSTD_CURRENT_MAX ((3U << 29) + (1U << ZSTD_WINDOWLOG_MAX))
+/* Maximum chunk size before overflow correction needs to be called again */
+#define ZSTD_CHUNKSIZE_MAX \
+ ( ((U32)-1) /* Maximum ending current index */ \
+ - ZSTD_CURRENT_MAX) /* Maximum beginning lowLimit */
+
+/**
+ * ZSTD_window_clear():
+ * Clears the window containing the history by simply setting it to empty.
+ */
+MEM_STATIC void ZSTD_window_clear(ZSTD_window_t* window)
+{
+ size_t const endT = (size_t)(window->nextSrc - window->base);
+ U32 const end = (U32)endT;
+
+ window->lowLimit = end;
+ window->dictLimit = end;
+}
+
+/**
+ * ZSTD_window_hasExtDict():
+ * Returns non-zero if the window has a non-empty extDict.
+ */
+MEM_STATIC U32 ZSTD_window_hasExtDict(ZSTD_window_t const window)
+{
+ return window.lowLimit < window.dictLimit;
+}
+
+/**
+ * ZSTD_matchState_dictMode():
+ * Inspects the provided matchState and figures out what dictMode should be
+ * passed to the compressor.
+ */
+MEM_STATIC ZSTD_dictMode_e ZSTD_matchState_dictMode(const ZSTD_matchState_t *ms)
+{
+ return ZSTD_window_hasExtDict(ms->window) ?
+ ZSTD_extDict :
+ ms->dictMatchState != NULL ?
+ ZSTD_dictMatchState :
+ ZSTD_noDict;
+}
+
+/**
+ * ZSTD_window_needOverflowCorrection():
+ * Returns non-zero if the indices are getting too large and need overflow
+ * protection.
+ */
+MEM_STATIC U32 ZSTD_window_needOverflowCorrection(ZSTD_window_t const window,
+ void const* srcEnd)
+{
+ U32 const current = (U32)((BYTE const*)srcEnd - window.base);
+ return current > ZSTD_CURRENT_MAX;
+}
+
+/**
+ * ZSTD_window_correctOverflow():
+ * Reduces the indices to protect from index overflow.
+ * Returns the correction made to the indices, which must be applied to every
+ * stored index.
+ *
+ * The least significant cycleLog bits of the indices must remain the same,
+ * which may be 0. Every index up to maxDist in the past must be valid.
+ * NOTE: (maxDist & cycleMask) must be zero.
+ */
+MEM_STATIC U32 ZSTD_window_correctOverflow(ZSTD_window_t* window, U32 cycleLog,
+ U32 maxDist, void const* src)
+{
+ /* preemptive overflow correction:
+ * 1. correction is large enough:
+ * lowLimit > (3<<29) ==> current > 3<<29 + 1<<windowLog
+ * 1<<windowLog <= newCurrent < 1<<chainLog + 1<<windowLog
+ *
+ * current - newCurrent
+ * > (3<<29 + 1<<windowLog) - (1<<windowLog + 1<<chainLog)
+ * > (3<<29) - (1<<chainLog)
+ * > (3<<29) - (1<<30) (NOTE: chainLog <= 30)
+ * > 1<<29
+ *
+ * 2. (ip+ZSTD_CHUNKSIZE_MAX - cctx->base) doesn't overflow:
+ * After correction, current is less than (1<<chainLog + 1<<windowLog).
+ * In 64-bit mode we are safe, because we have 64-bit ptrdiff_t.
+ * In 32-bit mode we are safe, because (chainLog <= 29), so
+ * ip+ZSTD_CHUNKSIZE_MAX - cctx->base < 1<<32.
+ * 3. (cctx->lowLimit + 1<<windowLog) < 1<<32:
+ * windowLog <= 31 ==> 3<<29 + 1<<windowLog < 7<<29 < 1<<32.
+ */
+ U32 const cycleMask = (1U << cycleLog) - 1;
+ U32 const current = (U32)((BYTE const*)src - window->base);
+ U32 const newCurrent = (current & cycleMask) + maxDist;
+ U32 const correction = current - newCurrent;
+ assert((maxDist & cycleMask) == 0);
+ assert(current > newCurrent);
+ /* Loose bound, should be around 1<<29 (see above) */
+ assert(correction > 1<<28);
+
+ window->base += correction;
+ window->dictBase += correction;
+ window->lowLimit -= correction;
+ window->dictLimit -= correction;
+
+ DEBUGLOG(4, "Correction of 0x%x bytes to lowLimit=0x%x", correction,
+ window->lowLimit);
+ return correction;
+}
+
+/**
+ * ZSTD_window_enforceMaxDist():
+ * Updates lowLimit so that:
+ * (srcEnd - base) - lowLimit == maxDist + loadedDictEnd
+ *
+ * This allows a simple check that index >= lowLimit to see if index is valid.
+ * This must be called before a block compression call, with srcEnd as the block
+ * source end.
+ *
+ * If loadedDictEndPtr is not NULL, we set it to zero once we update lowLimit.
+ * This is because dictionaries are allowed to be referenced as long as the last
+ * byte of the dictionary is in the window, but once they are out of range,
+ * they cannot be referenced. If loadedDictEndPtr is NULL, we use
+ * loadedDictEnd == 0.
+ *
+ * In normal dict mode, the dict is between lowLimit and dictLimit. In
+ * dictMatchState mode, lowLimit and dictLimit are the same, and the dictionary
+ * is below them. forceWindow and dictMatchState are therefore incompatible.
+ */
+MEM_STATIC void
+ZSTD_window_enforceMaxDist(ZSTD_window_t* window,
+ void const* srcEnd,
+ U32 maxDist,
+ U32* loadedDictEndPtr,
+ const ZSTD_matchState_t** dictMatchStatePtr)
+{
+ U32 const blockEndIdx = (U32)((BYTE const*)srcEnd - window->base);
+ U32 loadedDictEnd = (loadedDictEndPtr != NULL) ? *loadedDictEndPtr : 0;
+ DEBUGLOG(5, "ZSTD_window_enforceMaxDist: blockEndIdx=%u, maxDist=%u",
+ (unsigned)blockEndIdx, (unsigned)maxDist);
+ if (blockEndIdx > maxDist + loadedDictEnd) {
+ U32 const newLowLimit = blockEndIdx - maxDist;
+ if (window->lowLimit < newLowLimit) window->lowLimit = newLowLimit;
+ if (window->dictLimit < window->lowLimit) {
+ DEBUGLOG(5, "Update dictLimit to match lowLimit, from %u to %u",
+ (unsigned)window->dictLimit, (unsigned)window->lowLimit);
+ window->dictLimit = window->lowLimit;
+ }
+ if (loadedDictEndPtr)
+ *loadedDictEndPtr = 0;
+ if (dictMatchStatePtr)
+ *dictMatchStatePtr = NULL;
+ }
+}
+
+/**
+ * ZSTD_window_update():
+ * Updates the window by appending [src, src + srcSize) to the window.
+ * If it is not contiguous, the current prefix becomes the extDict, and we
+ * forget about the extDict. Handles overlap of the prefix and extDict.
+ * Returns non-zero if the segment is contiguous.
+ */
+MEM_STATIC U32 ZSTD_window_update(ZSTD_window_t* window,
+ void const* src, size_t srcSize)
+{
+ BYTE const* const ip = (BYTE const*)src;
+ U32 contiguous = 1;
+ DEBUGLOG(5, "ZSTD_window_update");
+ /* Check if blocks follow each other */
+ if (src != window->nextSrc) {
+ /* not contiguous */
+ size_t const distanceFromBase = (size_t)(window->nextSrc - window->base);
+ DEBUGLOG(5, "Non contiguous blocks, new segment starts at %u", window->dictLimit);
+ window->lowLimit = window->dictLimit;
+ assert(distanceFromBase == (size_t)(U32)distanceFromBase); /* should never overflow */
+ window->dictLimit = (U32)distanceFromBase;
+ window->dictBase = window->base;
+ window->base = ip - distanceFromBase;
+ // ms->nextToUpdate = window->dictLimit;
+ if (window->dictLimit - window->lowLimit < HASH_READ_SIZE) window->lowLimit = window->dictLimit; /* too small extDict */
+ contiguous = 0;
+ }
+ window->nextSrc = ip + srcSize;
+ /* if input and dictionary overlap : reduce dictionary (area presumed modified by input) */
+ if ( (ip+srcSize > window->dictBase + window->lowLimit)
+ & (ip < window->dictBase + window->dictLimit)) {
+ ptrdiff_t const highInputIdx = (ip + srcSize) - window->dictBase;
+ U32 const lowLimitMax = (highInputIdx > (ptrdiff_t)window->dictLimit) ? window->dictLimit : (U32)highInputIdx;
+ window->lowLimit = lowLimitMax;
+ DEBUGLOG(5, "Overlapping extDict and input : new lowLimit = %u", window->lowLimit);
+ }
+ return contiguous;
+}
+
+
+/* debug functions */
+#if (DEBUGLEVEL>=2)
+
+MEM_STATIC double ZSTD_fWeight(U32 rawStat)
+{
+ U32 const fp_accuracy = 8;
+ U32 const fp_multiplier = (1 << fp_accuracy);
+ U32 const newStat = rawStat + 1;
+ U32 const hb = ZSTD_highbit32(newStat);
+ U32 const BWeight = hb * fp_multiplier;
+ U32 const FWeight = (newStat << fp_accuracy) >> hb;
+ U32 const weight = BWeight + FWeight;
+ assert(hb + fp_accuracy < 31);
+ return (double)weight / fp_multiplier;
+}
+
+/* display a table content,
+ * listing each element, its frequency, and its predicted bit cost */
+MEM_STATIC void ZSTD_debugTable(const U32* table, U32 max)
+{
+ unsigned u, sum;
+ for (u=0, sum=0; u<=max; u++) sum += table[u];
+ DEBUGLOG(2, "total nb elts: %u", sum);
+ for (u=0; u<=max; u++) {
+ DEBUGLOG(2, "%2u: %5u (%.2f)",
+ u, table[u], ZSTD_fWeight(sum) - ZSTD_fWeight(table[u]) );
+ }
+}
+
+#endif
+
+
+#if defined (__cplusplus)
+}
+#endif
+
+
+/* ==============================================================
+ * Private declarations
+ * These prototypes shall only be called from within lib/compress
+ * ============================================================== */
+
+/* ZSTD_getCParamsFromCCtxParams() :
+ * cParams are built depending on compressionLevel, src size hints,
+ * LDM and manually set compression parameters.
+ */
+ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams(
+ const ZSTD_CCtx_params* CCtxParams, U64 srcSizeHint, size_t dictSize);
+
+/*! ZSTD_initCStream_internal() :
+ * Private use only. Init streaming operation.
+ * expects params to be valid.
+ * must receive dict, or cdict, or none, but not both.
+ * @return : 0, or an error code */
+size_t ZSTD_initCStream_internal(ZSTD_CStream* zcs,
+ const void* dict, size_t dictSize,
+ const ZSTD_CDict* cdict,
+ ZSTD_CCtx_params params, unsigned long long pledgedSrcSize);
+
+void ZSTD_resetSeqStore(seqStore_t* ssPtr);
+
+/*! ZSTD_compressStream_generic() :
+ * Private use only. To be called from zstdmt_compress.c in single-thread mode. */
+size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs,
+ ZSTD_outBuffer* output,
+ ZSTD_inBuffer* input,
+ ZSTD_EndDirective const flushMode);
+
+/*! ZSTD_getCParamsFromCDict() :
+ * as the name implies */
+ZSTD_compressionParameters ZSTD_getCParamsFromCDict(const ZSTD_CDict* cdict);
+
+/* ZSTD_compressBegin_advanced_internal() :
+ * Private use only. To be called from zstdmt_compress.c. */
+size_t ZSTD_compressBegin_advanced_internal(ZSTD_CCtx* cctx,
+ const void* dict, size_t dictSize,
+ ZSTD_dictContentType_e dictContentType,
+ ZSTD_dictTableLoadMethod_e dtlm,
+ const ZSTD_CDict* cdict,
+ ZSTD_CCtx_params params,
+ unsigned long long pledgedSrcSize);
+
+/* ZSTD_compress_advanced_internal() :
+ * Private use only. To be called from zstdmt_compress.c. */
+size_t ZSTD_compress_advanced_internal(ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ const void* dict,size_t dictSize,
+ ZSTD_CCtx_params params);
+
+
+/* ZSTD_writeLastEmptyBlock() :
+ * output an empty Block with end-of-frame mark to complete a frame
+ * @return : size of data written into `dst` (== ZSTD_blockHeaderSize (defined in zstd_internal.h))
+ * or an error code if `dstCapcity` is too small (<ZSTD_blockHeaderSize)
+ */
+size_t ZSTD_writeLastEmptyBlock(void* dst, size_t dstCapacity);
+
+
+/* ZSTD_referenceExternalSequences() :
+ * Must be called before starting a compression operation.
+ * seqs must parse a prefix of the source.
+ * This cannot be used when long range matching is enabled.
+ * Zstd will use these sequences, and pass the literals to a secondary block
+ * compressor.
+ * @return : An error code on failure.
+ * NOTE: seqs are not verified! Invalid sequences can cause out-of-bounds memory
+ * access and data corruption.
+ */
+size_t ZSTD_referenceExternalSequences(ZSTD_CCtx* cctx, rawSeq* seq, size_t nbSeq);
+
+
+#endif /* ZSTD_COMPRESS_H */
diff --git a/Utilities/cmzstd/lib/compress/zstd_double_fast.c b/Utilities/cmzstd/lib/compress/zstd_double_fast.c
new file mode 100644
index 000000000..47faf6d64
--- /dev/null
+++ b/Utilities/cmzstd/lib/compress/zstd_double_fast.c
@@ -0,0 +1,499 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#include "zstd_compress_internal.h"
+#include "zstd_double_fast.h"
+
+
+void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms,
+ void const* end, ZSTD_dictTableLoadMethod_e dtlm)
+{
+ const ZSTD_compressionParameters* const cParams = &ms->cParams;
+ U32* const hashLarge = ms->hashTable;
+ U32 const hBitsL = cParams->hashLog;
+ U32 const mls = cParams->minMatch;
+ U32* const hashSmall = ms->chainTable;
+ U32 const hBitsS = cParams->chainLog;
+ const BYTE* const base = ms->window.base;
+ const BYTE* ip = base + ms->nextToUpdate;
+ const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE;
+ const U32 fastHashFillStep = 3;
+
+ /* Always insert every fastHashFillStep position into the hash tables.
+ * Insert the other positions into the large hash table if their entry
+ * is empty.
+ */
+ for (; ip + fastHashFillStep - 1 <= iend; ip += fastHashFillStep) {
+ U32 const current = (U32)(ip - base);
+ U32 i;
+ for (i = 0; i < fastHashFillStep; ++i) {
+ size_t const smHash = ZSTD_hashPtr(ip + i, hBitsS, mls);
+ size_t const lgHash = ZSTD_hashPtr(ip + i, hBitsL, 8);
+ if (i == 0)
+ hashSmall[smHash] = current + i;
+ if (i == 0 || hashLarge[lgHash] == 0)
+ hashLarge[lgHash] = current + i;
+ /* Only load extra positions for ZSTD_dtlm_full */
+ if (dtlm == ZSTD_dtlm_fast)
+ break;
+ }
+ }
+}
+
+
+FORCE_INLINE_TEMPLATE
+size_t ZSTD_compressBlock_doubleFast_generic(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize,
+ U32 const mls /* template */, ZSTD_dictMode_e const dictMode)
+{
+ ZSTD_compressionParameters const* cParams = &ms->cParams;
+ U32* const hashLong = ms->hashTable;
+ const U32 hBitsL = cParams->hashLog;
+ U32* const hashSmall = ms->chainTable;
+ const U32 hBitsS = cParams->chainLog;
+ const BYTE* const base = ms->window.base;
+ const BYTE* const istart = (const BYTE*)src;
+ const BYTE* ip = istart;
+ const BYTE* anchor = istart;
+ const U32 prefixLowestIndex = ms->window.dictLimit;
+ const BYTE* const prefixLowest = base + prefixLowestIndex;
+ const BYTE* const iend = istart + srcSize;
+ const BYTE* const ilimit = iend - HASH_READ_SIZE;
+ U32 offset_1=rep[0], offset_2=rep[1];
+ U32 offsetSaved = 0;
+
+ const ZSTD_matchState_t* const dms = ms->dictMatchState;
+ const ZSTD_compressionParameters* const dictCParams =
+ dictMode == ZSTD_dictMatchState ?
+ &dms->cParams : NULL;
+ const U32* const dictHashLong = dictMode == ZSTD_dictMatchState ?
+ dms->hashTable : NULL;
+ const U32* const dictHashSmall = dictMode == ZSTD_dictMatchState ?
+ dms->chainTable : NULL;
+ const U32 dictStartIndex = dictMode == ZSTD_dictMatchState ?
+ dms->window.dictLimit : 0;
+ const BYTE* const dictBase = dictMode == ZSTD_dictMatchState ?
+ dms->window.base : NULL;
+ const BYTE* const dictStart = dictMode == ZSTD_dictMatchState ?
+ dictBase + dictStartIndex : NULL;
+ const BYTE* const dictEnd = dictMode == ZSTD_dictMatchState ?
+ dms->window.nextSrc : NULL;
+ const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ?
+ prefixLowestIndex - (U32)(dictEnd - dictBase) :
+ 0;
+ const U32 dictHBitsL = dictMode == ZSTD_dictMatchState ?
+ dictCParams->hashLog : hBitsL;
+ const U32 dictHBitsS = dictMode == ZSTD_dictMatchState ?
+ dictCParams->chainLog : hBitsS;
+ const U32 dictAndPrefixLength = (U32)(ip - prefixLowest + dictEnd - dictStart);
+
+ assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState);
+
+ /* init */
+ ip += (dictAndPrefixLength == 0);
+ if (dictMode == ZSTD_noDict) {
+ U32 const maxRep = (U32)(ip - prefixLowest);
+ if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0;
+ if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0;
+ }
+ if (dictMode == ZSTD_dictMatchState) {
+ /* dictMatchState repCode checks don't currently handle repCode == 0
+ * disabling. */
+ assert(offset_1 <= dictAndPrefixLength);
+ assert(offset_2 <= dictAndPrefixLength);
+ }
+
+ /* Main Search Loop */
+ while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */
+ size_t mLength;
+ U32 offset;
+ size_t const h2 = ZSTD_hashPtr(ip, hBitsL, 8);
+ size_t const h = ZSTD_hashPtr(ip, hBitsS, mls);
+ size_t const dictHL = ZSTD_hashPtr(ip, dictHBitsL, 8);
+ size_t const dictHS = ZSTD_hashPtr(ip, dictHBitsS, mls);
+ U32 const current = (U32)(ip-base);
+ U32 const matchIndexL = hashLong[h2];
+ U32 matchIndexS = hashSmall[h];
+ const BYTE* matchLong = base + matchIndexL;
+ const BYTE* match = base + matchIndexS;
+ const U32 repIndex = current + 1 - offset_1;
+ const BYTE* repMatch = (dictMode == ZSTD_dictMatchState
+ && repIndex < prefixLowestIndex) ?
+ dictBase + (repIndex - dictIndexDelta) :
+ base + repIndex;
+ hashLong[h2] = hashSmall[h] = current; /* update hash tables */
+
+ /* check dictMatchState repcode */
+ if (dictMode == ZSTD_dictMatchState
+ && ((U32)((prefixLowestIndex-1) - repIndex) >= 3 /* intentional underflow */)
+ && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) {
+ const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend;
+ mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4;
+ ip++;
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH);
+ goto _match_stored;
+ }
+
+ /* check noDict repcode */
+ if ( dictMode == ZSTD_noDict
+ && ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1)))) {
+ mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4;
+ ip++;
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH);
+ goto _match_stored;
+ }
+
+ if (matchIndexL > prefixLowestIndex) {
+ /* check prefix long match */
+ if (MEM_read64(matchLong) == MEM_read64(ip)) {
+ mLength = ZSTD_count(ip+8, matchLong+8, iend) + 8;
+ offset = (U32)(ip-matchLong);
+ while (((ip>anchor) & (matchLong>prefixLowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */
+ goto _match_found;
+ }
+ } else if (dictMode == ZSTD_dictMatchState) {
+ /* check dictMatchState long match */
+ U32 const dictMatchIndexL = dictHashLong[dictHL];
+ const BYTE* dictMatchL = dictBase + dictMatchIndexL;
+ assert(dictMatchL < dictEnd);
+
+ if (dictMatchL > dictStart && MEM_read64(dictMatchL) == MEM_read64(ip)) {
+ mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, prefixLowest) + 8;
+ offset = (U32)(current - dictMatchIndexL - dictIndexDelta);
+ while (((ip>anchor) & (dictMatchL>dictStart)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */
+ goto _match_found;
+ }
+ }
+
+ if (matchIndexS > prefixLowestIndex) {
+ /* check prefix short match */
+ if (MEM_read32(match) == MEM_read32(ip)) {
+ goto _search_next_long;
+ }
+ } else if (dictMode == ZSTD_dictMatchState) {
+ /* check dictMatchState short match */
+ U32 const dictMatchIndexS = dictHashSmall[dictHS];
+ match = dictBase + dictMatchIndexS;
+ matchIndexS = dictMatchIndexS + dictIndexDelta;
+
+ if (match > dictStart && MEM_read32(match) == MEM_read32(ip)) {
+ goto _search_next_long;
+ }
+ }
+
+ ip += ((ip-anchor) >> kSearchStrength) + 1;
+ continue;
+
+_search_next_long:
+
+ {
+ size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8);
+ size_t const dictHLNext = ZSTD_hashPtr(ip+1, dictHBitsL, 8);
+ U32 const matchIndexL3 = hashLong[hl3];
+ const BYTE* matchL3 = base + matchIndexL3;
+ hashLong[hl3] = current + 1;
+
+ /* check prefix long +1 match */
+ if (matchIndexL3 > prefixLowestIndex) {
+ if (MEM_read64(matchL3) == MEM_read64(ip+1)) {
+ mLength = ZSTD_count(ip+9, matchL3+8, iend) + 8;
+ ip++;
+ offset = (U32)(ip-matchL3);
+ while (((ip>anchor) & (matchL3>prefixLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */
+ goto _match_found;
+ }
+ } else if (dictMode == ZSTD_dictMatchState) {
+ /* check dict long +1 match */
+ U32 const dictMatchIndexL3 = dictHashLong[dictHLNext];
+ const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3;
+ assert(dictMatchL3 < dictEnd);
+ if (dictMatchL3 > dictStart && MEM_read64(dictMatchL3) == MEM_read64(ip+1)) {
+ mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, prefixLowest) + 8;
+ ip++;
+ offset = (U32)(current + 1 - dictMatchIndexL3 - dictIndexDelta);
+ while (((ip>anchor) & (dictMatchL3>dictStart)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */
+ goto _match_found;
+ }
+ }
+ }
+
+ /* if no long +1 match, explore the short match we found */
+ if (dictMode == ZSTD_dictMatchState && matchIndexS < prefixLowestIndex) {
+ mLength = ZSTD_count_2segments(ip+4, match+4, iend, dictEnd, prefixLowest) + 4;
+ offset = (U32)(current - matchIndexS);
+ while (((ip>anchor) & (match>dictStart)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */
+ } else {
+ mLength = ZSTD_count(ip+4, match+4, iend) + 4;
+ offset = (U32)(ip - match);
+ while (((ip>anchor) & (match>prefixLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */
+ }
+
+ /* fall-through */
+
+_match_found:
+ offset_2 = offset_1;
+ offset_1 = offset;
+
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+
+_match_stored:
+ /* match found */
+ ip += mLength;
+ anchor = ip;
+
+ if (ip <= ilimit) {
+ /* Fill Table */
+ hashLong[ZSTD_hashPtr(base+current+2, hBitsL, 8)] =
+ hashSmall[ZSTD_hashPtr(base+current+2, hBitsS, mls)] = current+2; /* here because current+2 could be > iend-8 */
+ hashLong[ZSTD_hashPtr(ip-2, hBitsL, 8)] =
+ hashSmall[ZSTD_hashPtr(ip-2, hBitsS, mls)] = (U32)(ip-2-base);
+
+ /* check immediate repcode */
+ if (dictMode == ZSTD_dictMatchState) {
+ while (ip <= ilimit) {
+ U32 const current2 = (U32)(ip-base);
+ U32 const repIndex2 = current2 - offset_2;
+ const BYTE* repMatch2 = dictMode == ZSTD_dictMatchState
+ && repIndex2 < prefixLowestIndex ?
+ dictBase - dictIndexDelta + repIndex2 :
+ base + repIndex2;
+ if ( ((U32)((prefixLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */)
+ && (MEM_read32(repMatch2) == MEM_read32(ip)) ) {
+ const BYTE* const repEnd2 = repIndex2 < prefixLowestIndex ? dictEnd : iend;
+ size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixLowest) + 4;
+ U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */
+ ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH);
+ hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2;
+ hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2;
+ ip += repLength2;
+ anchor = ip;
+ continue;
+ }
+ break;
+ }
+ }
+
+ if (dictMode == ZSTD_noDict) {
+ while ( (ip <= ilimit)
+ && ( (offset_2>0)
+ & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) {
+ /* store sequence */
+ size_t const rLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4;
+ U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; /* swap offset_2 <=> offset_1 */
+ hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = (U32)(ip-base);
+ hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = (U32)(ip-base);
+ ZSTD_storeSeq(seqStore, 0, anchor, 0, rLength-MINMATCH);
+ ip += rLength;
+ anchor = ip;
+ continue; /* faster when present ... (?) */
+ } } } }
+
+ /* save reps for next block */
+ rep[0] = offset_1 ? offset_1 : offsetSaved;
+ rep[1] = offset_2 ? offset_2 : offsetSaved;
+
+ /* Return the last literals size */
+ return iend - anchor;
+}
+
+
+size_t ZSTD_compressBlock_doubleFast(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize)
+{
+ const U32 mls = ms->cParams.minMatch;
+ switch(mls)
+ {
+ default: /* includes case 3 */
+ case 4 :
+ return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 4, ZSTD_noDict);
+ case 5 :
+ return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 5, ZSTD_noDict);
+ case 6 :
+ return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 6, ZSTD_noDict);
+ case 7 :
+ return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 7, ZSTD_noDict);
+ }
+}
+
+
+size_t ZSTD_compressBlock_doubleFast_dictMatchState(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize)
+{
+ const U32 mls = ms->cParams.minMatch;
+ switch(mls)
+ {
+ default: /* includes case 3 */
+ case 4 :
+ return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 4, ZSTD_dictMatchState);
+ case 5 :
+ return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 5, ZSTD_dictMatchState);
+ case 6 :
+ return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 6, ZSTD_dictMatchState);
+ case 7 :
+ return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 7, ZSTD_dictMatchState);
+ }
+}
+
+
+static size_t ZSTD_compressBlock_doubleFast_extDict_generic(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize,
+ U32 const mls /* template */)
+{
+ ZSTD_compressionParameters const* cParams = &ms->cParams;
+ U32* const hashLong = ms->hashTable;
+ U32 const hBitsL = cParams->hashLog;
+ U32* const hashSmall = ms->chainTable;
+ U32 const hBitsS = cParams->chainLog;
+ const BYTE* const istart = (const BYTE*)src;
+ const BYTE* ip = istart;
+ const BYTE* anchor = istart;
+ const BYTE* const iend = istart + srcSize;
+ const BYTE* const ilimit = iend - 8;
+ const U32 prefixStartIndex = ms->window.dictLimit;
+ const BYTE* const base = ms->window.base;
+ const BYTE* const prefixStart = base + prefixStartIndex;
+ const U32 dictStartIndex = ms->window.lowLimit;
+ const BYTE* const dictBase = ms->window.dictBase;
+ const BYTE* const dictStart = dictBase + dictStartIndex;
+ const BYTE* const dictEnd = dictBase + prefixStartIndex;
+ U32 offset_1=rep[0], offset_2=rep[1];
+
+ DEBUGLOG(5, "ZSTD_compressBlock_doubleFast_extDict_generic (srcSize=%zu)", srcSize);
+
+ /* Search Loop */
+ while (ip < ilimit) { /* < instead of <=, because (ip+1) */
+ const size_t hSmall = ZSTD_hashPtr(ip, hBitsS, mls);
+ const U32 matchIndex = hashSmall[hSmall];
+ const BYTE* const matchBase = matchIndex < prefixStartIndex ? dictBase : base;
+ const BYTE* match = matchBase + matchIndex;
+
+ const size_t hLong = ZSTD_hashPtr(ip, hBitsL, 8);
+ const U32 matchLongIndex = hashLong[hLong];
+ const BYTE* const matchLongBase = matchLongIndex < prefixStartIndex ? dictBase : base;
+ const BYTE* matchLong = matchLongBase + matchLongIndex;
+
+ const U32 current = (U32)(ip-base);
+ const U32 repIndex = current + 1 - offset_1; /* offset_1 expected <= current +1 */
+ const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base;
+ const BYTE* const repMatch = repBase + repIndex;
+ size_t mLength;
+ hashSmall[hSmall] = hashLong[hLong] = current; /* update hash table */
+
+ if ((((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow : ensure repIndex doesn't overlap dict + prefix */
+ & (repIndex > dictStartIndex))
+ && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) {
+ const BYTE* repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
+ mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4;
+ ip++;
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH);
+ } else {
+ if ((matchLongIndex > dictStartIndex) && (MEM_read64(matchLong) == MEM_read64(ip))) {
+ const BYTE* const matchEnd = matchLongIndex < prefixStartIndex ? dictEnd : iend;
+ const BYTE* const lowMatchPtr = matchLongIndex < prefixStartIndex ? dictStart : prefixStart;
+ U32 offset;
+ mLength = ZSTD_count_2segments(ip+8, matchLong+8, iend, matchEnd, prefixStart) + 8;
+ offset = current - matchLongIndex;
+ while (((ip>anchor) & (matchLong>lowMatchPtr)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */
+ offset_2 = offset_1;
+ offset_1 = offset;
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+
+ } else if ((matchIndex > dictStartIndex) && (MEM_read32(match) == MEM_read32(ip))) {
+ size_t const h3 = ZSTD_hashPtr(ip+1, hBitsL, 8);
+ U32 const matchIndex3 = hashLong[h3];
+ const BYTE* const match3Base = matchIndex3 < prefixStartIndex ? dictBase : base;
+ const BYTE* match3 = match3Base + matchIndex3;
+ U32 offset;
+ hashLong[h3] = current + 1;
+ if ( (matchIndex3 > dictStartIndex) && (MEM_read64(match3) == MEM_read64(ip+1)) ) {
+ const BYTE* const matchEnd = matchIndex3 < prefixStartIndex ? dictEnd : iend;
+ const BYTE* const lowMatchPtr = matchIndex3 < prefixStartIndex ? dictStart : prefixStart;
+ mLength = ZSTD_count_2segments(ip+9, match3+8, iend, matchEnd, prefixStart) + 8;
+ ip++;
+ offset = current+1 - matchIndex3;
+ while (((ip>anchor) & (match3>lowMatchPtr)) && (ip[-1] == match3[-1])) { ip--; match3--; mLength++; } /* catch up */
+ } else {
+ const BYTE* const matchEnd = matchIndex < prefixStartIndex ? dictEnd : iend;
+ const BYTE* const lowMatchPtr = matchIndex < prefixStartIndex ? dictStart : prefixStart;
+ mLength = ZSTD_count_2segments(ip+4, match+4, iend, matchEnd, prefixStart) + 4;
+ offset = current - matchIndex;
+ while (((ip>anchor) & (match>lowMatchPtr)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */
+ }
+ offset_2 = offset_1;
+ offset_1 = offset;
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+
+ } else {
+ ip += ((ip-anchor) >> kSearchStrength) + 1;
+ continue;
+ } }
+
+ /* found a match : store it */
+ ip += mLength;
+ anchor = ip;
+
+ if (ip <= ilimit) {
+ /* Fill Table */
+ hashSmall[ZSTD_hashPtr(base+current+2, hBitsS, mls)] = current+2;
+ hashLong[ZSTD_hashPtr(base+current+2, hBitsL, 8)] = current+2;
+ hashSmall[ZSTD_hashPtr(ip-2, hBitsS, mls)] = (U32)(ip-2-base);
+ hashLong[ZSTD_hashPtr(ip-2, hBitsL, 8)] = (U32)(ip-2-base);
+ /* check immediate repcode */
+ while (ip <= ilimit) {
+ U32 const current2 = (U32)(ip-base);
+ U32 const repIndex2 = current2 - offset_2;
+ const BYTE* repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2;
+ if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) /* intentional overflow : ensure repIndex2 doesn't overlap dict + prefix */
+ & (repIndex2 > dictStartIndex))
+ && (MEM_read32(repMatch2) == MEM_read32(ip)) ) {
+ const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend;
+ size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4;
+ U32 const tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */
+ ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH);
+ hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2;
+ hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2;
+ ip += repLength2;
+ anchor = ip;
+ continue;
+ }
+ break;
+ } } }
+
+ /* save reps for next block */
+ rep[0] = offset_1;
+ rep[1] = offset_2;
+
+ /* Return the last literals size */
+ return iend - anchor;
+}
+
+
+size_t ZSTD_compressBlock_doubleFast_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize)
+{
+ U32 const mls = ms->cParams.minMatch;
+ switch(mls)
+ {
+ default: /* includes case 3 */
+ case 4 :
+ return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, src, srcSize, 4);
+ case 5 :
+ return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, src, srcSize, 5);
+ case 6 :
+ return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, src, srcSize, 6);
+ case 7 :
+ return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, src, srcSize, 7);
+ }
+}
diff --git a/Utilities/cmzstd/lib/compress/zstd_double_fast.h b/Utilities/cmzstd/lib/compress/zstd_double_fast.h
new file mode 100644
index 000000000..4fa31acfc
--- /dev/null
+++ b/Utilities/cmzstd/lib/compress/zstd_double_fast.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#ifndef ZSTD_DOUBLE_FAST_H
+#define ZSTD_DOUBLE_FAST_H
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#include "mem.h" /* U32 */
+#include "zstd_compress_internal.h" /* ZSTD_CCtx, size_t */
+
+void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms,
+ void const* end, ZSTD_dictTableLoadMethod_e dtlm);
+size_t ZSTD_compressBlock_doubleFast(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_doubleFast_dictMatchState(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_doubleFast_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* ZSTD_DOUBLE_FAST_H */
diff --git a/Utilities/cmzstd/lib/compress/zstd_fast.c b/Utilities/cmzstd/lib/compress/zstd_fast.c
new file mode 100644
index 000000000..40ba0f73e
--- /dev/null
+++ b/Utilities/cmzstd/lib/compress/zstd_fast.c
@@ -0,0 +1,391 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#include "zstd_compress_internal.h"
+#include "zstd_fast.h"
+
+
+void ZSTD_fillHashTable(ZSTD_matchState_t* ms,
+ void const* end, ZSTD_dictTableLoadMethod_e dtlm)
+{
+ const ZSTD_compressionParameters* const cParams = &ms->cParams;
+ U32* const hashTable = ms->hashTable;
+ U32 const hBits = cParams->hashLog;
+ U32 const mls = cParams->minMatch;
+ const BYTE* const base = ms->window.base;
+ const BYTE* ip = base + ms->nextToUpdate;
+ const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE;
+ const U32 fastHashFillStep = 3;
+
+ /* Always insert every fastHashFillStep position into the hash table.
+ * Insert the other positions if their hash entry is empty.
+ */
+ for ( ; ip + fastHashFillStep < iend + 2; ip += fastHashFillStep) {
+ U32 const current = (U32)(ip - base);
+ size_t const hash0 = ZSTD_hashPtr(ip, hBits, mls);
+ hashTable[hash0] = current;
+ if (dtlm == ZSTD_dtlm_fast) continue;
+ /* Only load extra positions for ZSTD_dtlm_full */
+ { U32 p;
+ for (p = 1; p < fastHashFillStep; ++p) {
+ size_t const hash = ZSTD_hashPtr(ip + p, hBits, mls);
+ if (hashTable[hash] == 0) { /* not yet filled */
+ hashTable[hash] = current + p;
+ } } } }
+}
+
+FORCE_INLINE_TEMPLATE
+size_t ZSTD_compressBlock_fast_generic(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize,
+ U32 const mls, ZSTD_dictMode_e const dictMode)
+{
+ const ZSTD_compressionParameters* const cParams = &ms->cParams;
+ U32* const hashTable = ms->hashTable;
+ U32 const hlog = cParams->hashLog;
+ /* support stepSize of 0 */
+ U32 const stepSize = cParams->targetLength + !(cParams->targetLength);
+ const BYTE* const base = ms->window.base;
+ const BYTE* const istart = (const BYTE*)src;
+ const BYTE* ip = istart;
+ const BYTE* anchor = istart;
+ const U32 prefixStartIndex = ms->window.dictLimit;
+ const BYTE* const prefixStart = base + prefixStartIndex;
+ const BYTE* const iend = istart + srcSize;
+ const BYTE* const ilimit = iend - HASH_READ_SIZE;
+ U32 offset_1=rep[0], offset_2=rep[1];
+ U32 offsetSaved = 0;
+
+ const ZSTD_matchState_t* const dms = ms->dictMatchState;
+ const ZSTD_compressionParameters* const dictCParams =
+ dictMode == ZSTD_dictMatchState ?
+ &dms->cParams : NULL;
+ const U32* const dictHashTable = dictMode == ZSTD_dictMatchState ?
+ dms->hashTable : NULL;
+ const U32 dictStartIndex = dictMode == ZSTD_dictMatchState ?
+ dms->window.dictLimit : 0;
+ const BYTE* const dictBase = dictMode == ZSTD_dictMatchState ?
+ dms->window.base : NULL;
+ const BYTE* const dictStart = dictMode == ZSTD_dictMatchState ?
+ dictBase + dictStartIndex : NULL;
+ const BYTE* const dictEnd = dictMode == ZSTD_dictMatchState ?
+ dms->window.nextSrc : NULL;
+ const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ?
+ prefixStartIndex - (U32)(dictEnd - dictBase) :
+ 0;
+ const U32 dictAndPrefixLength = (U32)(ip - prefixStart + dictEnd - dictStart);
+ const U32 dictHLog = dictMode == ZSTD_dictMatchState ?
+ dictCParams->hashLog : hlog;
+
+ assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState);
+
+ /* otherwise, we would get index underflow when translating a dict index
+ * into a local index */
+ assert(dictMode != ZSTD_dictMatchState
+ || prefixStartIndex >= (U32)(dictEnd - dictBase));
+
+ /* init */
+ ip += (dictAndPrefixLength == 0);
+ if (dictMode == ZSTD_noDict) {
+ U32 const maxRep = (U32)(ip - prefixStart);
+ if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0;
+ if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0;
+ }
+ if (dictMode == ZSTD_dictMatchState) {
+ /* dictMatchState repCode checks don't currently handle repCode == 0
+ * disabling. */
+ assert(offset_1 <= dictAndPrefixLength);
+ assert(offset_2 <= dictAndPrefixLength);
+ }
+
+ /* Main Search Loop */
+ while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */
+ size_t mLength;
+ size_t const h = ZSTD_hashPtr(ip, hlog, mls);
+ U32 const current = (U32)(ip-base);
+ U32 const matchIndex = hashTable[h];
+ const BYTE* match = base + matchIndex;
+ const U32 repIndex = current + 1 - offset_1;
+ const BYTE* repMatch = (dictMode == ZSTD_dictMatchState
+ && repIndex < prefixStartIndex) ?
+ dictBase + (repIndex - dictIndexDelta) :
+ base + repIndex;
+ hashTable[h] = current; /* update hash table */
+
+ if ( (dictMode == ZSTD_dictMatchState)
+ && ((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow : ensure repIndex isn't overlapping dict + prefix */
+ && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) {
+ const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
+ mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4;
+ ip++;
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH);
+ } else if ( dictMode == ZSTD_noDict
+ && ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1)))) {
+ mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4;
+ ip++;
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH);
+ } else if ( (matchIndex <= prefixStartIndex) ) {
+ if (dictMode == ZSTD_dictMatchState) {
+ size_t const dictHash = ZSTD_hashPtr(ip, dictHLog, mls);
+ U32 const dictMatchIndex = dictHashTable[dictHash];
+ const BYTE* dictMatch = dictBase + dictMatchIndex;
+ if (dictMatchIndex <= dictStartIndex ||
+ MEM_read32(dictMatch) != MEM_read32(ip)) {
+ assert(stepSize >= 1);
+ ip += ((ip-anchor) >> kSearchStrength) + stepSize;
+ continue;
+ } else {
+ /* found a dict match */
+ U32 const offset = (U32)(current-dictMatchIndex-dictIndexDelta);
+ mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, prefixStart) + 4;
+ while (((ip>anchor) & (dictMatch>dictStart))
+ && (ip[-1] == dictMatch[-1])) {
+ ip--; dictMatch--; mLength++;
+ } /* catch up */
+ offset_2 = offset_1;
+ offset_1 = offset;
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+ }
+ } else {
+ assert(stepSize >= 1);
+ ip += ((ip-anchor) >> kSearchStrength) + stepSize;
+ continue;
+ }
+ } else if (MEM_read32(match) != MEM_read32(ip)) {
+ /* it's not a match, and we're not going to check the dictionary */
+ assert(stepSize >= 1);
+ ip += ((ip-anchor) >> kSearchStrength) + stepSize;
+ continue;
+ } else {
+ /* found a regular match */
+ U32 const offset = (U32)(ip-match);
+ mLength = ZSTD_count(ip+4, match+4, iend) + 4;
+ while (((ip>anchor) & (match>prefixStart))
+ && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */
+ offset_2 = offset_1;
+ offset_1 = offset;
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+ }
+
+ /* match found */
+ ip += mLength;
+ anchor = ip;
+
+ if (ip <= ilimit) {
+ /* Fill Table */
+ assert(base+current+2 > istart); /* check base overflow */
+ hashTable[ZSTD_hashPtr(base+current+2, hlog, mls)] = current+2; /* here because current+2 could be > iend-8 */
+ hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base);
+
+ /* check immediate repcode */
+ if (dictMode == ZSTD_dictMatchState) {
+ while (ip <= ilimit) {
+ U32 const current2 = (U32)(ip-base);
+ U32 const repIndex2 = current2 - offset_2;
+ const BYTE* repMatch2 = repIndex2 < prefixStartIndex ?
+ dictBase - dictIndexDelta + repIndex2 :
+ base + repIndex2;
+ if ( ((U32)((prefixStartIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */)
+ && (MEM_read32(repMatch2) == MEM_read32(ip)) ) {
+ const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend;
+ size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4;
+ U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */
+ ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH);
+ hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2;
+ ip += repLength2;
+ anchor = ip;
+ continue;
+ }
+ break;
+ }
+ }
+
+ if (dictMode == ZSTD_noDict) {
+ while ( (ip <= ilimit)
+ && ( (offset_2>0)
+ & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) {
+ /* store sequence */
+ size_t const rLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4;
+ U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; /* swap offset_2 <=> offset_1 */
+ hashTable[ZSTD_hashPtr(ip, hlog, mls)] = (U32)(ip-base);
+ ZSTD_storeSeq(seqStore, 0, anchor, 0, rLength-MINMATCH);
+ ip += rLength;
+ anchor = ip;
+ continue; /* faster when present ... (?) */
+ } } } }
+
+ /* save reps for next block */
+ rep[0] = offset_1 ? offset_1 : offsetSaved;
+ rep[1] = offset_2 ? offset_2 : offsetSaved;
+
+ /* Return the last literals size */
+ return iend - anchor;
+}
+
+
+size_t ZSTD_compressBlock_fast(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize)
+{
+ ZSTD_compressionParameters const* cParams = &ms->cParams;
+ U32 const mls = cParams->minMatch;
+ assert(ms->dictMatchState == NULL);
+ switch(mls)
+ {
+ default: /* includes case 3 */
+ case 4 :
+ return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 4, ZSTD_noDict);
+ case 5 :
+ return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 5, ZSTD_noDict);
+ case 6 :
+ return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 6, ZSTD_noDict);
+ case 7 :
+ return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 7, ZSTD_noDict);
+ }
+}
+
+size_t ZSTD_compressBlock_fast_dictMatchState(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize)
+{
+ ZSTD_compressionParameters const* cParams = &ms->cParams;
+ U32 const mls = cParams->minMatch;
+ assert(ms->dictMatchState != NULL);
+ switch(mls)
+ {
+ default: /* includes case 3 */
+ case 4 :
+ return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 4, ZSTD_dictMatchState);
+ case 5 :
+ return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 5, ZSTD_dictMatchState);
+ case 6 :
+ return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 6, ZSTD_dictMatchState);
+ case 7 :
+ return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 7, ZSTD_dictMatchState);
+ }
+}
+
+
+static size_t ZSTD_compressBlock_fast_extDict_generic(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize, U32 const mls)
+{
+ const ZSTD_compressionParameters* const cParams = &ms->cParams;
+ U32* const hashTable = ms->hashTable;
+ U32 const hlog = cParams->hashLog;
+ /* support stepSize of 0 */
+ U32 const stepSize = cParams->targetLength + !(cParams->targetLength);
+ const BYTE* const base = ms->window.base;
+ const BYTE* const dictBase = ms->window.dictBase;
+ const BYTE* const istart = (const BYTE*)src;
+ const BYTE* ip = istart;
+ const BYTE* anchor = istart;
+ const U32 dictStartIndex = ms->window.lowLimit;
+ const BYTE* const dictStart = dictBase + dictStartIndex;
+ const U32 prefixStartIndex = ms->window.dictLimit;
+ const BYTE* const prefixStart = base + prefixStartIndex;
+ const BYTE* const dictEnd = dictBase + prefixStartIndex;
+ const BYTE* const iend = istart + srcSize;
+ const BYTE* const ilimit = iend - 8;
+ U32 offset_1=rep[0], offset_2=rep[1];
+
+ /* Search Loop */
+ while (ip < ilimit) { /* < instead of <=, because (ip+1) */
+ const size_t h = ZSTD_hashPtr(ip, hlog, mls);
+ const U32 matchIndex = hashTable[h];
+ const BYTE* const matchBase = matchIndex < prefixStartIndex ? dictBase : base;
+ const BYTE* match = matchBase + matchIndex;
+ const U32 current = (U32)(ip-base);
+ const U32 repIndex = current + 1 - offset_1;
+ const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base;
+ const BYTE* const repMatch = repBase + repIndex;
+ size_t mLength;
+ hashTable[h] = current; /* update hash table */
+ assert(offset_1 <= current +1); /* check repIndex */
+
+ if ( (((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow */ & (repIndex > dictStartIndex))
+ && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) {
+ const BYTE* repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
+ mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4;
+ ip++;
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH);
+ } else {
+ if ( (matchIndex < dictStartIndex) ||
+ (MEM_read32(match) != MEM_read32(ip)) ) {
+ assert(stepSize >= 1);
+ ip += ((ip-anchor) >> kSearchStrength) + stepSize;
+ continue;
+ }
+ { const BYTE* matchEnd = matchIndex < prefixStartIndex ? dictEnd : iend;
+ const BYTE* lowMatchPtr = matchIndex < prefixStartIndex ? dictStart : prefixStart;
+ U32 offset;
+ mLength = ZSTD_count_2segments(ip+4, match+4, iend, matchEnd, prefixStart) + 4;
+ while (((ip>anchor) & (match>lowMatchPtr)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */
+ offset = current - matchIndex;
+ offset_2 = offset_1;
+ offset_1 = offset;
+ ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+ } }
+
+ /* found a match : store it */
+ ip += mLength;
+ anchor = ip;
+
+ if (ip <= ilimit) {
+ /* Fill Table */
+ hashTable[ZSTD_hashPtr(base+current+2, hlog, mls)] = current+2;
+ hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base);
+ /* check immediate repcode */
+ while (ip <= ilimit) {
+ U32 const current2 = (U32)(ip-base);
+ U32 const repIndex2 = current2 - offset_2;
+ const BYTE* repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2;
+ if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) & (repIndex2 > dictStartIndex)) /* intentional overflow */
+ && (MEM_read32(repMatch2) == MEM_read32(ip)) ) {
+ const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend;
+ size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4;
+ U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */
+ ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH);
+ hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2;
+ ip += repLength2;
+ anchor = ip;
+ continue;
+ }
+ break;
+ } } }
+
+ /* save reps for next block */
+ rep[0] = offset_1;
+ rep[1] = offset_2;
+
+ /* Return the last literals size */
+ return iend - anchor;
+}
+
+
+size_t ZSTD_compressBlock_fast_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize)
+{
+ ZSTD_compressionParameters const* cParams = &ms->cParams;
+ U32 const mls = cParams->minMatch;
+ switch(mls)
+ {
+ default: /* includes case 3 */
+ case 4 :
+ return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, 4);
+ case 5 :
+ return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, 5);
+ case 6 :
+ return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, 6);
+ case 7 :
+ return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, 7);
+ }
+}
diff --git a/Utilities/cmzstd/lib/compress/zstd_fast.h b/Utilities/cmzstd/lib/compress/zstd_fast.h
new file mode 100644
index 000000000..b74a88c57
--- /dev/null
+++ b/Utilities/cmzstd/lib/compress/zstd_fast.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#ifndef ZSTD_FAST_H
+#define ZSTD_FAST_H
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#include "mem.h" /* U32 */
+#include "zstd_compress_internal.h"
+
+void ZSTD_fillHashTable(ZSTD_matchState_t* ms,
+ void const* end, ZSTD_dictTableLoadMethod_e dtlm);
+size_t ZSTD_compressBlock_fast(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_fast_dictMatchState(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_fast_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* ZSTD_FAST_H */
diff --git a/Utilities/cmzstd/lib/compress/zstd_lazy.c b/Utilities/cmzstd/lib/compress/zstd_lazy.c
new file mode 100644
index 000000000..53f998a43
--- /dev/null
+++ b/Utilities/cmzstd/lib/compress/zstd_lazy.c
@@ -0,0 +1,1106 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#include "zstd_compress_internal.h"
+#include "zstd_lazy.h"
+
+
+/*-*************************************
+* Binary Tree search
+***************************************/
+
+static void
+ZSTD_updateDUBT(ZSTD_matchState_t* ms,
+ const BYTE* ip, const BYTE* iend,
+ U32 mls)
+{
+ const ZSTD_compressionParameters* const cParams = &ms->cParams;
+ U32* const hashTable = ms->hashTable;
+ U32 const hashLog = cParams->hashLog;
+
+ U32* const bt = ms->chainTable;
+ U32 const btLog = cParams->chainLog - 1;
+ U32 const btMask = (1 << btLog) - 1;
+
+ const BYTE* const base = ms->window.base;
+ U32 const target = (U32)(ip - base);
+ U32 idx = ms->nextToUpdate;
+
+ if (idx != target)
+ DEBUGLOG(7, "ZSTD_updateDUBT, from %u to %u (dictLimit:%u)",
+ idx, target, ms->window.dictLimit);
+ assert(ip + 8 <= iend); /* condition for ZSTD_hashPtr */
+ (void)iend;
+
+ assert(idx >= ms->window.dictLimit); /* condition for valid base+idx */
+ for ( ; idx < target ; idx++) {
+ size_t const h = ZSTD_hashPtr(base + idx, hashLog, mls); /* assumption : ip + 8 <= iend */
+ U32 const matchIndex = hashTable[h];
+
+ U32* const nextCandidatePtr = bt + 2*(idx&btMask);
+ U32* const sortMarkPtr = nextCandidatePtr + 1;
+
+ DEBUGLOG(8, "ZSTD_updateDUBT: insert %u", idx);
+ hashTable[h] = idx; /* Update Hash Table */
+ *nextCandidatePtr = matchIndex; /* update BT like a chain */
+ *sortMarkPtr = ZSTD_DUBT_UNSORTED_MARK;
+ }
+ ms->nextToUpdate = target;
+}
+
+
+/** ZSTD_insertDUBT1() :
+ * sort one already inserted but unsorted position
+ * assumption : current >= btlow == (current - btmask)
+ * doesn't fail */
+static void
+ZSTD_insertDUBT1(ZSTD_matchState_t* ms,
+ U32 current, const BYTE* inputEnd,
+ U32 nbCompares, U32 btLow,
+ const ZSTD_dictMode_e dictMode)
+{
+ const ZSTD_compressionParameters* const cParams = &ms->cParams;
+ U32* const bt = ms->chainTable;
+ U32 const btLog = cParams->chainLog - 1;
+ U32 const btMask = (1 << btLog) - 1;
+ size_t commonLengthSmaller=0, commonLengthLarger=0;
+ const BYTE* const base = ms->window.base;
+ const BYTE* const dictBase = ms->window.dictBase;
+ const U32 dictLimit = ms->window.dictLimit;
+ const BYTE* const ip = (current>=dictLimit) ? base + current : dictBase + current;
+ const BYTE* const iend = (current>=dictLimit) ? inputEnd : dictBase + dictLimit;
+ const BYTE* const dictEnd = dictBase + dictLimit;
+ const BYTE* const prefixStart = base + dictLimit;
+ const BYTE* match;
+ U32* smallerPtr = bt + 2*(current&btMask);
+ U32* largerPtr = smallerPtr + 1;
+ U32 matchIndex = *smallerPtr; /* this candidate is unsorted : next sorted candidate is reached through *smallerPtr, while *largerPtr contains previous unsorted candidate (which is already saved and can be overwritten) */
+ U32 dummy32; /* to be nullified at the end */
+ U32 const windowLow = ms->window.lowLimit;
+
+ DEBUGLOG(8, "ZSTD_insertDUBT1(%u) (dictLimit=%u, lowLimit=%u)",
+ current, dictLimit, windowLow);
+ assert(current >= btLow);
+ assert(ip < iend); /* condition for ZSTD_count */
+
+ while (nbCompares-- && (matchIndex > windowLow)) {
+ U32* const nextPtr = bt + 2*(matchIndex & btMask);
+ size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */
+ assert(matchIndex < current);
+ /* note : all candidates are now supposed sorted,
+ * but it's still possible to have nextPtr[1] == ZSTD_DUBT_UNSORTED_MARK
+ * when a real index has the same value as ZSTD_DUBT_UNSORTED_MARK */
+
+ if ( (dictMode != ZSTD_extDict)
+ || (matchIndex+matchLength >= dictLimit) /* both in current segment*/
+ || (current < dictLimit) /* both in extDict */) {
+ const BYTE* const mBase = ( (dictMode != ZSTD_extDict)
+ || (matchIndex+matchLength >= dictLimit)) ?
+ base : dictBase;
+ assert( (matchIndex+matchLength >= dictLimit) /* might be wrong if extDict is incorrectly set to 0 */
+ || (current < dictLimit) );
+ match = mBase + matchIndex;
+ matchLength += ZSTD_count(ip+matchLength, match+matchLength, iend);
+ } else {
+ match = dictBase + matchIndex;
+ matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iend, dictEnd, prefixStart);
+ if (matchIndex+matchLength >= dictLimit)
+ match = base + matchIndex; /* preparation for next read of match[matchLength] */
+ }
+
+ DEBUGLOG(8, "ZSTD_insertDUBT1: comparing %u with %u : found %u common bytes ",
+ current, matchIndex, (U32)matchLength);
+
+ if (ip+matchLength == iend) { /* equal : no way to know if inf or sup */
+ break; /* drop , to guarantee consistency ; miss a bit of compression, but other solutions can corrupt tree */
+ }
+
+ if (match[matchLength] < ip[matchLength]) { /* necessarily within buffer */
+ /* match is smaller than current */
+ *smallerPtr = matchIndex; /* update smaller idx */
+ commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */
+ if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop searching */
+ DEBUGLOG(8, "ZSTD_insertDUBT1: %u (>btLow=%u) is smaller : next => %u",
+ matchIndex, btLow, nextPtr[1]);
+ smallerPtr = nextPtr+1; /* new "candidate" => larger than match, which was smaller than target */
+ matchIndex = nextPtr[1]; /* new matchIndex, larger than previous and closer to current */
+ } else {
+ /* match is larger than current */
+ *largerPtr = matchIndex;
+ commonLengthLarger = matchLength;
+ if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop searching */
+ DEBUGLOG(8, "ZSTD_insertDUBT1: %u (>btLow=%u) is larger => %u",
+ matchIndex, btLow, nextPtr[0]);
+ largerPtr = nextPtr;
+ matchIndex = nextPtr[0];
+ } }
+
+ *smallerPtr = *largerPtr = 0;
+}
+
+
+static size_t
+ZSTD_DUBT_findBetterDictMatch (
+ ZSTD_matchState_t* ms,
+ const BYTE* const ip, const BYTE* const iend,
+ size_t* offsetPtr,
+ size_t bestLength,
+ U32 nbCompares,
+ U32 const mls,
+ const ZSTD_dictMode_e dictMode)
+{
+ const ZSTD_matchState_t * const dms = ms->dictMatchState;
+ const ZSTD_compressionParameters* const dmsCParams = &dms->cParams;
+ const U32 * const dictHashTable = dms->hashTable;
+ U32 const hashLog = dmsCParams->hashLog;
+ size_t const h = ZSTD_hashPtr(ip, hashLog, mls);
+ U32 dictMatchIndex = dictHashTable[h];
+
+ const BYTE* const base = ms->window.base;
+ const BYTE* const prefixStart = base + ms->window.dictLimit;
+ U32 const current = (U32)(ip-base);
+ const BYTE* const dictBase = dms->window.base;
+ const BYTE* const dictEnd = dms->window.nextSrc;
+ U32 const dictHighLimit = (U32)(dms->window.nextSrc - dms->window.base);
+ U32 const dictLowLimit = dms->window.lowLimit;
+ U32 const dictIndexDelta = ms->window.lowLimit - dictHighLimit;
+
+ U32* const dictBt = dms->chainTable;
+ U32 const btLog = dmsCParams->chainLog - 1;
+ U32 const btMask = (1 << btLog) - 1;
+ U32 const btLow = (btMask >= dictHighLimit - dictLowLimit) ? dictLowLimit : dictHighLimit - btMask;
+
+ size_t commonLengthSmaller=0, commonLengthLarger=0;
+
+ (void)dictMode;
+ assert(dictMode == ZSTD_dictMatchState);
+
+ while (nbCompares-- && (dictMatchIndex > dictLowLimit)) {
+ U32* const nextPtr = dictBt + 2*(dictMatchIndex & btMask);
+ size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */
+ const BYTE* match = dictBase + dictMatchIndex;
+ matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iend, dictEnd, prefixStart);
+ if (dictMatchIndex+matchLength >= dictHighLimit)
+ match = base + dictMatchIndex + dictIndexDelta; /* to prepare for next usage of match[matchLength] */
+
+ if (matchLength > bestLength) {
+ U32 matchIndex = dictMatchIndex + dictIndexDelta;
+ if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(current-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) ) {
+ DEBUGLOG(9, "ZSTD_DUBT_findBetterDictMatch(%u) : found better match length %u -> %u and offsetCode %u -> %u (dictMatchIndex %u, matchIndex %u)",
+ current, (U32)bestLength, (U32)matchLength, (U32)*offsetPtr, ZSTD_REP_MOVE + current - matchIndex, dictMatchIndex, matchIndex);
+ bestLength = matchLength, *offsetPtr = ZSTD_REP_MOVE + current - matchIndex;
+ }
+ if (ip+matchLength == iend) { /* reached end of input : ip[matchLength] is not valid, no way to know if it's larger or smaller than match */
+ break; /* drop, to guarantee consistency (miss a little bit of compression) */
+ }
+ }
+
+ if (match[matchLength] < ip[matchLength]) {
+ if (dictMatchIndex <= btLow) { break; } /* beyond tree size, stop the search */
+ commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */
+ dictMatchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */
+ } else {
+ /* match is larger than current */
+ if (dictMatchIndex <= btLow) { break; } /* beyond tree size, stop the search */
+ commonLengthLarger = matchLength;
+ dictMatchIndex = nextPtr[0];
+ }
+ }
+
+ if (bestLength >= MINMATCH) {
+ U32 const mIndex = current - ((U32)*offsetPtr - ZSTD_REP_MOVE); (void)mIndex;
+ DEBUGLOG(8, "ZSTD_DUBT_findBetterDictMatch(%u) : found match of length %u and offsetCode %u (pos %u)",
+ current, (U32)bestLength, (U32)*offsetPtr, mIndex);
+ }
+ return bestLength;
+
+}
+
+
+static size_t
+ZSTD_DUBT_findBestMatch(ZSTD_matchState_t* ms,
+ const BYTE* const ip, const BYTE* const iend,
+ size_t* offsetPtr,
+ U32 const mls,
+ const ZSTD_dictMode_e dictMode)
+{
+ const ZSTD_compressionParameters* const cParams = &ms->cParams;
+ U32* const hashTable = ms->hashTable;
+ U32 const hashLog = cParams->hashLog;
+ size_t const h = ZSTD_hashPtr(ip, hashLog, mls);
+ U32 matchIndex = hashTable[h];
+
+ const BYTE* const base = ms->window.base;
+ U32 const current = (U32)(ip-base);
+ U32 const windowLow = ms->window.lowLimit;
+
+ U32* const bt = ms->chainTable;
+ U32 const btLog = cParams->chainLog - 1;
+ U32 const btMask = (1 << btLog) - 1;
+ U32 const btLow = (btMask >= current) ? 0 : current - btMask;
+ U32 const unsortLimit = MAX(btLow, windowLow);
+
+ U32* nextCandidate = bt + 2*(matchIndex&btMask);
+ U32* unsortedMark = bt + 2*(matchIndex&btMask) + 1;
+ U32 nbCompares = 1U << cParams->searchLog;
+ U32 nbCandidates = nbCompares;
+ U32 previousCandidate = 0;
+
+ DEBUGLOG(7, "ZSTD_DUBT_findBestMatch (%u) ", current);
+ assert(ip <= iend-8); /* required for h calculation */
+
+ /* reach end of unsorted candidates list */
+ while ( (matchIndex > unsortLimit)
+ && (*unsortedMark == ZSTD_DUBT_UNSORTED_MARK)
+ && (nbCandidates > 1) ) {
+ DEBUGLOG(8, "ZSTD_DUBT_findBestMatch: candidate %u is unsorted",
+ matchIndex);
+ *unsortedMark = previousCandidate; /* the unsortedMark becomes a reversed chain, to move up back to original position */
+ previousCandidate = matchIndex;
+ matchIndex = *nextCandidate;
+ nextCandidate = bt + 2*(matchIndex&btMask);
+ unsortedMark = bt + 2*(matchIndex&btMask) + 1;
+ nbCandidates --;
+ }
+
+ /* nullify last candidate if it's still unsorted
+ * simplification, detrimental to compression ratio, beneficial for speed */
+ if ( (matchIndex > unsortLimit)
+ && (*unsortedMark==ZSTD_DUBT_UNSORTED_MARK) ) {
+ DEBUGLOG(7, "ZSTD_DUBT_findBestMatch: nullify last unsorted candidate %u",
+ matchIndex);
+ *nextCandidate = *unsortedMark = 0;
+ }
+
+ /* batch sort stacked candidates */
+ matchIndex = previousCandidate;
+ while (matchIndex) { /* will end on matchIndex == 0 */
+ U32* const nextCandidateIdxPtr = bt + 2*(matchIndex&btMask) + 1;
+ U32 const nextCandidateIdx = *nextCandidateIdxPtr;
+ ZSTD_insertDUBT1(ms, matchIndex, iend,
+ nbCandidates, unsortLimit, dictMode);
+ matchIndex = nextCandidateIdx;
+ nbCandidates++;
+ }
+
+ /* find longest match */
+ { size_t commonLengthSmaller = 0, commonLengthLarger = 0;
+ const BYTE* const dictBase = ms->window.dictBase;
+ const U32 dictLimit = ms->window.dictLimit;
+ const BYTE* const dictEnd = dictBase + dictLimit;
+ const BYTE* const prefixStart = base + dictLimit;
+ U32* smallerPtr = bt + 2*(current&btMask);
+ U32* largerPtr = bt + 2*(current&btMask) + 1;
+ U32 matchEndIdx = current + 8 + 1;
+ U32 dummy32; /* to be nullified at the end */
+ size_t bestLength = 0;
+
+ matchIndex = hashTable[h];
+ hashTable[h] = current; /* Update Hash Table */
+
+ while (nbCompares-- && (matchIndex > windowLow)) {
+ U32* const nextPtr = bt + 2*(matchIndex & btMask);
+ size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */
+ const BYTE* match;
+
+ if ((dictMode != ZSTD_extDict) || (matchIndex+matchLength >= dictLimit)) {
+ match = base + matchIndex;
+ matchLength += ZSTD_count(ip+matchLength, match+matchLength, iend);
+ } else {
+ match = dictBase + matchIndex;
+ matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iend, dictEnd, prefixStart);
+ if (matchIndex+matchLength >= dictLimit)
+ match = base + matchIndex; /* to prepare for next usage of match[matchLength] */
+ }
+
+ if (matchLength > bestLength) {
+ if (matchLength > matchEndIdx - matchIndex)
+ matchEndIdx = matchIndex + (U32)matchLength;
+ if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(current-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) )
+ bestLength = matchLength, *offsetPtr = ZSTD_REP_MOVE + current - matchIndex;
+ if (ip+matchLength == iend) { /* equal : no way to know if inf or sup */
+ if (dictMode == ZSTD_dictMatchState) {
+ nbCompares = 0; /* in addition to avoiding checking any
+ * further in this loop, make sure we
+ * skip checking in the dictionary. */
+ }
+ break; /* drop, to guarantee consistency (miss a little bit of compression) */
+ }
+ }
+
+ if (match[matchLength] < ip[matchLength]) {
+ /* match is smaller than current */
+ *smallerPtr = matchIndex; /* update smaller idx */
+ commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */
+ if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop the search */
+ smallerPtr = nextPtr+1; /* new "smaller" => larger of match */
+ matchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */
+ } else {
+ /* match is larger than current */
+ *largerPtr = matchIndex;
+ commonLengthLarger = matchLength;
+ if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop the search */
+ largerPtr = nextPtr;
+ matchIndex = nextPtr[0];
+ } }
+
+ *smallerPtr = *largerPtr = 0;
+
+ if (dictMode == ZSTD_dictMatchState && nbCompares) {
+ bestLength = ZSTD_DUBT_findBetterDictMatch(
+ ms, ip, iend,
+ offsetPtr, bestLength, nbCompares,
+ mls, dictMode);
+ }
+
+ assert(matchEndIdx > current+8); /* ensure nextToUpdate is increased */
+ ms->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */
+ if (bestLength >= MINMATCH) {
+ U32 const mIndex = current - ((U32)*offsetPtr - ZSTD_REP_MOVE); (void)mIndex;
+ DEBUGLOG(8, "ZSTD_DUBT_findBestMatch(%u) : found match of length %u and offsetCode %u (pos %u)",
+ current, (U32)bestLength, (U32)*offsetPtr, mIndex);
+ }
+ return bestLength;
+ }
+}
+
+
+/** ZSTD_BtFindBestMatch() : Tree updater, providing best match */
+FORCE_INLINE_TEMPLATE size_t
+ZSTD_BtFindBestMatch( ZSTD_matchState_t* ms,
+ const BYTE* const ip, const BYTE* const iLimit,
+ size_t* offsetPtr,
+ const U32 mls /* template */,
+ const ZSTD_dictMode_e dictMode)
+{
+ DEBUGLOG(7, "ZSTD_BtFindBestMatch");
+ if (ip < ms->window.base + ms->nextToUpdate) return 0; /* skipped area */
+ ZSTD_updateDUBT(ms, ip, iLimit, mls);
+ return ZSTD_DUBT_findBestMatch(ms, ip, iLimit, offsetPtr, mls, dictMode);
+}
+
+
+static size_t
+ZSTD_BtFindBestMatch_selectMLS ( ZSTD_matchState_t* ms,
+ const BYTE* ip, const BYTE* const iLimit,
+ size_t* offsetPtr)
+{
+ switch(ms->cParams.minMatch)
+ {
+ default : /* includes case 3 */
+ case 4 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 4, ZSTD_noDict);
+ case 5 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 5, ZSTD_noDict);
+ case 7 :
+ case 6 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 6, ZSTD_noDict);
+ }
+}
+
+
+static size_t ZSTD_BtFindBestMatch_dictMatchState_selectMLS (
+ ZSTD_matchState_t* ms,
+ const BYTE* ip, const BYTE* const iLimit,
+ size_t* offsetPtr)
+{
+ switch(ms->cParams.minMatch)
+ {
+ default : /* includes case 3 */
+ case 4 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 4, ZSTD_dictMatchState);
+ case 5 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 5, ZSTD_dictMatchState);
+ case 7 :
+ case 6 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 6, ZSTD_dictMatchState);
+ }
+}
+
+
+static size_t ZSTD_BtFindBestMatch_extDict_selectMLS (
+ ZSTD_matchState_t* ms,
+ const BYTE* ip, const BYTE* const iLimit,
+ size_t* offsetPtr)
+{
+ switch(ms->cParams.minMatch)
+ {
+ default : /* includes case 3 */
+ case 4 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 4, ZSTD_extDict);
+ case 5 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 5, ZSTD_extDict);
+ case 7 :
+ case 6 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 6, ZSTD_extDict);
+ }
+}
+
+
+
+/* *********************************
+* Hash Chain
+***********************************/
+#define NEXT_IN_CHAIN(d, mask) chainTable[(d) & (mask)]
+
+/* Update chains up to ip (excluded)
+ Assumption : always within prefix (i.e. not within extDict) */
+static U32 ZSTD_insertAndFindFirstIndex_internal(
+ ZSTD_matchState_t* ms,
+ const ZSTD_compressionParameters* const cParams,
+ const BYTE* ip, U32 const mls)
+{
+ U32* const hashTable = ms->hashTable;
+ const U32 hashLog = cParams->hashLog;
+ U32* const chainTable = ms->chainTable;
+ const U32 chainMask = (1 << cParams->chainLog) - 1;
+ const BYTE* const base = ms->window.base;
+ const U32 target = (U32)(ip - base);
+ U32 idx = ms->nextToUpdate;
+
+ while(idx < target) { /* catch up */
+ size_t const h = ZSTD_hashPtr(base+idx, hashLog, mls);
+ NEXT_IN_CHAIN(idx, chainMask) = hashTable[h];
+ hashTable[h] = idx;
+ idx++;
+ }
+
+ ms->nextToUpdate = target;
+ return hashTable[ZSTD_hashPtr(ip, hashLog, mls)];
+}
+
+U32 ZSTD_insertAndFindFirstIndex(ZSTD_matchState_t* ms, const BYTE* ip) {
+ const ZSTD_compressionParameters* const cParams = &ms->cParams;
+ return ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, ms->cParams.minMatch);
+}
+
+
+/* inlining is important to hardwire a hot branch (template emulation) */
+FORCE_INLINE_TEMPLATE
+size_t ZSTD_HcFindBestMatch_generic (
+ ZSTD_matchState_t* ms,
+ const BYTE* const ip, const BYTE* const iLimit,
+ size_t* offsetPtr,
+ const U32 mls, const ZSTD_dictMode_e dictMode)
+{
+ const ZSTD_compressionParameters* const cParams = &ms->cParams;
+ U32* const chainTable = ms->chainTable;
+ const U32 chainSize = (1 << cParams->chainLog);
+ const U32 chainMask = chainSize-1;
+ const BYTE* const base = ms->window.base;
+ const BYTE* const dictBase = ms->window.dictBase;
+ const U32 dictLimit = ms->window.dictLimit;
+ const BYTE* const prefixStart = base + dictLimit;
+ const BYTE* const dictEnd = dictBase + dictLimit;
+ const U32 lowLimit = ms->window.lowLimit;
+ const U32 current = (U32)(ip-base);
+ const U32 minChain = current > chainSize ? current - chainSize : 0;
+ U32 nbAttempts = 1U << cParams->searchLog;
+ size_t ml=4-1;
+
+ /* HC4 match finder */
+ U32 matchIndex = ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, mls);
+
+ for ( ; (matchIndex>lowLimit) & (nbAttempts>0) ; nbAttempts--) {
+ size_t currentMl=0;
+ if ((dictMode != ZSTD_extDict) || matchIndex >= dictLimit) {
+ const BYTE* const match = base + matchIndex;
+ assert(matchIndex >= dictLimit); /* ensures this is true if dictMode != ZSTD_extDict */
+ if (match[ml] == ip[ml]) /* potentially better */
+ currentMl = ZSTD_count(ip, match, iLimit);
+ } else {
+ const BYTE* const match = dictBase + matchIndex;
+ assert(match+4 <= dictEnd);
+ if (MEM_read32(match) == MEM_read32(ip)) /* assumption : matchIndex <= dictLimit-4 (by table construction) */
+ currentMl = ZSTD_count_2segments(ip+4, match+4, iLimit, dictEnd, prefixStart) + 4;
+ }
+
+ /* save best solution */
+ if (currentMl > ml) {
+ ml = currentMl;
+ *offsetPtr = current - matchIndex + ZSTD_REP_MOVE;
+ if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */
+ }
+
+ if (matchIndex <= minChain) break;
+ matchIndex = NEXT_IN_CHAIN(matchIndex, chainMask);
+ }
+
+ if (dictMode == ZSTD_dictMatchState) {
+ const ZSTD_matchState_t* const dms = ms->dictMatchState;
+ const U32* const dmsChainTable = dms->chainTable;
+ const U32 dmsChainSize = (1 << dms->cParams.chainLog);
+ const U32 dmsChainMask = dmsChainSize - 1;
+ const U32 dmsLowestIndex = dms->window.dictLimit;
+ const BYTE* const dmsBase = dms->window.base;
+ const BYTE* const dmsEnd = dms->window.nextSrc;
+ const U32 dmsSize = (U32)(dmsEnd - dmsBase);
+ const U32 dmsIndexDelta = dictLimit - dmsSize;
+ const U32 dmsMinChain = dmsSize > dmsChainSize ? dmsSize - dmsChainSize : 0;
+
+ matchIndex = dms->hashTable[ZSTD_hashPtr(ip, dms->cParams.hashLog, mls)];
+
+ for ( ; (matchIndex>dmsLowestIndex) & (nbAttempts>0) ; nbAttempts--) {
+ size_t currentMl=0;
+ const BYTE* const match = dmsBase + matchIndex;
+ assert(match+4 <= dmsEnd);
+ if (MEM_read32(match) == MEM_read32(ip)) /* assumption : matchIndex <= dictLimit-4 (by table construction) */
+ currentMl = ZSTD_count_2segments(ip+4, match+4, iLimit, dmsEnd, prefixStart) + 4;
+
+ /* save best solution */
+ if (currentMl > ml) {
+ ml = currentMl;
+ *offsetPtr = current - (matchIndex + dmsIndexDelta) + ZSTD_REP_MOVE;
+ if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */
+ }
+
+ if (matchIndex <= dmsMinChain) break;
+ matchIndex = dmsChainTable[matchIndex & dmsChainMask];
+ }
+ }
+
+ return ml;
+}
+
+
+FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_selectMLS (
+ ZSTD_matchState_t* ms,
+ const BYTE* ip, const BYTE* const iLimit,
+ size_t* offsetPtr)
+{
+ switch(ms->cParams.minMatch)
+ {
+ default : /* includes case 3 */
+ case 4 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 4, ZSTD_noDict);
+ case 5 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 5, ZSTD_noDict);
+ case 7 :
+ case 6 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 6, ZSTD_noDict);
+ }
+}
+
+
+static size_t ZSTD_HcFindBestMatch_dictMatchState_selectMLS (
+ ZSTD_matchState_t* ms,
+ const BYTE* ip, const BYTE* const iLimit,
+ size_t* offsetPtr)
+{
+ switch(ms->cParams.minMatch)
+ {
+ default : /* includes case 3 */
+ case 4 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 4, ZSTD_dictMatchState);
+ case 5 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 5, ZSTD_dictMatchState);
+ case 7 :
+ case 6 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 6, ZSTD_dictMatchState);
+ }
+}
+
+
+FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_extDict_selectMLS (
+ ZSTD_matchState_t* ms,
+ const BYTE* ip, const BYTE* const iLimit,
+ size_t* offsetPtr)
+{
+ switch(ms->cParams.minMatch)
+ {
+ default : /* includes case 3 */
+ case 4 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 4, ZSTD_extDict);
+ case 5 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 5, ZSTD_extDict);
+ case 7 :
+ case 6 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 6, ZSTD_extDict);
+ }
+}
+
+
+/* *******************************
+* Common parser - lazy strategy
+*********************************/
+FORCE_INLINE_TEMPLATE
+size_t ZSTD_compressBlock_lazy_generic(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore,
+ U32 rep[ZSTD_REP_NUM],
+ const void* src, size_t srcSize,
+ const U32 searchMethod, const U32 depth,
+ ZSTD_dictMode_e const dictMode)
+{
+ const BYTE* const istart = (const BYTE*)src;
+ const BYTE* ip = istart;
+ const BYTE* anchor = istart;
+ const BYTE* const iend = istart + srcSize;
+ const BYTE* const ilimit = iend - 8;
+ const BYTE* const base = ms->window.base;
+ const U32 prefixLowestIndex = ms->window.dictLimit;
+ const BYTE* const prefixLowest = base + prefixLowestIndex;
+
+ typedef size_t (*searchMax_f)(
+ ZSTD_matchState_t* ms,
+ const BYTE* ip, const BYTE* iLimit, size_t* offsetPtr);
+ searchMax_f const searchMax = dictMode == ZSTD_dictMatchState ?
+ (searchMethod ? ZSTD_BtFindBestMatch_dictMatchState_selectMLS : ZSTD_HcFindBestMatch_dictMatchState_selectMLS) :
+ (searchMethod ? ZSTD_BtFindBestMatch_selectMLS : ZSTD_HcFindBestMatch_selectMLS);
+ U32 offset_1 = rep[0], offset_2 = rep[1], savedOffset=0;
+
+ const ZSTD_matchState_t* const dms = ms->dictMatchState;
+ const U32 dictLowestIndex = dictMode == ZSTD_dictMatchState ?
+ dms->window.dictLimit : 0;
+ const BYTE* const dictBase = dictMode == ZSTD_dictMatchState ?
+ dms->window.base : NULL;
+ const BYTE* const dictLowest = dictMode == ZSTD_dictMatchState ?
+ dictBase + dictLowestIndex : NULL;
+ const BYTE* const dictEnd = dictMode == ZSTD_dictMatchState ?
+ dms->window.nextSrc : NULL;
+ const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ?
+ prefixLowestIndex - (U32)(dictEnd - dictBase) :
+ 0;
+ const U32 dictAndPrefixLength = (U32)(ip - prefixLowest + dictEnd - dictLowest);
+
+ /* init */
+ ip += (dictAndPrefixLength == 0);
+ ms->nextToUpdate3 = ms->nextToUpdate;
+ if (dictMode == ZSTD_noDict) {
+ U32 const maxRep = (U32)(ip - prefixLowest);
+ if (offset_2 > maxRep) savedOffset = offset_2, offset_2 = 0;
+ if (offset_1 > maxRep) savedOffset = offset_1, offset_1 = 0;
+ }
+ if (dictMode == ZSTD_dictMatchState) {
+ /* dictMatchState repCode checks don't currently handle repCode == 0
+ * disabling. */
+ assert(offset_1 <= dictAndPrefixLength);
+ assert(offset_2 <= dictAndPrefixLength);
+ }
+
+ /* Match Loop */
+ while (ip < ilimit) {
+ size_t matchLength=0;
+ size_t offset=0;
+ const BYTE* start=ip+1;
+
+ /* check repCode */
+ if (dictMode == ZSTD_dictMatchState) {
+ const U32 repIndex = (U32)(ip - base) + 1 - offset_1;
+ const BYTE* repMatch = (dictMode == ZSTD_dictMatchState
+ && repIndex < prefixLowestIndex) ?
+ dictBase + (repIndex - dictIndexDelta) :
+ base + repIndex;
+ if (((U32)((prefixLowestIndex-1) - repIndex) >= 3 /* intentional underflow */)
+ && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) {
+ const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend;
+ matchLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4;
+ if (depth==0) goto _storeSequence;
+ }
+ }
+ if ( dictMode == ZSTD_noDict
+ && ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1)))) {
+ matchLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4;
+ if (depth==0) goto _storeSequence;
+ }
+
+ /* first search (depth 0) */
+ { size_t offsetFound = 999999999;
+ size_t const ml2 = searchMax(ms, ip, iend, &offsetFound);
+ if (ml2 > matchLength)
+ matchLength = ml2, start = ip, offset=offsetFound;
+ }
+
+ if (matchLength < 4) {
+ ip += ((ip-anchor) >> kSearchStrength) + 1; /* jump faster over incompressible sections */
+ continue;
+ }
+
+ /* let's try to find a better solution */
+ if (depth>=1)
+ while (ip<ilimit) {
+ ip ++;
+ if ( (dictMode == ZSTD_noDict)
+ && (offset) && ((offset_1>0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) {
+ size_t const mlRep = ZSTD_count(ip+4, ip+4-offset_1, iend) + 4;
+ int const gain2 = (int)(mlRep * 3);
+ int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offset+1) + 1);
+ if ((mlRep >= 4) && (gain2 > gain1))
+ matchLength = mlRep, offset = 0, start = ip;
+ }
+ if (dictMode == ZSTD_dictMatchState) {
+ const U32 repIndex = (U32)(ip - base) - offset_1;
+ const BYTE* repMatch = repIndex < prefixLowestIndex ?
+ dictBase + (repIndex - dictIndexDelta) :
+ base + repIndex;
+ if (((U32)((prefixLowestIndex-1) - repIndex) >= 3 /* intentional underflow */)
+ && (MEM_read32(repMatch) == MEM_read32(ip)) ) {
+ const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend;
+ size_t const mlRep = ZSTD_count_2segments(ip+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4;
+ int const gain2 = (int)(mlRep * 3);
+ int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offset+1) + 1);
+ if ((mlRep >= 4) && (gain2 > gain1))
+ matchLength = mlRep, offset = 0, start = ip;
+ }
+ }
+ { size_t offset2=999999999;
+ size_t const ml2 = searchMax(ms, ip, iend, &offset2);
+ int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */
+ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 4);
+ if ((ml2 >= 4) && (gain2 > gain1)) {
+ matchLength = ml2, offset = offset2, start = ip;
+ continue; /* search a better one */
+ } }
+
+ /* let's find an even better one */
+ if ((depth==2) && (ip<ilimit)) {
+ ip ++;
+ if ( (dictMode == ZSTD_noDict)
+ && (offset) && ((offset_1>0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) {
+ size_t const mlRep = ZSTD_count(ip+4, ip+4-offset_1, iend) + 4;
+ int const gain2 = (int)(mlRep * 4);
+ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 1);
+ if ((mlRep >= 4) && (gain2 > gain1))
+ matchLength = mlRep, offset = 0, start = ip;
+ }
+ if (dictMode == ZSTD_dictMatchState) {
+ const U32 repIndex = (U32)(ip - base) - offset_1;
+ const BYTE* repMatch = repIndex < prefixLowestIndex ?
+ dictBase + (repIndex - dictIndexDelta) :
+ base + repIndex;
+ if (((U32)((prefixLowestIndex-1) - repIndex) >= 3 /* intentional underflow */)
+ && (MEM_read32(repMatch) == MEM_read32(ip)) ) {
+ const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend;
+ size_t const mlRep = ZSTD_count_2segments(ip+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4;
+ int const gain2 = (int)(mlRep * 4);
+ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 1);
+ if ((mlRep >= 4) && (gain2 > gain1))
+ matchLength = mlRep, offset = 0, start = ip;
+ }
+ }
+ { size_t offset2=999999999;
+ size_t const ml2 = searchMax(ms, ip, iend, &offset2);
+ int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */
+ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 7);
+ if ((ml2 >= 4) && (gain2 > gain1)) {
+ matchLength = ml2, offset = offset2, start = ip;
+ continue;
+ } } }
+ break; /* nothing found : store previous solution */
+ }
+
+ /* NOTE:
+ * start[-offset+ZSTD_REP_MOVE-1] is undefined behavior.
+ * (-offset+ZSTD_REP_MOVE-1) is unsigned, and is added to start, which
+ * overflows the pointer, which is undefined behavior.
+ */
+ /* catch up */
+ if (offset) {
+ if (dictMode == ZSTD_noDict) {
+ while ( ((start > anchor) & (start - (offset-ZSTD_REP_MOVE) > prefixLowest))
+ && (start[-1] == (start-(offset-ZSTD_REP_MOVE))[-1]) ) /* only search for offset within prefix */
+ { start--; matchLength++; }
+ }
+ if (dictMode == ZSTD_dictMatchState) {
+ U32 const matchIndex = (U32)((start-base) - (offset - ZSTD_REP_MOVE));
+ const BYTE* match = (matchIndex < prefixLowestIndex) ? dictBase + matchIndex - dictIndexDelta : base + matchIndex;
+ const BYTE* const mStart = (matchIndex < prefixLowestIndex) ? dictLowest : prefixLowest;
+ while ((start>anchor) && (match>mStart) && (start[-1] == match[-1])) { start--; match--; matchLength++; } /* catch up */
+ }
+ offset_2 = offset_1; offset_1 = (U32)(offset - ZSTD_REP_MOVE);
+ }
+ /* store sequence */
+_storeSequence:
+ { size_t const litLength = start - anchor;
+ ZSTD_storeSeq(seqStore, litLength, anchor, (U32)offset, matchLength-MINMATCH);
+ anchor = ip = start + matchLength;
+ }
+
+ /* check immediate repcode */
+ if (dictMode == ZSTD_dictMatchState) {
+ while (ip <= ilimit) {
+ U32 const current2 = (U32)(ip-base);
+ U32 const repIndex = current2 - offset_2;
+ const BYTE* repMatch = dictMode == ZSTD_dictMatchState
+ && repIndex < prefixLowestIndex ?
+ dictBase - dictIndexDelta + repIndex :
+ base + repIndex;
+ if ( ((U32)((prefixLowestIndex-1) - (U32)repIndex) >= 3 /* intentional overflow */)
+ && (MEM_read32(repMatch) == MEM_read32(ip)) ) {
+ const BYTE* const repEnd2 = repIndex < prefixLowestIndex ? dictEnd : iend;
+ matchLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd2, prefixLowest) + 4;
+ offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset; /* swap offset_2 <=> offset_1 */
+ ZSTD_storeSeq(seqStore, 0, anchor, 0, matchLength-MINMATCH);
+ ip += matchLength;
+ anchor = ip;
+ continue;
+ }
+ break;
+ }
+ }
+
+ if (dictMode == ZSTD_noDict) {
+ while ( ((ip <= ilimit) & (offset_2>0))
+ && (MEM_read32(ip) == MEM_read32(ip - offset_2)) ) {
+ /* store sequence */
+ matchLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4;
+ offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset; /* swap repcodes */
+ ZSTD_storeSeq(seqStore, 0, anchor, 0, matchLength-MINMATCH);
+ ip += matchLength;
+ anchor = ip;
+ continue; /* faster when present ... (?) */
+ } } }
+
+ /* Save reps for next block */
+ rep[0] = offset_1 ? offset_1 : savedOffset;
+ rep[1] = offset_2 ? offset_2 : savedOffset;
+
+ /* Return the last literals size */
+ return iend - anchor;
+}
+
+
+size_t ZSTD_compressBlock_btlazy2(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize)
+{
+ return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, 1, 2, ZSTD_noDict);
+}
+
+size_t ZSTD_compressBlock_lazy2(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize)
+{
+ return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, 0, 2, ZSTD_noDict);
+}
+
+size_t ZSTD_compressBlock_lazy(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize)
+{
+ return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, 0, 1, ZSTD_noDict);
+}
+
+size_t ZSTD_compressBlock_greedy(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize)
+{
+ return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, 0, 0, ZSTD_noDict);
+}
+
+size_t ZSTD_compressBlock_btlazy2_dictMatchState(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize)
+{
+ return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, 1, 2, ZSTD_dictMatchState);
+}
+
+size_t ZSTD_compressBlock_lazy2_dictMatchState(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize)
+{
+ return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, 0, 2, ZSTD_dictMatchState);
+}
+
+size_t ZSTD_compressBlock_lazy_dictMatchState(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize)
+{
+ return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, 0, 1, ZSTD_dictMatchState);
+}
+
+size_t ZSTD_compressBlock_greedy_dictMatchState(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize)
+{
+ return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, 0, 0, ZSTD_dictMatchState);
+}
+
+
+FORCE_INLINE_TEMPLATE
+size_t ZSTD_compressBlock_lazy_extDict_generic(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore,
+ U32 rep[ZSTD_REP_NUM],
+ const void* src, size_t srcSize,
+ const U32 searchMethod, const U32 depth)
+{
+ const BYTE* const istart = (const BYTE*)src;
+ const BYTE* ip = istart;
+ const BYTE* anchor = istart;
+ const BYTE* const iend = istart + srcSize;
+ const BYTE* const ilimit = iend - 8;
+ const BYTE* const base = ms->window.base;
+ const U32 dictLimit = ms->window.dictLimit;
+ const U32 lowestIndex = ms->window.lowLimit;
+ const BYTE* const prefixStart = base + dictLimit;
+ const BYTE* const dictBase = ms->window.dictBase;
+ const BYTE* const dictEnd = dictBase + dictLimit;
+ const BYTE* const dictStart = dictBase + lowestIndex;
+
+ typedef size_t (*searchMax_f)(
+ ZSTD_matchState_t* ms,
+ const BYTE* ip, const BYTE* iLimit, size_t* offsetPtr);
+ searchMax_f searchMax = searchMethod ? ZSTD_BtFindBestMatch_extDict_selectMLS : ZSTD_HcFindBestMatch_extDict_selectMLS;
+
+ U32 offset_1 = rep[0], offset_2 = rep[1];
+
+ /* init */
+ ms->nextToUpdate3 = ms->nextToUpdate;
+ ip += (ip == prefixStart);
+
+ /* Match Loop */
+ while (ip < ilimit) {
+ size_t matchLength=0;
+ size_t offset=0;
+ const BYTE* start=ip+1;
+ U32 current = (U32)(ip-base);
+
+ /* check repCode */
+ { const U32 repIndex = (U32)(current+1 - offset_1);
+ const BYTE* const repBase = repIndex < dictLimit ? dictBase : base;
+ const BYTE* const repMatch = repBase + repIndex;
+ if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > lowestIndex)) /* intentional overflow */
+ if (MEM_read32(ip+1) == MEM_read32(repMatch)) {
+ /* repcode detected we should take it */
+ const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend;
+ matchLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repEnd, prefixStart) + 4;
+ if (depth==0) goto _storeSequence;
+ } }
+
+ /* first search (depth 0) */
+ { size_t offsetFound = 999999999;
+ size_t const ml2 = searchMax(ms, ip, iend, &offsetFound);
+ if (ml2 > matchLength)
+ matchLength = ml2, start = ip, offset=offsetFound;
+ }
+
+ if (matchLength < 4) {
+ ip += ((ip-anchor) >> kSearchStrength) + 1; /* jump faster over incompressible sections */
+ continue;
+ }
+
+ /* let's try to find a better solution */
+ if (depth>=1)
+ while (ip<ilimit) {
+ ip ++;
+ current++;
+ /* check repCode */
+ if (offset) {
+ const U32 repIndex = (U32)(current - offset_1);
+ const BYTE* const repBase = repIndex < dictLimit ? dictBase : base;
+ const BYTE* const repMatch = repBase + repIndex;
+ if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > lowestIndex)) /* intentional overflow */
+ if (MEM_read32(ip) == MEM_read32(repMatch)) {
+ /* repcode detected */
+ const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend;
+ size_t const repLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd, prefixStart) + 4;
+ int const gain2 = (int)(repLength * 3);
+ int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offset+1) + 1);
+ if ((repLength >= 4) && (gain2 > gain1))
+ matchLength = repLength, offset = 0, start = ip;
+ } }
+
+ /* search match, depth 1 */
+ { size_t offset2=999999999;
+ size_t const ml2 = searchMax(ms, ip, iend, &offset2);
+ int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */
+ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 4);
+ if ((ml2 >= 4) && (gain2 > gain1)) {
+ matchLength = ml2, offset = offset2, start = ip;
+ continue; /* search a better one */
+ } }
+
+ /* let's find an even better one */
+ if ((depth==2) && (ip<ilimit)) {
+ ip ++;
+ current++;
+ /* check repCode */
+ if (offset) {
+ const U32 repIndex = (U32)(current - offset_1);
+ const BYTE* const repBase = repIndex < dictLimit ? dictBase : base;
+ const BYTE* const repMatch = repBase + repIndex;
+ if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > lowestIndex)) /* intentional overflow */
+ if (MEM_read32(ip) == MEM_read32(repMatch)) {
+ /* repcode detected */
+ const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend;
+ size_t const repLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd, prefixStart) + 4;
+ int const gain2 = (int)(repLength * 4);
+ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 1);
+ if ((repLength >= 4) && (gain2 > gain1))
+ matchLength = repLength, offset = 0, start = ip;
+ } }
+
+ /* search match, depth 2 */
+ { size_t offset2=999999999;
+ size_t const ml2 = searchMax(ms, ip, iend, &offset2);
+ int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1)); /* raw approx */
+ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 7);
+ if ((ml2 >= 4) && (gain2 > gain1)) {
+ matchLength = ml2, offset = offset2, start = ip;
+ continue;
+ } } }
+ break; /* nothing found : store previous solution */
+ }
+
+ /* catch up */
+ if (offset) {
+ U32 const matchIndex = (U32)((start-base) - (offset - ZSTD_REP_MOVE));
+ const BYTE* match = (matchIndex < dictLimit) ? dictBase + matchIndex : base + matchIndex;
+ const BYTE* const mStart = (matchIndex < dictLimit) ? dictStart : prefixStart;
+ while ((start>anchor) && (match>mStart) && (start[-1] == match[-1])) { start--; match--; matchLength++; } /* catch up */
+ offset_2 = offset_1; offset_1 = (U32)(offset - ZSTD_REP_MOVE);
+ }
+
+ /* store sequence */
+_storeSequence:
+ { size_t const litLength = start - anchor;
+ ZSTD_storeSeq(seqStore, litLength, anchor, (U32)offset, matchLength-MINMATCH);
+ anchor = ip = start + matchLength;
+ }
+
+ /* check immediate repcode */
+ while (ip <= ilimit) {
+ const U32 repIndex = (U32)((ip-base) - offset_2);
+ const BYTE* const repBase = repIndex < dictLimit ? dictBase : base;
+ const BYTE* const repMatch = repBase + repIndex;
+ if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > lowestIndex)) /* intentional overflow */
+ if (MEM_read32(ip) == MEM_read32(repMatch)) {
+ /* repcode detected we should take it */
+ const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend;
+ matchLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd, prefixStart) + 4;
+ offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset; /* swap offset history */
+ ZSTD_storeSeq(seqStore, 0, anchor, 0, matchLength-MINMATCH);
+ ip += matchLength;
+ anchor = ip;
+ continue; /* faster when present ... (?) */
+ }
+ break;
+ } }
+
+ /* Save reps for next block */
+ rep[0] = offset_1;
+ rep[1] = offset_2;
+
+ /* Return the last literals size */
+ return iend - anchor;
+}
+
+
+size_t ZSTD_compressBlock_greedy_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize)
+{
+ return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, 0, 0);
+}
+
+size_t ZSTD_compressBlock_lazy_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize)
+
+{
+ return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, 0, 1);
+}
+
+size_t ZSTD_compressBlock_lazy2_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize)
+
+{
+ return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, 0, 2);
+}
+
+size_t ZSTD_compressBlock_btlazy2_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize)
+
+{
+ return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, 1, 2);
+}
diff --git a/Utilities/cmzstd/lib/compress/zstd_lazy.h b/Utilities/cmzstd/lib/compress/zstd_lazy.h
new file mode 100644
index 000000000..ef85a6df9
--- /dev/null
+++ b/Utilities/cmzstd/lib/compress/zstd_lazy.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#ifndef ZSTD_LAZY_H
+#define ZSTD_LAZY_H
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#include "zstd_compress_internal.h"
+
+U32 ZSTD_insertAndFindFirstIndex(ZSTD_matchState_t* ms, const BYTE* ip);
+
+void ZSTD_preserveUnsortedMark (U32* const table, U32 const size, U32 const reducerValue); /*! used in ZSTD_reduceIndex(). pre-emptively increase value of ZSTD_DUBT_UNSORTED_MARK */
+
+size_t ZSTD_compressBlock_btlazy2(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_lazy2(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_lazy(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_greedy(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+
+size_t ZSTD_compressBlock_btlazy2_dictMatchState(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_lazy2_dictMatchState(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_lazy_dictMatchState(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_greedy_dictMatchState(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+
+size_t ZSTD_compressBlock_greedy_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_lazy_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_lazy2_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_btlazy2_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* ZSTD_LAZY_H */
diff --git a/Utilities/cmzstd/lib/compress/zstd_ldm.c b/Utilities/cmzstd/lib/compress/zstd_ldm.c
new file mode 100644
index 000000000..58eb2ffe4
--- /dev/null
+++ b/Utilities/cmzstd/lib/compress/zstd_ldm.c
@@ -0,0 +1,597 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ */
+
+#include "zstd_ldm.h"
+
+#include "debug.h"
+#include "zstd_fast.h" /* ZSTD_fillHashTable() */
+#include "zstd_double_fast.h" /* ZSTD_fillDoubleHashTable() */
+
+#define LDM_BUCKET_SIZE_LOG 3
+#define LDM_MIN_MATCH_LENGTH 64
+#define LDM_HASH_RLOG 7
+#define LDM_HASH_CHAR_OFFSET 10
+
+void ZSTD_ldm_adjustParameters(ldmParams_t* params,
+ ZSTD_compressionParameters const* cParams)
+{
+ params->windowLog = cParams->windowLog;
+ ZSTD_STATIC_ASSERT(LDM_BUCKET_SIZE_LOG <= ZSTD_LDM_BUCKETSIZELOG_MAX);
+ DEBUGLOG(4, "ZSTD_ldm_adjustParameters");
+ if (!params->bucketSizeLog) params->bucketSizeLog = LDM_BUCKET_SIZE_LOG;
+ if (!params->minMatchLength) params->minMatchLength = LDM_MIN_MATCH_LENGTH;
+ if (cParams->strategy >= ZSTD_btopt) {
+ /* Get out of the way of the optimal parser */
+ U32 const minMatch = MAX(cParams->targetLength, params->minMatchLength);
+ assert(minMatch >= ZSTD_LDM_MINMATCH_MIN);
+ assert(minMatch <= ZSTD_LDM_MINMATCH_MAX);
+ params->minMatchLength = minMatch;
+ }
+ if (params->hashLog == 0) {
+ params->hashLog = MAX(ZSTD_HASHLOG_MIN, params->windowLog - LDM_HASH_RLOG);
+ assert(params->hashLog <= ZSTD_HASHLOG_MAX);
+ }
+ if (params->hashRateLog == 0) {
+ params->hashRateLog = params->windowLog < params->hashLog
+ ? 0
+ : params->windowLog - params->hashLog;
+ }
+ params->bucketSizeLog = MIN(params->bucketSizeLog, params->hashLog);
+}
+
+size_t ZSTD_ldm_getTableSize(ldmParams_t params)
+{
+ size_t const ldmHSize = ((size_t)1) << params.hashLog;
+ size_t const ldmBucketSizeLog = MIN(params.bucketSizeLog, params.hashLog);
+ size_t const ldmBucketSize =
+ ((size_t)1) << (params.hashLog - ldmBucketSizeLog);
+ size_t const totalSize = ldmBucketSize + ldmHSize * sizeof(ldmEntry_t);
+ return params.enableLdm ? totalSize : 0;
+}
+
+size_t ZSTD_ldm_getMaxNbSeq(ldmParams_t params, size_t maxChunkSize)
+{
+ return params.enableLdm ? (maxChunkSize / params.minMatchLength) : 0;
+}
+
+/** ZSTD_ldm_getSmallHash() :
+ * numBits should be <= 32
+ * If numBits==0, returns 0.
+ * @return : the most significant numBits of value. */
+static U32 ZSTD_ldm_getSmallHash(U64 value, U32 numBits)
+{
+ assert(numBits <= 32);
+ return numBits == 0 ? 0 : (U32)(value >> (64 - numBits));
+}
+
+/** ZSTD_ldm_getChecksum() :
+ * numBitsToDiscard should be <= 32
+ * @return : the next most significant 32 bits after numBitsToDiscard */
+static U32 ZSTD_ldm_getChecksum(U64 hash, U32 numBitsToDiscard)
+{
+ assert(numBitsToDiscard <= 32);
+ return (hash >> (64 - 32 - numBitsToDiscard)) & 0xFFFFFFFF;
+}
+
+/** ZSTD_ldm_getTag() ;
+ * Given the hash, returns the most significant numTagBits bits
+ * after (32 + hbits) bits.
+ *
+ * If there are not enough bits remaining, return the last
+ * numTagBits bits. */
+static U32 ZSTD_ldm_getTag(U64 hash, U32 hbits, U32 numTagBits)
+{
+ assert(numTagBits < 32 && hbits <= 32);
+ if (32 - hbits < numTagBits) {
+ return hash & (((U32)1 << numTagBits) - 1);
+ } else {
+ return (hash >> (32 - hbits - numTagBits)) & (((U32)1 << numTagBits) - 1);
+ }
+}
+
+/** ZSTD_ldm_getBucket() :
+ * Returns a pointer to the start of the bucket associated with hash. */
+static ldmEntry_t* ZSTD_ldm_getBucket(
+ ldmState_t* ldmState, size_t hash, ldmParams_t const ldmParams)
+{
+ return ldmState->hashTable + (hash << ldmParams.bucketSizeLog);
+}
+
+/** ZSTD_ldm_insertEntry() :
+ * Insert the entry with corresponding hash into the hash table */
+static void ZSTD_ldm_insertEntry(ldmState_t* ldmState,
+ size_t const hash, const ldmEntry_t entry,
+ ldmParams_t const ldmParams)
+{
+ BYTE* const bucketOffsets = ldmState->bucketOffsets;
+ *(ZSTD_ldm_getBucket(ldmState, hash, ldmParams) + bucketOffsets[hash]) = entry;
+ bucketOffsets[hash]++;
+ bucketOffsets[hash] &= ((U32)1 << ldmParams.bucketSizeLog) - 1;
+}
+
+/** ZSTD_ldm_makeEntryAndInsertByTag() :
+ *
+ * Gets the small hash, checksum, and tag from the rollingHash.
+ *
+ * If the tag matches (1 << ldmParams.hashRateLog)-1, then
+ * creates an ldmEntry from the offset, and inserts it into the hash table.
+ *
+ * hBits is the length of the small hash, which is the most significant hBits
+ * of rollingHash. The checksum is the next 32 most significant bits, followed
+ * by ldmParams.hashRateLog bits that make up the tag. */
+static void ZSTD_ldm_makeEntryAndInsertByTag(ldmState_t* ldmState,
+ U64 const rollingHash,
+ U32 const hBits,
+ U32 const offset,
+ ldmParams_t const ldmParams)
+{
+ U32 const tag = ZSTD_ldm_getTag(rollingHash, hBits, ldmParams.hashRateLog);
+ U32 const tagMask = ((U32)1 << ldmParams.hashRateLog) - 1;
+ if (tag == tagMask) {
+ U32 const hash = ZSTD_ldm_getSmallHash(rollingHash, hBits);
+ U32 const checksum = ZSTD_ldm_getChecksum(rollingHash, hBits);
+ ldmEntry_t entry;
+ entry.offset = offset;
+ entry.checksum = checksum;
+ ZSTD_ldm_insertEntry(ldmState, hash, entry, ldmParams);
+ }
+}
+
+/** ZSTD_ldm_countBackwardsMatch() :
+ * Returns the number of bytes that match backwards before pIn and pMatch.
+ *
+ * We count only bytes where pMatch >= pBase and pIn >= pAnchor. */
+static size_t ZSTD_ldm_countBackwardsMatch(
+ const BYTE* pIn, const BYTE* pAnchor,
+ const BYTE* pMatch, const BYTE* pBase)
+{
+ size_t matchLength = 0;
+ while (pIn > pAnchor && pMatch > pBase && pIn[-1] == pMatch[-1]) {
+ pIn--;
+ pMatch--;
+ matchLength++;
+ }
+ return matchLength;
+}
+
+/** ZSTD_ldm_fillFastTables() :
+ *
+ * Fills the relevant tables for the ZSTD_fast and ZSTD_dfast strategies.
+ * This is similar to ZSTD_loadDictionaryContent.
+ *
+ * The tables for the other strategies are filled within their
+ * block compressors. */
+static size_t ZSTD_ldm_fillFastTables(ZSTD_matchState_t* ms,
+ void const* end)
+{
+ const BYTE* const iend = (const BYTE*)end;
+
+ switch(ms->cParams.strategy)
+ {
+ case ZSTD_fast:
+ ZSTD_fillHashTable(ms, iend, ZSTD_dtlm_fast);
+ break;
+
+ case ZSTD_dfast:
+ ZSTD_fillDoubleHashTable(ms, iend, ZSTD_dtlm_fast);
+ break;
+
+ case ZSTD_greedy:
+ case ZSTD_lazy:
+ case ZSTD_lazy2:
+ case ZSTD_btlazy2:
+ case ZSTD_btopt:
+ case ZSTD_btultra:
+ case ZSTD_btultra2:
+ break;
+ default:
+ assert(0); /* not possible : not a valid strategy id */
+ }
+
+ return 0;
+}
+
+/** ZSTD_ldm_fillLdmHashTable() :
+ *
+ * Fills hashTable from (lastHashed + 1) to iend (non-inclusive).
+ * lastHash is the rolling hash that corresponds to lastHashed.
+ *
+ * Returns the rolling hash corresponding to position iend-1. */
+static U64 ZSTD_ldm_fillLdmHashTable(ldmState_t* state,
+ U64 lastHash, const BYTE* lastHashed,
+ const BYTE* iend, const BYTE* base,
+ U32 hBits, ldmParams_t const ldmParams)
+{
+ U64 rollingHash = lastHash;
+ const BYTE* cur = lastHashed + 1;
+
+ while (cur < iend) {
+ rollingHash = ZSTD_rollingHash_rotate(rollingHash, cur[-1],
+ cur[ldmParams.minMatchLength-1],
+ state->hashPower);
+ ZSTD_ldm_makeEntryAndInsertByTag(state,
+ rollingHash, hBits,
+ (U32)(cur - base), ldmParams);
+ ++cur;
+ }
+ return rollingHash;
+}
+
+
+/** ZSTD_ldm_limitTableUpdate() :
+ *
+ * Sets cctx->nextToUpdate to a position corresponding closer to anchor
+ * if it is far way
+ * (after a long match, only update tables a limited amount). */
+static void ZSTD_ldm_limitTableUpdate(ZSTD_matchState_t* ms, const BYTE* anchor)
+{
+ U32 const current = (U32)(anchor - ms->window.base);
+ if (current > ms->nextToUpdate + 1024) {
+ ms->nextToUpdate =
+ current - MIN(512, current - ms->nextToUpdate - 1024);
+ }
+}
+
+static size_t ZSTD_ldm_generateSequences_internal(
+ ldmState_t* ldmState, rawSeqStore_t* rawSeqStore,
+ ldmParams_t const* params, void const* src, size_t srcSize)
+{
+ /* LDM parameters */
+ int const extDict = ZSTD_window_hasExtDict(ldmState->window);
+ U32 const minMatchLength = params->minMatchLength;
+ U64 const hashPower = ldmState->hashPower;
+ U32 const hBits = params->hashLog - params->bucketSizeLog;
+ U32 const ldmBucketSize = 1U << params->bucketSizeLog;
+ U32 const hashRateLog = params->hashRateLog;
+ U32 const ldmTagMask = (1U << params->hashRateLog) - 1;
+ /* Prefix and extDict parameters */
+ U32 const dictLimit = ldmState->window.dictLimit;
+ U32 const lowestIndex = extDict ? ldmState->window.lowLimit : dictLimit;
+ BYTE const* const base = ldmState->window.base;
+ BYTE const* const dictBase = extDict ? ldmState->window.dictBase : NULL;
+ BYTE const* const dictStart = extDict ? dictBase + lowestIndex : NULL;
+ BYTE const* const dictEnd = extDict ? dictBase + dictLimit : NULL;
+ BYTE const* const lowPrefixPtr = base + dictLimit;
+ /* Input bounds */
+ BYTE const* const istart = (BYTE const*)src;
+ BYTE const* const iend = istart + srcSize;
+ BYTE const* const ilimit = iend - MAX(minMatchLength, HASH_READ_SIZE);
+ /* Input positions */
+ BYTE const* anchor = istart;
+ BYTE const* ip = istart;
+ /* Rolling hash */
+ BYTE const* lastHashed = NULL;
+ U64 rollingHash = 0;
+
+ while (ip <= ilimit) {
+ size_t mLength;
+ U32 const current = (U32)(ip - base);
+ size_t forwardMatchLength = 0, backwardMatchLength = 0;
+ ldmEntry_t* bestEntry = NULL;
+ if (ip != istart) {
+ rollingHash = ZSTD_rollingHash_rotate(rollingHash, lastHashed[0],
+ lastHashed[minMatchLength],
+ hashPower);
+ } else {
+ rollingHash = ZSTD_rollingHash_compute(ip, minMatchLength);
+ }
+ lastHashed = ip;
+
+ /* Do not insert and do not look for a match */
+ if (ZSTD_ldm_getTag(rollingHash, hBits, hashRateLog) != ldmTagMask) {
+ ip++;
+ continue;
+ }
+
+ /* Get the best entry and compute the match lengths */
+ {
+ ldmEntry_t* const bucket =
+ ZSTD_ldm_getBucket(ldmState,
+ ZSTD_ldm_getSmallHash(rollingHash, hBits),
+ *params);
+ ldmEntry_t* cur;
+ size_t bestMatchLength = 0;
+ U32 const checksum = ZSTD_ldm_getChecksum(rollingHash, hBits);
+
+ for (cur = bucket; cur < bucket + ldmBucketSize; ++cur) {
+ size_t curForwardMatchLength, curBackwardMatchLength,
+ curTotalMatchLength;
+ if (cur->checksum != checksum || cur->offset <= lowestIndex) {
+ continue;
+ }
+ if (extDict) {
+ BYTE const* const curMatchBase =
+ cur->offset < dictLimit ? dictBase : base;
+ BYTE const* const pMatch = curMatchBase + cur->offset;
+ BYTE const* const matchEnd =
+ cur->offset < dictLimit ? dictEnd : iend;
+ BYTE const* const lowMatchPtr =
+ cur->offset < dictLimit ? dictStart : lowPrefixPtr;
+
+ curForwardMatchLength = ZSTD_count_2segments(
+ ip, pMatch, iend,
+ matchEnd, lowPrefixPtr);
+ if (curForwardMatchLength < minMatchLength) {
+ continue;
+ }
+ curBackwardMatchLength =
+ ZSTD_ldm_countBackwardsMatch(ip, anchor, pMatch,
+ lowMatchPtr);
+ curTotalMatchLength = curForwardMatchLength +
+ curBackwardMatchLength;
+ } else { /* !extDict */
+ BYTE const* const pMatch = base + cur->offset;
+ curForwardMatchLength = ZSTD_count(ip, pMatch, iend);
+ if (curForwardMatchLength < minMatchLength) {
+ continue;
+ }
+ curBackwardMatchLength =
+ ZSTD_ldm_countBackwardsMatch(ip, anchor, pMatch,
+ lowPrefixPtr);
+ curTotalMatchLength = curForwardMatchLength +
+ curBackwardMatchLength;
+ }
+
+ if (curTotalMatchLength > bestMatchLength) {
+ bestMatchLength = curTotalMatchLength;
+ forwardMatchLength = curForwardMatchLength;
+ backwardMatchLength = curBackwardMatchLength;
+ bestEntry = cur;
+ }
+ }
+ }
+
+ /* No match found -- continue searching */
+ if (bestEntry == NULL) {
+ ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash,
+ hBits, current,
+ *params);
+ ip++;
+ continue;
+ }
+
+ /* Match found */
+ mLength = forwardMatchLength + backwardMatchLength;
+ ip -= backwardMatchLength;
+
+ {
+ /* Store the sequence:
+ * ip = current - backwardMatchLength
+ * The match is at (bestEntry->offset - backwardMatchLength)
+ */
+ U32 const matchIndex = bestEntry->offset;
+ U32 const offset = current - matchIndex;
+ rawSeq* const seq = rawSeqStore->seq + rawSeqStore->size;
+
+ /* Out of sequence storage */
+ if (rawSeqStore->size == rawSeqStore->capacity)
+ return ERROR(dstSize_tooSmall);
+ seq->litLength = (U32)(ip - anchor);
+ seq->matchLength = (U32)mLength;
+ seq->offset = offset;
+ rawSeqStore->size++;
+ }
+
+ /* Insert the current entry into the hash table */
+ ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash, hBits,
+ (U32)(lastHashed - base),
+ *params);
+
+ assert(ip + backwardMatchLength == lastHashed);
+
+ /* Fill the hash table from lastHashed+1 to ip+mLength*/
+ /* Heuristic: don't need to fill the entire table at end of block */
+ if (ip + mLength <= ilimit) {
+ rollingHash = ZSTD_ldm_fillLdmHashTable(
+ ldmState, rollingHash, lastHashed,
+ ip + mLength, base, hBits, *params);
+ lastHashed = ip + mLength - 1;
+ }
+ ip += mLength;
+ anchor = ip;
+ }
+ return iend - anchor;
+}
+
+/*! ZSTD_ldm_reduceTable() :
+ * reduce table indexes by `reducerValue` */
+static void ZSTD_ldm_reduceTable(ldmEntry_t* const table, U32 const size,
+ U32 const reducerValue)
+{
+ U32 u;
+ for (u = 0; u < size; u++) {
+ if (table[u].offset < reducerValue) table[u].offset = 0;
+ else table[u].offset -= reducerValue;
+ }
+}
+
+size_t ZSTD_ldm_generateSequences(
+ ldmState_t* ldmState, rawSeqStore_t* sequences,
+ ldmParams_t const* params, void const* src, size_t srcSize)
+{
+ U32 const maxDist = 1U << params->windowLog;
+ BYTE const* const istart = (BYTE const*)src;
+ BYTE const* const iend = istart + srcSize;
+ size_t const kMaxChunkSize = 1 << 20;
+ size_t const nbChunks = (srcSize / kMaxChunkSize) + ((srcSize % kMaxChunkSize) != 0);
+ size_t chunk;
+ size_t leftoverSize = 0;
+
+ assert(ZSTD_CHUNKSIZE_MAX >= kMaxChunkSize);
+ /* Check that ZSTD_window_update() has been called for this chunk prior
+ * to passing it to this function.
+ */
+ assert(ldmState->window.nextSrc >= (BYTE const*)src + srcSize);
+ /* The input could be very large (in zstdmt), so it must be broken up into
+ * chunks to enforce the maximmum distance and handle overflow correction.
+ */
+ assert(sequences->pos <= sequences->size);
+ assert(sequences->size <= sequences->capacity);
+ for (chunk = 0; chunk < nbChunks && sequences->size < sequences->capacity; ++chunk) {
+ BYTE const* const chunkStart = istart + chunk * kMaxChunkSize;
+ size_t const remaining = (size_t)(iend - chunkStart);
+ BYTE const *const chunkEnd =
+ (remaining < kMaxChunkSize) ? iend : chunkStart + kMaxChunkSize;
+ size_t const chunkSize = chunkEnd - chunkStart;
+ size_t newLeftoverSize;
+ size_t const prevSize = sequences->size;
+
+ assert(chunkStart < iend);
+ /* 1. Perform overflow correction if necessary. */
+ if (ZSTD_window_needOverflowCorrection(ldmState->window, chunkEnd)) {
+ U32 const ldmHSize = 1U << params->hashLog;
+ U32 const correction = ZSTD_window_correctOverflow(
+ &ldmState->window, /* cycleLog */ 0, maxDist, src);
+ ZSTD_ldm_reduceTable(ldmState->hashTable, ldmHSize, correction);
+ }
+ /* 2. We enforce the maximum offset allowed.
+ *
+ * kMaxChunkSize should be small enough that we don't lose too much of
+ * the window through early invalidation.
+ * TODO: * Test the chunk size.
+ * * Try invalidation after the sequence generation and test the
+ * the offset against maxDist directly.
+ */
+ ZSTD_window_enforceMaxDist(&ldmState->window, chunkEnd, maxDist, NULL, NULL);
+ /* 3. Generate the sequences for the chunk, and get newLeftoverSize. */
+ newLeftoverSize = ZSTD_ldm_generateSequences_internal(
+ ldmState, sequences, params, chunkStart, chunkSize);
+ if (ZSTD_isError(newLeftoverSize))
+ return newLeftoverSize;
+ /* 4. We add the leftover literals from previous iterations to the first
+ * newly generated sequence, or add the `newLeftoverSize` if none are
+ * generated.
+ */
+ /* Prepend the leftover literals from the last call */
+ if (prevSize < sequences->size) {
+ sequences->seq[prevSize].litLength += (U32)leftoverSize;
+ leftoverSize = newLeftoverSize;
+ } else {
+ assert(newLeftoverSize == chunkSize);
+ leftoverSize += chunkSize;
+ }
+ }
+ return 0;
+}
+
+void ZSTD_ldm_skipSequences(rawSeqStore_t* rawSeqStore, size_t srcSize, U32 const minMatch) {
+ while (srcSize > 0 && rawSeqStore->pos < rawSeqStore->size) {
+ rawSeq* seq = rawSeqStore->seq + rawSeqStore->pos;
+ if (srcSize <= seq->litLength) {
+ /* Skip past srcSize literals */
+ seq->litLength -= (U32)srcSize;
+ return;
+ }
+ srcSize -= seq->litLength;
+ seq->litLength = 0;
+ if (srcSize < seq->matchLength) {
+ /* Skip past the first srcSize of the match */
+ seq->matchLength -= (U32)srcSize;
+ if (seq->matchLength < minMatch) {
+ /* The match is too short, omit it */
+ if (rawSeqStore->pos + 1 < rawSeqStore->size) {
+ seq[1].litLength += seq[0].matchLength;
+ }
+ rawSeqStore->pos++;
+ }
+ return;
+ }
+ srcSize -= seq->matchLength;
+ seq->matchLength = 0;
+ rawSeqStore->pos++;
+ }
+}
+
+/**
+ * If the sequence length is longer than remaining then the sequence is split
+ * between this block and the next.
+ *
+ * Returns the current sequence to handle, or if the rest of the block should
+ * be literals, it returns a sequence with offset == 0.
+ */
+static rawSeq maybeSplitSequence(rawSeqStore_t* rawSeqStore,
+ U32 const remaining, U32 const minMatch)
+{
+ rawSeq sequence = rawSeqStore->seq[rawSeqStore->pos];
+ assert(sequence.offset > 0);
+ /* Likely: No partial sequence */
+ if (remaining >= sequence.litLength + sequence.matchLength) {
+ rawSeqStore->pos++;
+ return sequence;
+ }
+ /* Cut the sequence short (offset == 0 ==> rest is literals). */
+ if (remaining <= sequence.litLength) {
+ sequence.offset = 0;
+ } else if (remaining < sequence.litLength + sequence.matchLength) {
+ sequence.matchLength = remaining - sequence.litLength;
+ if (sequence.matchLength < minMatch) {
+ sequence.offset = 0;
+ }
+ }
+ /* Skip past `remaining` bytes for the future sequences. */
+ ZSTD_ldm_skipSequences(rawSeqStore, remaining, minMatch);
+ return sequence;
+}
+
+size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore,
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize)
+{
+ const ZSTD_compressionParameters* const cParams = &ms->cParams;
+ unsigned const minMatch = cParams->minMatch;
+ ZSTD_blockCompressor const blockCompressor =
+ ZSTD_selectBlockCompressor(cParams->strategy, ZSTD_matchState_dictMode(ms));
+ /* Input bounds */
+ BYTE const* const istart = (BYTE const*)src;
+ BYTE const* const iend = istart + srcSize;
+ /* Input positions */
+ BYTE const* ip = istart;
+
+ DEBUGLOG(5, "ZSTD_ldm_blockCompress: srcSize=%zu", srcSize);
+ assert(rawSeqStore->pos <= rawSeqStore->size);
+ assert(rawSeqStore->size <= rawSeqStore->capacity);
+ /* Loop through each sequence and apply the block compressor to the lits */
+ while (rawSeqStore->pos < rawSeqStore->size && ip < iend) {
+ /* maybeSplitSequence updates rawSeqStore->pos */
+ rawSeq const sequence = maybeSplitSequence(rawSeqStore,
+ (U32)(iend - ip), minMatch);
+ int i;
+ /* End signal */
+ if (sequence.offset == 0)
+ break;
+
+ assert(sequence.offset <= (1U << cParams->windowLog));
+ assert(ip + sequence.litLength + sequence.matchLength <= iend);
+
+ /* Fill tables for block compressor */
+ ZSTD_ldm_limitTableUpdate(ms, ip);
+ ZSTD_ldm_fillFastTables(ms, ip);
+ /* Run the block compressor */
+ DEBUGLOG(5, "calling block compressor on segment of size %u", sequence.litLength);
+ {
+ size_t const newLitLength =
+ blockCompressor(ms, seqStore, rep, ip, sequence.litLength);
+ ip += sequence.litLength;
+ /* Update the repcodes */
+ for (i = ZSTD_REP_NUM - 1; i > 0; i--)
+ rep[i] = rep[i-1];
+ rep[0] = sequence.offset;
+ /* Store the sequence */
+ ZSTD_storeSeq(seqStore, newLitLength, ip - newLitLength,
+ sequence.offset + ZSTD_REP_MOVE,
+ sequence.matchLength - MINMATCH);
+ ip += sequence.matchLength;
+ }
+ }
+ /* Fill the tables for the block compressor */
+ ZSTD_ldm_limitTableUpdate(ms, ip);
+ ZSTD_ldm_fillFastTables(ms, ip);
+ /* Compress the last literals */
+ return blockCompressor(ms, seqStore, rep, ip, iend - ip);
+}
diff --git a/Utilities/cmzstd/lib/compress/zstd_ldm.h b/Utilities/cmzstd/lib/compress/zstd_ldm.h
new file mode 100644
index 000000000..a47846128
--- /dev/null
+++ b/Utilities/cmzstd/lib/compress/zstd_ldm.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ */
+
+#ifndef ZSTD_LDM_H
+#define ZSTD_LDM_H
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#include "zstd_compress_internal.h" /* ldmParams_t, U32 */
+#include "zstd.h" /* ZSTD_CCtx, size_t */
+
+/*-*************************************
+* Long distance matching
+***************************************/
+
+#define ZSTD_LDM_DEFAULT_WINDOW_LOG ZSTD_WINDOWLOG_LIMIT_DEFAULT
+
+/**
+ * ZSTD_ldm_generateSequences():
+ *
+ * Generates the sequences using the long distance match finder.
+ * Generates long range matching sequences in `sequences`, which parse a prefix
+ * of the source. `sequences` must be large enough to store every sequence,
+ * which can be checked with `ZSTD_ldm_getMaxNbSeq()`.
+ * @returns 0 or an error code.
+ *
+ * NOTE: The user must have called ZSTD_window_update() for all of the input
+ * they have, even if they pass it to ZSTD_ldm_generateSequences() in chunks.
+ * NOTE: This function returns an error if it runs out of space to store
+ * sequences.
+ */
+size_t ZSTD_ldm_generateSequences(
+ ldmState_t* ldms, rawSeqStore_t* sequences,
+ ldmParams_t const* params, void const* src, size_t srcSize);
+
+/**
+ * ZSTD_ldm_blockCompress():
+ *
+ * Compresses a block using the predefined sequences, along with a secondary
+ * block compressor. The literals section of every sequence is passed to the
+ * secondary block compressor, and those sequences are interspersed with the
+ * predefined sequences. Returns the length of the last literals.
+ * Updates `rawSeqStore.pos` to indicate how many sequences have been consumed.
+ * `rawSeqStore.seq` may also be updated to split the last sequence between two
+ * blocks.
+ * @return The length of the last literals.
+ *
+ * NOTE: The source must be at most the maximum block size, but the predefined
+ * sequences can be any size, and may be longer than the block. In the case that
+ * they are longer than the block, the last sequences may need to be split into
+ * two. We handle that case correctly, and update `rawSeqStore` appropriately.
+ * NOTE: This function does not return any errors.
+ */
+size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore,
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+
+/**
+ * ZSTD_ldm_skipSequences():
+ *
+ * Skip past `srcSize` bytes worth of sequences in `rawSeqStore`.
+ * Avoids emitting matches less than `minMatch` bytes.
+ * Must be called for data with is not passed to ZSTD_ldm_blockCompress().
+ */
+void ZSTD_ldm_skipSequences(rawSeqStore_t* rawSeqStore, size_t srcSize,
+ U32 const minMatch);
+
+
+/** ZSTD_ldm_getTableSize() :
+ * Estimate the space needed for long distance matching tables or 0 if LDM is
+ * disabled.
+ */
+size_t ZSTD_ldm_getTableSize(ldmParams_t params);
+
+/** ZSTD_ldm_getSeqSpace() :
+ * Return an upper bound on the number of sequences that can be produced by
+ * the long distance matcher, or 0 if LDM is disabled.
+ */
+size_t ZSTD_ldm_getMaxNbSeq(ldmParams_t params, size_t maxChunkSize);
+
+/** ZSTD_ldm_adjustParameters() :
+ * If the params->hashRateLog is not set, set it to its default value based on
+ * windowLog and params->hashLog.
+ *
+ * Ensures that params->bucketSizeLog is <= params->hashLog (setting it to
+ * params->hashLog if it is not).
+ *
+ * Ensures that the minMatchLength >= targetLength during optimal parsing.
+ */
+void ZSTD_ldm_adjustParameters(ldmParams_t* params,
+ ZSTD_compressionParameters const* cParams);
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* ZSTD_FAST_H */
diff --git a/Utilities/cmzstd/lib/compress/zstd_opt.c b/Utilities/cmzstd/lib/compress/zstd_opt.c
new file mode 100644
index 000000000..44de6e97f
--- /dev/null
+++ b/Utilities/cmzstd/lib/compress/zstd_opt.c
@@ -0,0 +1,1217 @@
+/*
+ * Copyright (c) 2016-present, Przemyslaw Skibinski, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#include "zstd_compress_internal.h"
+#include "hist.h"
+#include "zstd_opt.h"
+
+
+#define ZSTD_LITFREQ_ADD 2 /* scaling factor for litFreq, so that frequencies adapt faster to new stats */
+#define ZSTD_FREQ_DIV 4 /* log factor when using previous stats to init next stats */
+#define ZSTD_MAX_PRICE (1<<30)
+
+#define ZSTD_PREDEF_THRESHOLD 1024 /* if srcSize < ZSTD_PREDEF_THRESHOLD, symbols' cost is assumed static, directly determined by pre-defined distributions */
+
+
+/*-*************************************
+* Price functions for optimal parser
+***************************************/
+
+#if 0 /* approximation at bit level */
+# define BITCOST_ACCURACY 0
+# define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY)
+# define WEIGHT(stat) ((void)opt, ZSTD_bitWeight(stat))
+#elif 0 /* fractional bit accuracy */
+# define BITCOST_ACCURACY 8
+# define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY)
+# define WEIGHT(stat,opt) ((void)opt, ZSTD_fracWeight(stat))
+#else /* opt==approx, ultra==accurate */
+# define BITCOST_ACCURACY 8
+# define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY)
+# define WEIGHT(stat,opt) (opt ? ZSTD_fracWeight(stat) : ZSTD_bitWeight(stat))
+#endif
+
+MEM_STATIC U32 ZSTD_bitWeight(U32 stat)
+{
+ return (ZSTD_highbit32(stat+1) * BITCOST_MULTIPLIER);
+}
+
+MEM_STATIC U32 ZSTD_fracWeight(U32 rawStat)
+{
+ U32 const stat = rawStat + 1;
+ U32 const hb = ZSTD_highbit32(stat);
+ U32 const BWeight = hb * BITCOST_MULTIPLIER;
+ U32 const FWeight = (stat << BITCOST_ACCURACY) >> hb;
+ U32 const weight = BWeight + FWeight;
+ assert(hb + BITCOST_ACCURACY < 31);
+ return weight;
+}
+
+#if (DEBUGLEVEL>=2)
+/* debugging function,
+ * @return price in bytes as fractional value
+ * for debug messages only */
+MEM_STATIC double ZSTD_fCost(U32 price)
+{
+ return (double)price / (BITCOST_MULTIPLIER*8);
+}
+#endif
+
+static void ZSTD_setBasePrices(optState_t* optPtr, int optLevel)
+{
+ optPtr->litSumBasePrice = WEIGHT(optPtr->litSum, optLevel);
+ optPtr->litLengthSumBasePrice = WEIGHT(optPtr->litLengthSum, optLevel);
+ optPtr->matchLengthSumBasePrice = WEIGHT(optPtr->matchLengthSum, optLevel);
+ optPtr->offCodeSumBasePrice = WEIGHT(optPtr->offCodeSum, optLevel);
+}
+
+
+/* ZSTD_downscaleStat() :
+ * reduce all elements in table by a factor 2^(ZSTD_FREQ_DIV+malus)
+ * return the resulting sum of elements */
+static U32 ZSTD_downscaleStat(unsigned* table, U32 lastEltIndex, int malus)
+{
+ U32 s, sum=0;
+ DEBUGLOG(5, "ZSTD_downscaleStat (nbElts=%u)", (unsigned)lastEltIndex+1);
+ assert(ZSTD_FREQ_DIV+malus > 0 && ZSTD_FREQ_DIV+malus < 31);
+ for (s=0; s<lastEltIndex+1; s++) {
+ table[s] = 1 + (table[s] >> (ZSTD_FREQ_DIV+malus));
+ sum += table[s];
+ }
+ return sum;
+}
+
+/* ZSTD_rescaleFreqs() :
+ * if first block (detected by optPtr->litLengthSum == 0) : init statistics
+ * take hints from dictionary if there is one
+ * or init from zero, using src for literals stats, or flat 1 for match symbols
+ * otherwise downscale existing stats, to be used as seed for next block.
+ */
+static void
+ZSTD_rescaleFreqs(optState_t* const optPtr,
+ const BYTE* const src, size_t const srcSize,
+ int const optLevel)
+{
+ DEBUGLOG(5, "ZSTD_rescaleFreqs (srcSize=%u)", (unsigned)srcSize);
+ optPtr->priceType = zop_dynamic;
+
+ if (optPtr->litLengthSum == 0) { /* first block : init */
+ if (srcSize <= ZSTD_PREDEF_THRESHOLD) { /* heuristic */
+ DEBUGLOG(5, "(srcSize <= ZSTD_PREDEF_THRESHOLD) => zop_predef");
+ optPtr->priceType = zop_predef;
+ }
+
+ assert(optPtr->symbolCosts != NULL);
+ if (optPtr->symbolCosts->huf.repeatMode == HUF_repeat_valid) {
+ /* huffman table presumed generated by dictionary */
+ optPtr->priceType = zop_dynamic;
+
+ assert(optPtr->litFreq != NULL);
+ optPtr->litSum = 0;
+ { unsigned lit;
+ for (lit=0; lit<=MaxLit; lit++) {
+ U32 const scaleLog = 11; /* scale to 2K */
+ U32 const bitCost = HUF_getNbBits(optPtr->symbolCosts->huf.CTable, lit);
+ assert(bitCost <= scaleLog);
+ optPtr->litFreq[lit] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/;
+ optPtr->litSum += optPtr->litFreq[lit];
+ } }
+
+ { unsigned ll;
+ FSE_CState_t llstate;
+ FSE_initCState(&llstate, optPtr->symbolCosts->fse.litlengthCTable);
+ optPtr->litLengthSum = 0;
+ for (ll=0; ll<=MaxLL; ll++) {
+ U32 const scaleLog = 10; /* scale to 1K */
+ U32 const bitCost = FSE_getMaxNbBits(llstate.symbolTT, ll);
+ assert(bitCost < scaleLog);
+ optPtr->litLengthFreq[ll] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/;
+ optPtr->litLengthSum += optPtr->litLengthFreq[ll];
+ } }
+
+ { unsigned ml;
+ FSE_CState_t mlstate;
+ FSE_initCState(&mlstate, optPtr->symbolCosts->fse.matchlengthCTable);
+ optPtr->matchLengthSum = 0;
+ for (ml=0; ml<=MaxML; ml++) {
+ U32 const scaleLog = 10;
+ U32 const bitCost = FSE_getMaxNbBits(mlstate.symbolTT, ml);
+ assert(bitCost < scaleLog);
+ optPtr->matchLengthFreq[ml] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/;
+ optPtr->matchLengthSum += optPtr->matchLengthFreq[ml];
+ } }
+
+ { unsigned of;
+ FSE_CState_t ofstate;
+ FSE_initCState(&ofstate, optPtr->symbolCosts->fse.offcodeCTable);
+ optPtr->offCodeSum = 0;
+ for (of=0; of<=MaxOff; of++) {
+ U32 const scaleLog = 10;
+ U32 const bitCost = FSE_getMaxNbBits(ofstate.symbolTT, of);
+ assert(bitCost < scaleLog);
+ optPtr->offCodeFreq[of] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/;
+ optPtr->offCodeSum += optPtr->offCodeFreq[of];
+ } }
+
+ } else { /* not a dictionary */
+
+ assert(optPtr->litFreq != NULL);
+ { unsigned lit = MaxLit;
+ HIST_count_simple(optPtr->litFreq, &lit, src, srcSize); /* use raw first block to init statistics */
+ }
+ optPtr->litSum = ZSTD_downscaleStat(optPtr->litFreq, MaxLit, 1);
+
+ { unsigned ll;
+ for (ll=0; ll<=MaxLL; ll++)
+ optPtr->litLengthFreq[ll] = 1;
+ }
+ optPtr->litLengthSum = MaxLL+1;
+
+ { unsigned ml;
+ for (ml=0; ml<=MaxML; ml++)
+ optPtr->matchLengthFreq[ml] = 1;
+ }
+ optPtr->matchLengthSum = MaxML+1;
+
+ { unsigned of;
+ for (of=0; of<=MaxOff; of++)
+ optPtr->offCodeFreq[of] = 1;
+ }
+ optPtr->offCodeSum = MaxOff+1;
+
+ }
+
+ } else { /* new block : re-use previous statistics, scaled down */
+
+ optPtr->litSum = ZSTD_downscaleStat(optPtr->litFreq, MaxLit, 1);
+ optPtr->litLengthSum = ZSTD_downscaleStat(optPtr->litLengthFreq, MaxLL, 0);
+ optPtr->matchLengthSum = ZSTD_downscaleStat(optPtr->matchLengthFreq, MaxML, 0);
+ optPtr->offCodeSum = ZSTD_downscaleStat(optPtr->offCodeFreq, MaxOff, 0);
+ }
+
+ ZSTD_setBasePrices(optPtr, optLevel);
+}
+
+/* ZSTD_rawLiteralsCost() :
+ * price of literals (only) in specified segment (which length can be 0).
+ * does not include price of literalLength symbol */
+static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength,
+ const optState_t* const optPtr,
+ int optLevel)
+{
+ if (litLength == 0) return 0;
+ if (optPtr->priceType == zop_predef)
+ return (litLength*6) * BITCOST_MULTIPLIER; /* 6 bit per literal - no statistic used */
+
+ /* dynamic statistics */
+ { U32 price = litLength * optPtr->litSumBasePrice;
+ U32 u;
+ for (u=0; u < litLength; u++) {
+ assert(WEIGHT(optPtr->litFreq[literals[u]], optLevel) <= optPtr->litSumBasePrice); /* literal cost should never be negative */
+ price -= WEIGHT(optPtr->litFreq[literals[u]], optLevel);
+ }
+ return price;
+ }
+}
+
+/* ZSTD_litLengthPrice() :
+ * cost of literalLength symbol */
+static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optPtr, int optLevel)
+{
+ if (optPtr->priceType == zop_predef) return WEIGHT(litLength, optLevel);
+
+ /* dynamic statistics */
+ { U32 const llCode = ZSTD_LLcode(litLength);
+ return (LL_bits[llCode] * BITCOST_MULTIPLIER)
+ + optPtr->litLengthSumBasePrice
+ - WEIGHT(optPtr->litLengthFreq[llCode], optLevel);
+ }
+}
+
+/* ZSTD_litLengthContribution() :
+ * @return ( cost(litlength) - cost(0) )
+ * this value can then be added to rawLiteralsCost()
+ * to provide a cost which is directly comparable to a match ending at same position */
+static int ZSTD_litLengthContribution(U32 const litLength, const optState_t* const optPtr, int optLevel)
+{
+ if (optPtr->priceType >= zop_predef) return WEIGHT(litLength, optLevel);
+
+ /* dynamic statistics */
+ { U32 const llCode = ZSTD_LLcode(litLength);
+ int const contribution = (LL_bits[llCode] * BITCOST_MULTIPLIER)
+ + WEIGHT(optPtr->litLengthFreq[0], optLevel) /* note: log2litLengthSum cancel out */
+ - WEIGHT(optPtr->litLengthFreq[llCode], optLevel);
+#if 1
+ return contribution;
+#else
+ return MAX(0, contribution); /* sometimes better, sometimes not ... */
+#endif
+ }
+}
+
+/* ZSTD_literalsContribution() :
+ * creates a fake cost for the literals part of a sequence
+ * which can be compared to the ending cost of a match
+ * should a new match start at this position */
+static int ZSTD_literalsContribution(const BYTE* const literals, U32 const litLength,
+ const optState_t* const optPtr,
+ int optLevel)
+{
+ int const contribution = ZSTD_rawLiteralsCost(literals, litLength, optPtr, optLevel)
+ + ZSTD_litLengthContribution(litLength, optPtr, optLevel);
+ return contribution;
+}
+
+/* ZSTD_getMatchPrice() :
+ * Provides the cost of the match part (offset + matchLength) of a sequence
+ * Must be combined with ZSTD_fullLiteralsCost() to get the full cost of a sequence.
+ * optLevel: when <2, favors small offset for decompression speed (improved cache efficiency) */
+FORCE_INLINE_TEMPLATE U32
+ZSTD_getMatchPrice(U32 const offset,
+ U32 const matchLength,
+ const optState_t* const optPtr,
+ int const optLevel)
+{
+ U32 price;
+ U32 const offCode = ZSTD_highbit32(offset+1);
+ U32 const mlBase = matchLength - MINMATCH;
+ assert(matchLength >= MINMATCH);
+
+ if (optPtr->priceType == zop_predef) /* fixed scheme, do not use statistics */
+ return WEIGHT(mlBase, optLevel) + ((16 + offCode) * BITCOST_MULTIPLIER);
+
+ /* dynamic statistics */
+ price = (offCode * BITCOST_MULTIPLIER) + (optPtr->offCodeSumBasePrice - WEIGHT(optPtr->offCodeFreq[offCode], optLevel));
+ if ((optLevel<2) /*static*/ && offCode >= 20)
+ price += (offCode-19)*2 * BITCOST_MULTIPLIER; /* handicap for long distance offsets, favor decompression speed */
+
+ /* match Length */
+ { U32 const mlCode = ZSTD_MLcode(mlBase);
+ price += (ML_bits[mlCode] * BITCOST_MULTIPLIER) + (optPtr->matchLengthSumBasePrice - WEIGHT(optPtr->matchLengthFreq[mlCode], optLevel));
+ }
+
+ price += BITCOST_MULTIPLIER / 5; /* heuristic : make matches a bit more costly to favor less sequences -> faster decompression speed */
+
+ DEBUGLOG(8, "ZSTD_getMatchPrice(ml:%u) = %u", matchLength, price);
+ return price;
+}
+
+/* ZSTD_updateStats() :
+ * assumption : literals + litLengtn <= iend */
+static void ZSTD_updateStats(optState_t* const optPtr,
+ U32 litLength, const BYTE* literals,
+ U32 offsetCode, U32 matchLength)
+{
+ /* literals */
+ { U32 u;
+ for (u=0; u < litLength; u++)
+ optPtr->litFreq[literals[u]] += ZSTD_LITFREQ_ADD;
+ optPtr->litSum += litLength*ZSTD_LITFREQ_ADD;
+ }
+
+ /* literal Length */
+ { U32 const llCode = ZSTD_LLcode(litLength);
+ optPtr->litLengthFreq[llCode]++;
+ optPtr->litLengthSum++;
+ }
+
+ /* match offset code (0-2=>repCode; 3+=>offset+2) */
+ { U32 const offCode = ZSTD_highbit32(offsetCode+1);
+ assert(offCode <= MaxOff);
+ optPtr->offCodeFreq[offCode]++;
+ optPtr->offCodeSum++;
+ }
+
+ /* match Length */
+ { U32 const mlBase = matchLength - MINMATCH;
+ U32 const mlCode = ZSTD_MLcode(mlBase);
+ optPtr->matchLengthFreq[mlCode]++;
+ optPtr->matchLengthSum++;
+ }
+}
+
+
+/* ZSTD_readMINMATCH() :
+ * function safe only for comparisons
+ * assumption : memPtr must be at least 4 bytes before end of buffer */
+MEM_STATIC U32 ZSTD_readMINMATCH(const void* memPtr, U32 length)
+{
+ switch (length)
+ {
+ default :
+ case 4 : return MEM_read32(memPtr);
+ case 3 : if (MEM_isLittleEndian())
+ return MEM_read32(memPtr)<<8;
+ else
+ return MEM_read32(memPtr)>>8;
+ }
+}
+
+
+/* Update hashTable3 up to ip (excluded)
+ Assumption : always within prefix (i.e. not within extDict) */
+static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_matchState_t* ms, const BYTE* const ip)
+{
+ U32* const hashTable3 = ms->hashTable3;
+ U32 const hashLog3 = ms->hashLog3;
+ const BYTE* const base = ms->window.base;
+ U32 idx = ms->nextToUpdate3;
+ U32 const target = ms->nextToUpdate3 = (U32)(ip - base);
+ size_t const hash3 = ZSTD_hash3Ptr(ip, hashLog3);
+ assert(hashLog3 > 0);
+
+ while(idx < target) {
+ hashTable3[ZSTD_hash3Ptr(base+idx, hashLog3)] = idx;
+ idx++;
+ }
+
+ return hashTable3[hash3];
+}
+
+
+/*-*************************************
+* Binary Tree search
+***************************************/
+/** ZSTD_insertBt1() : add one or multiple positions to tree.
+ * ip : assumed <= iend-8 .
+ * @return : nb of positions added */
+static U32 ZSTD_insertBt1(
+ ZSTD_matchState_t* ms,
+ const BYTE* const ip, const BYTE* const iend,
+ U32 const mls, const int extDict)
+{
+ const ZSTD_compressionParameters* const cParams = &ms->cParams;
+ U32* const hashTable = ms->hashTable;
+ U32 const hashLog = cParams->hashLog;
+ size_t const h = ZSTD_hashPtr(ip, hashLog, mls);
+ U32* const bt = ms->chainTable;
+ U32 const btLog = cParams->chainLog - 1;
+ U32 const btMask = (1 << btLog) - 1;
+ U32 matchIndex = hashTable[h];
+ size_t commonLengthSmaller=0, commonLengthLarger=0;
+ const BYTE* const base = ms->window.base;
+ const BYTE* const dictBase = ms->window.dictBase;
+ const U32 dictLimit = ms->window.dictLimit;
+ const BYTE* const dictEnd = dictBase + dictLimit;
+ const BYTE* const prefixStart = base + dictLimit;
+ const BYTE* match;
+ const U32 current = (U32)(ip-base);
+ const U32 btLow = btMask >= current ? 0 : current - btMask;
+ U32* smallerPtr = bt + 2*(current&btMask);
+ U32* largerPtr = smallerPtr + 1;
+ U32 dummy32; /* to be nullified at the end */
+ U32 const windowLow = ms->window.lowLimit;
+ U32 matchEndIdx = current+8+1;
+ size_t bestLength = 8;
+ U32 nbCompares = 1U << cParams->searchLog;
+#ifdef ZSTD_C_PREDICT
+ U32 predictedSmall = *(bt + 2*((current-1)&btMask) + 0);
+ U32 predictedLarge = *(bt + 2*((current-1)&btMask) + 1);
+ predictedSmall += (predictedSmall>0);
+ predictedLarge += (predictedLarge>0);
+#endif /* ZSTD_C_PREDICT */
+
+ DEBUGLOG(8, "ZSTD_insertBt1 (%u)", current);
+
+ assert(ip <= iend-8); /* required for h calculation */
+ hashTable[h] = current; /* Update Hash Table */
+
+ assert(windowLow > 0);
+ while (nbCompares-- && (matchIndex >= windowLow)) {
+ U32* const nextPtr = bt + 2*(matchIndex & btMask);
+ size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */
+ assert(matchIndex < current);
+
+#ifdef ZSTD_C_PREDICT /* note : can create issues when hlog small <= 11 */
+ const U32* predictPtr = bt + 2*((matchIndex-1) & btMask); /* written this way, as bt is a roll buffer */
+ if (matchIndex == predictedSmall) {
+ /* no need to check length, result known */
+ *smallerPtr = matchIndex;
+ if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop the search */
+ smallerPtr = nextPtr+1; /* new "smaller" => larger of match */
+ matchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */
+ predictedSmall = predictPtr[1] + (predictPtr[1]>0);
+ continue;
+ }
+ if (matchIndex == predictedLarge) {
+ *largerPtr = matchIndex;
+ if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop the search */
+ largerPtr = nextPtr;
+ matchIndex = nextPtr[0];
+ predictedLarge = predictPtr[0] + (predictPtr[0]>0);
+ continue;
+ }
+#endif
+
+ if (!extDict || (matchIndex+matchLength >= dictLimit)) {
+ assert(matchIndex+matchLength >= dictLimit); /* might be wrong if actually extDict */
+ match = base + matchIndex;
+ matchLength += ZSTD_count(ip+matchLength, match+matchLength, iend);
+ } else {
+ match = dictBase + matchIndex;
+ matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iend, dictEnd, prefixStart);
+ if (matchIndex+matchLength >= dictLimit)
+ match = base + matchIndex; /* to prepare for next usage of match[matchLength] */
+ }
+
+ if (matchLength > bestLength) {
+ bestLength = matchLength;
+ if (matchLength > matchEndIdx - matchIndex)
+ matchEndIdx = matchIndex + (U32)matchLength;
+ }
+
+ if (ip+matchLength == iend) { /* equal : no way to know if inf or sup */
+ break; /* drop , to guarantee consistency ; miss a bit of compression, but other solutions can corrupt tree */
+ }
+
+ if (match[matchLength] < ip[matchLength]) { /* necessarily within buffer */
+ /* match is smaller than current */
+ *smallerPtr = matchIndex; /* update smaller idx */
+ commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */
+ if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop searching */
+ smallerPtr = nextPtr+1; /* new "candidate" => larger than match, which was smaller than target */
+ matchIndex = nextPtr[1]; /* new matchIndex, larger than previous and closer to current */
+ } else {
+ /* match is larger than current */
+ *largerPtr = matchIndex;
+ commonLengthLarger = matchLength;
+ if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop searching */
+ largerPtr = nextPtr;
+ matchIndex = nextPtr[0];
+ } }
+
+ *smallerPtr = *largerPtr = 0;
+ if (bestLength > 384) return MIN(192, (U32)(bestLength - 384)); /* speed optimization */
+ assert(matchEndIdx > current + 8);
+ return matchEndIdx - (current + 8);
+}
+
+FORCE_INLINE_TEMPLATE
+void ZSTD_updateTree_internal(
+ ZSTD_matchState_t* ms,
+ const BYTE* const ip, const BYTE* const iend,
+ const U32 mls, const ZSTD_dictMode_e dictMode)
+{
+ const BYTE* const base = ms->window.base;
+ U32 const target = (U32)(ip - base);
+ U32 idx = ms->nextToUpdate;
+ DEBUGLOG(6, "ZSTD_updateTree_internal, from %u to %u (dictMode:%u)",
+ idx, target, dictMode);
+
+ while(idx < target)
+ idx += ZSTD_insertBt1(ms, base+idx, iend, mls, dictMode == ZSTD_extDict);
+ ms->nextToUpdate = target;
+}
+
+void ZSTD_updateTree(ZSTD_matchState_t* ms, const BYTE* ip, const BYTE* iend) {
+ ZSTD_updateTree_internal(ms, ip, iend, ms->cParams.minMatch, ZSTD_noDict);
+}
+
+FORCE_INLINE_TEMPLATE
+U32 ZSTD_insertBtAndGetAllMatches (
+ ZSTD_matchState_t* ms,
+ const BYTE* const ip, const BYTE* const iLimit, const ZSTD_dictMode_e dictMode,
+ U32 rep[ZSTD_REP_NUM],
+ U32 const ll0, /* tells if associated literal length is 0 or not. This value must be 0 or 1 */
+ ZSTD_match_t* matches,
+ const U32 lengthToBeat,
+ U32 const mls /* template */)
+{
+ const ZSTD_compressionParameters* const cParams = &ms->cParams;
+ U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1);
+ const BYTE* const base = ms->window.base;
+ U32 const current = (U32)(ip-base);
+ U32 const hashLog = cParams->hashLog;
+ U32 const minMatch = (mls==3) ? 3 : 4;
+ U32* const hashTable = ms->hashTable;
+ size_t const h = ZSTD_hashPtr(ip, hashLog, mls);
+ U32 matchIndex = hashTable[h];
+ U32* const bt = ms->chainTable;
+ U32 const btLog = cParams->chainLog - 1;
+ U32 const btMask= (1U << btLog) - 1;
+ size_t commonLengthSmaller=0, commonLengthLarger=0;
+ const BYTE* const dictBase = ms->window.dictBase;
+ U32 const dictLimit = ms->window.dictLimit;
+ const BYTE* const dictEnd = dictBase + dictLimit;
+ const BYTE* const prefixStart = base + dictLimit;
+ U32 const btLow = btMask >= current ? 0 : current - btMask;
+ U32 const windowLow = ms->window.lowLimit;
+ U32 const matchLow = windowLow ? windowLow : 1;
+ U32* smallerPtr = bt + 2*(current&btMask);
+ U32* largerPtr = bt + 2*(current&btMask) + 1;
+ U32 matchEndIdx = current+8+1; /* farthest referenced position of any match => detects repetitive patterns */
+ U32 dummy32; /* to be nullified at the end */
+ U32 mnum = 0;
+ U32 nbCompares = 1U << cParams->searchLog;
+
+ const ZSTD_matchState_t* dms = dictMode == ZSTD_dictMatchState ? ms->dictMatchState : NULL;
+ const ZSTD_compressionParameters* const dmsCParams =
+ dictMode == ZSTD_dictMatchState ? &dms->cParams : NULL;
+ const BYTE* const dmsBase = dictMode == ZSTD_dictMatchState ? dms->window.base : NULL;
+ const BYTE* const dmsEnd = dictMode == ZSTD_dictMatchState ? dms->window.nextSrc : NULL;
+ U32 const dmsHighLimit = dictMode == ZSTD_dictMatchState ? (U32)(dmsEnd - dmsBase) : 0;
+ U32 const dmsLowLimit = dictMode == ZSTD_dictMatchState ? dms->window.lowLimit : 0;
+ U32 const dmsIndexDelta = dictMode == ZSTD_dictMatchState ? windowLow - dmsHighLimit : 0;
+ U32 const dmsHashLog = dictMode == ZSTD_dictMatchState ? dmsCParams->hashLog : hashLog;
+ U32 const dmsBtLog = dictMode == ZSTD_dictMatchState ? dmsCParams->chainLog - 1 : btLog;
+ U32 const dmsBtMask = dictMode == ZSTD_dictMatchState ? (1U << dmsBtLog) - 1 : 0;
+ U32 const dmsBtLow = dictMode == ZSTD_dictMatchState && dmsBtMask < dmsHighLimit - dmsLowLimit ? dmsHighLimit - dmsBtMask : dmsLowLimit;
+
+ size_t bestLength = lengthToBeat-1;
+ DEBUGLOG(8, "ZSTD_insertBtAndGetAllMatches: current=%u", current);
+
+ /* check repCode */
+ assert(ll0 <= 1); /* necessarily 1 or 0 */
+ { U32 const lastR = ZSTD_REP_NUM + ll0;
+ U32 repCode;
+ for (repCode = ll0; repCode < lastR; repCode++) {
+ U32 const repOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode];
+ U32 const repIndex = current - repOffset;
+ U32 repLen = 0;
+ assert(current >= dictLimit);
+ if (repOffset-1 /* intentional overflow, discards 0 and -1 */ < current-dictLimit) { /* equivalent to `current > repIndex >= dictLimit` */
+ if (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(ip - repOffset, minMatch)) {
+ repLen = (U32)ZSTD_count(ip+minMatch, ip+minMatch-repOffset, iLimit) + minMatch;
+ }
+ } else { /* repIndex < dictLimit || repIndex >= current */
+ const BYTE* const repMatch = dictMode == ZSTD_dictMatchState ?
+ dmsBase + repIndex - dmsIndexDelta :
+ dictBase + repIndex;
+ assert(current >= windowLow);
+ if ( dictMode == ZSTD_extDict
+ && ( ((repOffset-1) /*intentional overflow*/ < current - windowLow) /* equivalent to `current > repIndex >= windowLow` */
+ & (((U32)((dictLimit-1) - repIndex) >= 3) ) /* intentional overflow : do not test positions overlapping 2 memory segments */)
+ && (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch)) ) {
+ repLen = (U32)ZSTD_count_2segments(ip+minMatch, repMatch+minMatch, iLimit, dictEnd, prefixStart) + minMatch;
+ }
+ if (dictMode == ZSTD_dictMatchState
+ && ( ((repOffset-1) /*intentional overflow*/ < current - (dmsLowLimit + dmsIndexDelta)) /* equivalent to `current > repIndex >= dmsLowLimit` */
+ & ((U32)((dictLimit-1) - repIndex) >= 3) ) /* intentional overflow : do not test positions overlapping 2 memory segments */
+ && (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch)) ) {
+ repLen = (U32)ZSTD_count_2segments(ip+minMatch, repMatch+minMatch, iLimit, dmsEnd, prefixStart) + minMatch;
+ } }
+ /* save longer solution */
+ if (repLen > bestLength) {
+ DEBUGLOG(8, "found repCode %u (ll0:%u, offset:%u) of length %u",
+ repCode, ll0, repOffset, repLen);
+ bestLength = repLen;
+ matches[mnum].off = repCode - ll0;
+ matches[mnum].len = (U32)repLen;
+ mnum++;
+ if ( (repLen > sufficient_len)
+ | (ip+repLen == iLimit) ) { /* best possible */
+ return mnum;
+ } } } }
+
+ /* HC3 match finder */
+ if ((mls == 3) /*static*/ && (bestLength < mls)) {
+ U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(ms, ip);
+ if ((matchIndex3 >= matchLow)
+ & (current - matchIndex3 < (1<<18)) /*heuristic : longer distance likely too expensive*/ ) {
+ size_t mlen;
+ if ((dictMode == ZSTD_noDict) /*static*/ || (dictMode == ZSTD_dictMatchState) /*static*/ || (matchIndex3 >= dictLimit)) {
+ const BYTE* const match = base + matchIndex3;
+ mlen = ZSTD_count(ip, match, iLimit);
+ } else {
+ const BYTE* const match = dictBase + matchIndex3;
+ mlen = ZSTD_count_2segments(ip, match, iLimit, dictEnd, prefixStart);
+ }
+
+ /* save best solution */
+ if (mlen >= mls /* == 3 > bestLength */) {
+ DEBUGLOG(8, "found small match with hlog3, of length %u",
+ (U32)mlen);
+ bestLength = mlen;
+ assert(current > matchIndex3);
+ assert(mnum==0); /* no prior solution */
+ matches[0].off = (current - matchIndex3) + ZSTD_REP_MOVE;
+ matches[0].len = (U32)mlen;
+ mnum = 1;
+ if ( (mlen > sufficient_len) |
+ (ip+mlen == iLimit) ) { /* best possible length */
+ ms->nextToUpdate = current+1; /* skip insertion */
+ return 1;
+ }
+ }
+ }
+ /* no dictMatchState lookup: dicts don't have a populated HC3 table */
+ }
+
+ hashTable[h] = current; /* Update Hash Table */
+
+ while (nbCompares-- && (matchIndex >= matchLow)) {
+ U32* const nextPtr = bt + 2*(matchIndex & btMask);
+ size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */
+ const BYTE* match;
+ assert(current > matchIndex);
+
+ if ((dictMode == ZSTD_noDict) || (dictMode == ZSTD_dictMatchState) || (matchIndex+matchLength >= dictLimit)) {
+ assert(matchIndex+matchLength >= dictLimit); /* ensure the condition is correct when !extDict */
+ match = base + matchIndex;
+ matchLength += ZSTD_count(ip+matchLength, match+matchLength, iLimit);
+ } else {
+ match = dictBase + matchIndex;
+ matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iLimit, dictEnd, prefixStart);
+ if (matchIndex+matchLength >= dictLimit)
+ match = base + matchIndex; /* prepare for match[matchLength] */
+ }
+
+ if (matchLength > bestLength) {
+ DEBUGLOG(8, "found match of length %u at distance %u (offCode=%u)",
+ (U32)matchLength, current - matchIndex, current - matchIndex + ZSTD_REP_MOVE);
+ assert(matchEndIdx > matchIndex);
+ if (matchLength > matchEndIdx - matchIndex)
+ matchEndIdx = matchIndex + (U32)matchLength;
+ bestLength = matchLength;
+ matches[mnum].off = (current - matchIndex) + ZSTD_REP_MOVE;
+ matches[mnum].len = (U32)matchLength;
+ mnum++;
+ if ( (matchLength > ZSTD_OPT_NUM)
+ | (ip+matchLength == iLimit) /* equal : no way to know if inf or sup */) {
+ if (dictMode == ZSTD_dictMatchState) nbCompares = 0; /* break should also skip searching dms */
+ break; /* drop, to preserve bt consistency (miss a little bit of compression) */
+ }
+ }
+
+ if (match[matchLength] < ip[matchLength]) {
+ /* match smaller than current */
+ *smallerPtr = matchIndex; /* update smaller idx */
+ commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */
+ if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop the search */
+ smallerPtr = nextPtr+1; /* new candidate => larger than match, which was smaller than current */
+ matchIndex = nextPtr[1]; /* new matchIndex, larger than previous, closer to current */
+ } else {
+ *largerPtr = matchIndex;
+ commonLengthLarger = matchLength;
+ if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop the search */
+ largerPtr = nextPtr;
+ matchIndex = nextPtr[0];
+ } }
+
+ *smallerPtr = *largerPtr = 0;
+
+ if (dictMode == ZSTD_dictMatchState && nbCompares) {
+ size_t const dmsH = ZSTD_hashPtr(ip, dmsHashLog, mls);
+ U32 dictMatchIndex = dms->hashTable[dmsH];
+ const U32* const dmsBt = dms->chainTable;
+ commonLengthSmaller = commonLengthLarger = 0;
+ while (nbCompares-- && (dictMatchIndex > dmsLowLimit)) {
+ const U32* const nextPtr = dmsBt + 2*(dictMatchIndex & dmsBtMask);
+ size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */
+ const BYTE* match = dmsBase + dictMatchIndex;
+ matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iLimit, dmsEnd, prefixStart);
+ if (dictMatchIndex+matchLength >= dmsHighLimit)
+ match = base + dictMatchIndex + dmsIndexDelta; /* to prepare for next usage of match[matchLength] */
+
+ if (matchLength > bestLength) {
+ matchIndex = dictMatchIndex + dmsIndexDelta;
+ DEBUGLOG(8, "found dms match of length %u at distance %u (offCode=%u)",
+ (U32)matchLength, current - matchIndex, current - matchIndex + ZSTD_REP_MOVE);
+ if (matchLength > matchEndIdx - matchIndex)
+ matchEndIdx = matchIndex + (U32)matchLength;
+ bestLength = matchLength;
+ matches[mnum].off = (current - matchIndex) + ZSTD_REP_MOVE;
+ matches[mnum].len = (U32)matchLength;
+ mnum++;
+ if ( (matchLength > ZSTD_OPT_NUM)
+ | (ip+matchLength == iLimit) /* equal : no way to know if inf or sup */) {
+ break; /* drop, to guarantee consistency (miss a little bit of compression) */
+ }
+ }
+
+ if (dictMatchIndex <= dmsBtLow) { break; } /* beyond tree size, stop the search */
+ if (match[matchLength] < ip[matchLength]) {
+ commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */
+ dictMatchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */
+ } else {
+ /* match is larger than current */
+ commonLengthLarger = matchLength;
+ dictMatchIndex = nextPtr[0];
+ }
+ }
+ }
+
+ assert(matchEndIdx > current+8);
+ ms->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */
+ return mnum;
+}
+
+
+FORCE_INLINE_TEMPLATE U32 ZSTD_BtGetAllMatches (
+ ZSTD_matchState_t* ms,
+ const BYTE* ip, const BYTE* const iHighLimit, const ZSTD_dictMode_e dictMode,
+ U32 rep[ZSTD_REP_NUM], U32 const ll0,
+ ZSTD_match_t* matches, U32 const lengthToBeat)
+{
+ const ZSTD_compressionParameters* const cParams = &ms->cParams;
+ U32 const matchLengthSearch = cParams->minMatch;
+ DEBUGLOG(8, "ZSTD_BtGetAllMatches");
+ if (ip < ms->window.base + ms->nextToUpdate) return 0; /* skipped area */
+ ZSTD_updateTree_internal(ms, ip, iHighLimit, matchLengthSearch, dictMode);
+ switch(matchLengthSearch)
+ {
+ case 3 : return ZSTD_insertBtAndGetAllMatches(ms, ip, iHighLimit, dictMode, rep, ll0, matches, lengthToBeat, 3);
+ default :
+ case 4 : return ZSTD_insertBtAndGetAllMatches(ms, ip, iHighLimit, dictMode, rep, ll0, matches, lengthToBeat, 4);
+ case 5 : return ZSTD_insertBtAndGetAllMatches(ms, ip, iHighLimit, dictMode, rep, ll0, matches, lengthToBeat, 5);
+ case 7 :
+ case 6 : return ZSTD_insertBtAndGetAllMatches(ms, ip, iHighLimit, dictMode, rep, ll0, matches, lengthToBeat, 6);
+ }
+}
+
+
+/*-*******************************
+* Optimal parser
+*********************************/
+typedef struct repcodes_s {
+ U32 rep[3];
+} repcodes_t;
+
+static repcodes_t ZSTD_updateRep(U32 const rep[3], U32 const offset, U32 const ll0)
+{
+ repcodes_t newReps;
+ if (offset >= ZSTD_REP_NUM) { /* full offset */
+ newReps.rep[2] = rep[1];
+ newReps.rep[1] = rep[0];
+ newReps.rep[0] = offset - ZSTD_REP_MOVE;
+ } else { /* repcode */
+ U32 const repCode = offset + ll0;
+ if (repCode > 0) { /* note : if repCode==0, no change */
+ U32 const currentOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode];
+ newReps.rep[2] = (repCode >= 2) ? rep[1] : rep[2];
+ newReps.rep[1] = rep[0];
+ newReps.rep[0] = currentOffset;
+ } else { /* repCode == 0 */
+ memcpy(&newReps, rep, sizeof(newReps));
+ }
+ }
+ return newReps;
+}
+
+
+static U32 ZSTD_totalLen(ZSTD_optimal_t sol)
+{
+ return sol.litlen + sol.mlen;
+}
+
+#if 0 /* debug */
+
+static void
+listStats(const U32* table, int lastEltID)
+{
+ int const nbElts = lastEltID + 1;
+ int enb;
+ for (enb=0; enb < nbElts; enb++) {
+ (void)table;
+ //RAWLOG(2, "%3i:%3i, ", enb, table[enb]);
+ RAWLOG(2, "%4i,", table[enb]);
+ }
+ RAWLOG(2, " \n");
+}
+
+#endif
+
+FORCE_INLINE_TEMPLATE size_t
+ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
+ seqStore_t* seqStore,
+ U32 rep[ZSTD_REP_NUM],
+ const void* src, size_t srcSize,
+ const int optLevel,
+ const ZSTD_dictMode_e dictMode)
+{
+ optState_t* const optStatePtr = &ms->opt;
+ const BYTE* const istart = (const BYTE*)src;
+ const BYTE* ip = istart;
+ const BYTE* anchor = istart;
+ const BYTE* const iend = istart + srcSize;
+ const BYTE* const ilimit = iend - 8;
+ const BYTE* const base = ms->window.base;
+ const BYTE* const prefixStart = base + ms->window.dictLimit;
+ const ZSTD_compressionParameters* const cParams = &ms->cParams;
+
+ U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1);
+ U32 const minMatch = (cParams->minMatch == 3) ? 3 : 4;
+
+ ZSTD_optimal_t* const opt = optStatePtr->priceTable;
+ ZSTD_match_t* const matches = optStatePtr->matchTable;
+ ZSTD_optimal_t lastSequence;
+
+ /* init */
+ DEBUGLOG(5, "ZSTD_compressBlock_opt_generic: current=%u, prefix=%u, nextToUpdate=%u",
+ (U32)(ip - base), ms->window.dictLimit, ms->nextToUpdate);
+ assert(optLevel <= 2);
+ ms->nextToUpdate3 = ms->nextToUpdate;
+ ZSTD_rescaleFreqs(optStatePtr, (const BYTE*)src, srcSize, optLevel);
+ ip += (ip==prefixStart);
+
+ /* Match Loop */
+ while (ip < ilimit) {
+ U32 cur, last_pos = 0;
+
+ /* find first match */
+ { U32 const litlen = (U32)(ip - anchor);
+ U32 const ll0 = !litlen;
+ U32 const nbMatches = ZSTD_BtGetAllMatches(ms, ip, iend, dictMode, rep, ll0, matches, minMatch);
+ if (!nbMatches) { ip++; continue; }
+
+ /* initialize opt[0] */
+ { U32 i ; for (i=0; i<ZSTD_REP_NUM; i++) opt[0].rep[i] = rep[i]; }
+ opt[0].mlen = 0; /* means is_a_literal */
+ opt[0].litlen = litlen;
+ opt[0].price = ZSTD_literalsContribution(anchor, litlen, optStatePtr, optLevel);
+
+ /* large match -> immediate encoding */
+ { U32 const maxML = matches[nbMatches-1].len;
+ U32 const maxOffset = matches[nbMatches-1].off;
+ DEBUGLOG(6, "found %u matches of maxLength=%u and maxOffCode=%u at cPos=%u => start new serie",
+ nbMatches, maxML, maxOffset, (U32)(ip-prefixStart));
+
+ if (maxML > sufficient_len) {
+ lastSequence.litlen = litlen;
+ lastSequence.mlen = maxML;
+ lastSequence.off = maxOffset;
+ DEBUGLOG(6, "large match (%u>%u), immediate encoding",
+ maxML, sufficient_len);
+ cur = 0;
+ last_pos = ZSTD_totalLen(lastSequence);
+ goto _shortestPath;
+ } }
+
+ /* set prices for first matches starting position == 0 */
+ { U32 const literalsPrice = opt[0].price + ZSTD_litLengthPrice(0, optStatePtr, optLevel);
+ U32 pos;
+ U32 matchNb;
+ for (pos = 1; pos < minMatch; pos++) {
+ opt[pos].price = ZSTD_MAX_PRICE; /* mlen, litlen and price will be fixed during forward scanning */
+ }
+ for (matchNb = 0; matchNb < nbMatches; matchNb++) {
+ U32 const offset = matches[matchNb].off;
+ U32 const end = matches[matchNb].len;
+ repcodes_t const repHistory = ZSTD_updateRep(rep, offset, ll0);
+ for ( ; pos <= end ; pos++ ) {
+ U32 const matchPrice = ZSTD_getMatchPrice(offset, pos, optStatePtr, optLevel);
+ U32 const sequencePrice = literalsPrice + matchPrice;
+ DEBUGLOG(7, "rPos:%u => set initial price : %.2f",
+ pos, ZSTD_fCost(sequencePrice));
+ opt[pos].mlen = pos;
+ opt[pos].off = offset;
+ opt[pos].litlen = litlen;
+ opt[pos].price = sequencePrice;
+ ZSTD_STATIC_ASSERT(sizeof(opt[pos].rep) == sizeof(repHistory));
+ memcpy(opt[pos].rep, &repHistory, sizeof(repHistory));
+ } }
+ last_pos = pos-1;
+ }
+ }
+
+ /* check further positions */
+ for (cur = 1; cur <= last_pos; cur++) {
+ const BYTE* const inr = ip + cur;
+ assert(cur < ZSTD_OPT_NUM);
+ DEBUGLOG(7, "cPos:%zi==rPos:%u", inr-istart, cur)
+
+ /* Fix current position with one literal if cheaper */
+ { U32 const litlen = (opt[cur-1].mlen == 0) ? opt[cur-1].litlen + 1 : 1;
+ int const price = opt[cur-1].price
+ + ZSTD_rawLiteralsCost(ip+cur-1, 1, optStatePtr, optLevel)
+ + ZSTD_litLengthPrice(litlen, optStatePtr, optLevel)
+ - ZSTD_litLengthPrice(litlen-1, optStatePtr, optLevel);
+ assert(price < 1000000000); /* overflow check */
+ if (price <= opt[cur].price) {
+ DEBUGLOG(7, "cPos:%zi==rPos:%u : better price (%.2f<=%.2f) using literal (ll==%u) (hist:%u,%u,%u)",
+ inr-istart, cur, ZSTD_fCost(price), ZSTD_fCost(opt[cur].price), litlen,
+ opt[cur-1].rep[0], opt[cur-1].rep[1], opt[cur-1].rep[2]);
+ opt[cur].mlen = 0;
+ opt[cur].off = 0;
+ opt[cur].litlen = litlen;
+ opt[cur].price = price;
+ memcpy(opt[cur].rep, opt[cur-1].rep, sizeof(opt[cur].rep));
+ } else {
+ DEBUGLOG(7, "cPos:%zi==rPos:%u : literal would cost more (%.2f>%.2f) (hist:%u,%u,%u)",
+ inr-istart, cur, ZSTD_fCost(price), ZSTD_fCost(opt[cur].price),
+ opt[cur].rep[0], opt[cur].rep[1], opt[cur].rep[2]);
+ }
+ }
+
+ /* last match must start at a minimum distance of 8 from oend */
+ if (inr > ilimit) continue;
+
+ if (cur == last_pos) break;
+
+ if ( (optLevel==0) /*static_test*/
+ && (opt[cur+1].price <= opt[cur].price + (BITCOST_MULTIPLIER/2)) ) {
+ DEBUGLOG(7, "move to next rPos:%u : price is <=", cur+1);
+ continue; /* skip unpromising positions; about ~+6% speed, -0.01 ratio */
+ }
+
+ { U32 const ll0 = (opt[cur].mlen != 0);
+ U32 const litlen = (opt[cur].mlen == 0) ? opt[cur].litlen : 0;
+ U32 const previousPrice = opt[cur].price;
+ U32 const basePrice = previousPrice + ZSTD_litLengthPrice(0, optStatePtr, optLevel);
+ U32 const nbMatches = ZSTD_BtGetAllMatches(ms, inr, iend, dictMode, opt[cur].rep, ll0, matches, minMatch);
+ U32 matchNb;
+ if (!nbMatches) {
+ DEBUGLOG(7, "rPos:%u : no match found", cur);
+ continue;
+ }
+
+ { U32 const maxML = matches[nbMatches-1].len;
+ DEBUGLOG(7, "cPos:%zi==rPos:%u, found %u matches, of maxLength=%u",
+ inr-istart, cur, nbMatches, maxML);
+
+ if ( (maxML > sufficient_len)
+ || (cur + maxML >= ZSTD_OPT_NUM) ) {
+ lastSequence.mlen = maxML;
+ lastSequence.off = matches[nbMatches-1].off;
+ lastSequence.litlen = litlen;
+ cur -= (opt[cur].mlen==0) ? opt[cur].litlen : 0; /* last sequence is actually only literals, fix cur to last match - note : may underflow, in which case, it's first sequence, and it's okay */
+ last_pos = cur + ZSTD_totalLen(lastSequence);
+ if (cur > ZSTD_OPT_NUM) cur = 0; /* underflow => first match */
+ goto _shortestPath;
+ } }
+
+ /* set prices using matches found at position == cur */
+ for (matchNb = 0; matchNb < nbMatches; matchNb++) {
+ U32 const offset = matches[matchNb].off;
+ repcodes_t const repHistory = ZSTD_updateRep(opt[cur].rep, offset, ll0);
+ U32 const lastML = matches[matchNb].len;
+ U32 const startML = (matchNb>0) ? matches[matchNb-1].len+1 : minMatch;
+ U32 mlen;
+
+ DEBUGLOG(7, "testing match %u => offCode=%4u, mlen=%2u, llen=%2u",
+ matchNb, matches[matchNb].off, lastML, litlen);
+
+ for (mlen = lastML; mlen >= startML; mlen--) { /* scan downward */
+ U32 const pos = cur + mlen;
+ int const price = basePrice + ZSTD_getMatchPrice(offset, mlen, optStatePtr, optLevel);
+
+ if ((pos > last_pos) || (price < opt[pos].price)) {
+ DEBUGLOG(7, "rPos:%u (ml=%2u) => new better price (%.2f<%.2f)",
+ pos, mlen, ZSTD_fCost(price), ZSTD_fCost(opt[pos].price));
+ while (last_pos < pos) { opt[last_pos+1].price = ZSTD_MAX_PRICE; last_pos++; } /* fill empty positions */
+ opt[pos].mlen = mlen;
+ opt[pos].off = offset;
+ opt[pos].litlen = litlen;
+ opt[pos].price = price;
+ ZSTD_STATIC_ASSERT(sizeof(opt[pos].rep) == sizeof(repHistory));
+ memcpy(opt[pos].rep, &repHistory, sizeof(repHistory));
+ } else {
+ DEBUGLOG(7, "rPos:%u (ml=%2u) => new price is worse (%.2f>=%.2f)",
+ pos, mlen, ZSTD_fCost(price), ZSTD_fCost(opt[pos].price));
+ if (optLevel==0) break; /* early update abort; gets ~+10% speed for about -0.01 ratio loss */
+ }
+ } } }
+ } /* for (cur = 1; cur <= last_pos; cur++) */
+
+ lastSequence = opt[last_pos];
+ cur = last_pos > ZSTD_totalLen(lastSequence) ? last_pos - ZSTD_totalLen(lastSequence) : 0; /* single sequence, and it starts before `ip` */
+ assert(cur < ZSTD_OPT_NUM); /* control overflow*/
+
+_shortestPath: /* cur, last_pos, best_mlen, best_off have to be set */
+ assert(opt[0].mlen == 0);
+
+ { U32 const storeEnd = cur + 1;
+ U32 storeStart = storeEnd;
+ U32 seqPos = cur;
+
+ DEBUGLOG(6, "start reverse traversal (last_pos:%u, cur:%u)",
+ last_pos, cur); (void)last_pos;
+ assert(storeEnd < ZSTD_OPT_NUM);
+ DEBUGLOG(6, "last sequence copied into pos=%u (llen=%u,mlen=%u,ofc=%u)",
+ storeEnd, lastSequence.litlen, lastSequence.mlen, lastSequence.off);
+ opt[storeEnd] = lastSequence;
+ while (seqPos > 0) {
+ U32 const backDist = ZSTD_totalLen(opt[seqPos]);
+ storeStart--;
+ DEBUGLOG(6, "sequence from rPos=%u copied into pos=%u (llen=%u,mlen=%u,ofc=%u)",
+ seqPos, storeStart, opt[seqPos].litlen, opt[seqPos].mlen, opt[seqPos].off);
+ opt[storeStart] = opt[seqPos];
+ seqPos = (seqPos > backDist) ? seqPos - backDist : 0;
+ }
+
+ /* save sequences */
+ DEBUGLOG(6, "sending selected sequences into seqStore")
+ { U32 storePos;
+ for (storePos=storeStart; storePos <= storeEnd; storePos++) {
+ U32 const llen = opt[storePos].litlen;
+ U32 const mlen = opt[storePos].mlen;
+ U32 const offCode = opt[storePos].off;
+ U32 const advance = llen + mlen;
+ DEBUGLOG(6, "considering seq starting at %zi, llen=%u, mlen=%u",
+ anchor - istart, (unsigned)llen, (unsigned)mlen);
+
+ if (mlen==0) { /* only literals => must be last "sequence", actually starting a new stream of sequences */
+ assert(storePos == storeEnd); /* must be last sequence */
+ ip = anchor + llen; /* last "sequence" is a bunch of literals => don't progress anchor */
+ continue; /* will finish */
+ }
+
+ /* repcodes update : like ZSTD_updateRep(), but update in place */
+ if (offCode >= ZSTD_REP_NUM) { /* full offset */
+ rep[2] = rep[1];
+ rep[1] = rep[0];
+ rep[0] = offCode - ZSTD_REP_MOVE;
+ } else { /* repcode */
+ U32 const repCode = offCode + (llen==0);
+ if (repCode) { /* note : if repCode==0, no change */
+ U32 const currentOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode];
+ if (repCode >= 2) rep[2] = rep[1];
+ rep[1] = rep[0];
+ rep[0] = currentOffset;
+ } }
+
+ assert(anchor + llen <= iend);
+ ZSTD_updateStats(optStatePtr, llen, anchor, offCode, mlen);
+ ZSTD_storeSeq(seqStore, llen, anchor, offCode, mlen-MINMATCH);
+ anchor += advance;
+ ip = anchor;
+ } }
+ ZSTD_setBasePrices(optStatePtr, optLevel);
+ }
+
+ } /* while (ip < ilimit) */
+
+ /* Return the last literals size */
+ return iend - anchor;
+}
+
+
+size_t ZSTD_compressBlock_btopt(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ const void* src, size_t srcSize)
+{
+ DEBUGLOG(5, "ZSTD_compressBlock_btopt");
+ return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 0 /*optLevel*/, ZSTD_noDict);
+}
+
+
+/* used in 2-pass strategy */
+static U32 ZSTD_upscaleStat(unsigned* table, U32 lastEltIndex, int bonus)
+{
+ U32 s, sum=0;
+ assert(ZSTD_FREQ_DIV+bonus >= 0);
+ for (s=0; s<lastEltIndex+1; s++) {
+ table[s] <<= ZSTD_FREQ_DIV+bonus;
+ table[s]--;
+ sum += table[s];
+ }
+ return sum;
+}
+
+/* used in 2-pass strategy */
+MEM_STATIC void ZSTD_upscaleStats(optState_t* optPtr)
+{
+ optPtr->litSum = ZSTD_upscaleStat(optPtr->litFreq, MaxLit, 0);
+ optPtr->litLengthSum = ZSTD_upscaleStat(optPtr->litLengthFreq, MaxLL, 0);
+ optPtr->matchLengthSum = ZSTD_upscaleStat(optPtr->matchLengthFreq, MaxML, 0);
+ optPtr->offCodeSum = ZSTD_upscaleStat(optPtr->offCodeFreq, MaxOff, 0);
+}
+
+/* ZSTD_initStats_ultra():
+ * make a first compression pass, just to seed stats with more accurate starting values.
+ * only works on first block, with no dictionary and no ldm.
+ * this function cannot error, hence its constract must be respected.
+ */
+static void
+ZSTD_initStats_ultra(ZSTD_matchState_t* ms,
+ seqStore_t* seqStore,
+ U32 rep[ZSTD_REP_NUM],
+ const void* src, size_t srcSize)
+{
+ U32 tmpRep[ZSTD_REP_NUM]; /* updated rep codes will sink here */
+ memcpy(tmpRep, rep, sizeof(tmpRep));
+
+ DEBUGLOG(4, "ZSTD_initStats_ultra (srcSize=%zu)", srcSize);
+ assert(ms->opt.litLengthSum == 0); /* first block */
+ assert(seqStore->sequences == seqStore->sequencesStart); /* no ldm */
+ assert(ms->window.dictLimit == ms->window.lowLimit); /* no dictionary */
+ assert(ms->window.dictLimit - ms->nextToUpdate <= 1); /* no prefix (note: intentional overflow, defined as 2-complement) */
+
+ ZSTD_compressBlock_opt_generic(ms, seqStore, tmpRep, src, srcSize, 2 /*optLevel*/, ZSTD_noDict); /* generate stats into ms->opt*/
+
+ /* invalidate first scan from history */
+ ZSTD_resetSeqStore(seqStore);
+ ms->window.base -= srcSize;
+ ms->window.dictLimit += (U32)srcSize;
+ ms->window.lowLimit = ms->window.dictLimit;
+ ms->nextToUpdate = ms->window.dictLimit;
+ ms->nextToUpdate3 = ms->window.dictLimit;
+
+ /* re-inforce weight of collected statistics */
+ ZSTD_upscaleStats(&ms->opt);
+}
+
+size_t ZSTD_compressBlock_btultra(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ const void* src, size_t srcSize)
+{
+ DEBUGLOG(5, "ZSTD_compressBlock_btultra (srcSize=%zu)", srcSize);
+ return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 2 /*optLevel*/, ZSTD_noDict);
+}
+
+size_t ZSTD_compressBlock_btultra2(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ const void* src, size_t srcSize)
+{
+ U32 const current = (U32)((const BYTE*)src - ms->window.base);
+ DEBUGLOG(5, "ZSTD_compressBlock_btultra2 (srcSize=%zu)", srcSize);
+
+ /* 2-pass strategy:
+ * this strategy makes a first pass over first block to collect statistics
+ * and seed next round's statistics with it.
+ * After 1st pass, function forgets everything, and starts a new block.
+ * Consequently, this can only work if no data has been previously loaded in tables,
+ * aka, no dictionary, no prefix, no ldm preprocessing.
+ * The compression ratio gain is generally small (~0.5% on first block),
+ * the cost is 2x cpu time on first block. */
+ assert(srcSize <= ZSTD_BLOCKSIZE_MAX);
+ if ( (ms->opt.litLengthSum==0) /* first block */
+ && (seqStore->sequences == seqStore->sequencesStart) /* no ldm */
+ && (ms->window.dictLimit == ms->window.lowLimit) /* no dictionary */
+ && (current == ms->window.dictLimit) /* start of frame, nothing already loaded nor skipped */
+ && (srcSize > ZSTD_PREDEF_THRESHOLD)
+ ) {
+ ZSTD_initStats_ultra(ms, seqStore, rep, src, srcSize);
+ }
+
+ return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 2 /*optLevel*/, ZSTD_noDict);
+}
+
+size_t ZSTD_compressBlock_btopt_dictMatchState(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ const void* src, size_t srcSize)
+{
+ return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 0 /*optLevel*/, ZSTD_dictMatchState);
+}
+
+size_t ZSTD_compressBlock_btultra_dictMatchState(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ const void* src, size_t srcSize)
+{
+ return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 2 /*optLevel*/, ZSTD_dictMatchState);
+}
+
+size_t ZSTD_compressBlock_btopt_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ const void* src, size_t srcSize)
+{
+ return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 0 /*optLevel*/, ZSTD_extDict);
+}
+
+size_t ZSTD_compressBlock_btultra_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ const void* src, size_t srcSize)
+{
+ return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 2 /*optLevel*/, ZSTD_extDict);
+}
+
+/* note : no btultra2 variant for extDict nor dictMatchState,
+ * because btultra2 is not meant to work with dictionaries
+ * and is only specific for the first block (no prefix) */
diff --git a/Utilities/cmzstd/lib/compress/zstd_opt.h b/Utilities/cmzstd/lib/compress/zstd_opt.h
new file mode 100644
index 000000000..094f74766
--- /dev/null
+++ b/Utilities/cmzstd/lib/compress/zstd_opt.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#ifndef ZSTD_OPT_H
+#define ZSTD_OPT_H
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#include "zstd_compress_internal.h"
+
+/* used in ZSTD_loadDictionaryContent() */
+void ZSTD_updateTree(ZSTD_matchState_t* ms, const BYTE* ip, const BYTE* iend);
+
+size_t ZSTD_compressBlock_btopt(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_btultra(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_btultra2(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+
+
+size_t ZSTD_compressBlock_btopt_dictMatchState(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_btultra_dictMatchState(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+
+size_t ZSTD_compressBlock_btopt_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_btultra_extDict(
+ ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+ void const* src, size_t srcSize);
+
+ /* note : no btultra2 variant for extDict nor dictMatchState,
+ * because btultra2 is not meant to work with dictionaries
+ * and is only specific for the first block (no prefix) */
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* ZSTD_OPT_H */
diff --git a/Utilities/cmzstd/lib/compress/zstdmt_compress.c b/Utilities/cmzstd/lib/compress/zstdmt_compress.c
new file mode 100644
index 000000000..2cbd6ffad
--- /dev/null
+++ b/Utilities/cmzstd/lib/compress/zstdmt_compress.c
@@ -0,0 +1,2107 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+
+/* ====== Compiler specifics ====== */
+#if defined(_MSC_VER)
+# pragma warning(disable : 4204) /* disable: C4204: non-constant aggregate initializer */
+#endif
+
+
+/* ====== Constants ====== */
+#define ZSTDMT_OVERLAPLOG_DEFAULT 0
+
+
+/* ====== Dependencies ====== */
+#include <string.h> /* memcpy, memset */
+#include <limits.h> /* INT_MAX, UINT_MAX */
+#include "pool.h" /* threadpool */
+#include "threading.h" /* mutex */
+#include "zstd_compress_internal.h" /* MIN, ERROR, ZSTD_*, ZSTD_highbit32 */
+#include "zstd_ldm.h"
+#include "zstdmt_compress.h"
+
+/* Guards code to support resizing the SeqPool.
+ * We will want to resize the SeqPool to save memory in the future.
+ * Until then, comment the code out since it is unused.
+ */
+#define ZSTD_RESIZE_SEQPOOL 0
+
+/* ====== Debug ====== */
+#if defined(DEBUGLEVEL) && (DEBUGLEVEL>=2) \
+ && !defined(_MSC_VER) \
+ && !defined(__MINGW32__)
+
+# include <stdio.h>
+# include <unistd.h>
+# include <sys/times.h>
+
+# define DEBUG_PRINTHEX(l,p,n) { \
+ unsigned debug_u; \
+ for (debug_u=0; debug_u<(n); debug_u++) \
+ RAWLOG(l, "%02X ", ((const unsigned char*)(p))[debug_u]); \
+ RAWLOG(l, " \n"); \
+}
+
+static unsigned long long GetCurrentClockTimeMicroseconds(void)
+{
+ static clock_t _ticksPerSecond = 0;
+ if (_ticksPerSecond <= 0) _ticksPerSecond = sysconf(_SC_CLK_TCK);
+
+ { struct tms junk; clock_t newTicks = (clock_t) times(&junk);
+ return ((((unsigned long long)newTicks)*(1000000))/_ticksPerSecond);
+} }
+
+#define MUTEX_WAIT_TIME_DLEVEL 6
+#define ZSTD_PTHREAD_MUTEX_LOCK(mutex) { \
+ if (DEBUGLEVEL >= MUTEX_WAIT_TIME_DLEVEL) { \
+ unsigned long long const beforeTime = GetCurrentClockTimeMicroseconds(); \
+ ZSTD_pthread_mutex_lock(mutex); \
+ { unsigned long long const afterTime = GetCurrentClockTimeMicroseconds(); \
+ unsigned long long const elapsedTime = (afterTime-beforeTime); \
+ if (elapsedTime > 1000) { /* or whatever threshold you like; I'm using 1 millisecond here */ \
+ DEBUGLOG(MUTEX_WAIT_TIME_DLEVEL, "Thread took %llu microseconds to acquire mutex %s \n", \
+ elapsedTime, #mutex); \
+ } } \
+ } else { \
+ ZSTD_pthread_mutex_lock(mutex); \
+ } \
+}
+
+#else
+
+# define ZSTD_PTHREAD_MUTEX_LOCK(m) ZSTD_pthread_mutex_lock(m)
+# define DEBUG_PRINTHEX(l,p,n) {}
+
+#endif
+
+
+/* ===== Buffer Pool ===== */
+/* a single Buffer Pool can be invoked from multiple threads in parallel */
+
+typedef struct buffer_s {
+ void* start;
+ size_t capacity;
+} buffer_t;
+
+static const buffer_t g_nullBuffer = { NULL, 0 };
+
+typedef struct ZSTDMT_bufferPool_s {
+ ZSTD_pthread_mutex_t poolMutex;
+ size_t bufferSize;
+ unsigned totalBuffers;
+ unsigned nbBuffers;
+ ZSTD_customMem cMem;
+ buffer_t bTable[1]; /* variable size */
+} ZSTDMT_bufferPool;
+
+static ZSTDMT_bufferPool* ZSTDMT_createBufferPool(unsigned nbWorkers, ZSTD_customMem cMem)
+{
+ unsigned const maxNbBuffers = 2*nbWorkers + 3;
+ ZSTDMT_bufferPool* const bufPool = (ZSTDMT_bufferPool*)ZSTD_calloc(
+ sizeof(ZSTDMT_bufferPool) + (maxNbBuffers-1) * sizeof(buffer_t), cMem);
+ if (bufPool==NULL) return NULL;
+ if (ZSTD_pthread_mutex_init(&bufPool->poolMutex, NULL)) {
+ ZSTD_free(bufPool, cMem);
+ return NULL;
+ }
+ bufPool->bufferSize = 64 KB;
+ bufPool->totalBuffers = maxNbBuffers;
+ bufPool->nbBuffers = 0;
+ bufPool->cMem = cMem;
+ return bufPool;
+}
+
+static void ZSTDMT_freeBufferPool(ZSTDMT_bufferPool* bufPool)
+{
+ unsigned u;
+ DEBUGLOG(3, "ZSTDMT_freeBufferPool (address:%08X)", (U32)(size_t)bufPool);
+ if (!bufPool) return; /* compatibility with free on NULL */
+ for (u=0; u<bufPool->totalBuffers; u++) {
+ DEBUGLOG(4, "free buffer %2u (address:%08X)", u, (U32)(size_t)bufPool->bTable[u].start);
+ ZSTD_free(bufPool->bTable[u].start, bufPool->cMem);
+ }
+ ZSTD_pthread_mutex_destroy(&bufPool->poolMutex);
+ ZSTD_free(bufPool, bufPool->cMem);
+}
+
+/* only works at initialization, not during compression */
+static size_t ZSTDMT_sizeof_bufferPool(ZSTDMT_bufferPool* bufPool)
+{
+ size_t const poolSize = sizeof(*bufPool)
+ + (bufPool->totalBuffers - 1) * sizeof(buffer_t);
+ unsigned u;
+ size_t totalBufferSize = 0;
+ ZSTD_pthread_mutex_lock(&bufPool->poolMutex);
+ for (u=0; u<bufPool->totalBuffers; u++)
+ totalBufferSize += bufPool->bTable[u].capacity;
+ ZSTD_pthread_mutex_unlock(&bufPool->poolMutex);
+
+ return poolSize + totalBufferSize;
+}
+
+/* ZSTDMT_setBufferSize() :
+ * all future buffers provided by this buffer pool will have _at least_ this size
+ * note : it's better for all buffers to have same size,
+ * as they become freely interchangeable, reducing malloc/free usages and memory fragmentation */
+static void ZSTDMT_setBufferSize(ZSTDMT_bufferPool* const bufPool, size_t const bSize)
+{
+ ZSTD_pthread_mutex_lock(&bufPool->poolMutex);
+ DEBUGLOG(4, "ZSTDMT_setBufferSize: bSize = %u", (U32)bSize);
+ bufPool->bufferSize = bSize;
+ ZSTD_pthread_mutex_unlock(&bufPool->poolMutex);
+}
+
+
+static ZSTDMT_bufferPool* ZSTDMT_expandBufferPool(ZSTDMT_bufferPool* srcBufPool, U32 nbWorkers)
+{
+ unsigned const maxNbBuffers = 2*nbWorkers + 3;
+ if (srcBufPool==NULL) return NULL;
+ if (srcBufPool->totalBuffers >= maxNbBuffers) /* good enough */
+ return srcBufPool;
+ /* need a larger buffer pool */
+ { ZSTD_customMem const cMem = srcBufPool->cMem;
+ size_t const bSize = srcBufPool->bufferSize; /* forward parameters */
+ ZSTDMT_bufferPool* newBufPool;
+ ZSTDMT_freeBufferPool(srcBufPool);
+ newBufPool = ZSTDMT_createBufferPool(nbWorkers, cMem);
+ if (newBufPool==NULL) return newBufPool;
+ ZSTDMT_setBufferSize(newBufPool, bSize);
+ return newBufPool;
+ }
+}
+
+/** ZSTDMT_getBuffer() :
+ * assumption : bufPool must be valid
+ * @return : a buffer, with start pointer and size
+ * note: allocation may fail, in this case, start==NULL and size==0 */
+static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* bufPool)
+{
+ size_t const bSize = bufPool->bufferSize;
+ DEBUGLOG(5, "ZSTDMT_getBuffer: bSize = %u", (U32)bufPool->bufferSize);
+ ZSTD_pthread_mutex_lock(&bufPool->poolMutex);
+ if (bufPool->nbBuffers) { /* try to use an existing buffer */
+ buffer_t const buf = bufPool->bTable[--(bufPool->nbBuffers)];
+ size_t const availBufferSize = buf.capacity;
+ bufPool->bTable[bufPool->nbBuffers] = g_nullBuffer;
+ if ((availBufferSize >= bSize) & ((availBufferSize>>3) <= bSize)) {
+ /* large enough, but not too much */
+ DEBUGLOG(5, "ZSTDMT_getBuffer: provide buffer %u of size %u",
+ bufPool->nbBuffers, (U32)buf.capacity);
+ ZSTD_pthread_mutex_unlock(&bufPool->poolMutex);
+ return buf;
+ }
+ /* size conditions not respected : scratch this buffer, create new one */
+ DEBUGLOG(5, "ZSTDMT_getBuffer: existing buffer does not meet size conditions => freeing");
+ ZSTD_free(buf.start, bufPool->cMem);
+ }
+ ZSTD_pthread_mutex_unlock(&bufPool->poolMutex);
+ /* create new buffer */
+ DEBUGLOG(5, "ZSTDMT_getBuffer: create a new buffer");
+ { buffer_t buffer;
+ void* const start = ZSTD_malloc(bSize, bufPool->cMem);
+ buffer.start = start; /* note : start can be NULL if malloc fails ! */
+ buffer.capacity = (start==NULL) ? 0 : bSize;
+ if (start==NULL) {
+ DEBUGLOG(5, "ZSTDMT_getBuffer: buffer allocation failure !!");
+ } else {
+ DEBUGLOG(5, "ZSTDMT_getBuffer: created buffer of size %u", (U32)bSize);
+ }
+ return buffer;
+ }
+}
+
+#if ZSTD_RESIZE_SEQPOOL
+/** ZSTDMT_resizeBuffer() :
+ * assumption : bufPool must be valid
+ * @return : a buffer that is at least the buffer pool buffer size.
+ * If a reallocation happens, the data in the input buffer is copied.
+ */
+static buffer_t ZSTDMT_resizeBuffer(ZSTDMT_bufferPool* bufPool, buffer_t buffer)
+{
+ size_t const bSize = bufPool->bufferSize;
+ if (buffer.capacity < bSize) {
+ void* const start = ZSTD_malloc(bSize, bufPool->cMem);
+ buffer_t newBuffer;
+ newBuffer.start = start;
+ newBuffer.capacity = start == NULL ? 0 : bSize;
+ if (start != NULL) {
+ assert(newBuffer.capacity >= buffer.capacity);
+ memcpy(newBuffer.start, buffer.start, buffer.capacity);
+ DEBUGLOG(5, "ZSTDMT_resizeBuffer: created buffer of size %u", (U32)bSize);
+ return newBuffer;
+ }
+ DEBUGLOG(5, "ZSTDMT_resizeBuffer: buffer allocation failure !!");
+ }
+ return buffer;
+}
+#endif
+
+/* store buffer for later re-use, up to pool capacity */
+static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* bufPool, buffer_t buf)
+{
+ DEBUGLOG(5, "ZSTDMT_releaseBuffer");
+ if (buf.start == NULL) return; /* compatible with release on NULL */
+ ZSTD_pthread_mutex_lock(&bufPool->poolMutex);
+ if (bufPool->nbBuffers < bufPool->totalBuffers) {
+ bufPool->bTable[bufPool->nbBuffers++] = buf; /* stored for later use */
+ DEBUGLOG(5, "ZSTDMT_releaseBuffer: stored buffer of size %u in slot %u",
+ (U32)buf.capacity, (U32)(bufPool->nbBuffers-1));
+ ZSTD_pthread_mutex_unlock(&bufPool->poolMutex);
+ return;
+ }
+ ZSTD_pthread_mutex_unlock(&bufPool->poolMutex);
+ /* Reached bufferPool capacity (should not happen) */
+ DEBUGLOG(5, "ZSTDMT_releaseBuffer: pool capacity reached => freeing ");
+ ZSTD_free(buf.start, bufPool->cMem);
+}
+
+
+/* ===== Seq Pool Wrapper ====== */
+
+static rawSeqStore_t kNullRawSeqStore = {NULL, 0, 0, 0};
+
+typedef ZSTDMT_bufferPool ZSTDMT_seqPool;
+
+static size_t ZSTDMT_sizeof_seqPool(ZSTDMT_seqPool* seqPool)
+{
+ return ZSTDMT_sizeof_bufferPool(seqPool);
+}
+
+static rawSeqStore_t bufferToSeq(buffer_t buffer)
+{
+ rawSeqStore_t seq = {NULL, 0, 0, 0};
+ seq.seq = (rawSeq*)buffer.start;
+ seq.capacity = buffer.capacity / sizeof(rawSeq);
+ return seq;
+}
+
+static buffer_t seqToBuffer(rawSeqStore_t seq)
+{
+ buffer_t buffer;
+ buffer.start = seq.seq;
+ buffer.capacity = seq.capacity * sizeof(rawSeq);
+ return buffer;
+}
+
+static rawSeqStore_t ZSTDMT_getSeq(ZSTDMT_seqPool* seqPool)
+{
+ if (seqPool->bufferSize == 0) {
+ return kNullRawSeqStore;
+ }
+ return bufferToSeq(ZSTDMT_getBuffer(seqPool));
+}
+
+#if ZSTD_RESIZE_SEQPOOL
+static rawSeqStore_t ZSTDMT_resizeSeq(ZSTDMT_seqPool* seqPool, rawSeqStore_t seq)
+{
+ return bufferToSeq(ZSTDMT_resizeBuffer(seqPool, seqToBuffer(seq)));
+}
+#endif
+
+static void ZSTDMT_releaseSeq(ZSTDMT_seqPool* seqPool, rawSeqStore_t seq)
+{
+ ZSTDMT_releaseBuffer(seqPool, seqToBuffer(seq));
+}
+
+static void ZSTDMT_setNbSeq(ZSTDMT_seqPool* const seqPool, size_t const nbSeq)
+{
+ ZSTDMT_setBufferSize(seqPool, nbSeq * sizeof(rawSeq));
+}
+
+static ZSTDMT_seqPool* ZSTDMT_createSeqPool(unsigned nbWorkers, ZSTD_customMem cMem)
+{
+ ZSTDMT_seqPool* const seqPool = ZSTDMT_createBufferPool(nbWorkers, cMem);
+ if (seqPool == NULL) return NULL;
+ ZSTDMT_setNbSeq(seqPool, 0);
+ return seqPool;
+}
+
+static void ZSTDMT_freeSeqPool(ZSTDMT_seqPool* seqPool)
+{
+ ZSTDMT_freeBufferPool(seqPool);
+}
+
+static ZSTDMT_seqPool* ZSTDMT_expandSeqPool(ZSTDMT_seqPool* pool, U32 nbWorkers)
+{
+ return ZSTDMT_expandBufferPool(pool, nbWorkers);
+}
+
+
+/* ===== CCtx Pool ===== */
+/* a single CCtx Pool can be invoked from multiple threads in parallel */
+
+typedef struct {
+ ZSTD_pthread_mutex_t poolMutex;
+ int totalCCtx;
+ int availCCtx;
+ ZSTD_customMem cMem;
+ ZSTD_CCtx* cctx[1]; /* variable size */
+} ZSTDMT_CCtxPool;
+
+/* note : all CCtx borrowed from the pool should be released back to the pool _before_ freeing the pool */
+static void ZSTDMT_freeCCtxPool(ZSTDMT_CCtxPool* pool)
+{
+ int cid;
+ for (cid=0; cid<pool->totalCCtx; cid++)
+ ZSTD_freeCCtx(pool->cctx[cid]); /* note : compatible with free on NULL */
+ ZSTD_pthread_mutex_destroy(&pool->poolMutex);
+ ZSTD_free(pool, pool->cMem);
+}
+
+/* ZSTDMT_createCCtxPool() :
+ * implies nbWorkers >= 1 , checked by caller ZSTDMT_createCCtx() */
+static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(int nbWorkers,
+ ZSTD_customMem cMem)
+{
+ ZSTDMT_CCtxPool* const cctxPool = (ZSTDMT_CCtxPool*) ZSTD_calloc(
+ sizeof(ZSTDMT_CCtxPool) + (nbWorkers-1)*sizeof(ZSTD_CCtx*), cMem);
+ assert(nbWorkers > 0);
+ if (!cctxPool) return NULL;
+ if (ZSTD_pthread_mutex_init(&cctxPool->poolMutex, NULL)) {
+ ZSTD_free(cctxPool, cMem);
+ return NULL;
+ }
+ cctxPool->cMem = cMem;
+ cctxPool->totalCCtx = nbWorkers;
+ cctxPool->availCCtx = 1; /* at least one cctx for single-thread mode */
+ cctxPool->cctx[0] = ZSTD_createCCtx_advanced(cMem);
+ if (!cctxPool->cctx[0]) { ZSTDMT_freeCCtxPool(cctxPool); return NULL; }
+ DEBUGLOG(3, "cctxPool created, with %u workers", nbWorkers);
+ return cctxPool;
+}
+
+static ZSTDMT_CCtxPool* ZSTDMT_expandCCtxPool(ZSTDMT_CCtxPool* srcPool,
+ int nbWorkers)
+{
+ if (srcPool==NULL) return NULL;
+ if (nbWorkers <= srcPool->totalCCtx) return srcPool; /* good enough */
+ /* need a larger cctx pool */
+ { ZSTD_customMem const cMem = srcPool->cMem;
+ ZSTDMT_freeCCtxPool(srcPool);
+ return ZSTDMT_createCCtxPool(nbWorkers, cMem);
+ }
+}
+
+/* only works during initialization phase, not during compression */
+static size_t ZSTDMT_sizeof_CCtxPool(ZSTDMT_CCtxPool* cctxPool)
+{
+ ZSTD_pthread_mutex_lock(&cctxPool->poolMutex);
+ { unsigned const nbWorkers = cctxPool->totalCCtx;
+ size_t const poolSize = sizeof(*cctxPool)
+ + (nbWorkers-1) * sizeof(ZSTD_CCtx*);
+ unsigned u;
+ size_t totalCCtxSize = 0;
+ for (u=0; u<nbWorkers; u++) {
+ totalCCtxSize += ZSTD_sizeof_CCtx(cctxPool->cctx[u]);
+ }
+ ZSTD_pthread_mutex_unlock(&cctxPool->poolMutex);
+ assert(nbWorkers > 0);
+ return poolSize + totalCCtxSize;
+ }
+}
+
+static ZSTD_CCtx* ZSTDMT_getCCtx(ZSTDMT_CCtxPool* cctxPool)
+{
+ DEBUGLOG(5, "ZSTDMT_getCCtx");
+ ZSTD_pthread_mutex_lock(&cctxPool->poolMutex);
+ if (cctxPool->availCCtx) {
+ cctxPool->availCCtx--;
+ { ZSTD_CCtx* const cctx = cctxPool->cctx[cctxPool->availCCtx];
+ ZSTD_pthread_mutex_unlock(&cctxPool->poolMutex);
+ return cctx;
+ } }
+ ZSTD_pthread_mutex_unlock(&cctxPool->poolMutex);
+ DEBUGLOG(5, "create one more CCtx");
+ return ZSTD_createCCtx_advanced(cctxPool->cMem); /* note : can be NULL, when creation fails ! */
+}
+
+static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx)
+{
+ if (cctx==NULL) return; /* compatibility with release on NULL */
+ ZSTD_pthread_mutex_lock(&pool->poolMutex);
+ if (pool->availCCtx < pool->totalCCtx)
+ pool->cctx[pool->availCCtx++] = cctx;
+ else {
+ /* pool overflow : should not happen, since totalCCtx==nbWorkers */
+ DEBUGLOG(4, "CCtx pool overflow : free cctx");
+ ZSTD_freeCCtx(cctx);
+ }
+ ZSTD_pthread_mutex_unlock(&pool->poolMutex);
+}
+
+/* ==== Serial State ==== */
+
+typedef struct {
+ void const* start;
+ size_t size;
+} range_t;
+
+typedef struct {
+ /* All variables in the struct are protected by mutex. */
+ ZSTD_pthread_mutex_t mutex;
+ ZSTD_pthread_cond_t cond;
+ ZSTD_CCtx_params params;
+ ldmState_t ldmState;
+ XXH64_state_t xxhState;
+ unsigned nextJobID;
+ /* Protects ldmWindow.
+ * Must be acquired after the main mutex when acquiring both.
+ */
+ ZSTD_pthread_mutex_t ldmWindowMutex;
+ ZSTD_pthread_cond_t ldmWindowCond; /* Signaled when ldmWindow is udpated */
+ ZSTD_window_t ldmWindow; /* A thread-safe copy of ldmState.window */
+} serialState_t;
+
+static int ZSTDMT_serialState_reset(serialState_t* serialState, ZSTDMT_seqPool* seqPool, ZSTD_CCtx_params params, size_t jobSize)
+{
+ /* Adjust parameters */
+ if (params.ldmParams.enableLdm) {
+ DEBUGLOG(4, "LDM window size = %u KB", (1U << params.cParams.windowLog) >> 10);
+ ZSTD_ldm_adjustParameters(&params.ldmParams, &params.cParams);
+ assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog);
+ assert(params.ldmParams.hashRateLog < 32);
+ serialState->ldmState.hashPower =
+ ZSTD_rollingHash_primePower(params.ldmParams.minMatchLength);
+ } else {
+ memset(&params.ldmParams, 0, sizeof(params.ldmParams));
+ }
+ serialState->nextJobID = 0;
+ if (params.fParams.checksumFlag)
+ XXH64_reset(&serialState->xxhState, 0);
+ if (params.ldmParams.enableLdm) {
+ ZSTD_customMem cMem = params.customMem;
+ unsigned const hashLog = params.ldmParams.hashLog;
+ size_t const hashSize = ((size_t)1 << hashLog) * sizeof(ldmEntry_t);
+ unsigned const bucketLog =
+ params.ldmParams.hashLog - params.ldmParams.bucketSizeLog;
+ size_t const bucketSize = (size_t)1 << bucketLog;
+ unsigned const prevBucketLog =
+ serialState->params.ldmParams.hashLog -
+ serialState->params.ldmParams.bucketSizeLog;
+ /* Size the seq pool tables */
+ ZSTDMT_setNbSeq(seqPool, ZSTD_ldm_getMaxNbSeq(params.ldmParams, jobSize));
+ /* Reset the window */
+ ZSTD_window_clear(&serialState->ldmState.window);
+ serialState->ldmWindow = serialState->ldmState.window;
+ /* Resize tables and output space if necessary. */
+ if (serialState->ldmState.hashTable == NULL || serialState->params.ldmParams.hashLog < hashLog) {
+ ZSTD_free(serialState->ldmState.hashTable, cMem);
+ serialState->ldmState.hashTable = (ldmEntry_t*)ZSTD_malloc(hashSize, cMem);
+ }
+ if (serialState->ldmState.bucketOffsets == NULL || prevBucketLog < bucketLog) {
+ ZSTD_free(serialState->ldmState.bucketOffsets, cMem);
+ serialState->ldmState.bucketOffsets = (BYTE*)ZSTD_malloc(bucketSize, cMem);
+ }
+ if (!serialState->ldmState.hashTable || !serialState->ldmState.bucketOffsets)
+ return 1;
+ /* Zero the tables */
+ memset(serialState->ldmState.hashTable, 0, hashSize);
+ memset(serialState->ldmState.bucketOffsets, 0, bucketSize);
+ }
+ serialState->params = params;
+ serialState->params.jobSize = (U32)jobSize;
+ return 0;
+}
+
+static int ZSTDMT_serialState_init(serialState_t* serialState)
+{
+ int initError = 0;
+ memset(serialState, 0, sizeof(*serialState));
+ initError |= ZSTD_pthread_mutex_init(&serialState->mutex, NULL);
+ initError |= ZSTD_pthread_cond_init(&serialState->cond, NULL);
+ initError |= ZSTD_pthread_mutex_init(&serialState->ldmWindowMutex, NULL);
+ initError |= ZSTD_pthread_cond_init(&serialState->ldmWindowCond, NULL);
+ return initError;
+}
+
+static void ZSTDMT_serialState_free(serialState_t* serialState)
+{
+ ZSTD_customMem cMem = serialState->params.customMem;
+ ZSTD_pthread_mutex_destroy(&serialState->mutex);
+ ZSTD_pthread_cond_destroy(&serialState->cond);
+ ZSTD_pthread_mutex_destroy(&serialState->ldmWindowMutex);
+ ZSTD_pthread_cond_destroy(&serialState->ldmWindowCond);
+ ZSTD_free(serialState->ldmState.hashTable, cMem);
+ ZSTD_free(serialState->ldmState.bucketOffsets, cMem);
+}
+
+static void ZSTDMT_serialState_update(serialState_t* serialState,
+ ZSTD_CCtx* jobCCtx, rawSeqStore_t seqStore,
+ range_t src, unsigned jobID)
+{
+ /* Wait for our turn */
+ ZSTD_PTHREAD_MUTEX_LOCK(&serialState->mutex);
+ while (serialState->nextJobID < jobID) {
+ DEBUGLOG(5, "wait for serialState->cond");
+ ZSTD_pthread_cond_wait(&serialState->cond, &serialState->mutex);
+ }
+ /* A future job may error and skip our job */
+ if (serialState->nextJobID == jobID) {
+ /* It is now our turn, do any processing necessary */
+ if (serialState->params.ldmParams.enableLdm) {
+ size_t error;
+ assert(seqStore.seq != NULL && seqStore.pos == 0 &&
+ seqStore.size == 0 && seqStore.capacity > 0);
+ assert(src.size <= serialState->params.jobSize);
+ ZSTD_window_update(&serialState->ldmState.window, src.start, src.size);
+ error = ZSTD_ldm_generateSequences(
+ &serialState->ldmState, &seqStore,
+ &serialState->params.ldmParams, src.start, src.size);
+ /* We provide a large enough buffer to never fail. */
+ assert(!ZSTD_isError(error)); (void)error;
+ /* Update ldmWindow to match the ldmState.window and signal the main
+ * thread if it is waiting for a buffer.
+ */
+ ZSTD_PTHREAD_MUTEX_LOCK(&serialState->ldmWindowMutex);
+ serialState->ldmWindow = serialState->ldmState.window;
+ ZSTD_pthread_cond_signal(&serialState->ldmWindowCond);
+ ZSTD_pthread_mutex_unlock(&serialState->ldmWindowMutex);
+ }
+ if (serialState->params.fParams.checksumFlag && src.size > 0)
+ XXH64_update(&serialState->xxhState, src.start, src.size);
+ }
+ /* Now it is the next jobs turn */
+ serialState->nextJobID++;
+ ZSTD_pthread_cond_broadcast(&serialState->cond);
+ ZSTD_pthread_mutex_unlock(&serialState->mutex);
+
+ if (seqStore.size > 0) {
+ size_t const err = ZSTD_referenceExternalSequences(
+ jobCCtx, seqStore.seq, seqStore.size);
+ assert(serialState->params.ldmParams.enableLdm);
+ assert(!ZSTD_isError(err));
+ (void)err;
+ }
+}
+
+static void ZSTDMT_serialState_ensureFinished(serialState_t* serialState,
+ unsigned jobID, size_t cSize)
+{
+ ZSTD_PTHREAD_MUTEX_LOCK(&serialState->mutex);
+ if (serialState->nextJobID <= jobID) {
+ assert(ZSTD_isError(cSize)); (void)cSize;
+ DEBUGLOG(5, "Skipping past job %u because of error", jobID);
+ serialState->nextJobID = jobID + 1;
+ ZSTD_pthread_cond_broadcast(&serialState->cond);
+
+ ZSTD_PTHREAD_MUTEX_LOCK(&serialState->ldmWindowMutex);
+ ZSTD_window_clear(&serialState->ldmWindow);
+ ZSTD_pthread_cond_signal(&serialState->ldmWindowCond);
+ ZSTD_pthread_mutex_unlock(&serialState->ldmWindowMutex);
+ }
+ ZSTD_pthread_mutex_unlock(&serialState->mutex);
+
+}
+
+
+/* ------------------------------------------ */
+/* ===== Worker thread ===== */
+/* ------------------------------------------ */
+
+static const range_t kNullRange = { NULL, 0 };
+
+typedef struct {
+ size_t consumed; /* SHARED - set0 by mtctx, then modified by worker AND read by mtctx */
+ size_t cSize; /* SHARED - set0 by mtctx, then modified by worker AND read by mtctx, then set0 by mtctx */
+ ZSTD_pthread_mutex_t job_mutex; /* Thread-safe - used by mtctx and worker */
+ ZSTD_pthread_cond_t job_cond; /* Thread-safe - used by mtctx and worker */
+ ZSTDMT_CCtxPool* cctxPool; /* Thread-safe - used by mtctx and (all) workers */
+ ZSTDMT_bufferPool* bufPool; /* Thread-safe - used by mtctx and (all) workers */
+ ZSTDMT_seqPool* seqPool; /* Thread-safe - used by mtctx and (all) workers */
+ serialState_t* serial; /* Thread-safe - used by mtctx and (all) workers */
+ buffer_t dstBuff; /* set by worker (or mtctx), then read by worker & mtctx, then modified by mtctx => no barrier */
+ range_t prefix; /* set by mtctx, then read by worker & mtctx => no barrier */
+ range_t src; /* set by mtctx, then read by worker & mtctx => no barrier */
+ unsigned jobID; /* set by mtctx, then read by worker => no barrier */
+ unsigned firstJob; /* set by mtctx, then read by worker => no barrier */
+ unsigned lastJob; /* set by mtctx, then read by worker => no barrier */
+ ZSTD_CCtx_params params; /* set by mtctx, then read by worker => no barrier */
+ const ZSTD_CDict* cdict; /* set by mtctx, then read by worker => no barrier */
+ unsigned long long fullFrameSize; /* set by mtctx, then read by worker => no barrier */
+ size_t dstFlushed; /* used only by mtctx */
+ unsigned frameChecksumNeeded; /* used only by mtctx */
+} ZSTDMT_jobDescription;
+
+#define JOB_ERROR(e) { \
+ ZSTD_PTHREAD_MUTEX_LOCK(&job->job_mutex); \
+ job->cSize = e; \
+ ZSTD_pthread_mutex_unlock(&job->job_mutex); \
+ goto _endJob; \
+}
+
+/* ZSTDMT_compressionJob() is a POOL_function type */
+static void ZSTDMT_compressionJob(void* jobDescription)
+{
+ ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription;
+ ZSTD_CCtx_params jobParams = job->params; /* do not modify job->params ! copy it, modify the copy */
+ ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(job->cctxPool);
+ rawSeqStore_t rawSeqStore = ZSTDMT_getSeq(job->seqPool);
+ buffer_t dstBuff = job->dstBuff;
+ size_t lastCBlockSize = 0;
+
+ /* ressources */
+ if (cctx==NULL) JOB_ERROR(ERROR(memory_allocation));
+ if (dstBuff.start == NULL) { /* streaming job : doesn't provide a dstBuffer */
+ dstBuff = ZSTDMT_getBuffer(job->bufPool);
+ if (dstBuff.start==NULL) JOB_ERROR(ERROR(memory_allocation));
+ job->dstBuff = dstBuff; /* this value can be read in ZSTDMT_flush, when it copies the whole job */
+ }
+ if (jobParams.ldmParams.enableLdm && rawSeqStore.seq == NULL)
+ JOB_ERROR(ERROR(memory_allocation));
+
+ /* Don't compute the checksum for chunks, since we compute it externally,
+ * but write it in the header.
+ */
+ if (job->jobID != 0) jobParams.fParams.checksumFlag = 0;
+ /* Don't run LDM for the chunks, since we handle it externally */
+ jobParams.ldmParams.enableLdm = 0;
+
+
+ /* init */
+ if (job->cdict) {
+ size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, NULL, 0, ZSTD_dct_auto, ZSTD_dtlm_fast, job->cdict, jobParams, job->fullFrameSize);
+ assert(job->firstJob); /* only allowed for first job */
+ if (ZSTD_isError(initError)) JOB_ERROR(initError);
+ } else { /* srcStart points at reloaded section */
+ U64 const pledgedSrcSize = job->firstJob ? job->fullFrameSize : job->src.size;
+ { size_t const forceWindowError = ZSTD_CCtxParam_setParameter(&jobParams, ZSTD_c_forceMaxWindow, !job->firstJob);
+ if (ZSTD_isError(forceWindowError)) JOB_ERROR(forceWindowError);
+ }
+ { size_t const initError = ZSTD_compressBegin_advanced_internal(cctx,
+ job->prefix.start, job->prefix.size, ZSTD_dct_rawContent, /* load dictionary in "content-only" mode (no header analysis) */
+ ZSTD_dtlm_fast,
+ NULL, /*cdict*/
+ jobParams, pledgedSrcSize);
+ if (ZSTD_isError(initError)) JOB_ERROR(initError);
+ } }
+
+ /* Perform serial step as early as possible, but after CCtx initialization */
+ ZSTDMT_serialState_update(job->serial, cctx, rawSeqStore, job->src, job->jobID);
+
+ if (!job->firstJob) { /* flush and overwrite frame header when it's not first job */
+ size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.capacity, job->src.start, 0);
+ if (ZSTD_isError(hSize)) JOB_ERROR(hSize);
+ DEBUGLOG(5, "ZSTDMT_compressionJob: flush and overwrite %u bytes of frame header (not first job)", (U32)hSize);
+ ZSTD_invalidateRepCodes(cctx);
+ }
+
+ /* compress */
+ { size_t const chunkSize = 4*ZSTD_BLOCKSIZE_MAX;
+ int const nbChunks = (int)((job->src.size + (chunkSize-1)) / chunkSize);
+ const BYTE* ip = (const BYTE*) job->src.start;
+ BYTE* const ostart = (BYTE*)dstBuff.start;
+ BYTE* op = ostart;
+ BYTE* oend = op + dstBuff.capacity;
+ int chunkNb;
+ if (sizeof(size_t) > sizeof(int)) assert(job->src.size < ((size_t)INT_MAX) * chunkSize); /* check overflow */
+ DEBUGLOG(5, "ZSTDMT_compressionJob: compress %u bytes in %i blocks", (U32)job->src.size, nbChunks);
+ assert(job->cSize == 0);
+ for (chunkNb = 1; chunkNb < nbChunks; chunkNb++) {
+ size_t const cSize = ZSTD_compressContinue(cctx, op, oend-op, ip, chunkSize);
+ if (ZSTD_isError(cSize)) JOB_ERROR(cSize);
+ ip += chunkSize;
+ op += cSize; assert(op < oend);
+ /* stats */
+ ZSTD_PTHREAD_MUTEX_LOCK(&job->job_mutex);
+ job->cSize += cSize;
+ job->consumed = chunkSize * chunkNb;
+ DEBUGLOG(5, "ZSTDMT_compressionJob: compress new block : cSize==%u bytes (total: %u)",
+ (U32)cSize, (U32)job->cSize);
+ ZSTD_pthread_cond_signal(&job->job_cond); /* warns some more data is ready to be flushed */
+ ZSTD_pthread_mutex_unlock(&job->job_mutex);
+ }
+ /* last block */
+ assert(chunkSize > 0);
+ assert((chunkSize & (chunkSize - 1)) == 0); /* chunkSize must be power of 2 for mask==(chunkSize-1) to work */
+ if ((nbChunks > 0) | job->lastJob /*must output a "last block" flag*/ ) {
+ size_t const lastBlockSize1 = job->src.size & (chunkSize-1);
+ size_t const lastBlockSize = ((lastBlockSize1==0) & (job->src.size>=chunkSize)) ? chunkSize : lastBlockSize1;
+ size_t const cSize = (job->lastJob) ?
+ ZSTD_compressEnd (cctx, op, oend-op, ip, lastBlockSize) :
+ ZSTD_compressContinue(cctx, op, oend-op, ip, lastBlockSize);
+ if (ZSTD_isError(cSize)) JOB_ERROR(cSize);
+ lastCBlockSize = cSize;
+ } }
+
+_endJob:
+ ZSTDMT_serialState_ensureFinished(job->serial, job->jobID, job->cSize);
+ if (job->prefix.size > 0)
+ DEBUGLOG(5, "Finished with prefix: %zx", (size_t)job->prefix.start);
+ DEBUGLOG(5, "Finished with source: %zx", (size_t)job->src.start);
+ /* release resources */
+ ZSTDMT_releaseSeq(job->seqPool, rawSeqStore);
+ ZSTDMT_releaseCCtx(job->cctxPool, cctx);
+ /* report */
+ ZSTD_PTHREAD_MUTEX_LOCK(&job->job_mutex);
+ if (ZSTD_isError(job->cSize)) assert(lastCBlockSize == 0);
+ job->cSize += lastCBlockSize;
+ job->consumed = job->src.size; /* when job->consumed == job->src.size , compression job is presumed completed */
+ ZSTD_pthread_cond_signal(&job->job_cond);
+ ZSTD_pthread_mutex_unlock(&job->job_mutex);
+}
+
+
+/* ------------------------------------------ */
+/* ===== Multi-threaded compression ===== */
+/* ------------------------------------------ */
+
+typedef struct {
+ range_t prefix; /* read-only non-owned prefix buffer */
+ buffer_t buffer;
+ size_t filled;
+} inBuff_t;
+
+typedef struct {
+ BYTE* buffer; /* The round input buffer. All jobs get references
+ * to pieces of the buffer. ZSTDMT_tryGetInputRange()
+ * handles handing out job input buffers, and makes
+ * sure it doesn't overlap with any pieces still in use.
+ */
+ size_t capacity; /* The capacity of buffer. */
+ size_t pos; /* The position of the current inBuff in the round
+ * buffer. Updated past the end if the inBuff once
+ * the inBuff is sent to the worker thread.
+ * pos <= capacity.
+ */
+} roundBuff_t;
+
+static const roundBuff_t kNullRoundBuff = {NULL, 0, 0};
+
+#define RSYNC_LENGTH 32
+
+typedef struct {
+ U64 hash;
+ U64 hitMask;
+ U64 primePower;
+} rsyncState_t;
+
+struct ZSTDMT_CCtx_s {
+ POOL_ctx* factory;
+ ZSTDMT_jobDescription* jobs;
+ ZSTDMT_bufferPool* bufPool;
+ ZSTDMT_CCtxPool* cctxPool;
+ ZSTDMT_seqPool* seqPool;
+ ZSTD_CCtx_params params;
+ size_t targetSectionSize;
+ size_t targetPrefixSize;
+ int jobReady; /* 1 => one job is already prepared, but pool has shortage of workers. Don't create a new job. */
+ inBuff_t inBuff;
+ roundBuff_t roundBuff;
+ serialState_t serial;
+ rsyncState_t rsync;
+ unsigned singleBlockingThread;
+ unsigned jobIDMask;
+ unsigned doneJobID;
+ unsigned nextJobID;
+ unsigned frameEnded;
+ unsigned allJobsCompleted;
+ unsigned long long frameContentSize;
+ unsigned long long consumed;
+ unsigned long long produced;
+ ZSTD_customMem cMem;
+ ZSTD_CDict* cdictLocal;
+ const ZSTD_CDict* cdict;
+};
+
+static void ZSTDMT_freeJobsTable(ZSTDMT_jobDescription* jobTable, U32 nbJobs, ZSTD_customMem cMem)
+{
+ U32 jobNb;
+ if (jobTable == NULL) return;
+ for (jobNb=0; jobNb<nbJobs; jobNb++) {
+ ZSTD_pthread_mutex_destroy(&jobTable[jobNb].job_mutex);
+ ZSTD_pthread_cond_destroy(&jobTable[jobNb].job_cond);
+ }
+ ZSTD_free(jobTable, cMem);
+}
+
+/* ZSTDMT_allocJobsTable()
+ * allocate and init a job table.
+ * update *nbJobsPtr to next power of 2 value, as size of table */
+static ZSTDMT_jobDescription* ZSTDMT_createJobsTable(U32* nbJobsPtr, ZSTD_customMem cMem)
+{
+ U32 const nbJobsLog2 = ZSTD_highbit32(*nbJobsPtr) + 1;
+ U32 const nbJobs = 1 << nbJobsLog2;
+ U32 jobNb;
+ ZSTDMT_jobDescription* const jobTable = (ZSTDMT_jobDescription*)
+ ZSTD_calloc(nbJobs * sizeof(ZSTDMT_jobDescription), cMem);
+ int initError = 0;
+ if (jobTable==NULL) return NULL;
+ *nbJobsPtr = nbJobs;
+ for (jobNb=0; jobNb<nbJobs; jobNb++) {
+ initError |= ZSTD_pthread_mutex_init(&jobTable[jobNb].job_mutex, NULL);
+ initError |= ZSTD_pthread_cond_init(&jobTable[jobNb].job_cond, NULL);
+ }
+ if (initError != 0) {
+ ZSTDMT_freeJobsTable(jobTable, nbJobs, cMem);
+ return NULL;
+ }
+ return jobTable;
+}
+
+static size_t ZSTDMT_expandJobsTable (ZSTDMT_CCtx* mtctx, U32 nbWorkers) {
+ U32 nbJobs = nbWorkers + 2;
+ if (nbJobs > mtctx->jobIDMask+1) { /* need more job capacity */
+ ZSTDMT_freeJobsTable(mtctx->jobs, mtctx->jobIDMask+1, mtctx->cMem);
+ mtctx->jobIDMask = 0;
+ mtctx->jobs = ZSTDMT_createJobsTable(&nbJobs, mtctx->cMem);
+ if (mtctx->jobs==NULL) return ERROR(memory_allocation);
+ assert((nbJobs != 0) && ((nbJobs & (nbJobs - 1)) == 0)); /* ensure nbJobs is a power of 2 */
+ mtctx->jobIDMask = nbJobs - 1;
+ }
+ return 0;
+}
+
+
+/* ZSTDMT_CCtxParam_setNbWorkers():
+ * Internal use only */
+size_t ZSTDMT_CCtxParam_setNbWorkers(ZSTD_CCtx_params* params, unsigned nbWorkers)
+{
+ if (nbWorkers > ZSTDMT_NBWORKERS_MAX) nbWorkers = ZSTDMT_NBWORKERS_MAX;
+ params->nbWorkers = nbWorkers;
+ params->overlapLog = ZSTDMT_OVERLAPLOG_DEFAULT;
+ params->jobSize = 0;
+ return nbWorkers;
+}
+
+ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers, ZSTD_customMem cMem)
+{
+ ZSTDMT_CCtx* mtctx;
+ U32 nbJobs = nbWorkers + 2;
+ int initError;
+ DEBUGLOG(3, "ZSTDMT_createCCtx_advanced (nbWorkers = %u)", nbWorkers);
+
+ if (nbWorkers < 1) return NULL;
+ nbWorkers = MIN(nbWorkers , ZSTDMT_NBWORKERS_MAX);
+ if ((cMem.customAlloc!=NULL) ^ (cMem.customFree!=NULL))
+ /* invalid custom allocator */
+ return NULL;
+
+ mtctx = (ZSTDMT_CCtx*) ZSTD_calloc(sizeof(ZSTDMT_CCtx), cMem);
+ if (!mtctx) return NULL;
+ ZSTDMT_CCtxParam_setNbWorkers(&mtctx->params, nbWorkers);
+ mtctx->cMem = cMem;
+ mtctx->allJobsCompleted = 1;
+ mtctx->factory = POOL_create_advanced(nbWorkers, 0, cMem);
+ mtctx->jobs = ZSTDMT_createJobsTable(&nbJobs, cMem);
+ assert(nbJobs > 0); assert((nbJobs & (nbJobs - 1)) == 0); /* ensure nbJobs is a power of 2 */
+ mtctx->jobIDMask = nbJobs - 1;
+ mtctx->bufPool = ZSTDMT_createBufferPool(nbWorkers, cMem);
+ mtctx->cctxPool = ZSTDMT_createCCtxPool(nbWorkers, cMem);
+ mtctx->seqPool = ZSTDMT_createSeqPool(nbWorkers, cMem);
+ initError = ZSTDMT_serialState_init(&mtctx->serial);
+ mtctx->roundBuff = kNullRoundBuff;
+ if (!mtctx->factory | !mtctx->jobs | !mtctx->bufPool | !mtctx->cctxPool | !mtctx->seqPool | initError) {
+ ZSTDMT_freeCCtx(mtctx);
+ return NULL;
+ }
+ DEBUGLOG(3, "mt_cctx created, for %u threads", nbWorkers);
+ return mtctx;
+}
+
+ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbWorkers)
+{
+ return ZSTDMT_createCCtx_advanced(nbWorkers, ZSTD_defaultCMem);
+}
+
+
+/* ZSTDMT_releaseAllJobResources() :
+ * note : ensure all workers are killed first ! */
+static void ZSTDMT_releaseAllJobResources(ZSTDMT_CCtx* mtctx)
+{
+ unsigned jobID;
+ DEBUGLOG(3, "ZSTDMT_releaseAllJobResources");
+ for (jobID=0; jobID <= mtctx->jobIDMask; jobID++) {
+ DEBUGLOG(4, "job%02u: release dst address %08X", jobID, (U32)(size_t)mtctx->jobs[jobID].dstBuff.start);
+ ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].dstBuff);
+ mtctx->jobs[jobID].dstBuff = g_nullBuffer;
+ mtctx->jobs[jobID].cSize = 0;
+ }
+ memset(mtctx->jobs, 0, (mtctx->jobIDMask+1)*sizeof(ZSTDMT_jobDescription));
+ mtctx->inBuff.buffer = g_nullBuffer;
+ mtctx->inBuff.filled = 0;
+ mtctx->allJobsCompleted = 1;
+}
+
+static void ZSTDMT_waitForAllJobsCompleted(ZSTDMT_CCtx* mtctx)
+{
+ DEBUGLOG(4, "ZSTDMT_waitForAllJobsCompleted");
+ while (mtctx->doneJobID < mtctx->nextJobID) {
+ unsigned const jobID = mtctx->doneJobID & mtctx->jobIDMask;
+ ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobs[jobID].job_mutex);
+ while (mtctx->jobs[jobID].consumed < mtctx->jobs[jobID].src.size) {
+ DEBUGLOG(4, "waiting for jobCompleted signal from job %u", mtctx->doneJobID); /* we want to block when waiting for data to flush */
+ ZSTD_pthread_cond_wait(&mtctx->jobs[jobID].job_cond, &mtctx->jobs[jobID].job_mutex);
+ }
+ ZSTD_pthread_mutex_unlock(&mtctx->jobs[jobID].job_mutex);
+ mtctx->doneJobID++;
+ }
+}
+
+size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx)
+{
+ if (mtctx==NULL) return 0; /* compatible with free on NULL */
+ POOL_free(mtctx->factory); /* stop and free worker threads */
+ ZSTDMT_releaseAllJobResources(mtctx); /* release job resources into pools first */
+ ZSTDMT_freeJobsTable(mtctx->jobs, mtctx->jobIDMask+1, mtctx->cMem);
+ ZSTDMT_freeBufferPool(mtctx->bufPool);
+ ZSTDMT_freeCCtxPool(mtctx->cctxPool);
+ ZSTDMT_freeSeqPool(mtctx->seqPool);
+ ZSTDMT_serialState_free(&mtctx->serial);
+ ZSTD_freeCDict(mtctx->cdictLocal);
+ if (mtctx->roundBuff.buffer)
+ ZSTD_free(mtctx->roundBuff.buffer, mtctx->cMem);
+ ZSTD_free(mtctx, mtctx->cMem);
+ return 0;
+}
+
+size_t ZSTDMT_sizeof_CCtx(ZSTDMT_CCtx* mtctx)
+{
+ if (mtctx == NULL) return 0; /* supports sizeof NULL */
+ return sizeof(*mtctx)
+ + POOL_sizeof(mtctx->factory)
+ + ZSTDMT_sizeof_bufferPool(mtctx->bufPool)
+ + (mtctx->jobIDMask+1) * sizeof(ZSTDMT_jobDescription)
+ + ZSTDMT_sizeof_CCtxPool(mtctx->cctxPool)
+ + ZSTDMT_sizeof_seqPool(mtctx->seqPool)
+ + ZSTD_sizeof_CDict(mtctx->cdictLocal)
+ + mtctx->roundBuff.capacity;
+}
+
+/* Internal only */
+size_t
+ZSTDMT_CCtxParam_setMTCtxParameter(ZSTD_CCtx_params* params,
+ ZSTDMT_parameter parameter,
+ int value)
+{
+ DEBUGLOG(4, "ZSTDMT_CCtxParam_setMTCtxParameter");
+ switch(parameter)
+ {
+ case ZSTDMT_p_jobSize :
+ DEBUGLOG(4, "ZSTDMT_CCtxParam_setMTCtxParameter : set jobSize to %i", value);
+ if ( value != 0 /* default */
+ && value < ZSTDMT_JOBSIZE_MIN)
+ value = ZSTDMT_JOBSIZE_MIN;
+ assert(value >= 0);
+ if (value > ZSTDMT_JOBSIZE_MAX) value = ZSTDMT_JOBSIZE_MAX;
+ params->jobSize = value;
+ return value;
+
+ case ZSTDMT_p_overlapLog :
+ DEBUGLOG(4, "ZSTDMT_p_overlapLog : %i", value);
+ if (value < ZSTD_OVERLAPLOG_MIN) value = ZSTD_OVERLAPLOG_MIN;
+ if (value > ZSTD_OVERLAPLOG_MAX) value = ZSTD_OVERLAPLOG_MAX;
+ params->overlapLog = value;
+ return value;
+
+ case ZSTDMT_p_rsyncable :
+ value = (value != 0);
+ params->rsyncable = value;
+ return value;
+
+ default :
+ return ERROR(parameter_unsupported);
+ }
+}
+
+size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, int value)
+{
+ DEBUGLOG(4, "ZSTDMT_setMTCtxParameter");
+ return ZSTDMT_CCtxParam_setMTCtxParameter(&mtctx->params, parameter, value);
+}
+
+size_t ZSTDMT_getMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, int* value)
+{
+ switch (parameter) {
+ case ZSTDMT_p_jobSize:
+ assert(mtctx->params.jobSize <= INT_MAX);
+ *value = (int)(mtctx->params.jobSize);
+ break;
+ case ZSTDMT_p_overlapLog:
+ *value = mtctx->params.overlapLog;
+ break;
+ case ZSTDMT_p_rsyncable:
+ *value = mtctx->params.rsyncable;
+ break;
+ default:
+ return ERROR(parameter_unsupported);
+ }
+ return 0;
+}
+
+/* Sets parameters relevant to the compression job,
+ * initializing others to default values. */
+static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(ZSTD_CCtx_params const params)
+{
+ ZSTD_CCtx_params jobParams;
+ memset(&jobParams, 0, sizeof(jobParams));
+
+ jobParams.cParams = params.cParams;
+ jobParams.fParams = params.fParams;
+ jobParams.compressionLevel = params.compressionLevel;
+
+ return jobParams;
+}
+
+
+/* ZSTDMT_resize() :
+ * @return : error code if fails, 0 on success */
+static size_t ZSTDMT_resize(ZSTDMT_CCtx* mtctx, unsigned nbWorkers)
+{
+ if (POOL_resize(mtctx->factory, nbWorkers)) return ERROR(memory_allocation);
+ CHECK_F( ZSTDMT_expandJobsTable(mtctx, nbWorkers) );
+ mtctx->bufPool = ZSTDMT_expandBufferPool(mtctx->bufPool, nbWorkers);
+ if (mtctx->bufPool == NULL) return ERROR(memory_allocation);
+ mtctx->cctxPool = ZSTDMT_expandCCtxPool(mtctx->cctxPool, nbWorkers);
+ if (mtctx->cctxPool == NULL) return ERROR(memory_allocation);
+ mtctx->seqPool = ZSTDMT_expandSeqPool(mtctx->seqPool, nbWorkers);
+ if (mtctx->seqPool == NULL) return ERROR(memory_allocation);
+ ZSTDMT_CCtxParam_setNbWorkers(&mtctx->params, nbWorkers);
+ return 0;
+}
+
+
+/*! ZSTDMT_updateCParams_whileCompressing() :
+ * Updates a selected set of compression parameters, remaining compatible with currently active frame.
+ * New parameters will be applied to next compression job. */
+void ZSTDMT_updateCParams_whileCompressing(ZSTDMT_CCtx* mtctx, const ZSTD_CCtx_params* cctxParams)
+{
+ U32 const saved_wlog = mtctx->params.cParams.windowLog; /* Do not modify windowLog while compressing */
+ int const compressionLevel = cctxParams->compressionLevel;
+ DEBUGLOG(5, "ZSTDMT_updateCParams_whileCompressing (level:%i)",
+ compressionLevel);
+ mtctx->params.compressionLevel = compressionLevel;
+ { ZSTD_compressionParameters cParams = ZSTD_getCParamsFromCCtxParams(cctxParams, 0, 0);
+ cParams.windowLog = saved_wlog;
+ mtctx->params.cParams = cParams;
+ }
+}
+
+/* ZSTDMT_getFrameProgression():
+ * tells how much data has been consumed (input) and produced (output) for current frame.
+ * able to count progression inside worker threads.
+ * Note : mutex will be acquired during statistics collection inside workers. */
+ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx)
+{
+ ZSTD_frameProgression fps;
+ DEBUGLOG(5, "ZSTDMT_getFrameProgression");
+ fps.ingested = mtctx->consumed + mtctx->inBuff.filled;
+ fps.consumed = mtctx->consumed;
+ fps.produced = fps.flushed = mtctx->produced;
+ fps.currentJobID = mtctx->nextJobID;
+ fps.nbActiveWorkers = 0;
+ { unsigned jobNb;
+ unsigned lastJobNb = mtctx->nextJobID + mtctx->jobReady; assert(mtctx->jobReady <= 1);
+ DEBUGLOG(6, "ZSTDMT_getFrameProgression: jobs: from %u to <%u (jobReady:%u)",
+ mtctx->doneJobID, lastJobNb, mtctx->jobReady)
+ for (jobNb = mtctx->doneJobID ; jobNb < lastJobNb ; jobNb++) {
+ unsigned const wJobID = jobNb & mtctx->jobIDMask;
+ ZSTDMT_jobDescription* jobPtr = &mtctx->jobs[wJobID];
+ ZSTD_pthread_mutex_lock(&jobPtr->job_mutex);
+ { size_t const cResult = jobPtr->cSize;
+ size_t const produced = ZSTD_isError(cResult) ? 0 : cResult;
+ size_t const flushed = ZSTD_isError(cResult) ? 0 : jobPtr->dstFlushed;
+ assert(flushed <= produced);
+ fps.ingested += jobPtr->src.size;
+ fps.consumed += jobPtr->consumed;
+ fps.produced += produced;
+ fps.flushed += flushed;
+ fps.nbActiveWorkers += (jobPtr->consumed < jobPtr->src.size);
+ }
+ ZSTD_pthread_mutex_unlock(&mtctx->jobs[wJobID].job_mutex);
+ }
+ }
+ return fps;
+}
+
+
+size_t ZSTDMT_toFlushNow(ZSTDMT_CCtx* mtctx)
+{
+ size_t toFlush;
+ unsigned const jobID = mtctx->doneJobID;
+ assert(jobID <= mtctx->nextJobID);
+ if (jobID == mtctx->nextJobID) return 0; /* no active job => nothing to flush */
+
+ /* look into oldest non-fully-flushed job */
+ { unsigned const wJobID = jobID & mtctx->jobIDMask;
+ ZSTDMT_jobDescription* const jobPtr = &mtctx->jobs[wJobID];
+ ZSTD_pthread_mutex_lock(&jobPtr->job_mutex);
+ { size_t const cResult = jobPtr->cSize;
+ size_t const produced = ZSTD_isError(cResult) ? 0 : cResult;
+ size_t const flushed = ZSTD_isError(cResult) ? 0 : jobPtr->dstFlushed;
+ assert(flushed <= produced);
+ toFlush = produced - flushed;
+ if (toFlush==0 && (jobPtr->consumed >= jobPtr->src.size)) {
+ /* doneJobID is not-fully-flushed, but toFlush==0 : doneJobID should be compressing some more data */
+ assert(jobPtr->consumed < jobPtr->src.size);
+ }
+ }
+ ZSTD_pthread_mutex_unlock(&mtctx->jobs[wJobID].job_mutex);
+ }
+
+ return toFlush;
+}
+
+
+/* ------------------------------------------ */
+/* ===== Multi-threaded compression ===== */
+/* ------------------------------------------ */
+
+static unsigned ZSTDMT_computeTargetJobLog(ZSTD_CCtx_params const params)
+{
+ if (params.ldmParams.enableLdm)
+ /* In Long Range Mode, the windowLog is typically oversized.
+ * In which case, it's preferable to determine the jobSize
+ * based on chainLog instead. */
+ return MAX(21, params.cParams.chainLog + 4);
+ return MAX(20, params.cParams.windowLog + 2);
+}
+
+static int ZSTDMT_overlapLog_default(ZSTD_strategy strat)
+{
+ switch(strat)
+ {
+ case ZSTD_btultra2:
+ return 9;
+ case ZSTD_btultra:
+ case ZSTD_btopt:
+ return 8;
+ case ZSTD_btlazy2:
+ case ZSTD_lazy2:
+ return 7;
+ case ZSTD_lazy:
+ case ZSTD_greedy:
+ case ZSTD_dfast:
+ case ZSTD_fast:
+ default:;
+ }
+ return 6;
+}
+
+static int ZSTDMT_overlapLog(int ovlog, ZSTD_strategy strat)
+{
+ assert(0 <= ovlog && ovlog <= 9);
+ if (ovlog == 0) return ZSTDMT_overlapLog_default(strat);
+ return ovlog;
+}
+
+static size_t ZSTDMT_computeOverlapSize(ZSTD_CCtx_params const params)
+{
+ int const overlapRLog = 9 - ZSTDMT_overlapLog(params.overlapLog, params.cParams.strategy);
+ int ovLog = (overlapRLog >= 8) ? 0 : (params.cParams.windowLog - overlapRLog);
+ assert(0 <= overlapRLog && overlapRLog <= 8);
+ if (params.ldmParams.enableLdm) {
+ /* In Long Range Mode, the windowLog is typically oversized.
+ * In which case, it's preferable to determine the jobSize
+ * based on chainLog instead.
+ * Then, ovLog becomes a fraction of the jobSize, rather than windowSize */
+ ovLog = MIN(params.cParams.windowLog, ZSTDMT_computeTargetJobLog(params) - 2)
+ - overlapRLog;
+ }
+ assert(0 <= ovLog && ovLog <= 30);
+ DEBUGLOG(4, "overlapLog : %i", params.overlapLog);
+ DEBUGLOG(4, "overlap size : %i", 1 << ovLog);
+ return (ovLog==0) ? 0 : (size_t)1 << ovLog;
+}
+
+static unsigned
+ZSTDMT_computeNbJobs(ZSTD_CCtx_params params, size_t srcSize, unsigned nbWorkers)
+{
+ assert(nbWorkers>0);
+ { size_t const jobSizeTarget = (size_t)1 << ZSTDMT_computeTargetJobLog(params);
+ size_t const jobMaxSize = jobSizeTarget << 2;
+ size_t const passSizeMax = jobMaxSize * nbWorkers;
+ unsigned const multiplier = (unsigned)(srcSize / passSizeMax) + 1;
+ unsigned const nbJobsLarge = multiplier * nbWorkers;
+ unsigned const nbJobsMax = (unsigned)(srcSize / jobSizeTarget) + 1;
+ unsigned const nbJobsSmall = MIN(nbJobsMax, nbWorkers);
+ return (multiplier>1) ? nbJobsLarge : nbJobsSmall;
+} }
+
+/* ZSTDMT_compress_advanced_internal() :
+ * This is a blocking function : it will only give back control to caller after finishing its compression job.
+ */
+static size_t ZSTDMT_compress_advanced_internal(
+ ZSTDMT_CCtx* mtctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ const ZSTD_CDict* cdict,
+ ZSTD_CCtx_params params)
+{
+ ZSTD_CCtx_params const jobParams = ZSTDMT_initJobCCtxParams(params);
+ size_t const overlapSize = ZSTDMT_computeOverlapSize(params);
+ unsigned const nbJobs = ZSTDMT_computeNbJobs(params, srcSize, params.nbWorkers);
+ size_t const proposedJobSize = (srcSize + (nbJobs-1)) / nbJobs;
+ size_t const avgJobSize = (((proposedJobSize-1) & 0x1FFFF) < 0x7FFF) ? proposedJobSize + 0xFFFF : proposedJobSize; /* avoid too small last block */
+ const char* const srcStart = (const char*)src;
+ size_t remainingSrcSize = srcSize;
+ unsigned const compressWithinDst = (dstCapacity >= ZSTD_compressBound(srcSize)) ? nbJobs : (unsigned)(dstCapacity / ZSTD_compressBound(avgJobSize)); /* presumes avgJobSize >= 256 KB, which should be the case */
+ size_t frameStartPos = 0, dstBufferPos = 0;
+ assert(jobParams.nbWorkers == 0);
+ assert(mtctx->cctxPool->totalCCtx == params.nbWorkers);
+
+ params.jobSize = (U32)avgJobSize;
+ DEBUGLOG(4, "ZSTDMT_compress_advanced_internal: nbJobs=%2u (rawSize=%u bytes; fixedSize=%u) ",
+ nbJobs, (U32)proposedJobSize, (U32)avgJobSize);
+
+ if ((nbJobs==1) | (params.nbWorkers<=1)) { /* fallback to single-thread mode : this is a blocking invocation anyway */
+ ZSTD_CCtx* const cctx = mtctx->cctxPool->cctx[0];
+ DEBUGLOG(4, "ZSTDMT_compress_advanced_internal: fallback to single-thread mode");
+ if (cdict) return ZSTD_compress_usingCDict_advanced(cctx, dst, dstCapacity, src, srcSize, cdict, jobParams.fParams);
+ return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, NULL, 0, jobParams);
+ }
+
+ assert(avgJobSize >= 256 KB); /* condition for ZSTD_compressBound(A) + ZSTD_compressBound(B) <= ZSTD_compressBound(A+B), required to compress directly into Dst (no additional buffer) */
+ ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(avgJobSize) );
+ if (ZSTDMT_serialState_reset(&mtctx->serial, mtctx->seqPool, params, avgJobSize))
+ return ERROR(memory_allocation);
+
+ CHECK_F( ZSTDMT_expandJobsTable(mtctx, nbJobs) ); /* only expands if necessary */
+
+ { unsigned u;
+ for (u=0; u<nbJobs; u++) {
+ size_t const jobSize = MIN(remainingSrcSize, avgJobSize);
+ size_t const dstBufferCapacity = ZSTD_compressBound(jobSize);
+ buffer_t const dstAsBuffer = { (char*)dst + dstBufferPos, dstBufferCapacity };
+ buffer_t const dstBuffer = u < compressWithinDst ? dstAsBuffer : g_nullBuffer;
+ size_t dictSize = u ? overlapSize : 0;
+
+ mtctx->jobs[u].prefix.start = srcStart + frameStartPos - dictSize;
+ mtctx->jobs[u].prefix.size = dictSize;
+ mtctx->jobs[u].src.start = srcStart + frameStartPos;
+ mtctx->jobs[u].src.size = jobSize; assert(jobSize > 0); /* avoid job.src.size == 0 */
+ mtctx->jobs[u].consumed = 0;
+ mtctx->jobs[u].cSize = 0;
+ mtctx->jobs[u].cdict = (u==0) ? cdict : NULL;
+ mtctx->jobs[u].fullFrameSize = srcSize;
+ mtctx->jobs[u].params = jobParams;
+ /* do not calculate checksum within sections, but write it in header for first section */
+ mtctx->jobs[u].dstBuff = dstBuffer;
+ mtctx->jobs[u].cctxPool = mtctx->cctxPool;
+ mtctx->jobs[u].bufPool = mtctx->bufPool;
+ mtctx->jobs[u].seqPool = mtctx->seqPool;
+ mtctx->jobs[u].serial = &mtctx->serial;
+ mtctx->jobs[u].jobID = u;
+ mtctx->jobs[u].firstJob = (u==0);
+ mtctx->jobs[u].lastJob = (u==nbJobs-1);
+
+ DEBUGLOG(5, "ZSTDMT_compress_advanced_internal: posting job %u (%u bytes)", u, (U32)jobSize);
+ DEBUG_PRINTHEX(6, mtctx->jobs[u].prefix.start, 12);
+ POOL_add(mtctx->factory, ZSTDMT_compressionJob, &mtctx->jobs[u]);
+
+ frameStartPos += jobSize;
+ dstBufferPos += dstBufferCapacity;
+ remainingSrcSize -= jobSize;
+ } }
+
+ /* collect result */
+ { size_t error = 0, dstPos = 0;
+ unsigned jobID;
+ for (jobID=0; jobID<nbJobs; jobID++) {
+ DEBUGLOG(5, "waiting for job %u ", jobID);
+ ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobs[jobID].job_mutex);
+ while (mtctx->jobs[jobID].consumed < mtctx->jobs[jobID].src.size) {
+ DEBUGLOG(5, "waiting for jobCompleted signal from job %u", jobID);
+ ZSTD_pthread_cond_wait(&mtctx->jobs[jobID].job_cond, &mtctx->jobs[jobID].job_mutex);
+ }
+ ZSTD_pthread_mutex_unlock(&mtctx->jobs[jobID].job_mutex);
+ DEBUGLOG(5, "ready to write job %u ", jobID);
+
+ { size_t const cSize = mtctx->jobs[jobID].cSize;
+ if (ZSTD_isError(cSize)) error = cSize;
+ if ((!error) && (dstPos + cSize > dstCapacity)) error = ERROR(dstSize_tooSmall);
+ if (jobID) { /* note : job 0 is written directly at dst, which is correct position */
+ if (!error)
+ memmove((char*)dst + dstPos, mtctx->jobs[jobID].dstBuff.start, cSize); /* may overlap when job compressed within dst */
+ if (jobID >= compressWithinDst) { /* job compressed into its own buffer, which must be released */
+ DEBUGLOG(5, "releasing buffer %u>=%u", jobID, compressWithinDst);
+ ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].dstBuff);
+ } }
+ mtctx->jobs[jobID].dstBuff = g_nullBuffer;
+ mtctx->jobs[jobID].cSize = 0;
+ dstPos += cSize ;
+ }
+ } /* for (jobID=0; jobID<nbJobs; jobID++) */
+
+ DEBUGLOG(4, "checksumFlag : %u ", params.fParams.checksumFlag);
+ if (params.fParams.checksumFlag) {
+ U32 const checksum = (U32)XXH64_digest(&mtctx->serial.xxhState);
+ if (dstPos + 4 > dstCapacity) {
+ error = ERROR(dstSize_tooSmall);
+ } else {
+ DEBUGLOG(4, "writing checksum : %08X \n", checksum);
+ MEM_writeLE32((char*)dst + dstPos, checksum);
+ dstPos += 4;
+ } }
+
+ if (!error) DEBUGLOG(4, "compressed size : %u ", (U32)dstPos);
+ return error ? error : dstPos;
+ }
+}
+
+size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ const ZSTD_CDict* cdict,
+ ZSTD_parameters params,
+ int overlapLog)
+{
+ ZSTD_CCtx_params cctxParams = mtctx->params;
+ cctxParams.cParams = params.cParams;
+ cctxParams.fParams = params.fParams;
+ assert(ZSTD_OVERLAPLOG_MIN <= overlapLog && overlapLog <= ZSTD_OVERLAPLOG_MAX);
+ cctxParams.overlapLog = overlapLog;
+ return ZSTDMT_compress_advanced_internal(mtctx,
+ dst, dstCapacity,
+ src, srcSize,
+ cdict, cctxParams);
+}
+
+
+size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ int compressionLevel)
+{
+ ZSTD_parameters params = ZSTD_getParams(compressionLevel, srcSize, 0);
+ int const overlapLog = ZSTDMT_overlapLog_default(params.cParams.strategy);
+ params.fParams.contentSizeFlag = 1;
+ return ZSTDMT_compress_advanced(mtctx, dst, dstCapacity, src, srcSize, NULL, params, overlapLog);
+}
+
+
+/* ====================================== */
+/* ======= Streaming API ======= */
+/* ====================================== */
+
+size_t ZSTDMT_initCStream_internal(
+ ZSTDMT_CCtx* mtctx,
+ const void* dict, size_t dictSize, ZSTD_dictContentType_e dictContentType,
+ const ZSTD_CDict* cdict, ZSTD_CCtx_params params,
+ unsigned long long pledgedSrcSize)
+{
+ DEBUGLOG(4, "ZSTDMT_initCStream_internal (pledgedSrcSize=%u, nbWorkers=%u, cctxPool=%u)",
+ (U32)pledgedSrcSize, params.nbWorkers, mtctx->cctxPool->totalCCtx);
+
+ /* params supposed partially fully validated at this point */
+ assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));
+ assert(!((dict) && (cdict))); /* either dict or cdict, not both */
+
+ /* init */
+ if (params.nbWorkers != mtctx->params.nbWorkers)
+ CHECK_F( ZSTDMT_resize(mtctx, params.nbWorkers) );
+
+ if (params.jobSize != 0 && params.jobSize < ZSTDMT_JOBSIZE_MIN) params.jobSize = ZSTDMT_JOBSIZE_MIN;
+ if (params.jobSize > (size_t)ZSTDMT_JOBSIZE_MAX) params.jobSize = ZSTDMT_JOBSIZE_MAX;
+
+ mtctx->singleBlockingThread = (pledgedSrcSize <= ZSTDMT_JOBSIZE_MIN); /* do not trigger multi-threading when srcSize is too small */
+ if (mtctx->singleBlockingThread) {
+ ZSTD_CCtx_params const singleThreadParams = ZSTDMT_initJobCCtxParams(params);
+ DEBUGLOG(5, "ZSTDMT_initCStream_internal: switch to single blocking thread mode");
+ assert(singleThreadParams.nbWorkers == 0);
+ return ZSTD_initCStream_internal(mtctx->cctxPool->cctx[0],
+ dict, dictSize, cdict,
+ singleThreadParams, pledgedSrcSize);
+ }
+
+ DEBUGLOG(4, "ZSTDMT_initCStream_internal: %u workers", params.nbWorkers);
+
+ if (mtctx->allJobsCompleted == 0) { /* previous compression not correctly finished */
+ ZSTDMT_waitForAllJobsCompleted(mtctx);
+ ZSTDMT_releaseAllJobResources(mtctx);
+ mtctx->allJobsCompleted = 1;
+ }
+
+ mtctx->params = params;
+ mtctx->frameContentSize = pledgedSrcSize;
+ if (dict) {
+ ZSTD_freeCDict(mtctx->cdictLocal);
+ mtctx->cdictLocal = ZSTD_createCDict_advanced(dict, dictSize,
+ ZSTD_dlm_byCopy, dictContentType, /* note : a loadPrefix becomes an internal CDict */
+ params.cParams, mtctx->cMem);
+ mtctx->cdict = mtctx->cdictLocal;
+ if (mtctx->cdictLocal == NULL) return ERROR(memory_allocation);
+ } else {
+ ZSTD_freeCDict(mtctx->cdictLocal);
+ mtctx->cdictLocal = NULL;
+ mtctx->cdict = cdict;
+ }
+
+ mtctx->targetPrefixSize = ZSTDMT_computeOverlapSize(params);
+ DEBUGLOG(4, "overlapLog=%i => %u KB", params.overlapLog, (U32)(mtctx->targetPrefixSize>>10));
+ mtctx->targetSectionSize = params.jobSize;
+ if (mtctx->targetSectionSize == 0) {
+ mtctx->targetSectionSize = 1ULL << ZSTDMT_computeTargetJobLog(params);
+ }
+ if (params.rsyncable) {
+ /* Aim for the targetsectionSize as the average job size. */
+ U32 const jobSizeMB = (U32)(mtctx->targetSectionSize >> 20);
+ U32 const rsyncBits = ZSTD_highbit32(jobSizeMB) + 20;
+ assert(jobSizeMB >= 1);
+ DEBUGLOG(4, "rsyncLog = %u", rsyncBits);
+ mtctx->rsync.hash = 0;
+ mtctx->rsync.hitMask = (1ULL << rsyncBits) - 1;
+ mtctx->rsync.primePower = ZSTD_rollingHash_primePower(RSYNC_LENGTH);
+ }
+ if (mtctx->targetSectionSize < mtctx->targetPrefixSize) mtctx->targetSectionSize = mtctx->targetPrefixSize; /* job size must be >= overlap size */
+ DEBUGLOG(4, "Job Size : %u KB (note : set to %u)", (U32)(mtctx->targetSectionSize>>10), (U32)params.jobSize);
+ DEBUGLOG(4, "inBuff Size : %u KB", (U32)(mtctx->targetSectionSize>>10));
+ ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(mtctx->targetSectionSize));
+ {
+ /* If ldm is enabled we need windowSize space. */
+ size_t const windowSize = mtctx->params.ldmParams.enableLdm ? (1U << mtctx->params.cParams.windowLog) : 0;
+ /* Two buffers of slack, plus extra space for the overlap
+ * This is the minimum slack that LDM works with. One extra because
+ * flush might waste up to targetSectionSize-1 bytes. Another extra
+ * for the overlap (if > 0), then one to fill which doesn't overlap
+ * with the LDM window.
+ */
+ size_t const nbSlackBuffers = 2 + (mtctx->targetPrefixSize > 0);
+ size_t const slackSize = mtctx->targetSectionSize * nbSlackBuffers;
+ /* Compute the total size, and always have enough slack */
+ size_t const nbWorkers = MAX(mtctx->params.nbWorkers, 1);
+ size_t const sectionsSize = mtctx->targetSectionSize * nbWorkers;
+ size_t const capacity = MAX(windowSize, sectionsSize) + slackSize;
+ if (mtctx->roundBuff.capacity < capacity) {
+ if (mtctx->roundBuff.buffer)
+ ZSTD_free(mtctx->roundBuff.buffer, mtctx->cMem);
+ mtctx->roundBuff.buffer = (BYTE*)ZSTD_malloc(capacity, mtctx->cMem);
+ if (mtctx->roundBuff.buffer == NULL) {
+ mtctx->roundBuff.capacity = 0;
+ return ERROR(memory_allocation);
+ }
+ mtctx->roundBuff.capacity = capacity;
+ }
+ }
+ DEBUGLOG(4, "roundBuff capacity : %u KB", (U32)(mtctx->roundBuff.capacity>>10));
+ mtctx->roundBuff.pos = 0;
+ mtctx->inBuff.buffer = g_nullBuffer;
+ mtctx->inBuff.filled = 0;
+ mtctx->inBuff.prefix = kNullRange;
+ mtctx->doneJobID = 0;
+ mtctx->nextJobID = 0;
+ mtctx->frameEnded = 0;
+ mtctx->allJobsCompleted = 0;
+ mtctx->consumed = 0;
+ mtctx->produced = 0;
+ if (ZSTDMT_serialState_reset(&mtctx->serial, mtctx->seqPool, params, mtctx->targetSectionSize))
+ return ERROR(memory_allocation);
+ return 0;
+}
+
+size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx,
+ const void* dict, size_t dictSize,
+ ZSTD_parameters params,
+ unsigned long long pledgedSrcSize)
+{
+ ZSTD_CCtx_params cctxParams = mtctx->params; /* retrieve sticky params */
+ DEBUGLOG(4, "ZSTDMT_initCStream_advanced (pledgedSrcSize=%u)", (U32)pledgedSrcSize);
+ cctxParams.cParams = params.cParams;
+ cctxParams.fParams = params.fParams;
+ return ZSTDMT_initCStream_internal(mtctx, dict, dictSize, ZSTD_dct_auto, NULL,
+ cctxParams, pledgedSrcSize);
+}
+
+size_t ZSTDMT_initCStream_usingCDict(ZSTDMT_CCtx* mtctx,
+ const ZSTD_CDict* cdict,
+ ZSTD_frameParameters fParams,
+ unsigned long long pledgedSrcSize)
+{
+ ZSTD_CCtx_params cctxParams = mtctx->params;
+ if (cdict==NULL) return ERROR(dictionary_wrong); /* method incompatible with NULL cdict */
+ cctxParams.cParams = ZSTD_getCParamsFromCDict(cdict);
+ cctxParams.fParams = fParams;
+ return ZSTDMT_initCStream_internal(mtctx, NULL, 0 /*dictSize*/, ZSTD_dct_auto, cdict,
+ cctxParams, pledgedSrcSize);
+}
+
+
+/* ZSTDMT_resetCStream() :
+ * pledgedSrcSize can be zero == unknown (for the time being)
+ * prefer using ZSTD_CONTENTSIZE_UNKNOWN,
+ * as `0` might mean "empty" in the future */
+size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize)
+{
+ if (!pledgedSrcSize) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;
+ return ZSTDMT_initCStream_internal(mtctx, NULL, 0, ZSTD_dct_auto, 0, mtctx->params,
+ pledgedSrcSize);
+}
+
+size_t ZSTDMT_initCStream(ZSTDMT_CCtx* mtctx, int compressionLevel) {
+ ZSTD_parameters const params = ZSTD_getParams(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, 0);
+ ZSTD_CCtx_params cctxParams = mtctx->params; /* retrieve sticky params */
+ DEBUGLOG(4, "ZSTDMT_initCStream (cLevel=%i)", compressionLevel);
+ cctxParams.cParams = params.cParams;
+ cctxParams.fParams = params.fParams;
+ return ZSTDMT_initCStream_internal(mtctx, NULL, 0, ZSTD_dct_auto, NULL, cctxParams, ZSTD_CONTENTSIZE_UNKNOWN);
+}
+
+
+/* ZSTDMT_writeLastEmptyBlock()
+ * Write a single empty block with an end-of-frame to finish a frame.
+ * Job must be created from streaming variant.
+ * This function is always successfull if expected conditions are fulfilled.
+ */
+static void ZSTDMT_writeLastEmptyBlock(ZSTDMT_jobDescription* job)
+{
+ assert(job->lastJob == 1);
+ assert(job->src.size == 0); /* last job is empty -> will be simplified into a last empty block */
+ assert(job->firstJob == 0); /* cannot be first job, as it also needs to create frame header */
+ assert(job->dstBuff.start == NULL); /* invoked from streaming variant only (otherwise, dstBuff might be user's output) */
+ job->dstBuff = ZSTDMT_getBuffer(job->bufPool);
+ if (job->dstBuff.start == NULL) {
+ job->cSize = ERROR(memory_allocation);
+ return;
+ }
+ assert(job->dstBuff.capacity >= ZSTD_blockHeaderSize); /* no buffer should ever be that small */
+ job->src = kNullRange;
+ job->cSize = ZSTD_writeLastEmptyBlock(job->dstBuff.start, job->dstBuff.capacity);
+ assert(!ZSTD_isError(job->cSize));
+ assert(job->consumed == 0);
+}
+
+static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZSTD_EndDirective endOp)
+{
+ unsigned const jobID = mtctx->nextJobID & mtctx->jobIDMask;
+ int const endFrame = (endOp == ZSTD_e_end);
+
+ if (mtctx->nextJobID > mtctx->doneJobID + mtctx->jobIDMask) {
+ DEBUGLOG(5, "ZSTDMT_createCompressionJob: will not create new job : table is full");
+ assert((mtctx->nextJobID & mtctx->jobIDMask) == (mtctx->doneJobID & mtctx->jobIDMask));
+ return 0;
+ }
+
+ if (!mtctx->jobReady) {
+ BYTE const* src = (BYTE const*)mtctx->inBuff.buffer.start;
+ DEBUGLOG(5, "ZSTDMT_createCompressionJob: preparing job %u to compress %u bytes with %u preload ",
+ mtctx->nextJobID, (U32)srcSize, (U32)mtctx->inBuff.prefix.size);
+ mtctx->jobs[jobID].src.start = src;
+ mtctx->jobs[jobID].src.size = srcSize;
+ assert(mtctx->inBuff.filled >= srcSize);
+ mtctx->jobs[jobID].prefix = mtctx->inBuff.prefix;
+ mtctx->jobs[jobID].consumed = 0;
+ mtctx->jobs[jobID].cSize = 0;
+ mtctx->jobs[jobID].params = mtctx->params;
+ mtctx->jobs[jobID].cdict = mtctx->nextJobID==0 ? mtctx->cdict : NULL;
+ mtctx->jobs[jobID].fullFrameSize = mtctx->frameContentSize;
+ mtctx->jobs[jobID].dstBuff = g_nullBuffer;
+ mtctx->jobs[jobID].cctxPool = mtctx->cctxPool;
+ mtctx->jobs[jobID].bufPool = mtctx->bufPool;
+ mtctx->jobs[jobID].seqPool = mtctx->seqPool;
+ mtctx->jobs[jobID].serial = &mtctx->serial;
+ mtctx->jobs[jobID].jobID = mtctx->nextJobID;
+ mtctx->jobs[jobID].firstJob = (mtctx->nextJobID==0);
+ mtctx->jobs[jobID].lastJob = endFrame;
+ mtctx->jobs[jobID].frameChecksumNeeded = mtctx->params.fParams.checksumFlag && endFrame && (mtctx->nextJobID>0);
+ mtctx->jobs[jobID].dstFlushed = 0;
+
+ /* Update the round buffer pos and clear the input buffer to be reset */
+ mtctx->roundBuff.pos += srcSize;
+ mtctx->inBuff.buffer = g_nullBuffer;
+ mtctx->inBuff.filled = 0;
+ /* Set the prefix */
+ if (!endFrame) {
+ size_t const newPrefixSize = MIN(srcSize, mtctx->targetPrefixSize);
+ mtctx->inBuff.prefix.start = src + srcSize - newPrefixSize;
+ mtctx->inBuff.prefix.size = newPrefixSize;
+ } else { /* endFrame==1 => no need for another input buffer */
+ mtctx->inBuff.prefix = kNullRange;
+ mtctx->frameEnded = endFrame;
+ if (mtctx->nextJobID == 0) {
+ /* single job exception : checksum is already calculated directly within worker thread */
+ mtctx->params.fParams.checksumFlag = 0;
+ } }
+
+ if ( (srcSize == 0)
+ && (mtctx->nextJobID>0)/*single job must also write frame header*/ ) {
+ DEBUGLOG(5, "ZSTDMT_createCompressionJob: creating a last empty block to end frame");
+ assert(endOp == ZSTD_e_end); /* only possible case : need to end the frame with an empty last block */
+ ZSTDMT_writeLastEmptyBlock(mtctx->jobs + jobID);
+ mtctx->nextJobID++;
+ return 0;
+ }
+ }
+
+ DEBUGLOG(5, "ZSTDMT_createCompressionJob: posting job %u : %u bytes (end:%u, jobNb == %u (mod:%u))",
+ mtctx->nextJobID,
+ (U32)mtctx->jobs[jobID].src.size,
+ mtctx->jobs[jobID].lastJob,
+ mtctx->nextJobID,
+ jobID);
+ if (POOL_tryAdd(mtctx->factory, ZSTDMT_compressionJob, &mtctx->jobs[jobID])) {
+ mtctx->nextJobID++;
+ mtctx->jobReady = 0;
+ } else {
+ DEBUGLOG(5, "ZSTDMT_createCompressionJob: no worker available for job %u", mtctx->nextJobID);
+ mtctx->jobReady = 1;
+ }
+ return 0;
+}
+
+
+/*! ZSTDMT_flushProduced() :
+ * flush whatever data has been produced but not yet flushed in current job.
+ * move to next job if current one is fully flushed.
+ * `output` : `pos` will be updated with amount of data flushed .
+ * `blockToFlush` : if >0, the function will block and wait if there is no data available to flush .
+ * @return : amount of data remaining within internal buffer, 0 if no more, 1 if unknown but > 0, or an error code */
+static size_t ZSTDMT_flushProduced(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, unsigned blockToFlush, ZSTD_EndDirective end)
+{
+ unsigned const wJobID = mtctx->doneJobID & mtctx->jobIDMask;
+ DEBUGLOG(5, "ZSTDMT_flushProduced (blocking:%u , job %u <= %u)",
+ blockToFlush, mtctx->doneJobID, mtctx->nextJobID);
+ assert(output->size >= output->pos);
+
+ ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobs[wJobID].job_mutex);
+ if ( blockToFlush
+ && (mtctx->doneJobID < mtctx->nextJobID) ) {
+ assert(mtctx->jobs[wJobID].dstFlushed <= mtctx->jobs[wJobID].cSize);
+ while (mtctx->jobs[wJobID].dstFlushed == mtctx->jobs[wJobID].cSize) { /* nothing to flush */
+ if (mtctx->jobs[wJobID].consumed == mtctx->jobs[wJobID].src.size) {
+ DEBUGLOG(5, "job %u is completely consumed (%u == %u) => don't wait for cond, there will be none",
+ mtctx->doneJobID, (U32)mtctx->jobs[wJobID].consumed, (U32)mtctx->jobs[wJobID].src.size);
+ break;
+ }
+ DEBUGLOG(5, "waiting for something to flush from job %u (currently flushed: %u bytes)",
+ mtctx->doneJobID, (U32)mtctx->jobs[wJobID].dstFlushed);
+ ZSTD_pthread_cond_wait(&mtctx->jobs[wJobID].job_cond, &mtctx->jobs[wJobID].job_mutex); /* block when nothing to flush but some to come */
+ } }
+
+ /* try to flush something */
+ { size_t cSize = mtctx->jobs[wJobID].cSize; /* shared */
+ size_t const srcConsumed = mtctx->jobs[wJobID].consumed; /* shared */
+ size_t const srcSize = mtctx->jobs[wJobID].src.size; /* read-only, could be done after mutex lock, but no-declaration-after-statement */
+ ZSTD_pthread_mutex_unlock(&mtctx->jobs[wJobID].job_mutex);
+ if (ZSTD_isError(cSize)) {
+ DEBUGLOG(5, "ZSTDMT_flushProduced: job %u : compression error detected : %s",
+ mtctx->doneJobID, ZSTD_getErrorName(cSize));
+ ZSTDMT_waitForAllJobsCompleted(mtctx);
+ ZSTDMT_releaseAllJobResources(mtctx);
+ return cSize;
+ }
+ /* add frame checksum if necessary (can only happen once) */
+ assert(srcConsumed <= srcSize);
+ if ( (srcConsumed == srcSize) /* job completed -> worker no longer active */
+ && mtctx->jobs[wJobID].frameChecksumNeeded ) {
+ U32 const checksum = (U32)XXH64_digest(&mtctx->serial.xxhState);
+ DEBUGLOG(4, "ZSTDMT_flushProduced: writing checksum : %08X \n", checksum);
+ MEM_writeLE32((char*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].cSize, checksum);
+ cSize += 4;
+ mtctx->jobs[wJobID].cSize += 4; /* can write this shared value, as worker is no longer active */
+ mtctx->jobs[wJobID].frameChecksumNeeded = 0;
+ }
+
+ if (cSize > 0) { /* compression is ongoing or completed */
+ size_t const toFlush = MIN(cSize - mtctx->jobs[wJobID].dstFlushed, output->size - output->pos);
+ DEBUGLOG(5, "ZSTDMT_flushProduced: Flushing %u bytes from job %u (completion:%u/%u, generated:%u)",
+ (U32)toFlush, mtctx->doneJobID, (U32)srcConsumed, (U32)srcSize, (U32)cSize);
+ assert(mtctx->doneJobID < mtctx->nextJobID);
+ assert(cSize >= mtctx->jobs[wJobID].dstFlushed);
+ assert(mtctx->jobs[wJobID].dstBuff.start != NULL);
+ memcpy((char*)output->dst + output->pos,
+ (const char*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].dstFlushed,
+ toFlush);
+ output->pos += toFlush;
+ mtctx->jobs[wJobID].dstFlushed += toFlush; /* can write : this value is only used by mtctx */
+
+ if ( (srcConsumed == srcSize) /* job is completed */
+ && (mtctx->jobs[wJobID].dstFlushed == cSize) ) { /* output buffer fully flushed => free this job position */
+ DEBUGLOG(5, "Job %u completed (%u bytes), moving to next one",
+ mtctx->doneJobID, (U32)mtctx->jobs[wJobID].dstFlushed);
+ ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[wJobID].dstBuff);
+ DEBUGLOG(5, "dstBuffer released");
+ mtctx->jobs[wJobID].dstBuff = g_nullBuffer;
+ mtctx->jobs[wJobID].cSize = 0; /* ensure this job slot is considered "not started" in future check */
+ mtctx->consumed += srcSize;
+ mtctx->produced += cSize;
+ mtctx->doneJobID++;
+ } }
+
+ /* return value : how many bytes left in buffer ; fake it to 1 when unknown but >0 */
+ if (cSize > mtctx->jobs[wJobID].dstFlushed) return (cSize - mtctx->jobs[wJobID].dstFlushed);
+ if (srcSize > srcConsumed) return 1; /* current job not completely compressed */
+ }
+ if (mtctx->doneJobID < mtctx->nextJobID) return 1; /* some more jobs ongoing */
+ if (mtctx->jobReady) return 1; /* one job is ready to push, just not yet in the list */
+ if (mtctx->inBuff.filled > 0) return 1; /* input is not empty, and still needs to be converted into a job */
+ mtctx->allJobsCompleted = mtctx->frameEnded; /* all jobs are entirely flushed => if this one is last one, frame is completed */
+ if (end == ZSTD_e_end) return !mtctx->frameEnded; /* for ZSTD_e_end, question becomes : is frame completed ? instead of : are internal buffers fully flushed ? */
+ return 0; /* internal buffers fully flushed */
+}
+
+/**
+ * Returns the range of data used by the earliest job that is not yet complete.
+ * If the data of the first job is broken up into two segments, we cover both
+ * sections.
+ */
+static range_t ZSTDMT_getInputDataInUse(ZSTDMT_CCtx* mtctx)
+{
+ unsigned const firstJobID = mtctx->doneJobID;
+ unsigned const lastJobID = mtctx->nextJobID;
+ unsigned jobID;
+
+ for (jobID = firstJobID; jobID < lastJobID; ++jobID) {
+ unsigned const wJobID = jobID & mtctx->jobIDMask;
+ size_t consumed;
+
+ ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobs[wJobID].job_mutex);
+ consumed = mtctx->jobs[wJobID].consumed;
+ ZSTD_pthread_mutex_unlock(&mtctx->jobs[wJobID].job_mutex);
+
+ if (consumed < mtctx->jobs[wJobID].src.size) {
+ range_t range = mtctx->jobs[wJobID].prefix;
+ if (range.size == 0) {
+ /* Empty prefix */
+ range = mtctx->jobs[wJobID].src;
+ }
+ /* Job source in multiple segments not supported yet */
+ assert(range.start <= mtctx->jobs[wJobID].src.start);
+ return range;
+ }
+ }
+ return kNullRange;
+}
+
+/**
+ * Returns non-zero iff buffer and range overlap.
+ */
+static int ZSTDMT_isOverlapped(buffer_t buffer, range_t range)
+{
+ BYTE const* const bufferStart = (BYTE const*)buffer.start;
+ BYTE const* const bufferEnd = bufferStart + buffer.capacity;
+ BYTE const* const rangeStart = (BYTE const*)range.start;
+ BYTE const* const rangeEnd = rangeStart + range.size;
+
+ if (rangeStart == NULL || bufferStart == NULL)
+ return 0;
+ /* Empty ranges cannot overlap */
+ if (bufferStart == bufferEnd || rangeStart == rangeEnd)
+ return 0;
+
+ return bufferStart < rangeEnd && rangeStart < bufferEnd;
+}
+
+static int ZSTDMT_doesOverlapWindow(buffer_t buffer, ZSTD_window_t window)
+{
+ range_t extDict;
+ range_t prefix;
+
+ DEBUGLOG(5, "ZSTDMT_doesOverlapWindow");
+ extDict.start = window.dictBase + window.lowLimit;
+ extDict.size = window.dictLimit - window.lowLimit;
+
+ prefix.start = window.base + window.dictLimit;
+ prefix.size = window.nextSrc - (window.base + window.dictLimit);
+ DEBUGLOG(5, "extDict [0x%zx, 0x%zx)",
+ (size_t)extDict.start,
+ (size_t)extDict.start + extDict.size);
+ DEBUGLOG(5, "prefix [0x%zx, 0x%zx)",
+ (size_t)prefix.start,
+ (size_t)prefix.start + prefix.size);
+
+ return ZSTDMT_isOverlapped(buffer, extDict)
+ || ZSTDMT_isOverlapped(buffer, prefix);
+}
+
+static void ZSTDMT_waitForLdmComplete(ZSTDMT_CCtx* mtctx, buffer_t buffer)
+{
+ if (mtctx->params.ldmParams.enableLdm) {
+ ZSTD_pthread_mutex_t* mutex = &mtctx->serial.ldmWindowMutex;
+ DEBUGLOG(5, "ZSTDMT_waitForLdmComplete");
+ DEBUGLOG(5, "source [0x%zx, 0x%zx)",
+ (size_t)buffer.start,
+ (size_t)buffer.start + buffer.capacity);
+ ZSTD_PTHREAD_MUTEX_LOCK(mutex);
+ while (ZSTDMT_doesOverlapWindow(buffer, mtctx->serial.ldmWindow)) {
+ DEBUGLOG(5, "Waiting for LDM to finish...");
+ ZSTD_pthread_cond_wait(&mtctx->serial.ldmWindowCond, mutex);
+ }
+ DEBUGLOG(6, "Done waiting for LDM to finish");
+ ZSTD_pthread_mutex_unlock(mutex);
+ }
+}
+
+/**
+ * Attempts to set the inBuff to the next section to fill.
+ * If any part of the new section is still in use we give up.
+ * Returns non-zero if the buffer is filled.
+ */
+static int ZSTDMT_tryGetInputRange(ZSTDMT_CCtx* mtctx)
+{
+ range_t const inUse = ZSTDMT_getInputDataInUse(mtctx);
+ size_t const spaceLeft = mtctx->roundBuff.capacity - mtctx->roundBuff.pos;
+ size_t const target = mtctx->targetSectionSize;
+ buffer_t buffer;
+
+ DEBUGLOG(5, "ZSTDMT_tryGetInputRange");
+ assert(mtctx->inBuff.buffer.start == NULL);
+ assert(mtctx->roundBuff.capacity >= target);
+
+ if (spaceLeft < target) {
+ /* ZSTD_invalidateRepCodes() doesn't work for extDict variants.
+ * Simply copy the prefix to the beginning in that case.
+ */
+ BYTE* const start = (BYTE*)mtctx->roundBuff.buffer;
+ size_t const prefixSize = mtctx->inBuff.prefix.size;
+
+ buffer.start = start;
+ buffer.capacity = prefixSize;
+ if (ZSTDMT_isOverlapped(buffer, inUse)) {
+ DEBUGLOG(5, "Waiting for buffer...");
+ return 0;
+ }
+ ZSTDMT_waitForLdmComplete(mtctx, buffer);
+ memmove(start, mtctx->inBuff.prefix.start, prefixSize);
+ mtctx->inBuff.prefix.start = start;
+ mtctx->roundBuff.pos = prefixSize;
+ }
+ buffer.start = mtctx->roundBuff.buffer + mtctx->roundBuff.pos;
+ buffer.capacity = target;
+
+ if (ZSTDMT_isOverlapped(buffer, inUse)) {
+ DEBUGLOG(5, "Waiting for buffer...");
+ return 0;
+ }
+ assert(!ZSTDMT_isOverlapped(buffer, mtctx->inBuff.prefix));
+
+ ZSTDMT_waitForLdmComplete(mtctx, buffer);
+
+ DEBUGLOG(5, "Using prefix range [%zx, %zx)",
+ (size_t)mtctx->inBuff.prefix.start,
+ (size_t)mtctx->inBuff.prefix.start + mtctx->inBuff.prefix.size);
+ DEBUGLOG(5, "Using source range [%zx, %zx)",
+ (size_t)buffer.start,
+ (size_t)buffer.start + buffer.capacity);
+
+
+ mtctx->inBuff.buffer = buffer;
+ mtctx->inBuff.filled = 0;
+ assert(mtctx->roundBuff.pos + buffer.capacity <= mtctx->roundBuff.capacity);
+ return 1;
+}
+
+typedef struct {
+ size_t toLoad; /* The number of bytes to load from the input. */
+ int flush; /* Boolean declaring if we must flush because we found a synchronization point. */
+} syncPoint_t;
+
+/**
+ * Searches through the input for a synchronization point. If one is found, we
+ * will instruct the caller to flush, and return the number of bytes to load.
+ * Otherwise, we will load as many bytes as possible and instruct the caller
+ * to continue as normal.
+ */
+static syncPoint_t
+findSynchronizationPoint(ZSTDMT_CCtx const* mtctx, ZSTD_inBuffer const input)
+{
+ BYTE const* const istart = (BYTE const*)input.src + input.pos;
+ U64 const primePower = mtctx->rsync.primePower;
+ U64 const hitMask = mtctx->rsync.hitMask;
+
+ syncPoint_t syncPoint;
+ U64 hash;
+ BYTE const* prev;
+ size_t pos;
+
+ syncPoint.toLoad = MIN(input.size - input.pos, mtctx->targetSectionSize - mtctx->inBuff.filled);
+ syncPoint.flush = 0;
+ if (!mtctx->params.rsyncable)
+ /* Rsync is disabled. */
+ return syncPoint;
+ if (mtctx->inBuff.filled + syncPoint.toLoad < RSYNC_LENGTH)
+ /* Not enough to compute the hash.
+ * We will miss any synchronization points in this RSYNC_LENGTH byte
+ * window. However, since it depends only in the internal buffers, if the
+ * state is already synchronized, we will remain synchronized.
+ * Additionally, the probability that we miss a synchronization point is
+ * low: RSYNC_LENGTH / targetSectionSize.
+ */
+ return syncPoint;
+ /* Initialize the loop variables. */
+ if (mtctx->inBuff.filled >= RSYNC_LENGTH) {
+ /* We have enough bytes buffered to initialize the hash.
+ * Start scanning at the beginning of the input.
+ */
+ pos = 0;
+ prev = (BYTE const*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled - RSYNC_LENGTH;
+ hash = ZSTD_rollingHash_compute(prev, RSYNC_LENGTH);
+ } else {
+ /* We don't have enough bytes buffered to initialize the hash, but
+ * we know we have at least RSYNC_LENGTH bytes total.
+ * Start scanning after the first RSYNC_LENGTH bytes less the bytes
+ * already buffered.
+ */
+ pos = RSYNC_LENGTH - mtctx->inBuff.filled;
+ prev = (BYTE const*)mtctx->inBuff.buffer.start - pos;
+ hash = ZSTD_rollingHash_compute(mtctx->inBuff.buffer.start, mtctx->inBuff.filled);
+ hash = ZSTD_rollingHash_append(hash, istart, pos);
+ }
+ /* Starting with the hash of the previous RSYNC_LENGTH bytes, roll
+ * through the input. If we hit a synchronization point, then cut the
+ * job off, and tell the compressor to flush the job. Otherwise, load
+ * all the bytes and continue as normal.
+ * If we go too long without a synchronization point (targetSectionSize)
+ * then a block will be emitted anyways, but this is okay, since if we
+ * are already synchronized we will remain synchronized.
+ */
+ for (; pos < syncPoint.toLoad; ++pos) {
+ BYTE const toRemove = pos < RSYNC_LENGTH ? prev[pos] : istart[pos - RSYNC_LENGTH];
+ /* if (pos >= RSYNC_LENGTH) assert(ZSTD_rollingHash_compute(istart + pos - RSYNC_LENGTH, RSYNC_LENGTH) == hash); */
+ hash = ZSTD_rollingHash_rotate(hash, toRemove, istart[pos], primePower);
+ if ((hash & hitMask) == hitMask) {
+ syncPoint.toLoad = pos + 1;
+ syncPoint.flush = 1;
+ break;
+ }
+ }
+ return syncPoint;
+}
+
+size_t ZSTDMT_nextInputSizeHint(const ZSTDMT_CCtx* mtctx)
+{
+ size_t hintInSize = mtctx->targetSectionSize - mtctx->inBuff.filled;
+ if (hintInSize==0) hintInSize = mtctx->targetSectionSize;
+ return hintInSize;
+}
+
+/** ZSTDMT_compressStream_generic() :
+ * internal use only - exposed to be invoked from zstd_compress.c
+ * assumption : output and input are valid (pos <= size)
+ * @return : minimum amount of data remaining to flush, 0 if none */
+size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
+ ZSTD_outBuffer* output,
+ ZSTD_inBuffer* input,
+ ZSTD_EndDirective endOp)
+{
+ unsigned forwardInputProgress = 0;
+ DEBUGLOG(5, "ZSTDMT_compressStream_generic (endOp=%u, srcSize=%u)",
+ (U32)endOp, (U32)(input->size - input->pos));
+ assert(output->pos <= output->size);
+ assert(input->pos <= input->size);
+
+ if (mtctx->singleBlockingThread) { /* delegate to single-thread (synchronous) */
+ return ZSTD_compressStream_generic(mtctx->cctxPool->cctx[0], output, input, endOp);
+ }
+
+ if ((mtctx->frameEnded) && (endOp==ZSTD_e_continue)) {
+ /* current frame being ended. Only flush/end are allowed */
+ return ERROR(stage_wrong);
+ }
+
+ /* single-pass shortcut (note : synchronous-mode) */
+ if ( (!mtctx->params.rsyncable) /* rsyncable mode is disabled */
+ && (mtctx->nextJobID == 0) /* just started */
+ && (mtctx->inBuff.filled == 0) /* nothing buffered */
+ && (!mtctx->jobReady) /* no job already created */
+ && (endOp == ZSTD_e_end) /* end order */
+ && (output->size - output->pos >= ZSTD_compressBound(input->size - input->pos)) ) { /* enough space in dst */
+ size_t const cSize = ZSTDMT_compress_advanced_internal(mtctx,
+ (char*)output->dst + output->pos, output->size - output->pos,
+ (const char*)input->src + input->pos, input->size - input->pos,
+ mtctx->cdict, mtctx->params);
+ if (ZSTD_isError(cSize)) return cSize;
+ input->pos = input->size;
+ output->pos += cSize;
+ mtctx->allJobsCompleted = 1;
+ mtctx->frameEnded = 1;
+ return 0;
+ }
+
+ /* fill input buffer */
+ if ( (!mtctx->jobReady)
+ && (input->size > input->pos) ) { /* support NULL input */
+ if (mtctx->inBuff.buffer.start == NULL) {
+ assert(mtctx->inBuff.filled == 0); /* Can't fill an empty buffer */
+ if (!ZSTDMT_tryGetInputRange(mtctx)) {
+ /* It is only possible for this operation to fail if there are
+ * still compression jobs ongoing.
+ */
+ DEBUGLOG(5, "ZSTDMT_tryGetInputRange failed");
+ assert(mtctx->doneJobID != mtctx->nextJobID);
+ } else
+ DEBUGLOG(5, "ZSTDMT_tryGetInputRange completed successfully : mtctx->inBuff.buffer.start = %p", mtctx->inBuff.buffer.start);
+ }
+ if (mtctx->inBuff.buffer.start != NULL) {
+ syncPoint_t const syncPoint = findSynchronizationPoint(mtctx, *input);
+ if (syncPoint.flush && endOp == ZSTD_e_continue) {
+ endOp = ZSTD_e_flush;
+ }
+ assert(mtctx->inBuff.buffer.capacity >= mtctx->targetSectionSize);
+ DEBUGLOG(5, "ZSTDMT_compressStream_generic: adding %u bytes on top of %u to buffer of size %u",
+ (U32)syncPoint.toLoad, (U32)mtctx->inBuff.filled, (U32)mtctx->targetSectionSize);
+ memcpy((char*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled, (const char*)input->src + input->pos, syncPoint.toLoad);
+ input->pos += syncPoint.toLoad;
+ mtctx->inBuff.filled += syncPoint.toLoad;
+ forwardInputProgress = syncPoint.toLoad>0;
+ }
+ if ((input->pos < input->size) && (endOp == ZSTD_e_end))
+ endOp = ZSTD_e_flush; /* can't end now : not all input consumed */
+ }
+
+ if ( (mtctx->jobReady)
+ || (mtctx->inBuff.filled >= mtctx->targetSectionSize) /* filled enough : let's compress */
+ || ((endOp != ZSTD_e_continue) && (mtctx->inBuff.filled > 0)) /* something to flush : let's go */
+ || ((endOp == ZSTD_e_end) && (!mtctx->frameEnded)) ) { /* must finish the frame with a zero-size block */
+ size_t const jobSize = mtctx->inBuff.filled;
+ assert(mtctx->inBuff.filled <= mtctx->targetSectionSize);
+ CHECK_F( ZSTDMT_createCompressionJob(mtctx, jobSize, endOp) );
+ }
+
+ /* check for potential compressed data ready to be flushed */
+ { size_t const remainingToFlush = ZSTDMT_flushProduced(mtctx, output, !forwardInputProgress, endOp); /* block if there was no forward input progress */
+ if (input->pos < input->size) return MAX(remainingToFlush, 1); /* input not consumed : do not end flush yet */
+ DEBUGLOG(5, "end of ZSTDMT_compressStream_generic: remainingToFlush = %u", (U32)remainingToFlush);
+ return remainingToFlush;
+ }
+}
+
+
+size_t ZSTDMT_compressStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input)
+{
+ CHECK_F( ZSTDMT_compressStream_generic(mtctx, output, input, ZSTD_e_continue) );
+
+ /* recommended next input size : fill current input buffer */
+ return mtctx->targetSectionSize - mtctx->inBuff.filled; /* note : could be zero when input buffer is fully filled and no more availability to create new job */
+}
+
+
+static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_EndDirective endFrame)
+{
+ size_t const srcSize = mtctx->inBuff.filled;
+ DEBUGLOG(5, "ZSTDMT_flushStream_internal");
+
+ if ( mtctx->jobReady /* one job ready for a worker to pick up */
+ || (srcSize > 0) /* still some data within input buffer */
+ || ((endFrame==ZSTD_e_end) && !mtctx->frameEnded)) { /* need a last 0-size block to end frame */
+ DEBUGLOG(5, "ZSTDMT_flushStream_internal : create a new job (%u bytes, end:%u)",
+ (U32)srcSize, (U32)endFrame);
+ CHECK_F( ZSTDMT_createCompressionJob(mtctx, srcSize, endFrame) );
+ }
+
+ /* check if there is any data available to flush */
+ return ZSTDMT_flushProduced(mtctx, output, 1 /* blockToFlush */, endFrame);
+}
+
+
+size_t ZSTDMT_flushStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output)
+{
+ DEBUGLOG(5, "ZSTDMT_flushStream");
+ if (mtctx->singleBlockingThread)
+ return ZSTD_flushStream(mtctx->cctxPool->cctx[0], output);
+ return ZSTDMT_flushStream_internal(mtctx, output, ZSTD_e_flush);
+}
+
+size_t ZSTDMT_endStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output)
+{
+ DEBUGLOG(4, "ZSTDMT_endStream");
+ if (mtctx->singleBlockingThread)
+ return ZSTD_endStream(mtctx->cctxPool->cctx[0], output);
+ return ZSTDMT_flushStream_internal(mtctx, output, ZSTD_e_end);
+}
diff --git a/Utilities/cmzstd/lib/compress/zstdmt_compress.h b/Utilities/cmzstd/lib/compress/zstdmt_compress.h
new file mode 100644
index 000000000..ee771681f
--- /dev/null
+++ b/Utilities/cmzstd/lib/compress/zstdmt_compress.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+ #ifndef ZSTDMT_COMPRESS_H
+ #define ZSTDMT_COMPRESS_H
+
+ #if defined (__cplusplus)
+ extern "C" {
+ #endif
+
+
+/* Note : This is an internal API.
+ * Some methods are still exposed (ZSTDLIB_API),
+ * because it used to be the only way to invoke MT compression.
+ * Now, it's recommended to use ZSTD_compress_generic() instead.
+ * These methods will stop being exposed in a future version */
+
+/* === Dependencies === */
+#include <stddef.h> /* size_t */
+#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_parameters */
+#include "zstd.h" /* ZSTD_inBuffer, ZSTD_outBuffer, ZSTDLIB_API */
+
+
+/* === Constants === */
+#ifndef ZSTDMT_NBWORKERS_MAX
+# define ZSTDMT_NBWORKERS_MAX 200
+#endif
+#ifndef ZSTDMT_JOBSIZE_MIN
+# define ZSTDMT_JOBSIZE_MIN (1 MB)
+#endif
+#define ZSTDMT_JOBSIZE_MAX (MEM_32bits() ? (512 MB) : (1024 MB))
+
+
+/* === Memory management === */
+typedef struct ZSTDMT_CCtx_s ZSTDMT_CCtx;
+ZSTDLIB_API ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbWorkers);
+ZSTDLIB_API ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers,
+ ZSTD_customMem cMem);
+ZSTDLIB_API size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx);
+
+ZSTDLIB_API size_t ZSTDMT_sizeof_CCtx(ZSTDMT_CCtx* mtctx);
+
+
+/* === Simple one-pass compression function === */
+
+ZSTDLIB_API size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ int compressionLevel);
+
+
+
+/* === Streaming functions === */
+
+ZSTDLIB_API size_t ZSTDMT_initCStream(ZSTDMT_CCtx* mtctx, int compressionLevel);
+ZSTDLIB_API size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize); /**< if srcSize is not known at reset time, use ZSTD_CONTENTSIZE_UNKNOWN. Note: for compatibility with older programs, 0 means the same as ZSTD_CONTENTSIZE_UNKNOWN, but it will change in the future to mean "empty" */
+
+ZSTDLIB_API size_t ZSTDMT_nextInputSizeHint(const ZSTDMT_CCtx* mtctx);
+ZSTDLIB_API size_t ZSTDMT_compressStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input);
+
+ZSTDLIB_API size_t ZSTDMT_flushStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output); /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */
+ZSTDLIB_API size_t ZSTDMT_endStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output); /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */
+
+
+/* === Advanced functions and parameters === */
+
+ZSTDLIB_API size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ const ZSTD_CDict* cdict,
+ ZSTD_parameters params,
+ int overlapLog);
+
+ZSTDLIB_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx,
+ const void* dict, size_t dictSize, /* dict can be released after init, a local copy is preserved within zcs */
+ ZSTD_parameters params,
+ unsigned long long pledgedSrcSize); /* pledgedSrcSize is optional and can be zero == unknown */
+
+ZSTDLIB_API size_t ZSTDMT_initCStream_usingCDict(ZSTDMT_CCtx* mtctx,
+ const ZSTD_CDict* cdict,
+ ZSTD_frameParameters fparams,
+ unsigned long long pledgedSrcSize); /* note : zero means empty */
+
+/* ZSTDMT_parameter :
+ * List of parameters that can be set using ZSTDMT_setMTCtxParameter() */
+typedef enum {
+ ZSTDMT_p_jobSize, /* Each job is compressed in parallel. By default, this value is dynamically determined depending on compression parameters. Can be set explicitly here. */
+ ZSTDMT_p_overlapLog, /* Each job may reload a part of previous job to enhance compressionr ratio; 0 == no overlap, 6(default) == use 1/8th of window, >=9 == use full window. This is a "sticky" parameter : its value will be re-used on next compression job */
+ ZSTDMT_p_rsyncable /* Enables rsyncable mode. */
+} ZSTDMT_parameter;
+
+/* ZSTDMT_setMTCtxParameter() :
+ * allow setting individual parameters, one at a time, among a list of enums defined in ZSTDMT_parameter.
+ * The function must be called typically after ZSTD_createCCtx() but __before ZSTDMT_init*() !__
+ * Parameters not explicitly reset by ZSTDMT_init*() remain the same in consecutive compression sessions.
+ * @return : 0, or an error code (which can be tested using ZSTD_isError()) */
+ZSTDLIB_API size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, int value);
+
+/* ZSTDMT_getMTCtxParameter() :
+ * Query the ZSTDMT_CCtx for a parameter value.
+ * @return : 0, or an error code (which can be tested using ZSTD_isError()) */
+ZSTDLIB_API size_t ZSTDMT_getMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, int* value);
+
+
+/*! ZSTDMT_compressStream_generic() :
+ * Combines ZSTDMT_compressStream() with optional ZSTDMT_flushStream() or ZSTDMT_endStream()
+ * depending on flush directive.
+ * @return : minimum amount of data still to be flushed
+ * 0 if fully flushed
+ * or an error code
+ * note : needs to be init using any ZSTD_initCStream*() variant */
+ZSTDLIB_API size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
+ ZSTD_outBuffer* output,
+ ZSTD_inBuffer* input,
+ ZSTD_EndDirective endOp);
+
+
+/* ========================================================
+ * === Private interface, for use by ZSTD_compress.c ===
+ * === Not exposed in libzstd. Never invoke directly ===
+ * ======================================================== */
+
+ /*! ZSTDMT_toFlushNow()
+ * Tell how many bytes are ready to be flushed immediately.
+ * Probe the oldest active job (not yet entirely flushed) and check its output buffer.
+ * If return 0, it means there is no active job,
+ * or, it means oldest job is still active, but everything produced has been flushed so far,
+ * therefore flushing is limited by speed of oldest job. */
+size_t ZSTDMT_toFlushNow(ZSTDMT_CCtx* mtctx);
+
+/*! ZSTDMT_CCtxParam_setMTCtxParameter()
+ * like ZSTDMT_setMTCtxParameter(), but into a ZSTD_CCtx_Params */
+size_t ZSTDMT_CCtxParam_setMTCtxParameter(ZSTD_CCtx_params* params, ZSTDMT_parameter parameter, int value);
+
+/*! ZSTDMT_CCtxParam_setNbWorkers()
+ * Set nbWorkers, and clamp it.
+ * Also reset jobSize and overlapLog */
+size_t ZSTDMT_CCtxParam_setNbWorkers(ZSTD_CCtx_params* params, unsigned nbWorkers);
+
+/*! ZSTDMT_updateCParams_whileCompressing() :
+ * Updates only a selected set of compression parameters, to remain compatible with current frame.
+ * New parameters will be applied to next compression job. */
+void ZSTDMT_updateCParams_whileCompressing(ZSTDMT_CCtx* mtctx, const ZSTD_CCtx_params* cctxParams);
+
+/*! ZSTDMT_getFrameProgression():
+ * tells how much data has been consumed (input) and produced (output) for current frame.
+ * able to count progression inside worker threads.
+ */
+ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx);
+
+
+/*! ZSTDMT_initCStream_internal() :
+ * Private use only. Init streaming operation.
+ * expects params to be valid.
+ * must receive dict, or cdict, or none, but not both.
+ * @return : 0, or an error code */
+size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs,
+ const void* dict, size_t dictSize, ZSTD_dictContentType_e dictContentType,
+ const ZSTD_CDict* cdict,
+ ZSTD_CCtx_params params, unsigned long long pledgedSrcSize);
+
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* ZSTDMT_COMPRESS_H */
diff --git a/Utilities/cmzstd/lib/decompress/huf_decompress.c b/Utilities/cmzstd/lib/decompress/huf_decompress.c
new file mode 100644
index 000000000..3f8bd2973
--- /dev/null
+++ b/Utilities/cmzstd/lib/decompress/huf_decompress.c
@@ -0,0 +1,1232 @@
+/* ******************************************************************
+ huff0 huffman decoder,
+ part of Finite State Entropy library
+ Copyright (C) 2013-present, 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 :
+ - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy
+****************************************************************** */
+
+/* **************************************************************
+* Dependencies
+****************************************************************/
+#include <string.h> /* memcpy, memset */
+#include "compiler.h"
+#include "bitstream.h" /* BIT_* */
+#include "fse.h" /* to compress headers */
+#define HUF_STATIC_LINKING_ONLY
+#include "huf.h"
+#include "error_private.h"
+
+/* **************************************************************
+* Macros
+****************************************************************/
+
+/* These two optional macros force the use one way or another of the two
+ * Huffman decompression implementations. You can't force in both directions
+ * at the same time.
+ */
+#if defined(HUF_FORCE_DECOMPRESS_X1) && \
+ defined(HUF_FORCE_DECOMPRESS_X2)
+#error "Cannot force the use of the X1 and X2 decoders at the same time!"
+#endif
+
+
+/* **************************************************************
+* Error Management
+****************************************************************/
+#define HUF_isError ERR_isError
+#define CHECK_F(f) { size_t const err_ = (f); if (HUF_isError(err_)) return err_; }
+
+
+/* **************************************************************
+* Byte alignment for workSpace management
+****************************************************************/
+#define HUF_ALIGN(x, a) HUF_ALIGN_MASK((x), (a) - 1)
+#define HUF_ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask))
+
+
+/* **************************************************************
+* BMI2 Variant Wrappers
+****************************************************************/
+#if DYNAMIC_BMI2
+
+#define HUF_DGEN(fn) \
+ \
+ static size_t fn##_default( \
+ void* dst, size_t dstSize, \
+ const void* cSrc, size_t cSrcSize, \
+ const HUF_DTable* DTable) \
+ { \
+ return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \
+ } \
+ \
+ static TARGET_ATTRIBUTE("bmi2") size_t fn##_bmi2( \
+ void* dst, size_t dstSize, \
+ const void* cSrc, size_t cSrcSize, \
+ const HUF_DTable* DTable) \
+ { \
+ return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \
+ } \
+ \
+ static size_t fn(void* dst, size_t dstSize, void const* cSrc, \
+ size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \
+ { \
+ if (bmi2) { \
+ return fn##_bmi2(dst, dstSize, cSrc, cSrcSize, DTable); \
+ } \
+ return fn##_default(dst, dstSize, cSrc, cSrcSize, DTable); \
+ }
+
+#else
+
+#define HUF_DGEN(fn) \
+ static size_t fn(void* dst, size_t dstSize, void const* cSrc, \
+ size_t cSrcSize, HUF_DTable const* DTable, int bmi2) \
+ { \
+ (void)bmi2; \
+ return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable); \
+ }
+
+#endif
+
+
+/*-***************************/
+/* generic DTableDesc */
+/*-***************************/
+typedef struct { BYTE maxTableLog; BYTE tableType; BYTE tableLog; BYTE reserved; } DTableDesc;
+
+static DTableDesc HUF_getDTableDesc(const HUF_DTable* table)
+{
+ DTableDesc dtd;
+ memcpy(&dtd, table, sizeof(dtd));
+ return dtd;
+}
+
+
+#ifndef HUF_FORCE_DECOMPRESS_X2
+
+/*-***************************/
+/* single-symbol decoding */
+/*-***************************/
+typedef struct { BYTE byte; BYTE nbBits; } HUF_DEltX1; /* single-symbol decoding */
+
+size_t HUF_readDTableX1_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize)
+{
+ U32 tableLog = 0;
+ U32 nbSymbols = 0;
+ size_t iSize;
+ void* const dtPtr = DTable + 1;
+ HUF_DEltX1* const dt = (HUF_DEltX1*)dtPtr;
+
+ U32* rankVal;
+ BYTE* huffWeight;
+ size_t spaceUsed32 = 0;
+
+ rankVal = (U32 *)workSpace + spaceUsed32;
+ spaceUsed32 += HUF_TABLELOG_ABSOLUTEMAX + 1;
+ huffWeight = (BYTE *)((U32 *)workSpace + spaceUsed32);
+ spaceUsed32 += HUF_ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2;
+
+ if ((spaceUsed32 << 2) > wkspSize) return ERROR(tableLog_tooLarge);
+
+ DEBUG_STATIC_ASSERT(sizeof(DTableDesc) == sizeof(HUF_DTable));
+ /* memset(huffWeight, 0, sizeof(huffWeight)); */ /* is not necessary, even though some analyzer complain ... */
+
+ iSize = HUF_readStats(huffWeight, HUF_SYMBOLVALUE_MAX + 1, rankVal, &nbSymbols, &tableLog, src, srcSize);
+ if (HUF_isError(iSize)) return iSize;
+
+ /* Table header */
+ { DTableDesc dtd = HUF_getDTableDesc(DTable);
+ if (tableLog > (U32)(dtd.maxTableLog+1)) return ERROR(tableLog_tooLarge); /* DTable too small, Huffman tree cannot fit in */
+ dtd.tableType = 0;
+ dtd.tableLog = (BYTE)tableLog;
+ memcpy(DTable, &dtd, sizeof(dtd));
+ }
+
+ /* Calculate starting value for each rank */
+ { U32 n, nextRankStart = 0;
+ for (n=1; n<tableLog+1; n++) {
+ U32 const current = nextRankStart;
+ nextRankStart += (rankVal[n] << (n-1));
+ rankVal[n] = current;
+ } }
+
+ /* fill DTable */
+ { U32 n;
+ for (n=0; n<nbSymbols; n++) {
+ U32 const w = huffWeight[n];
+ U32 const length = (1 << w) >> 1;
+ U32 u;
+ HUF_DEltX1 D;
+ D.byte = (BYTE)n; D.nbBits = (BYTE)(tableLog + 1 - w);
+ for (u = rankVal[w]; u < rankVal[w] + length; u++)
+ dt[u] = D;
+ rankVal[w] += length;
+ } }
+
+ return iSize;
+}
+
+size_t HUF_readDTableX1(HUF_DTable* DTable, const void* src, size_t srcSize)
+{
+ U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
+ return HUF_readDTableX1_wksp(DTable, src, srcSize,
+ workSpace, sizeof(workSpace));
+}
+
+FORCE_INLINE_TEMPLATE BYTE
+HUF_decodeSymbolX1(BIT_DStream_t* Dstream, const HUF_DEltX1* dt, const U32 dtLog)
+{
+ size_t const val = BIT_lookBitsFast(Dstream, dtLog); /* note : dtLog >= 1 */
+ BYTE const c = dt[val].byte;
+ BIT_skipBits(Dstream, dt[val].nbBits);
+ return c;
+}
+
+#define HUF_DECODE_SYMBOLX1_0(ptr, DStreamPtr) \
+ *ptr++ = HUF_decodeSymbolX1(DStreamPtr, dt, dtLog)
+
+#define HUF_DECODE_SYMBOLX1_1(ptr, DStreamPtr) \
+ if (MEM_64bits() || (HUF_TABLELOG_MAX<=12)) \
+ HUF_DECODE_SYMBOLX1_0(ptr, DStreamPtr)
+
+#define HUF_DECODE_SYMBOLX1_2(ptr, DStreamPtr) \
+ if (MEM_64bits()) \
+ HUF_DECODE_SYMBOLX1_0(ptr, DStreamPtr)
+
+HINT_INLINE size_t
+HUF_decodeStreamX1(BYTE* p, BIT_DStream_t* const bitDPtr, BYTE* const pEnd, const HUF_DEltX1* const dt, const U32 dtLog)
+{
+ BYTE* const pStart = p;
+
+ /* up to 4 symbols at a time */
+ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-3)) {
+ HUF_DECODE_SYMBOLX1_2(p, bitDPtr);
+ HUF_DECODE_SYMBOLX1_1(p, bitDPtr);
+ HUF_DECODE_SYMBOLX1_2(p, bitDPtr);
+ HUF_DECODE_SYMBOLX1_0(p, bitDPtr);
+ }
+
+ /* [0-3] symbols remaining */
+ if (MEM_32bits())
+ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd))
+ HUF_DECODE_SYMBOLX1_0(p, bitDPtr);
+
+ /* no more data to retrieve from bitstream, no need to reload */
+ while (p < pEnd)
+ HUF_DECODE_SYMBOLX1_0(p, bitDPtr);
+
+ return pEnd-pStart;
+}
+
+FORCE_INLINE_TEMPLATE size_t
+HUF_decompress1X1_usingDTable_internal_body(
+ void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ const HUF_DTable* DTable)
+{
+ BYTE* op = (BYTE*)dst;
+ BYTE* const oend = op + dstSize;
+ const void* dtPtr = DTable + 1;
+ const HUF_DEltX1* const dt = (const HUF_DEltX1*)dtPtr;
+ BIT_DStream_t bitD;
+ DTableDesc const dtd = HUF_getDTableDesc(DTable);
+ U32 const dtLog = dtd.tableLog;
+
+ CHECK_F( BIT_initDStream(&bitD, cSrc, cSrcSize) );
+
+ HUF_decodeStreamX1(op, &bitD, oend, dt, dtLog);
+
+ if (!BIT_endOfDStream(&bitD)) return ERROR(corruption_detected);
+
+ return dstSize;
+}
+
+FORCE_INLINE_TEMPLATE size_t
+HUF_decompress4X1_usingDTable_internal_body(
+ void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ const HUF_DTable* DTable)
+{
+ /* Check */
+ if (cSrcSize < 10) return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */
+
+ { const BYTE* const istart = (const BYTE*) cSrc;
+ BYTE* const ostart = (BYTE*) dst;
+ BYTE* const oend = ostart + dstSize;
+ const void* const dtPtr = DTable + 1;
+ const HUF_DEltX1* const dt = (const HUF_DEltX1*)dtPtr;
+
+ /* Init */
+ BIT_DStream_t bitD1;
+ BIT_DStream_t bitD2;
+ BIT_DStream_t bitD3;
+ BIT_DStream_t bitD4;
+ size_t const length1 = MEM_readLE16(istart);
+ size_t const length2 = MEM_readLE16(istart+2);
+ size_t const length3 = MEM_readLE16(istart+4);
+ size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6);
+ const BYTE* const istart1 = istart + 6; /* jumpTable */
+ const BYTE* const istart2 = istart1 + length1;
+ const BYTE* const istart3 = istart2 + length2;
+ const BYTE* const istart4 = istart3 + length3;
+ const size_t segmentSize = (dstSize+3) / 4;
+ BYTE* const opStart2 = ostart + segmentSize;
+ BYTE* const opStart3 = opStart2 + segmentSize;
+ BYTE* const opStart4 = opStart3 + segmentSize;
+ BYTE* op1 = ostart;
+ BYTE* op2 = opStart2;
+ BYTE* op3 = opStart3;
+ BYTE* op4 = opStart4;
+ U32 endSignal = BIT_DStream_unfinished;
+ DTableDesc const dtd = HUF_getDTableDesc(DTable);
+ U32 const dtLog = dtd.tableLog;
+
+ if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */
+ CHECK_F( BIT_initDStream(&bitD1, istart1, length1) );
+ CHECK_F( BIT_initDStream(&bitD2, istart2, length2) );
+ CHECK_F( BIT_initDStream(&bitD3, istart3, length3) );
+ CHECK_F( BIT_initDStream(&bitD4, istart4, length4) );
+
+ /* up to 16 symbols per loop (4 symbols per stream) in 64-bit mode */
+ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4);
+ while ( (endSignal==BIT_DStream_unfinished) && (op4<(oend-3)) ) {
+ HUF_DECODE_SYMBOLX1_2(op1, &bitD1);
+ HUF_DECODE_SYMBOLX1_2(op2, &bitD2);
+ HUF_DECODE_SYMBOLX1_2(op3, &bitD3);
+ HUF_DECODE_SYMBOLX1_2(op4, &bitD4);
+ HUF_DECODE_SYMBOLX1_1(op1, &bitD1);
+ HUF_DECODE_SYMBOLX1_1(op2, &bitD2);
+ HUF_DECODE_SYMBOLX1_1(op3, &bitD3);
+ HUF_DECODE_SYMBOLX1_1(op4, &bitD4);
+ HUF_DECODE_SYMBOLX1_2(op1, &bitD1);
+ HUF_DECODE_SYMBOLX1_2(op2, &bitD2);
+ HUF_DECODE_SYMBOLX1_2(op3, &bitD3);
+ HUF_DECODE_SYMBOLX1_2(op4, &bitD4);
+ HUF_DECODE_SYMBOLX1_0(op1, &bitD1);
+ HUF_DECODE_SYMBOLX1_0(op2, &bitD2);
+ HUF_DECODE_SYMBOLX1_0(op3, &bitD3);
+ HUF_DECODE_SYMBOLX1_0(op4, &bitD4);
+ BIT_reloadDStream(&bitD1);
+ BIT_reloadDStream(&bitD2);
+ BIT_reloadDStream(&bitD3);
+ BIT_reloadDStream(&bitD4);
+ }
+
+ /* check corruption */
+ /* note : should not be necessary : op# advance in lock step, and we control op4.
+ * but curiously, binary generated by gcc 7.2 & 7.3 with -mbmi2 runs faster when >=1 test is present */
+ if (op1 > opStart2) return ERROR(corruption_detected);
+ if (op2 > opStart3) return ERROR(corruption_detected);
+ if (op3 > opStart4) return ERROR(corruption_detected);
+ /* note : op4 supposed already verified within main loop */
+
+ /* finish bitStreams one by one */
+ HUF_decodeStreamX1(op1, &bitD1, opStart2, dt, dtLog);
+ HUF_decodeStreamX1(op2, &bitD2, opStart3, dt, dtLog);
+ HUF_decodeStreamX1(op3, &bitD3, opStart4, dt, dtLog);
+ HUF_decodeStreamX1(op4, &bitD4, oend, dt, dtLog);
+
+ /* check */
+ { U32 const endCheck = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4);
+ if (!endCheck) return ERROR(corruption_detected); }
+
+ /* decoded size */
+ return dstSize;
+ }
+}
+
+
+typedef size_t (*HUF_decompress_usingDTable_t)(void *dst, size_t dstSize,
+ const void *cSrc,
+ size_t cSrcSize,
+ const HUF_DTable *DTable);
+
+HUF_DGEN(HUF_decompress1X1_usingDTable_internal)
+HUF_DGEN(HUF_decompress4X1_usingDTable_internal)
+
+
+
+size_t HUF_decompress1X1_usingDTable(
+ void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ const HUF_DTable* DTable)
+{
+ DTableDesc dtd = HUF_getDTableDesc(DTable);
+ if (dtd.tableType != 0) return ERROR(GENERIC);
+ return HUF_decompress1X1_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
+}
+
+size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ void* workSpace, size_t wkspSize)
+{
+ const BYTE* ip = (const BYTE*) cSrc;
+
+ size_t const hSize = HUF_readDTableX1_wksp(DCtx, cSrc, cSrcSize, workSpace, wkspSize);
+ if (HUF_isError(hSize)) return hSize;
+ if (hSize >= cSrcSize) return ERROR(srcSize_wrong);
+ ip += hSize; cSrcSize -= hSize;
+
+ return HUF_decompress1X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, /* bmi2 */ 0);
+}
+
+
+size_t HUF_decompress1X1_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize)
+{
+ U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
+ return HUF_decompress1X1_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize,
+ workSpace, sizeof(workSpace));
+}
+
+size_t HUF_decompress1X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
+{
+ HUF_CREATE_STATIC_DTABLEX1(DTable, HUF_TABLELOG_MAX);
+ return HUF_decompress1X1_DCtx (DTable, dst, dstSize, cSrc, cSrcSize);
+}
+
+size_t HUF_decompress4X1_usingDTable(
+ void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ const HUF_DTable* DTable)
+{
+ DTableDesc dtd = HUF_getDTableDesc(DTable);
+ if (dtd.tableType != 0) return ERROR(GENERIC);
+ return HUF_decompress4X1_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
+}
+
+static size_t HUF_decompress4X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ void* workSpace, size_t wkspSize, int bmi2)
+{
+ const BYTE* ip = (const BYTE*) cSrc;
+
+ size_t const hSize = HUF_readDTableX1_wksp (dctx, cSrc, cSrcSize,
+ workSpace, wkspSize);
+ if (HUF_isError(hSize)) return hSize;
+ if (hSize >= cSrcSize) return ERROR(srcSize_wrong);
+ ip += hSize; cSrcSize -= hSize;
+
+ return HUF_decompress4X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2);
+}
+
+size_t HUF_decompress4X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ void* workSpace, size_t wkspSize)
+{
+ return HUF_decompress4X1_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, 0);
+}
+
+
+size_t HUF_decompress4X1_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
+{
+ U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
+ return HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize,
+ workSpace, sizeof(workSpace));
+}
+size_t HUF_decompress4X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
+{
+ HUF_CREATE_STATIC_DTABLEX1(DTable, HUF_TABLELOG_MAX);
+ return HUF_decompress4X1_DCtx(DTable, dst, dstSize, cSrc, cSrcSize);
+}
+
+#endif /* HUF_FORCE_DECOMPRESS_X2 */
+
+
+#ifndef HUF_FORCE_DECOMPRESS_X1
+
+/* *************************/
+/* double-symbols decoding */
+/* *************************/
+
+typedef struct { U16 sequence; BYTE nbBits; BYTE length; } HUF_DEltX2; /* double-symbols decoding */
+typedef struct { BYTE symbol; BYTE weight; } sortedSymbol_t;
+typedef U32 rankValCol_t[HUF_TABLELOG_MAX + 1];
+typedef rankValCol_t rankVal_t[HUF_TABLELOG_MAX];
+
+
+/* HUF_fillDTableX2Level2() :
+ * `rankValOrigin` must be a table of at least (HUF_TABLELOG_MAX + 1) U32 */
+static void HUF_fillDTableX2Level2(HUF_DEltX2* DTable, U32 sizeLog, const U32 consumed,
+ const U32* rankValOrigin, const int minWeight,
+ const sortedSymbol_t* sortedSymbols, const U32 sortedListSize,
+ U32 nbBitsBaseline, U16 baseSeq)
+{
+ HUF_DEltX2 DElt;
+ U32 rankVal[HUF_TABLELOG_MAX + 1];
+
+ /* get pre-calculated rankVal */
+ memcpy(rankVal, rankValOrigin, sizeof(rankVal));
+
+ /* fill skipped values */
+ if (minWeight>1) {
+ U32 i, skipSize = rankVal[minWeight];
+ MEM_writeLE16(&(DElt.sequence), baseSeq);
+ DElt.nbBits = (BYTE)(consumed);
+ DElt.length = 1;
+ for (i = 0; i < skipSize; i++)
+ DTable[i] = DElt;
+ }
+
+ /* fill DTable */
+ { U32 s; for (s=0; s<sortedListSize; s++) { /* note : sortedSymbols already skipped */
+ const U32 symbol = sortedSymbols[s].symbol;
+ const U32 weight = sortedSymbols[s].weight;
+ const U32 nbBits = nbBitsBaseline - weight;
+ const U32 length = 1 << (sizeLog-nbBits);
+ const U32 start = rankVal[weight];
+ U32 i = start;
+ const U32 end = start + length;
+
+ MEM_writeLE16(&(DElt.sequence), (U16)(baseSeq + (symbol << 8)));
+ DElt.nbBits = (BYTE)(nbBits + consumed);
+ DElt.length = 2;
+ do { DTable[i++] = DElt; } while (i<end); /* since length >= 1 */
+
+ rankVal[weight] += length;
+ } }
+}
+
+
+static void HUF_fillDTableX2(HUF_DEltX2* DTable, const U32 targetLog,
+ const sortedSymbol_t* sortedList, const U32 sortedListSize,
+ const U32* rankStart, rankVal_t rankValOrigin, const U32 maxWeight,
+ const U32 nbBitsBaseline)
+{
+ U32 rankVal[HUF_TABLELOG_MAX + 1];
+ const int scaleLog = nbBitsBaseline - targetLog; /* note : targetLog >= srcLog, hence scaleLog <= 1 */
+ const U32 minBits = nbBitsBaseline - maxWeight;
+ U32 s;
+
+ memcpy(rankVal, rankValOrigin, sizeof(rankVal));
+
+ /* fill DTable */
+ for (s=0; s<sortedListSize; s++) {
+ const U16 symbol = sortedList[s].symbol;
+ const U32 weight = sortedList[s].weight;
+ const U32 nbBits = nbBitsBaseline - weight;
+ const U32 start = rankVal[weight];
+ const U32 length = 1 << (targetLog-nbBits);
+
+ if (targetLog-nbBits >= minBits) { /* enough room for a second symbol */
+ U32 sortedRank;
+ int minWeight = nbBits + scaleLog;
+ if (minWeight < 1) minWeight = 1;
+ sortedRank = rankStart[minWeight];
+ HUF_fillDTableX2Level2(DTable+start, targetLog-nbBits, nbBits,
+ rankValOrigin[nbBits], minWeight,
+ sortedList+sortedRank, sortedListSize-sortedRank,
+ nbBitsBaseline, symbol);
+ } else {
+ HUF_DEltX2 DElt;
+ MEM_writeLE16(&(DElt.sequence), symbol);
+ DElt.nbBits = (BYTE)(nbBits);
+ DElt.length = 1;
+ { U32 const end = start + length;
+ U32 u;
+ for (u = start; u < end; u++) DTable[u] = DElt;
+ } }
+ rankVal[weight] += length;
+ }
+}
+
+size_t HUF_readDTableX2_wksp(HUF_DTable* DTable,
+ const void* src, size_t srcSize,
+ void* workSpace, size_t wkspSize)
+{
+ U32 tableLog, maxW, sizeOfSort, nbSymbols;
+ DTableDesc dtd = HUF_getDTableDesc(DTable);
+ U32 const maxTableLog = dtd.maxTableLog;
+ size_t iSize;
+ void* dtPtr = DTable+1; /* force compiler to avoid strict-aliasing */
+ HUF_DEltX2* const dt = (HUF_DEltX2*)dtPtr;
+ U32 *rankStart;
+
+ rankValCol_t* rankVal;
+ U32* rankStats;
+ U32* rankStart0;
+ sortedSymbol_t* sortedSymbol;
+ BYTE* weightList;
+ size_t spaceUsed32 = 0;
+
+ rankVal = (rankValCol_t *)((U32 *)workSpace + spaceUsed32);
+ spaceUsed32 += (sizeof(rankValCol_t) * HUF_TABLELOG_MAX) >> 2;
+ rankStats = (U32 *)workSpace + spaceUsed32;
+ spaceUsed32 += HUF_TABLELOG_MAX + 1;
+ rankStart0 = (U32 *)workSpace + spaceUsed32;
+ spaceUsed32 += HUF_TABLELOG_MAX + 2;
+ sortedSymbol = (sortedSymbol_t *)workSpace + (spaceUsed32 * sizeof(U32)) / sizeof(sortedSymbol_t);
+ spaceUsed32 += HUF_ALIGN(sizeof(sortedSymbol_t) * (HUF_SYMBOLVALUE_MAX + 1), sizeof(U32)) >> 2;
+ weightList = (BYTE *)((U32 *)workSpace + spaceUsed32);
+ spaceUsed32 += HUF_ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2;
+
+ if ((spaceUsed32 << 2) > wkspSize) return ERROR(tableLog_tooLarge);
+
+ rankStart = rankStart0 + 1;
+ memset(rankStats, 0, sizeof(U32) * (2 * HUF_TABLELOG_MAX + 2 + 1));
+
+ DEBUG_STATIC_ASSERT(sizeof(HUF_DEltX2) == sizeof(HUF_DTable)); /* if compiler fails here, assertion is wrong */
+ if (maxTableLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge);
+ /* memset(weightList, 0, sizeof(weightList)); */ /* is not necessary, even though some analyzer complain ... */
+
+ iSize = HUF_readStats(weightList, HUF_SYMBOLVALUE_MAX + 1, rankStats, &nbSymbols, &tableLog, src, srcSize);
+ if (HUF_isError(iSize)) return iSize;
+
+ /* check result */
+ if (tableLog > maxTableLog) return ERROR(tableLog_tooLarge); /* DTable can't fit code depth */
+
+ /* find maxWeight */
+ for (maxW = tableLog; rankStats[maxW]==0; maxW--) {} /* necessarily finds a solution before 0 */
+
+ /* Get start index of each weight */
+ { U32 w, nextRankStart = 0;
+ for (w=1; w<maxW+1; w++) {
+ U32 current = nextRankStart;
+ nextRankStart += rankStats[w];
+ rankStart[w] = current;
+ }
+ rankStart[0] = nextRankStart; /* put all 0w symbols at the end of sorted list*/
+ sizeOfSort = nextRankStart;
+ }
+
+ /* sort symbols by weight */
+ { U32 s;
+ for (s=0; s<nbSymbols; s++) {
+ U32 const w = weightList[s];
+ U32 const r = rankStart[w]++;
+ sortedSymbol[r].symbol = (BYTE)s;
+ sortedSymbol[r].weight = (BYTE)w;
+ }
+ rankStart[0] = 0; /* forget 0w symbols; this is beginning of weight(1) */
+ }
+
+ /* Build rankVal */
+ { U32* const rankVal0 = rankVal[0];
+ { int const rescale = (maxTableLog-tableLog) - 1; /* tableLog <= maxTableLog */
+ U32 nextRankVal = 0;
+ U32 w;
+ for (w=1; w<maxW+1; w++) {
+ U32 current = nextRankVal;
+ nextRankVal += rankStats[w] << (w+rescale);
+ rankVal0[w] = current;
+ } }
+ { U32 const minBits = tableLog+1 - maxW;
+ U32 consumed;
+ for (consumed = minBits; consumed < maxTableLog - minBits + 1; consumed++) {
+ U32* const rankValPtr = rankVal[consumed];
+ U32 w;
+ for (w = 1; w < maxW+1; w++) {
+ rankValPtr[w] = rankVal0[w] >> consumed;
+ } } } }
+
+ HUF_fillDTableX2(dt, maxTableLog,
+ sortedSymbol, sizeOfSort,
+ rankStart0, rankVal, maxW,
+ tableLog+1);
+
+ dtd.tableLog = (BYTE)maxTableLog;
+ dtd.tableType = 1;
+ memcpy(DTable, &dtd, sizeof(dtd));
+ return iSize;
+}
+
+size_t HUF_readDTableX2(HUF_DTable* DTable, const void* src, size_t srcSize)
+{
+ U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
+ return HUF_readDTableX2_wksp(DTable, src, srcSize,
+ workSpace, sizeof(workSpace));
+}
+
+
+FORCE_INLINE_TEMPLATE U32
+HUF_decodeSymbolX2(void* op, BIT_DStream_t* DStream, const HUF_DEltX2* dt, const U32 dtLog)
+{
+ size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */
+ memcpy(op, dt+val, 2);
+ BIT_skipBits(DStream, dt[val].nbBits);
+ return dt[val].length;
+}
+
+FORCE_INLINE_TEMPLATE U32
+HUF_decodeLastSymbolX2(void* op, BIT_DStream_t* DStream, const HUF_DEltX2* dt, const U32 dtLog)
+{
+ size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */
+ memcpy(op, dt+val, 1);
+ if (dt[val].length==1) BIT_skipBits(DStream, dt[val].nbBits);
+ else {
+ if (DStream->bitsConsumed < (sizeof(DStream->bitContainer)*8)) {
+ BIT_skipBits(DStream, dt[val].nbBits);
+ if (DStream->bitsConsumed > (sizeof(DStream->bitContainer)*8))
+ /* ugly hack; works only because it's the last symbol. Note : can't easily extract nbBits from just this symbol */
+ DStream->bitsConsumed = (sizeof(DStream->bitContainer)*8);
+ } }
+ return 1;
+}
+
+#define HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) \
+ ptr += HUF_decodeSymbolX2(ptr, DStreamPtr, dt, dtLog)
+
+#define HUF_DECODE_SYMBOLX2_1(ptr, DStreamPtr) \
+ if (MEM_64bits() || (HUF_TABLELOG_MAX<=12)) \
+ ptr += HUF_decodeSymbolX2(ptr, DStreamPtr, dt, dtLog)
+
+#define HUF_DECODE_SYMBOLX2_2(ptr, DStreamPtr) \
+ if (MEM_64bits()) \
+ ptr += HUF_decodeSymbolX2(ptr, DStreamPtr, dt, dtLog)
+
+HINT_INLINE size_t
+HUF_decodeStreamX2(BYTE* p, BIT_DStream_t* bitDPtr, BYTE* const pEnd,
+ const HUF_DEltX2* const dt, const U32 dtLog)
+{
+ BYTE* const pStart = p;
+
+ /* up to 8 symbols at a time */
+ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-(sizeof(bitDPtr->bitContainer)-1))) {
+ HUF_DECODE_SYMBOLX2_2(p, bitDPtr);
+ HUF_DECODE_SYMBOLX2_1(p, bitDPtr);
+ HUF_DECODE_SYMBOLX2_2(p, bitDPtr);
+ HUF_DECODE_SYMBOLX2_0(p, bitDPtr);
+ }
+
+ /* closer to end : up to 2 symbols at a time */
+ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p <= pEnd-2))
+ HUF_DECODE_SYMBOLX2_0(p, bitDPtr);
+
+ while (p <= pEnd-2)
+ HUF_DECODE_SYMBOLX2_0(p, bitDPtr); /* no need to reload : reached the end of DStream */
+
+ if (p < pEnd)
+ p += HUF_decodeLastSymbolX2(p, bitDPtr, dt, dtLog);
+
+ return p-pStart;
+}
+
+FORCE_INLINE_TEMPLATE size_t
+HUF_decompress1X2_usingDTable_internal_body(
+ void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ const HUF_DTable* DTable)
+{
+ BIT_DStream_t bitD;
+
+ /* Init */
+ CHECK_F( BIT_initDStream(&bitD, cSrc, cSrcSize) );
+
+ /* decode */
+ { BYTE* const ostart = (BYTE*) dst;
+ BYTE* const oend = ostart + dstSize;
+ const void* const dtPtr = DTable+1; /* force compiler to not use strict-aliasing */
+ const HUF_DEltX2* const dt = (const HUF_DEltX2*)dtPtr;
+ DTableDesc const dtd = HUF_getDTableDesc(DTable);
+ HUF_decodeStreamX2(ostart, &bitD, oend, dt, dtd.tableLog);
+ }
+
+ /* check */
+ if (!BIT_endOfDStream(&bitD)) return ERROR(corruption_detected);
+
+ /* decoded size */
+ return dstSize;
+}
+
+
+FORCE_INLINE_TEMPLATE size_t
+HUF_decompress4X2_usingDTable_internal_body(
+ void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ const HUF_DTable* DTable)
+{
+ if (cSrcSize < 10) return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */
+
+ { const BYTE* const istart = (const BYTE*) cSrc;
+ BYTE* const ostart = (BYTE*) dst;
+ BYTE* const oend = ostart + dstSize;
+ const void* const dtPtr = DTable+1;
+ const HUF_DEltX2* const dt = (const HUF_DEltX2*)dtPtr;
+
+ /* Init */
+ BIT_DStream_t bitD1;
+ BIT_DStream_t bitD2;
+ BIT_DStream_t bitD3;
+ BIT_DStream_t bitD4;
+ size_t const length1 = MEM_readLE16(istart);
+ size_t const length2 = MEM_readLE16(istart+2);
+ size_t const length3 = MEM_readLE16(istart+4);
+ size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6);
+ const BYTE* const istart1 = istart + 6; /* jumpTable */
+ const BYTE* const istart2 = istart1 + length1;
+ const BYTE* const istart3 = istart2 + length2;
+ const BYTE* const istart4 = istart3 + length3;
+ size_t const segmentSize = (dstSize+3) / 4;
+ BYTE* const opStart2 = ostart + segmentSize;
+ BYTE* const opStart3 = opStart2 + segmentSize;
+ BYTE* const opStart4 = opStart3 + segmentSize;
+ BYTE* op1 = ostart;
+ BYTE* op2 = opStart2;
+ BYTE* op3 = opStart3;
+ BYTE* op4 = opStart4;
+ U32 endSignal;
+ DTableDesc const dtd = HUF_getDTableDesc(DTable);
+ U32 const dtLog = dtd.tableLog;
+
+ if (length4 > cSrcSize) return ERROR(corruption_detected); /* overflow */
+ CHECK_F( BIT_initDStream(&bitD1, istart1, length1) );
+ CHECK_F( BIT_initDStream(&bitD2, istart2, length2) );
+ CHECK_F( BIT_initDStream(&bitD3, istart3, length3) );
+ CHECK_F( BIT_initDStream(&bitD4, istart4, length4) );
+
+ /* 16-32 symbols per loop (4-8 symbols per stream) */
+ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4);
+ for ( ; (endSignal==BIT_DStream_unfinished) & (op4<(oend-(sizeof(bitD4.bitContainer)-1))) ; ) {
+ HUF_DECODE_SYMBOLX2_2(op1, &bitD1);
+ HUF_DECODE_SYMBOLX2_2(op2, &bitD2);
+ HUF_DECODE_SYMBOLX2_2(op3, &bitD3);
+ HUF_DECODE_SYMBOLX2_2(op4, &bitD4);
+ HUF_DECODE_SYMBOLX2_1(op1, &bitD1);
+ HUF_DECODE_SYMBOLX2_1(op2, &bitD2);
+ HUF_DECODE_SYMBOLX2_1(op3, &bitD3);
+ HUF_DECODE_SYMBOLX2_1(op4, &bitD4);
+ HUF_DECODE_SYMBOLX2_2(op1, &bitD1);
+ HUF_DECODE_SYMBOLX2_2(op2, &bitD2);
+ HUF_DECODE_SYMBOLX2_2(op3, &bitD3);
+ HUF_DECODE_SYMBOLX2_2(op4, &bitD4);
+ HUF_DECODE_SYMBOLX2_0(op1, &bitD1);
+ HUF_DECODE_SYMBOLX2_0(op2, &bitD2);
+ HUF_DECODE_SYMBOLX2_0(op3, &bitD3);
+ HUF_DECODE_SYMBOLX2_0(op4, &bitD4);
+
+ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4);
+ }
+
+ /* check corruption */
+ if (op1 > opStart2) return ERROR(corruption_detected);
+ if (op2 > opStart3) return ERROR(corruption_detected);
+ if (op3 > opStart4) return ERROR(corruption_detected);
+ /* note : op4 already verified within main loop */
+
+ /* finish bitStreams one by one */
+ HUF_decodeStreamX2(op1, &bitD1, opStart2, dt, dtLog);
+ HUF_decodeStreamX2(op2, &bitD2, opStart3, dt, dtLog);
+ HUF_decodeStreamX2(op3, &bitD3, opStart4, dt, dtLog);
+ HUF_decodeStreamX2(op4, &bitD4, oend, dt, dtLog);
+
+ /* check */
+ { U32 const endCheck = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4);
+ if (!endCheck) return ERROR(corruption_detected); }
+
+ /* decoded size */
+ return dstSize;
+ }
+}
+
+HUF_DGEN(HUF_decompress1X2_usingDTable_internal)
+HUF_DGEN(HUF_decompress4X2_usingDTable_internal)
+
+size_t HUF_decompress1X2_usingDTable(
+ void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ const HUF_DTable* DTable)
+{
+ DTableDesc dtd = HUF_getDTableDesc(DTable);
+ if (dtd.tableType != 1) return ERROR(GENERIC);
+ return HUF_decompress1X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
+}
+
+size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ void* workSpace, size_t wkspSize)
+{
+ const BYTE* ip = (const BYTE*) cSrc;
+
+ size_t const hSize = HUF_readDTableX2_wksp(DCtx, cSrc, cSrcSize,
+ workSpace, wkspSize);
+ if (HUF_isError(hSize)) return hSize;
+ if (hSize >= cSrcSize) return ERROR(srcSize_wrong);
+ ip += hSize; cSrcSize -= hSize;
+
+ return HUF_decompress1X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, /* bmi2 */ 0);
+}
+
+
+size_t HUF_decompress1X2_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize)
+{
+ U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
+ return HUF_decompress1X2_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize,
+ workSpace, sizeof(workSpace));
+}
+
+size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
+{
+ HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_TABLELOG_MAX);
+ return HUF_decompress1X2_DCtx(DTable, dst, dstSize, cSrc, cSrcSize);
+}
+
+size_t HUF_decompress4X2_usingDTable(
+ void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ const HUF_DTable* DTable)
+{
+ DTableDesc dtd = HUF_getDTableDesc(DTable);
+ if (dtd.tableType != 1) return ERROR(GENERIC);
+ return HUF_decompress4X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
+}
+
+static size_t HUF_decompress4X2_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ void* workSpace, size_t wkspSize, int bmi2)
+{
+ const BYTE* ip = (const BYTE*) cSrc;
+
+ size_t hSize = HUF_readDTableX2_wksp(dctx, cSrc, cSrcSize,
+ workSpace, wkspSize);
+ if (HUF_isError(hSize)) return hSize;
+ if (hSize >= cSrcSize) return ERROR(srcSize_wrong);
+ ip += hSize; cSrcSize -= hSize;
+
+ return HUF_decompress4X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2);
+}
+
+size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ void* workSpace, size_t wkspSize)
+{
+ return HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, /* bmi2 */ 0);
+}
+
+
+size_t HUF_decompress4X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize)
+{
+ U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
+ return HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize,
+ workSpace, sizeof(workSpace));
+}
+
+size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
+{
+ HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_TABLELOG_MAX);
+ return HUF_decompress4X2_DCtx(DTable, dst, dstSize, cSrc, cSrcSize);
+}
+
+#endif /* HUF_FORCE_DECOMPRESS_X1 */
+
+
+/* ***********************************/
+/* Universal decompression selectors */
+/* ***********************************/
+
+size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize,
+ const void* cSrc, size_t cSrcSize,
+ const HUF_DTable* DTable)
+{
+ DTableDesc const dtd = HUF_getDTableDesc(DTable);
+#if defined(HUF_FORCE_DECOMPRESS_X1)
+ (void)dtd;
+ assert(dtd.tableType == 0);
+ return HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
+#elif defined(HUF_FORCE_DECOMPRESS_X2)
+ (void)dtd;
+ assert(dtd.tableType == 1);
+ return HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
+#else
+ return dtd.tableType ? HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0) :
+ HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
+#endif
+}
+
+size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize,
+ const void* cSrc, size_t cSrcSize,
+ const HUF_DTable* DTable)
+{
+ DTableDesc const dtd = HUF_getDTableDesc(DTable);
+#if defined(HUF_FORCE_DECOMPRESS_X1)
+ (void)dtd;
+ assert(dtd.tableType == 0);
+ return HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
+#elif defined(HUF_FORCE_DECOMPRESS_X2)
+ (void)dtd;
+ assert(dtd.tableType == 1);
+ return HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
+#else
+ return dtd.tableType ? HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0) :
+ HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
+#endif
+}
+
+
+#if !defined(HUF_FORCE_DECOMPRESS_X1) && !defined(HUF_FORCE_DECOMPRESS_X2)
+typedef struct { U32 tableTime; U32 decode256Time; } algo_time_t;
+static const algo_time_t algoTime[16 /* Quantization */][3 /* single, double, quad */] =
+{
+ /* single, double, quad */
+ {{0,0}, {1,1}, {2,2}}, /* Q==0 : impossible */
+ {{0,0}, {1,1}, {2,2}}, /* Q==1 : impossible */
+ {{ 38,130}, {1313, 74}, {2151, 38}}, /* Q == 2 : 12-18% */
+ {{ 448,128}, {1353, 74}, {2238, 41}}, /* Q == 3 : 18-25% */
+ {{ 556,128}, {1353, 74}, {2238, 47}}, /* Q == 4 : 25-32% */
+ {{ 714,128}, {1418, 74}, {2436, 53}}, /* Q == 5 : 32-38% */
+ {{ 883,128}, {1437, 74}, {2464, 61}}, /* Q == 6 : 38-44% */
+ {{ 897,128}, {1515, 75}, {2622, 68}}, /* Q == 7 : 44-50% */
+ {{ 926,128}, {1613, 75}, {2730, 75}}, /* Q == 8 : 50-56% */
+ {{ 947,128}, {1729, 77}, {3359, 77}}, /* Q == 9 : 56-62% */
+ {{1107,128}, {2083, 81}, {4006, 84}}, /* Q ==10 : 62-69% */
+ {{1177,128}, {2379, 87}, {4785, 88}}, /* Q ==11 : 69-75% */
+ {{1242,128}, {2415, 93}, {5155, 84}}, /* Q ==12 : 75-81% */
+ {{1349,128}, {2644,106}, {5260,106}}, /* Q ==13 : 81-87% */
+ {{1455,128}, {2422,124}, {4174,124}}, /* Q ==14 : 87-93% */
+ {{ 722,128}, {1891,145}, {1936,146}}, /* Q ==15 : 93-99% */
+};
+#endif
+
+/** HUF_selectDecoder() :
+ * Tells which decoder is likely to decode faster,
+ * based on a set of pre-computed metrics.
+ * @return : 0==HUF_decompress4X1, 1==HUF_decompress4X2 .
+ * Assumption : 0 < dstSize <= 128 KB */
+U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize)
+{
+ assert(dstSize > 0);
+ assert(dstSize <= 128*1024);
+#if defined(HUF_FORCE_DECOMPRESS_X1)
+ (void)dstSize;
+ (void)cSrcSize;
+ return 0;
+#elif defined(HUF_FORCE_DECOMPRESS_X2)
+ (void)dstSize;
+ (void)cSrcSize;
+ return 1;
+#else
+ /* decoder timing evaluation */
+ { U32 const Q = (cSrcSize >= dstSize) ? 15 : (U32)(cSrcSize * 16 / dstSize); /* Q < 16 */
+ U32 const D256 = (U32)(dstSize >> 8);
+ U32 const DTime0 = algoTime[Q][0].tableTime + (algoTime[Q][0].decode256Time * D256);
+ U32 DTime1 = algoTime[Q][1].tableTime + (algoTime[Q][1].decode256Time * D256);
+ DTime1 += DTime1 >> 3; /* advantage to algorithm using less memory, to reduce cache eviction */
+ return DTime1 < DTime0;
+ }
+#endif
+}
+
+
+typedef size_t (*decompressionAlgo)(void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize);
+
+size_t HUF_decompress (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
+{
+#if !defined(HUF_FORCE_DECOMPRESS_X1) && !defined(HUF_FORCE_DECOMPRESS_X2)
+ static const decompressionAlgo decompress[2] = { HUF_decompress4X1, HUF_decompress4X2 };
+#endif
+
+ /* validation checks */
+ if (dstSize == 0) return ERROR(dstSize_tooSmall);
+ if (cSrcSize > dstSize) return ERROR(corruption_detected); /* invalid */
+ if (cSrcSize == dstSize) { memcpy(dst, cSrc, dstSize); return dstSize; } /* not compressed */
+ if (cSrcSize == 1) { memset(dst, *(const BYTE*)cSrc, dstSize); return dstSize; } /* RLE */
+
+ { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize);
+#if defined(HUF_FORCE_DECOMPRESS_X1)
+ (void)algoNb;
+ assert(algoNb == 0);
+ return HUF_decompress4X1(dst, dstSize, cSrc, cSrcSize);
+#elif defined(HUF_FORCE_DECOMPRESS_X2)
+ (void)algoNb;
+ assert(algoNb == 1);
+ return HUF_decompress4X2(dst, dstSize, cSrc, cSrcSize);
+#else
+ return decompress[algoNb](dst, dstSize, cSrc, cSrcSize);
+#endif
+ }
+}
+
+size_t HUF_decompress4X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
+{
+ /* validation checks */
+ if (dstSize == 0) return ERROR(dstSize_tooSmall);
+ if (cSrcSize > dstSize) return ERROR(corruption_detected); /* invalid */
+ if (cSrcSize == dstSize) { memcpy(dst, cSrc, dstSize); return dstSize; } /* not compressed */
+ if (cSrcSize == 1) { memset(dst, *(const BYTE*)cSrc, dstSize); return dstSize; } /* RLE */
+
+ { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize);
+#if defined(HUF_FORCE_DECOMPRESS_X1)
+ (void)algoNb;
+ assert(algoNb == 0);
+ return HUF_decompress4X1_DCtx(dctx, dst, dstSize, cSrc, cSrcSize);
+#elif defined(HUF_FORCE_DECOMPRESS_X2)
+ (void)algoNb;
+ assert(algoNb == 1);
+ return HUF_decompress4X2_DCtx(dctx, dst, dstSize, cSrc, cSrcSize);
+#else
+ return algoNb ? HUF_decompress4X2_DCtx(dctx, dst, dstSize, cSrc, cSrcSize) :
+ HUF_decompress4X1_DCtx(dctx, dst, dstSize, cSrc, cSrcSize) ;
+#endif
+ }
+}
+
+size_t HUF_decompress4X_hufOnly(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
+{
+ U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
+ return HUF_decompress4X_hufOnly_wksp(dctx, dst, dstSize, cSrc, cSrcSize,
+ workSpace, sizeof(workSpace));
+}
+
+
+size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst,
+ size_t dstSize, const void* cSrc,
+ size_t cSrcSize, void* workSpace,
+ size_t wkspSize)
+{
+ /* validation checks */
+ if (dstSize == 0) return ERROR(dstSize_tooSmall);
+ if (cSrcSize == 0) return ERROR(corruption_detected);
+
+ { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize);
+#if defined(HUF_FORCE_DECOMPRESS_X1)
+ (void)algoNb;
+ assert(algoNb == 0);
+ return HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize);
+#elif defined(HUF_FORCE_DECOMPRESS_X2)
+ (void)algoNb;
+ assert(algoNb == 1);
+ return HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize);
+#else
+ return algoNb ? HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc,
+ cSrcSize, workSpace, wkspSize):
+ HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize);
+#endif
+ }
+}
+
+size_t HUF_decompress1X_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize,
+ void* workSpace, size_t wkspSize)
+{
+ /* validation checks */
+ if (dstSize == 0) return ERROR(dstSize_tooSmall);
+ if (cSrcSize > dstSize) return ERROR(corruption_detected); /* invalid */
+ if (cSrcSize == dstSize) { memcpy(dst, cSrc, dstSize); return dstSize; } /* not compressed */
+ if (cSrcSize == 1) { memset(dst, *(const BYTE*)cSrc, dstSize); return dstSize; } /* RLE */
+
+ { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize);
+#if defined(HUF_FORCE_DECOMPRESS_X1)
+ (void)algoNb;
+ assert(algoNb == 0);
+ return HUF_decompress1X1_DCtx_wksp(dctx, dst, dstSize, cSrc,
+ cSrcSize, workSpace, wkspSize);
+#elif defined(HUF_FORCE_DECOMPRESS_X2)
+ (void)algoNb;
+ assert(algoNb == 1);
+ return HUF_decompress1X2_DCtx_wksp(dctx, dst, dstSize, cSrc,
+ cSrcSize, workSpace, wkspSize);
+#else
+ return algoNb ? HUF_decompress1X2_DCtx_wksp(dctx, dst, dstSize, cSrc,
+ cSrcSize, workSpace, wkspSize):
+ HUF_decompress1X1_DCtx_wksp(dctx, dst, dstSize, cSrc,
+ cSrcSize, workSpace, wkspSize);
+#endif
+ }
+}
+
+size_t HUF_decompress1X_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize,
+ const void* cSrc, size_t cSrcSize)
+{
+ U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
+ return HUF_decompress1X_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize,
+ workSpace, sizeof(workSpace));
+}
+
+
+size_t HUF_decompress1X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2)
+{
+ DTableDesc const dtd = HUF_getDTableDesc(DTable);
+#if defined(HUF_FORCE_DECOMPRESS_X1)
+ (void)dtd;
+ assert(dtd.tableType == 0);
+ return HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2);
+#elif defined(HUF_FORCE_DECOMPRESS_X2)
+ (void)dtd;
+ assert(dtd.tableType == 1);
+ return HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2);
+#else
+ return dtd.tableType ? HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2) :
+ HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2);
+#endif
+}
+
+#ifndef HUF_FORCE_DECOMPRESS_X2
+size_t HUF_decompress1X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2)
+{
+ const BYTE* ip = (const BYTE*) cSrc;
+
+ size_t const hSize = HUF_readDTableX1_wksp(dctx, cSrc, cSrcSize, workSpace, wkspSize);
+ if (HUF_isError(hSize)) return hSize;
+ if (hSize >= cSrcSize) return ERROR(srcSize_wrong);
+ ip += hSize; cSrcSize -= hSize;
+
+ return HUF_decompress1X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2);
+}
+#endif
+
+size_t HUF_decompress4X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2)
+{
+ DTableDesc const dtd = HUF_getDTableDesc(DTable);
+#if defined(HUF_FORCE_DECOMPRESS_X1)
+ (void)dtd;
+ assert(dtd.tableType == 0);
+ return HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2);
+#elif defined(HUF_FORCE_DECOMPRESS_X2)
+ (void)dtd;
+ assert(dtd.tableType == 1);
+ return HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2);
+#else
+ return dtd.tableType ? HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2) :
+ HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2);
+#endif
+}
+
+size_t HUF_decompress4X_hufOnly_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2)
+{
+ /* validation checks */
+ if (dstSize == 0) return ERROR(dstSize_tooSmall);
+ if (cSrcSize == 0) return ERROR(corruption_detected);
+
+ { U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize);
+#if defined(HUF_FORCE_DECOMPRESS_X1)
+ (void)algoNb;
+ assert(algoNb == 0);
+ return HUF_decompress4X1_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2);
+#elif defined(HUF_FORCE_DECOMPRESS_X2)
+ (void)algoNb;
+ assert(algoNb == 1);
+ return HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2);
+#else
+ return algoNb ? HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2) :
+ HUF_decompress4X1_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2);
+#endif
+ }
+}
diff --git a/Utilities/cmzstd/lib/decompress/zstd_ddict.c b/Utilities/cmzstd/lib/decompress/zstd_ddict.c
new file mode 100644
index 000000000..2ad044068
--- /dev/null
+++ b/Utilities/cmzstd/lib/decompress/zstd_ddict.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+/* zstd_ddict.c :
+ * concentrates all logic that needs to know the internals of ZSTD_DDict object */
+
+/*-*******************************************************
+* Dependencies
+*********************************************************/
+#include <string.h> /* memcpy, memmove, memset */
+#include "cpu.h" /* bmi2 */
+#include "mem.h" /* low level memory routines */
+#define FSE_STATIC_LINKING_ONLY
+#include "fse.h"
+#define HUF_STATIC_LINKING_ONLY
+#include "huf.h"
+#include "zstd_decompress_internal.h"
+#include "zstd_ddict.h"
+
+#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1)
+# include "zstd_legacy.h"
+#endif
+
+
+
+/*-*******************************************************
+* Types
+*********************************************************/
+struct ZSTD_DDict_s {
+ void* dictBuffer;
+ const void* dictContent;
+ size_t dictSize;
+ ZSTD_entropyDTables_t entropy;
+ U32 dictID;
+ U32 entropyPresent;
+ ZSTD_customMem cMem;
+}; /* typedef'd to ZSTD_DDict within "zstd.h" */
+
+const void* ZSTD_DDict_dictContent(const ZSTD_DDict* ddict)
+{
+ assert(ddict != NULL);
+ return ddict->dictContent;
+}
+
+size_t ZSTD_DDict_dictSize(const ZSTD_DDict* ddict)
+{
+ assert(ddict != NULL);
+ return ddict->dictSize;
+}
+
+void ZSTD_copyDDictParameters(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict)
+{
+ DEBUGLOG(4, "ZSTD_copyDDictParameters");
+ assert(dctx != NULL);
+ assert(ddict != NULL);
+ dctx->dictID = ddict->dictID;
+ dctx->prefixStart = ddict->dictContent;
+ dctx->virtualStart = ddict->dictContent;
+ dctx->dictEnd = (const BYTE*)ddict->dictContent + ddict->dictSize;
+ dctx->previousDstEnd = dctx->dictEnd;
+ if (ddict->entropyPresent) {
+ dctx->litEntropy = 1;
+ dctx->fseEntropy = 1;
+ dctx->LLTptr = ddict->entropy.LLTable;
+ dctx->MLTptr = ddict->entropy.MLTable;
+ dctx->OFTptr = ddict->entropy.OFTable;
+ dctx->HUFptr = ddict->entropy.hufTable;
+ dctx->entropy.rep[0] = ddict->entropy.rep[0];
+ dctx->entropy.rep[1] = ddict->entropy.rep[1];
+ dctx->entropy.rep[2] = ddict->entropy.rep[2];
+ } else {
+ dctx->litEntropy = 0;
+ dctx->fseEntropy = 0;
+ }
+}
+
+
+static size_t
+ZSTD_loadEntropy_intoDDict(ZSTD_DDict* ddict,
+ ZSTD_dictContentType_e dictContentType)
+{
+ ddict->dictID = 0;
+ ddict->entropyPresent = 0;
+ if (dictContentType == ZSTD_dct_rawContent) return 0;
+
+ if (ddict->dictSize < 8) {
+ if (dictContentType == ZSTD_dct_fullDict)
+ return ERROR(dictionary_corrupted); /* only accept specified dictionaries */
+ return 0; /* pure content mode */
+ }
+ { U32 const magic = MEM_readLE32(ddict->dictContent);
+ if (magic != ZSTD_MAGIC_DICTIONARY) {
+ if (dictContentType == ZSTD_dct_fullDict)
+ return ERROR(dictionary_corrupted); /* only accept specified dictionaries */
+ return 0; /* pure content mode */
+ }
+ }
+ ddict->dictID = MEM_readLE32((const char*)ddict->dictContent + ZSTD_FRAMEIDSIZE);
+
+ /* load entropy tables */
+ CHECK_E( ZSTD_loadDEntropy(&ddict->entropy,
+ ddict->dictContent, ddict->dictSize),
+ dictionary_corrupted );
+ ddict->entropyPresent = 1;
+ return 0;
+}
+
+
+static size_t ZSTD_initDDict_internal(ZSTD_DDict* ddict,
+ const void* dict, size_t dictSize,
+ ZSTD_dictLoadMethod_e dictLoadMethod,
+ ZSTD_dictContentType_e dictContentType)
+{
+ if ((dictLoadMethod == ZSTD_dlm_byRef) || (!dict) || (!dictSize)) {
+ ddict->dictBuffer = NULL;
+ ddict->dictContent = dict;
+ if (!dict) dictSize = 0;
+ } else {
+ void* const internalBuffer = ZSTD_malloc(dictSize, ddict->cMem);
+ ddict->dictBuffer = internalBuffer;
+ ddict->dictContent = internalBuffer;
+ if (!internalBuffer) return ERROR(memory_allocation);
+ memcpy(internalBuffer, dict, dictSize);
+ }
+ ddict->dictSize = dictSize;
+ ddict->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */
+
+ /* parse dictionary content */
+ CHECK_F( ZSTD_loadEntropy_intoDDict(ddict, dictContentType) );
+
+ return 0;
+}
+
+ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize,
+ ZSTD_dictLoadMethod_e dictLoadMethod,
+ ZSTD_dictContentType_e dictContentType,
+ ZSTD_customMem customMem)
+{
+ if (!customMem.customAlloc ^ !customMem.customFree) return NULL;
+
+ { ZSTD_DDict* const ddict = (ZSTD_DDict*) ZSTD_malloc(sizeof(ZSTD_DDict), customMem);
+ if (ddict == NULL) return NULL;
+ ddict->cMem = customMem;
+ { size_t const initResult = ZSTD_initDDict_internal(ddict,
+ dict, dictSize,
+ dictLoadMethod, dictContentType);
+ if (ZSTD_isError(initResult)) {
+ ZSTD_freeDDict(ddict);
+ return NULL;
+ } }
+ return ddict;
+ }
+}
+
+/*! ZSTD_createDDict() :
+* Create a digested dictionary, to start decompression without startup delay.
+* `dict` content is copied inside DDict.
+* Consequently, `dict` can be released after `ZSTD_DDict` creation */
+ZSTD_DDict* ZSTD_createDDict(const void* dict, size_t dictSize)
+{
+ ZSTD_customMem const allocator = { NULL, NULL, NULL };
+ return ZSTD_createDDict_advanced(dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto, allocator);
+}
+
+/*! ZSTD_createDDict_byReference() :
+ * Create a digested dictionary, to start decompression without startup delay.
+ * Dictionary content is simply referenced, it will be accessed during decompression.
+ * Warning : dictBuffer must outlive DDict (DDict must be freed before dictBuffer) */
+ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize)
+{
+ ZSTD_customMem const allocator = { NULL, NULL, NULL };
+ return ZSTD_createDDict_advanced(dictBuffer, dictSize, ZSTD_dlm_byRef, ZSTD_dct_auto, allocator);
+}
+
+
+const ZSTD_DDict* ZSTD_initStaticDDict(
+ void* sBuffer, size_t sBufferSize,
+ const void* dict, size_t dictSize,
+ ZSTD_dictLoadMethod_e dictLoadMethod,
+ ZSTD_dictContentType_e dictContentType)
+{
+ size_t const neededSpace = sizeof(ZSTD_DDict)
+ + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : dictSize);
+ ZSTD_DDict* const ddict = (ZSTD_DDict*)sBuffer;
+ assert(sBuffer != NULL);
+ assert(dict != NULL);
+ if ((size_t)sBuffer & 7) return NULL; /* 8-aligned */
+ if (sBufferSize < neededSpace) return NULL;
+ if (dictLoadMethod == ZSTD_dlm_byCopy) {
+ memcpy(ddict+1, dict, dictSize); /* local copy */
+ dict = ddict+1;
+ }
+ if (ZSTD_isError( ZSTD_initDDict_internal(ddict,
+ dict, dictSize,
+ ZSTD_dlm_byRef, dictContentType) ))
+ return NULL;
+ return ddict;
+}
+
+
+size_t ZSTD_freeDDict(ZSTD_DDict* ddict)
+{
+ if (ddict==NULL) return 0; /* support free on NULL */
+ { ZSTD_customMem const cMem = ddict->cMem;
+ ZSTD_free(ddict->dictBuffer, cMem);
+ ZSTD_free(ddict, cMem);
+ return 0;
+ }
+}
+
+/*! ZSTD_estimateDDictSize() :
+ * Estimate amount of memory that will be needed to create a dictionary for decompression.
+ * Note : dictionary created by reference using ZSTD_dlm_byRef are smaller */
+size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod)
+{
+ return sizeof(ZSTD_DDict) + (dictLoadMethod == ZSTD_dlm_byRef ? 0 : dictSize);
+}
+
+size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict)
+{
+ if (ddict==NULL) return 0; /* support sizeof on NULL */
+ return sizeof(*ddict) + (ddict->dictBuffer ? ddict->dictSize : 0) ;
+}
+
+/*! ZSTD_getDictID_fromDDict() :
+ * Provides the dictID of the dictionary loaded into `ddict`.
+ * If @return == 0, the dictionary is not conformant to Zstandard specification, or empty.
+ * Non-conformant dictionaries can still be loaded, but as content-only dictionaries. */
+unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict)
+{
+ if (ddict==NULL) return 0;
+ return ZSTD_getDictID_fromDict(ddict->dictContent, ddict->dictSize);
+}
diff --git a/Utilities/cmzstd/lib/decompress/zstd_ddict.h b/Utilities/cmzstd/lib/decompress/zstd_ddict.h
new file mode 100644
index 000000000..0479d11bb
--- /dev/null
+++ b/Utilities/cmzstd/lib/decompress/zstd_ddict.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+
+#ifndef ZSTD_DDICT_H
+#define ZSTD_DDICT_H
+
+/*-*******************************************************
+ * Dependencies
+ *********************************************************/
+#include <stddef.h> /* size_t */
+#include "zstd.h" /* ZSTD_DDict, and several public functions */
+
+
+/*-*******************************************************
+ * Interface
+ *********************************************************/
+
+/* note: several prototypes are already published in `zstd.h` :
+ * ZSTD_createDDict()
+ * ZSTD_createDDict_byReference()
+ * ZSTD_createDDict_advanced()
+ * ZSTD_freeDDict()
+ * ZSTD_initStaticDDict()
+ * ZSTD_sizeof_DDict()
+ * ZSTD_estimateDDictSize()
+ * ZSTD_getDictID_fromDict()
+ */
+
+const void* ZSTD_DDict_dictContent(const ZSTD_DDict* ddict);
+size_t ZSTD_DDict_dictSize(const ZSTD_DDict* ddict);
+
+void ZSTD_copyDDictParameters(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict);
+
+
+
+#endif /* ZSTD_DDICT_H */
diff --git a/Utilities/cmzstd/lib/decompress/zstd_decompress.c b/Utilities/cmzstd/lib/decompress/zstd_decompress.c
new file mode 100644
index 000000000..feef1ef67
--- /dev/null
+++ b/Utilities/cmzstd/lib/decompress/zstd_decompress.c
@@ -0,0 +1,1672 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+
+/* ***************************************************************
+* Tuning parameters
+*****************************************************************/
+/*!
+ * HEAPMODE :
+ * Select how default decompression function ZSTD_decompress() allocates its context,
+ * on stack (0), or into heap (1, default; requires malloc()).
+ * Note that functions with explicit context such as ZSTD_decompressDCtx() are unaffected.
+ */
+#ifndef ZSTD_HEAPMODE
+# define ZSTD_HEAPMODE 1
+#endif
+
+/*!
+* LEGACY_SUPPORT :
+* if set to 1+, ZSTD_decompress() can decode older formats (v0.1+)
+*/
+#ifndef ZSTD_LEGACY_SUPPORT
+# define ZSTD_LEGACY_SUPPORT 0
+#endif
+
+/*!
+ * MAXWINDOWSIZE_DEFAULT :
+ * maximum window size accepted by DStream __by default__.
+ * Frames requiring more memory will be rejected.
+ * It's possible to set a different limit using ZSTD_DCtx_setMaxWindowSize().
+ */
+#ifndef ZSTD_MAXWINDOWSIZE_DEFAULT
+# define ZSTD_MAXWINDOWSIZE_DEFAULT (((U32)1 << ZSTD_WINDOWLOG_LIMIT_DEFAULT) + 1)
+#endif
+
+/*!
+ * NO_FORWARD_PROGRESS_MAX :
+ * maximum allowed nb of calls to ZSTD_decompressStream()
+ * without any forward progress
+ * (defined as: no byte read from input, and no byte flushed to output)
+ * before triggering an error.
+ */
+#ifndef ZSTD_NO_FORWARD_PROGRESS_MAX
+# define ZSTD_NO_FORWARD_PROGRESS_MAX 16
+#endif
+
+
+/*-*******************************************************
+* Dependencies
+*********************************************************/
+#include <string.h> /* memcpy, memmove, memset */
+#include "cpu.h" /* bmi2 */
+#include "mem.h" /* low level memory routines */
+#define FSE_STATIC_LINKING_ONLY
+#include "fse.h"
+#define HUF_STATIC_LINKING_ONLY
+#include "huf.h"
+#include "zstd_internal.h" /* blockProperties_t */
+#include "zstd_decompress_internal.h" /* ZSTD_DCtx */
+#include "zstd_ddict.h" /* ZSTD_DDictDictContent */
+#include "zstd_decompress_block.h" /* ZSTD_decompressBlock_internal */
+
+#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1)
+# include "zstd_legacy.h"
+#endif
+
+
+/*-*************************************************************
+* Context management
+***************************************************************/
+size_t ZSTD_sizeof_DCtx (const ZSTD_DCtx* dctx)
+{
+ if (dctx==NULL) return 0; /* support sizeof NULL */
+ return sizeof(*dctx)
+ + ZSTD_sizeof_DDict(dctx->ddictLocal)
+ + dctx->inBuffSize + dctx->outBuffSize;
+}
+
+size_t ZSTD_estimateDCtxSize(void) { return sizeof(ZSTD_DCtx); }
+
+
+static size_t ZSTD_startingInputLength(ZSTD_format_e format)
+{
+ size_t const startingInputLength = (format==ZSTD_f_zstd1_magicless) ?
+ ZSTD_FRAMEHEADERSIZE_PREFIX - ZSTD_FRAMEIDSIZE :
+ ZSTD_FRAMEHEADERSIZE_PREFIX;
+ ZSTD_STATIC_ASSERT(ZSTD_FRAMEHEADERSIZE_PREFIX >= ZSTD_FRAMEIDSIZE);
+ /* only supports formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless */
+ assert( (format == ZSTD_f_zstd1) || (format == ZSTD_f_zstd1_magicless) );
+ return startingInputLength;
+}
+
+static void ZSTD_initDCtx_internal(ZSTD_DCtx* dctx)
+{
+ dctx->format = ZSTD_f_zstd1; /* ZSTD_decompressBegin() invokes ZSTD_startingInputLength() with argument dctx->format */
+ dctx->staticSize = 0;
+ dctx->maxWindowSize = ZSTD_MAXWINDOWSIZE_DEFAULT;
+ dctx->ddict = NULL;
+ dctx->ddictLocal = NULL;
+ dctx->dictEnd = NULL;
+ dctx->ddictIsCold = 0;
+ dctx->inBuff = NULL;
+ dctx->inBuffSize = 0;
+ dctx->outBuffSize = 0;
+ dctx->streamStage = zdss_init;
+ dctx->legacyContext = NULL;
+ dctx->previousLegacyVersion = 0;
+ dctx->noForwardProgress = 0;
+ dctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid());
+}
+
+ZSTD_DCtx* ZSTD_initStaticDCtx(void *workspace, size_t workspaceSize)
+{
+ ZSTD_DCtx* const dctx = (ZSTD_DCtx*) workspace;
+
+ if ((size_t)workspace & 7) return NULL; /* 8-aligned */
+ if (workspaceSize < sizeof(ZSTD_DCtx)) return NULL; /* minimum size */
+
+ ZSTD_initDCtx_internal(dctx);
+ dctx->staticSize = workspaceSize;
+ dctx->inBuff = (char*)(dctx+1);
+ return dctx;
+}
+
+ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem)
+{
+ if (!customMem.customAlloc ^ !customMem.customFree) return NULL;
+
+ { ZSTD_DCtx* const dctx = (ZSTD_DCtx*)ZSTD_malloc(sizeof(*dctx), customMem);
+ if (!dctx) return NULL;
+ dctx->customMem = customMem;
+ ZSTD_initDCtx_internal(dctx);
+ return dctx;
+ }
+}
+
+ZSTD_DCtx* ZSTD_createDCtx(void)
+{
+ DEBUGLOG(3, "ZSTD_createDCtx");
+ return ZSTD_createDCtx_advanced(ZSTD_defaultCMem);
+}
+
+size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx)
+{
+ if (dctx==NULL) return 0; /* support free on NULL */
+ if (dctx->staticSize) return ERROR(memory_allocation); /* not compatible with static DCtx */
+ { ZSTD_customMem const cMem = dctx->customMem;
+ ZSTD_freeDDict(dctx->ddictLocal);
+ dctx->ddictLocal = NULL;
+ ZSTD_free(dctx->inBuff, cMem);
+ dctx->inBuff = NULL;
+#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
+ if (dctx->legacyContext)
+ ZSTD_freeLegacyStreamContext(dctx->legacyContext, dctx->previousLegacyVersion);
+#endif
+ ZSTD_free(dctx, cMem);
+ return 0;
+ }
+}
+
+/* no longer useful */
+void ZSTD_copyDCtx(ZSTD_DCtx* dstDCtx, const ZSTD_DCtx* srcDCtx)
+{
+ size_t const toCopy = (size_t)((char*)(&dstDCtx->inBuff) - (char*)dstDCtx);
+ memcpy(dstDCtx, srcDCtx, toCopy); /* no need to copy workspace */
+}
+
+
+/*-*************************************************************
+ * Frame header decoding
+ ***************************************************************/
+
+/*! ZSTD_isFrame() :
+ * Tells if the content of `buffer` starts with a valid Frame Identifier.
+ * Note : Frame Identifier is 4 bytes. If `size < 4`, @return will always be 0.
+ * Note 2 : Legacy Frame Identifiers are considered valid only if Legacy Support is enabled.
+ * Note 3 : Skippable Frame Identifiers are considered valid. */
+unsigned ZSTD_isFrame(const void* buffer, size_t size)
+{
+ if (size < ZSTD_FRAMEIDSIZE) return 0;
+ { U32 const magic = MEM_readLE32(buffer);
+ if (magic == ZSTD_MAGICNUMBER) return 1;
+ if ((magic & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) return 1;
+ }
+#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
+ if (ZSTD_isLegacy(buffer, size)) return 1;
+#endif
+ return 0;
+}
+
+/** ZSTD_frameHeaderSize_internal() :
+ * srcSize must be large enough to reach header size fields.
+ * note : only works for formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless.
+ * @return : size of the Frame Header
+ * or an error code, which can be tested with ZSTD_isError() */
+static size_t ZSTD_frameHeaderSize_internal(const void* src, size_t srcSize, ZSTD_format_e format)
+{
+ size_t const minInputSize = ZSTD_startingInputLength(format);
+ if (srcSize < minInputSize) return ERROR(srcSize_wrong);
+
+ { BYTE const fhd = ((const BYTE*)src)[minInputSize-1];
+ U32 const dictID= fhd & 3;
+ U32 const singleSegment = (fhd >> 5) & 1;
+ U32 const fcsId = fhd >> 6;
+ return minInputSize + !singleSegment
+ + ZSTD_did_fieldSize[dictID] + ZSTD_fcs_fieldSize[fcsId]
+ + (singleSegment && !fcsId);
+ }
+}
+
+/** ZSTD_frameHeaderSize() :
+ * srcSize must be >= ZSTD_frameHeaderSize_prefix.
+ * @return : size of the Frame Header,
+ * or an error code (if srcSize is too small) */
+size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize)
+{
+ return ZSTD_frameHeaderSize_internal(src, srcSize, ZSTD_f_zstd1);
+}
+
+
+/** ZSTD_getFrameHeader_advanced() :
+ * decode Frame Header, or require larger `srcSize`.
+ * note : only works for formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless
+ * @return : 0, `zfhPtr` is correctly filled,
+ * >0, `srcSize` is too small, value is wanted `srcSize` amount,
+ * or an error code, which can be tested using ZSTD_isError() */
+size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize, ZSTD_format_e format)
+{
+ const BYTE* ip = (const BYTE*)src;
+ size_t const minInputSize = ZSTD_startingInputLength(format);
+
+ memset(zfhPtr, 0, sizeof(*zfhPtr)); /* not strictly necessary, but static analyzer do not understand that zfhPtr is only going to be read only if return value is zero, since they are 2 different signals */
+ if (srcSize < minInputSize) return minInputSize;
+ if (src==NULL) return ERROR(GENERIC); /* invalid parameter */
+
+ if ( (format != ZSTD_f_zstd1_magicless)
+ && (MEM_readLE32(src) != ZSTD_MAGICNUMBER) ) {
+ if ((MEM_readLE32(src) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {
+ /* skippable frame */
+ if (srcSize < ZSTD_SKIPPABLEHEADERSIZE)
+ return ZSTD_SKIPPABLEHEADERSIZE; /* magic number + frame length */
+ memset(zfhPtr, 0, sizeof(*zfhPtr));
+ zfhPtr->frameContentSize = MEM_readLE32((const char *)src + ZSTD_FRAMEIDSIZE);
+ zfhPtr->frameType = ZSTD_skippableFrame;
+ return 0;
+ }
+ return ERROR(prefix_unknown);
+ }
+
+ /* ensure there is enough `srcSize` to fully read/decode frame header */
+ { size_t const fhsize = ZSTD_frameHeaderSize_internal(src, srcSize, format);
+ if (srcSize < fhsize) return fhsize;
+ zfhPtr->headerSize = (U32)fhsize;
+ }
+
+ { BYTE const fhdByte = ip[minInputSize-1];
+ size_t pos = minInputSize;
+ U32 const dictIDSizeCode = fhdByte&3;
+ U32 const checksumFlag = (fhdByte>>2)&1;
+ U32 const singleSegment = (fhdByte>>5)&1;
+ U32 const fcsID = fhdByte>>6;
+ U64 windowSize = 0;
+ U32 dictID = 0;
+ U64 frameContentSize = ZSTD_CONTENTSIZE_UNKNOWN;
+ if ((fhdByte & 0x08) != 0)
+ return ERROR(frameParameter_unsupported); /* reserved bits, must be zero */
+
+ if (!singleSegment) {
+ BYTE const wlByte = ip[pos++];
+ U32 const windowLog = (wlByte >> 3) + ZSTD_WINDOWLOG_ABSOLUTEMIN;
+ if (windowLog > ZSTD_WINDOWLOG_MAX)
+ return ERROR(frameParameter_windowTooLarge);
+ windowSize = (1ULL << windowLog);
+ windowSize += (windowSize >> 3) * (wlByte&7);
+ }
+ switch(dictIDSizeCode)
+ {
+ default: assert(0); /* impossible */
+ case 0 : break;
+ case 1 : dictID = ip[pos]; pos++; break;
+ case 2 : dictID = MEM_readLE16(ip+pos); pos+=2; break;
+ case 3 : dictID = MEM_readLE32(ip+pos); pos+=4; break;
+ }
+ switch(fcsID)
+ {
+ default: assert(0); /* impossible */
+ case 0 : if (singleSegment) frameContentSize = ip[pos]; break;
+ case 1 : frameContentSize = MEM_readLE16(ip+pos)+256; break;
+ case 2 : frameContentSize = MEM_readLE32(ip+pos); break;
+ case 3 : frameContentSize = MEM_readLE64(ip+pos); break;
+ }
+ if (singleSegment) windowSize = frameContentSize;
+
+ zfhPtr->frameType = ZSTD_frame;
+ zfhPtr->frameContentSize = frameContentSize;
+ zfhPtr->windowSize = windowSize;
+ zfhPtr->blockSizeMax = (unsigned) MIN(windowSize, ZSTD_BLOCKSIZE_MAX);
+ zfhPtr->dictID = dictID;
+ zfhPtr->checksumFlag = checksumFlag;
+ }
+ return 0;
+}
+
+/** ZSTD_getFrameHeader() :
+ * decode Frame Header, or require larger `srcSize`.
+ * note : this function does not consume input, it only reads it.
+ * @return : 0, `zfhPtr` is correctly filled,
+ * >0, `srcSize` is too small, value is wanted `srcSize` amount,
+ * or an error code, which can be tested using ZSTD_isError() */
+size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize)
+{
+ return ZSTD_getFrameHeader_advanced(zfhPtr, src, srcSize, ZSTD_f_zstd1);
+}
+
+
+/** ZSTD_getFrameContentSize() :
+ * compatible with legacy mode
+ * @return : decompressed size of the single frame pointed to be `src` if known, otherwise
+ * - ZSTD_CONTENTSIZE_UNKNOWN if the size cannot be determined
+ * - ZSTD_CONTENTSIZE_ERROR if an error occurred (e.g. invalid magic number, srcSize too small) */
+unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize)
+{
+#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
+ if (ZSTD_isLegacy(src, srcSize)) {
+ unsigned long long const ret = ZSTD_getDecompressedSize_legacy(src, srcSize);
+ return ret == 0 ? ZSTD_CONTENTSIZE_UNKNOWN : ret;
+ }
+#endif
+ { ZSTD_frameHeader zfh;
+ if (ZSTD_getFrameHeader(&zfh, src, srcSize) != 0)
+ return ZSTD_CONTENTSIZE_ERROR;
+ if (zfh.frameType == ZSTD_skippableFrame) {
+ return 0;
+ } else {
+ return zfh.frameContentSize;
+ } }
+}
+
+static size_t readSkippableFrameSize(void const* src, size_t srcSize)
+{
+ size_t const skippableHeaderSize = ZSTD_SKIPPABLEHEADERSIZE;
+ U32 sizeU32;
+
+ if (srcSize < ZSTD_SKIPPABLEHEADERSIZE)
+ return ERROR(srcSize_wrong);
+
+ sizeU32 = MEM_readLE32((BYTE const*)src + ZSTD_FRAMEIDSIZE);
+ if ((U32)(sizeU32 + ZSTD_SKIPPABLEHEADERSIZE) < sizeU32)
+ return ERROR(frameParameter_unsupported);
+
+ return skippableHeaderSize + sizeU32;
+}
+
+/** ZSTD_findDecompressedSize() :
+ * compatible with legacy mode
+ * `srcSize` must be the exact length of some number of ZSTD compressed and/or
+ * skippable frames
+ * @return : decompressed size of the frames contained */
+unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize)
+{
+ unsigned long long totalDstSize = 0;
+
+ while (srcSize >= ZSTD_FRAMEHEADERSIZE_PREFIX) {
+ U32 const magicNumber = MEM_readLE32(src);
+
+ if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {
+ size_t const skippableSize = readSkippableFrameSize(src, srcSize);
+ if (ZSTD_isError(skippableSize))
+ return skippableSize;
+ if (srcSize < skippableSize) {
+ return ZSTD_CONTENTSIZE_ERROR;
+ }
+
+ src = (const BYTE *)src + skippableSize;
+ srcSize -= skippableSize;
+ continue;
+ }
+
+ { unsigned long long const ret = ZSTD_getFrameContentSize(src, srcSize);
+ if (ret >= ZSTD_CONTENTSIZE_ERROR) return ret;
+
+ /* check for overflow */
+ if (totalDstSize + ret < totalDstSize) return ZSTD_CONTENTSIZE_ERROR;
+ totalDstSize += ret;
+ }
+ { size_t const frameSrcSize = ZSTD_findFrameCompressedSize(src, srcSize);
+ if (ZSTD_isError(frameSrcSize)) {
+ return ZSTD_CONTENTSIZE_ERROR;
+ }
+
+ src = (const BYTE *)src + frameSrcSize;
+ srcSize -= frameSrcSize;
+ }
+ } /* while (srcSize >= ZSTD_frameHeaderSize_prefix) */
+
+ if (srcSize) return ZSTD_CONTENTSIZE_ERROR;
+
+ return totalDstSize;
+}
+
+/** ZSTD_getDecompressedSize() :
+ * compatible with legacy mode
+ * @return : decompressed size if known, 0 otherwise
+ note : 0 can mean any of the following :
+ - frame content is empty
+ - decompressed size field is not present in frame header
+ - frame header unknown / not supported
+ - frame header not complete (`srcSize` too small) */
+unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize)
+{
+ unsigned long long const ret = ZSTD_getFrameContentSize(src, srcSize);
+ ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_ERROR < ZSTD_CONTENTSIZE_UNKNOWN);
+ return (ret >= ZSTD_CONTENTSIZE_ERROR) ? 0 : ret;
+}
+
+
+/** ZSTD_decodeFrameHeader() :
+ * `headerSize` must be the size provided by ZSTD_frameHeaderSize().
+ * @return : 0 if success, or an error code, which can be tested using ZSTD_isError() */
+static size_t ZSTD_decodeFrameHeader(ZSTD_DCtx* dctx, const void* src, size_t headerSize)
+{
+ size_t const result = ZSTD_getFrameHeader_advanced(&(dctx->fParams), src, headerSize, dctx->format);
+ if (ZSTD_isError(result)) return result; /* invalid header */
+ if (result>0) return ERROR(srcSize_wrong); /* headerSize too small */
+ if (dctx->fParams.dictID && (dctx->dictID != dctx->fParams.dictID))
+ return ERROR(dictionary_wrong);
+ if (dctx->fParams.checksumFlag) XXH64_reset(&dctx->xxhState, 0);
+ return 0;
+}
+
+
+/** ZSTD_findFrameCompressedSize() :
+ * compatible with legacy mode
+ * `src` must point to the start of a ZSTD frame, ZSTD legacy frame, or skippable frame
+ * `srcSize` must be at least as large as the frame contained
+ * @return : the compressed size of the frame starting at `src` */
+size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize)
+{
+#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
+ if (ZSTD_isLegacy(src, srcSize))
+ return ZSTD_findFrameCompressedSizeLegacy(src, srcSize);
+#endif
+ if ( (srcSize >= ZSTD_SKIPPABLEHEADERSIZE)
+ && (MEM_readLE32(src) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START ) {
+ return readSkippableFrameSize(src, srcSize);
+ } else {
+ const BYTE* ip = (const BYTE*)src;
+ const BYTE* const ipstart = ip;
+ size_t remainingSize = srcSize;
+ ZSTD_frameHeader zfh;
+
+ /* Extract Frame Header */
+ { size_t const ret = ZSTD_getFrameHeader(&zfh, src, srcSize);
+ if (ZSTD_isError(ret)) return ret;
+ if (ret > 0) return ERROR(srcSize_wrong);
+ }
+
+ ip += zfh.headerSize;
+ remainingSize -= zfh.headerSize;
+
+ /* Loop on each block */
+ while (1) {
+ blockProperties_t blockProperties;
+ size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSize, &blockProperties);
+ if (ZSTD_isError(cBlockSize)) return cBlockSize;
+
+ if (ZSTD_blockHeaderSize + cBlockSize > remainingSize)
+ return ERROR(srcSize_wrong);
+
+ ip += ZSTD_blockHeaderSize + cBlockSize;
+ remainingSize -= ZSTD_blockHeaderSize + cBlockSize;
+
+ if (blockProperties.lastBlock) break;
+ }
+
+ if (zfh.checksumFlag) { /* Final frame content checksum */
+ if (remainingSize < 4) return ERROR(srcSize_wrong);
+ ip += 4;
+ }
+
+ return ip - ipstart;
+ }
+}
+
+
+
+/*-*************************************************************
+ * Frame decoding
+ ***************************************************************/
+
+
+void ZSTD_checkContinuity(ZSTD_DCtx* dctx, const void* dst)
+{
+ if (dst != dctx->previousDstEnd) { /* not contiguous */
+ dctx->dictEnd = dctx->previousDstEnd;
+ dctx->virtualStart = (const char*)dst - ((const char*)(dctx->previousDstEnd) - (const char*)(dctx->prefixStart));
+ dctx->prefixStart = dst;
+ dctx->previousDstEnd = dst;
+ }
+}
+
+/** ZSTD_insertBlock() :
+ insert `src` block into `dctx` history. Useful to track uncompressed blocks. */
+size_t ZSTD_insertBlock(ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize)
+{
+ ZSTD_checkContinuity(dctx, blockStart);
+ dctx->previousDstEnd = (const char*)blockStart + blockSize;
+ return blockSize;
+}
+
+
+static size_t ZSTD_copyRawBlock(void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize)
+{
+ DEBUGLOG(5, "ZSTD_copyRawBlock");
+ if (dst == NULL) {
+ if (srcSize == 0) return 0;
+ return ERROR(dstBuffer_null);
+ }
+ if (srcSize > dstCapacity) return ERROR(dstSize_tooSmall);
+ memcpy(dst, src, srcSize);
+ return srcSize;
+}
+
+static size_t ZSTD_setRleBlock(void* dst, size_t dstCapacity,
+ BYTE b,
+ size_t regenSize)
+{
+ if (dst == NULL) {
+ if (regenSize == 0) return 0;
+ return ERROR(dstBuffer_null);
+ }
+ if (regenSize > dstCapacity) return ERROR(dstSize_tooSmall);
+ memset(dst, b, regenSize);
+ return regenSize;
+}
+
+
+/*! ZSTD_decompressFrame() :
+ * @dctx must be properly initialized
+ * will update *srcPtr and *srcSizePtr,
+ * to make *srcPtr progress by one frame. */
+static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx,
+ void* dst, size_t dstCapacity,
+ const void** srcPtr, size_t *srcSizePtr)
+{
+ const BYTE* ip = (const BYTE*)(*srcPtr);
+ BYTE* const ostart = (BYTE* const)dst;
+ BYTE* const oend = ostart + dstCapacity;
+ BYTE* op = ostart;
+ size_t remainingSrcSize = *srcSizePtr;
+
+ DEBUGLOG(4, "ZSTD_decompressFrame (srcSize:%i)", (int)*srcSizePtr);
+
+ /* check */
+ if (remainingSrcSize < ZSTD_FRAMEHEADERSIZE_MIN+ZSTD_blockHeaderSize)
+ return ERROR(srcSize_wrong);
+
+ /* Frame Header */
+ { size_t const frameHeaderSize = ZSTD_frameHeaderSize(ip, ZSTD_FRAMEHEADERSIZE_PREFIX);
+ if (ZSTD_isError(frameHeaderSize)) return frameHeaderSize;
+ if (remainingSrcSize < frameHeaderSize+ZSTD_blockHeaderSize)
+ return ERROR(srcSize_wrong);
+ CHECK_F( ZSTD_decodeFrameHeader(dctx, ip, frameHeaderSize) );
+ ip += frameHeaderSize; remainingSrcSize -= frameHeaderSize;
+ }
+
+ /* Loop on each block */
+ while (1) {
+ size_t decodedSize;
+ blockProperties_t blockProperties;
+ size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSrcSize, &blockProperties);
+ if (ZSTD_isError(cBlockSize)) return cBlockSize;
+
+ ip += ZSTD_blockHeaderSize;
+ remainingSrcSize -= ZSTD_blockHeaderSize;
+ if (cBlockSize > remainingSrcSize) return ERROR(srcSize_wrong);
+
+ switch(blockProperties.blockType)
+ {
+ case bt_compressed:
+ decodedSize = ZSTD_decompressBlock_internal(dctx, op, oend-op, ip, cBlockSize, /* frame */ 1);
+ break;
+ case bt_raw :
+ decodedSize = ZSTD_copyRawBlock(op, oend-op, ip, cBlockSize);
+ break;
+ case bt_rle :
+ decodedSize = ZSTD_setRleBlock(op, oend-op, *ip, blockProperties.origSize);
+ break;
+ case bt_reserved :
+ default:
+ return ERROR(corruption_detected);
+ }
+
+ if (ZSTD_isError(decodedSize)) return decodedSize;
+ if (dctx->fParams.checksumFlag)
+ XXH64_update(&dctx->xxhState, op, decodedSize);
+ op += decodedSize;
+ ip += cBlockSize;
+ remainingSrcSize -= cBlockSize;
+ if (blockProperties.lastBlock) break;
+ }
+
+ if (dctx->fParams.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN) {
+ if ((U64)(op-ostart) != dctx->fParams.frameContentSize) {
+ return ERROR(corruption_detected);
+ } }
+ if (dctx->fParams.checksumFlag) { /* Frame content checksum verification */
+ U32 const checkCalc = (U32)XXH64_digest(&dctx->xxhState);
+ U32 checkRead;
+ if (remainingSrcSize<4) return ERROR(checksum_wrong);
+ checkRead = MEM_readLE32(ip);
+ if (checkRead != checkCalc) return ERROR(checksum_wrong);
+ ip += 4;
+ remainingSrcSize -= 4;
+ }
+
+ /* Allow caller to get size read */
+ *srcPtr = ip;
+ *srcSizePtr = remainingSrcSize;
+ return op-ostart;
+}
+
+static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ const void* dict, size_t dictSize,
+ const ZSTD_DDict* ddict)
+{
+ void* const dststart = dst;
+ int moreThan1Frame = 0;
+
+ DEBUGLOG(5, "ZSTD_decompressMultiFrame");
+ assert(dict==NULL || ddict==NULL); /* either dict or ddict set, not both */
+
+ if (ddict) {
+ dict = ZSTD_DDict_dictContent(ddict);
+ dictSize = ZSTD_DDict_dictSize(ddict);
+ }
+
+ while (srcSize >= ZSTD_FRAMEHEADERSIZE_PREFIX) {
+
+#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
+ if (ZSTD_isLegacy(src, srcSize)) {
+ size_t decodedSize;
+ size_t const frameSize = ZSTD_findFrameCompressedSizeLegacy(src, srcSize);
+ if (ZSTD_isError(frameSize)) return frameSize;
+ /* legacy support is not compatible with static dctx */
+ if (dctx->staticSize) return ERROR(memory_allocation);
+
+ decodedSize = ZSTD_decompressLegacy(dst, dstCapacity, src, frameSize, dict, dictSize);
+ if (ZSTD_isError(decodedSize)) return decodedSize;
+
+ assert(decodedSize <=- dstCapacity);
+ dst = (BYTE*)dst + decodedSize;
+ dstCapacity -= decodedSize;
+
+ src = (const BYTE*)src + frameSize;
+ srcSize -= frameSize;
+
+ continue;
+ }
+#endif
+
+ { U32 const magicNumber = MEM_readLE32(src);
+ DEBUGLOG(4, "reading magic number %08X (expecting %08X)",
+ (unsigned)magicNumber, ZSTD_MAGICNUMBER);
+ if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {
+ size_t const skippableSize = readSkippableFrameSize(src, srcSize);
+ if (ZSTD_isError(skippableSize))
+ return skippableSize;
+ if (srcSize < skippableSize) return ERROR(srcSize_wrong);
+
+ src = (const BYTE *)src + skippableSize;
+ srcSize -= skippableSize;
+ continue;
+ } }
+
+ if (ddict) {
+ /* we were called from ZSTD_decompress_usingDDict */
+ CHECK_F(ZSTD_decompressBegin_usingDDict(dctx, ddict));
+ } else {
+ /* this will initialize correctly with no dict if dict == NULL, so
+ * use this in all cases but ddict */
+ CHECK_F(ZSTD_decompressBegin_usingDict(dctx, dict, dictSize));
+ }
+ ZSTD_checkContinuity(dctx, dst);
+
+ { const size_t res = ZSTD_decompressFrame(dctx, dst, dstCapacity,
+ &src, &srcSize);
+ if ( (ZSTD_getErrorCode(res) == ZSTD_error_prefix_unknown)
+ && (moreThan1Frame==1) ) {
+ /* at least one frame successfully completed,
+ * but following bytes are garbage :
+ * it's more likely to be a srcSize error,
+ * specifying more bytes than compressed size of frame(s).
+ * This error message replaces ERROR(prefix_unknown),
+ * which would be confusing, as the first header is actually correct.
+ * Note that one could be unlucky, it might be a corruption error instead,
+ * happening right at the place where we expect zstd magic bytes.
+ * But this is _much_ less likely than a srcSize field error. */
+ return ERROR(srcSize_wrong);
+ }
+ if (ZSTD_isError(res)) return res;
+ assert(res <= dstCapacity);
+ dst = (BYTE*)dst + res;
+ dstCapacity -= res;
+ }
+ moreThan1Frame = 1;
+ } /* while (srcSize >= ZSTD_frameHeaderSize_prefix) */
+
+ if (srcSize) return ERROR(srcSize_wrong); /* input not entirely consumed */
+
+ return (BYTE*)dst - (BYTE*)dststart;
+}
+
+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)
+{
+ return ZSTD_decompressMultiFrame(dctx, dst, dstCapacity, src, srcSize, dict, dictSize, NULL);
+}
+
+
+size_t ZSTD_decompressDCtx(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize)
+{
+ return ZSTD_decompress_usingDict(dctx, dst, dstCapacity, src, srcSize, NULL, 0);
+}
+
+
+size_t ZSTD_decompress(void* dst, size_t dstCapacity, const void* src, size_t srcSize)
+{
+#if defined(ZSTD_HEAPMODE) && (ZSTD_HEAPMODE>=1)
+ size_t regenSize;
+ ZSTD_DCtx* const dctx = ZSTD_createDCtx();
+ if (dctx==NULL) return ERROR(memory_allocation);
+ regenSize = ZSTD_decompressDCtx(dctx, dst, dstCapacity, src, srcSize);
+ ZSTD_freeDCtx(dctx);
+ return regenSize;
+#else /* stack mode */
+ ZSTD_DCtx dctx;
+ ZSTD_initDCtx_internal(&dctx);
+ return ZSTD_decompressDCtx(&dctx, dst, dstCapacity, src, srcSize);
+#endif
+}
+
+
+/*-**************************************
+* Advanced Streaming Decompression API
+* Bufferless and synchronous
+****************************************/
+size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx) { return dctx->expected; }
+
+ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx) {
+ switch(dctx->stage)
+ {
+ default: /* should not happen */
+ assert(0);
+ case ZSTDds_getFrameHeaderSize:
+ case ZSTDds_decodeFrameHeader:
+ return ZSTDnit_frameHeader;
+ case ZSTDds_decodeBlockHeader:
+ return ZSTDnit_blockHeader;
+ case ZSTDds_decompressBlock:
+ return ZSTDnit_block;
+ case ZSTDds_decompressLastBlock:
+ return ZSTDnit_lastBlock;
+ case ZSTDds_checkChecksum:
+ return ZSTDnit_checksum;
+ case ZSTDds_decodeSkippableHeader:
+ case ZSTDds_skipFrame:
+ return ZSTDnit_skippableFrame;
+ }
+}
+
+static int ZSTD_isSkipFrame(ZSTD_DCtx* dctx) { return dctx->stage == ZSTDds_skipFrame; }
+
+/** ZSTD_decompressContinue() :
+ * srcSize : must be the exact nb of bytes expected (see ZSTD_nextSrcSizeToDecompress())
+ * @return : nb of bytes generated into `dst` (necessarily <= `dstCapacity)
+ * or an error code, which can be tested using ZSTD_isError() */
+size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize)
+{
+ DEBUGLOG(5, "ZSTD_decompressContinue (srcSize:%u)", (unsigned)srcSize);
+ /* Sanity check */
+ if (srcSize != dctx->expected)
+ return ERROR(srcSize_wrong); /* not allowed */
+ if (dstCapacity) ZSTD_checkContinuity(dctx, dst);
+
+ switch (dctx->stage)
+ {
+ case ZSTDds_getFrameHeaderSize :
+ assert(src != NULL);
+ if (dctx->format == ZSTD_f_zstd1) { /* allows header */
+ assert(srcSize >= ZSTD_FRAMEIDSIZE); /* to read skippable magic number */
+ if ((MEM_readLE32(src) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) { /* skippable frame */
+ memcpy(dctx->headerBuffer, src, srcSize);
+ dctx->expected = ZSTD_SKIPPABLEHEADERSIZE - srcSize; /* remaining to load to get full skippable frame header */
+ dctx->stage = ZSTDds_decodeSkippableHeader;
+ return 0;
+ } }
+ dctx->headerSize = ZSTD_frameHeaderSize_internal(src, srcSize, dctx->format);
+ if (ZSTD_isError(dctx->headerSize)) return dctx->headerSize;
+ memcpy(dctx->headerBuffer, src, srcSize);
+ dctx->expected = dctx->headerSize - srcSize;
+ dctx->stage = ZSTDds_decodeFrameHeader;
+ return 0;
+
+ case ZSTDds_decodeFrameHeader:
+ assert(src != NULL);
+ memcpy(dctx->headerBuffer + (dctx->headerSize - srcSize), src, srcSize);
+ 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);
+ if (ZSTD_isError(cBlockSize)) return cBlockSize;
+ dctx->expected = cBlockSize;
+ dctx->bType = bp.blockType;
+ dctx->rleSize = bp.origSize;
+ if (cBlockSize) {
+ dctx->stage = bp.lastBlock ? ZSTDds_decompressLastBlock : ZSTDds_decompressBlock;
+ return 0;
+ }
+ /* empty block */
+ if (bp.lastBlock) {
+ if (dctx->fParams.checksumFlag) {
+ dctx->expected = 4;
+ dctx->stage = ZSTDds_checkChecksum;
+ } else {
+ dctx->expected = 0; /* end of frame */
+ dctx->stage = ZSTDds_getFrameHeaderSize;
+ }
+ } else {
+ dctx->expected = ZSTD_blockHeaderSize; /* jump to next header */
+ dctx->stage = ZSTDds_decodeBlockHeader;
+ }
+ return 0;
+ }
+
+ case ZSTDds_decompressLastBlock:
+ case ZSTDds_decompressBlock:
+ DEBUGLOG(5, "ZSTD_decompressContinue: case ZSTDds_decompressBlock");
+ { size_t rSize;
+ switch(dctx->bType)
+ {
+ case bt_compressed:
+ DEBUGLOG(5, "ZSTD_decompressContinue: case bt_compressed");
+ rSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize, /* frame */ 1);
+ break;
+ case bt_raw :
+ rSize = ZSTD_copyRawBlock(dst, dstCapacity, src, srcSize);
+ break;
+ case bt_rle :
+ rSize = ZSTD_setRleBlock(dst, dstCapacity, *(const BYTE*)src, dctx->rleSize);
+ break;
+ case bt_reserved : /* should never happen */
+ default:
+ return ERROR(corruption_detected);
+ }
+ if (ZSTD_isError(rSize)) return rSize;
+ DEBUGLOG(5, "ZSTD_decompressContinue: decoded size from block : %u", (unsigned)rSize);
+ dctx->decodedSize += rSize;
+ if (dctx->fParams.checksumFlag) XXH64_update(&dctx->xxhState, dst, rSize);
+
+ if (dctx->stage == ZSTDds_decompressLastBlock) { /* end of frame */
+ DEBUGLOG(4, "ZSTD_decompressContinue: decoded size from frame : %u", (unsigned)dctx->decodedSize);
+ if (dctx->fParams.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN) {
+ if (dctx->decodedSize != dctx->fParams.frameContentSize) {
+ return ERROR(corruption_detected);
+ } }
+ if (dctx->fParams.checksumFlag) { /* another round for frame checksum */
+ dctx->expected = 4;
+ dctx->stage = ZSTDds_checkChecksum;
+ } else {
+ dctx->expected = 0; /* ends here */
+ dctx->stage = ZSTDds_getFrameHeaderSize;
+ }
+ } else {
+ dctx->stage = ZSTDds_decodeBlockHeader;
+ dctx->expected = ZSTD_blockHeaderSize;
+ dctx->previousDstEnd = (char*)dst + rSize;
+ }
+ return rSize;
+ }
+
+ case ZSTDds_checkChecksum:
+ assert(srcSize == 4); /* guaranteed by dctx->expected */
+ { U32 const h32 = (U32)XXH64_digest(&dctx->xxhState);
+ U32 const check32 = MEM_readLE32(src);
+ DEBUGLOG(4, "ZSTD_decompressContinue: checksum : calculated %08X :: %08X read", (unsigned)h32, (unsigned)check32);
+ if (check32 != h32) return ERROR(checksum_wrong);
+ dctx->expected = 0;
+ dctx->stage = ZSTDds_getFrameHeaderSize;
+ return 0;
+ }
+
+ case ZSTDds_decodeSkippableHeader:
+ assert(src != NULL);
+ assert(srcSize <= ZSTD_SKIPPABLEHEADERSIZE);
+ memcpy(dctx->headerBuffer + (ZSTD_SKIPPABLEHEADERSIZE - srcSize), src, srcSize); /* complete skippable header */
+ dctx->expected = MEM_readLE32(dctx->headerBuffer + ZSTD_FRAMEIDSIZE); /* note : dctx->expected can grow seriously large, beyond local buffer size */
+ dctx->stage = ZSTDds_skipFrame;
+ return 0;
+
+ case ZSTDds_skipFrame:
+ dctx->expected = 0;
+ dctx->stage = ZSTDds_getFrameHeaderSize;
+ return 0;
+
+ default:
+ assert(0); /* impossible */
+ return ERROR(GENERIC); /* some compiler require default to do something */
+ }
+}
+
+
+static size_t ZSTD_refDictContent(ZSTD_DCtx* dctx, const void* dict, size_t dictSize)
+{
+ dctx->dictEnd = dctx->previousDstEnd;
+ dctx->virtualStart = (const char*)dict - ((const char*)(dctx->previousDstEnd) - (const char*)(dctx->prefixStart));
+ dctx->prefixStart = dict;
+ dctx->previousDstEnd = (const char*)dict + dictSize;
+ return 0;
+}
+
+/*! ZSTD_loadDEntropy() :
+ * dict : must point at beginning of a valid zstd dictionary.
+ * @return : size of entropy tables read */
+size_t
+ZSTD_loadDEntropy(ZSTD_entropyDTables_t* entropy,
+ const void* const dict, size_t const dictSize)
+{
+ const BYTE* dictPtr = (const BYTE*)dict;
+ const BYTE* const dictEnd = dictPtr + dictSize;
+
+ if (dictSize <= 8) return ERROR(dictionary_corrupted);
+ assert(MEM_readLE32(dict) == ZSTD_MAGIC_DICTIONARY); /* dict must be valid */
+ dictPtr += 8; /* skip header = magic + dictID */
+
+ ZSTD_STATIC_ASSERT(offsetof(ZSTD_entropyDTables_t, OFTable) == offsetof(ZSTD_entropyDTables_t, LLTable) + sizeof(entropy->LLTable));
+ ZSTD_STATIC_ASSERT(offsetof(ZSTD_entropyDTables_t, MLTable) == offsetof(ZSTD_entropyDTables_t, OFTable) + sizeof(entropy->OFTable));
+ ZSTD_STATIC_ASSERT(sizeof(entropy->LLTable) + sizeof(entropy->OFTable) + sizeof(entropy->MLTable) >= HUF_DECOMPRESS_WORKSPACE_SIZE);
+ { void* const workspace = &entropy->LLTable; /* use fse tables as temporary workspace; implies fse tables are grouped together */
+ size_t const workspaceSize = sizeof(entropy->LLTable) + sizeof(entropy->OFTable) + sizeof(entropy->MLTable);
+#ifdef HUF_FORCE_DECOMPRESS_X1
+ /* in minimal huffman, we always use X1 variants */
+ size_t const hSize = HUF_readDTableX1_wksp(entropy->hufTable,
+ dictPtr, dictEnd - dictPtr,
+ workspace, workspaceSize);
+#else
+ size_t const hSize = HUF_readDTableX2_wksp(entropy->hufTable,
+ dictPtr, dictEnd - dictPtr,
+ workspace, workspaceSize);
+#endif
+ if (HUF_isError(hSize)) return ERROR(dictionary_corrupted);
+ dictPtr += hSize;
+ }
+
+ { short offcodeNCount[MaxOff+1];
+ unsigned offcodeMaxValue = MaxOff, offcodeLog;
+ size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, &offcodeMaxValue, &offcodeLog, dictPtr, dictEnd-dictPtr);
+ if (FSE_isError(offcodeHeaderSize)) return ERROR(dictionary_corrupted);
+ if (offcodeMaxValue > MaxOff) return ERROR(dictionary_corrupted);
+ if (offcodeLog > OffFSELog) return ERROR(dictionary_corrupted);
+ ZSTD_buildFSETable( entropy->OFTable,
+ offcodeNCount, offcodeMaxValue,
+ OF_base, OF_bits,
+ offcodeLog);
+ dictPtr += offcodeHeaderSize;
+ }
+
+ { short matchlengthNCount[MaxML+1];
+ unsigned matchlengthMaxValue = MaxML, matchlengthLog;
+ size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, dictEnd-dictPtr);
+ if (FSE_isError(matchlengthHeaderSize)) return ERROR(dictionary_corrupted);
+ if (matchlengthMaxValue > MaxML) return ERROR(dictionary_corrupted);
+ if (matchlengthLog > MLFSELog) return ERROR(dictionary_corrupted);
+ ZSTD_buildFSETable( entropy->MLTable,
+ matchlengthNCount, matchlengthMaxValue,
+ ML_base, ML_bits,
+ matchlengthLog);
+ dictPtr += matchlengthHeaderSize;
+ }
+
+ { short litlengthNCount[MaxLL+1];
+ unsigned litlengthMaxValue = MaxLL, litlengthLog;
+ size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, dictEnd-dictPtr);
+ if (FSE_isError(litlengthHeaderSize)) return ERROR(dictionary_corrupted);
+ if (litlengthMaxValue > MaxLL) return ERROR(dictionary_corrupted);
+ if (litlengthLog > LLFSELog) return ERROR(dictionary_corrupted);
+ ZSTD_buildFSETable( entropy->LLTable,
+ litlengthNCount, litlengthMaxValue,
+ LL_base, LL_bits,
+ litlengthLog);
+ dictPtr += litlengthHeaderSize;
+ }
+
+ if (dictPtr+12 > dictEnd) return ERROR(dictionary_corrupted);
+ { int i;
+ size_t const dictContentSize = (size_t)(dictEnd - (dictPtr+12));
+ for (i=0; i<3; i++) {
+ U32 const rep = MEM_readLE32(dictPtr); dictPtr += 4;
+ if (rep==0 || rep >= dictContentSize) return ERROR(dictionary_corrupted);
+ entropy->rep[i] = rep;
+ } }
+
+ return dictPtr - (const BYTE*)dict;
+}
+
+static size_t ZSTD_decompress_insertDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize)
+{
+ if (dictSize < 8) return ZSTD_refDictContent(dctx, dict, dictSize);
+ { U32 const magic = MEM_readLE32(dict);
+ if (magic != ZSTD_MAGIC_DICTIONARY) {
+ return ZSTD_refDictContent(dctx, dict, dictSize); /* pure content mode */
+ } }
+ dctx->dictID = MEM_readLE32((const char*)dict + ZSTD_FRAMEIDSIZE);
+
+ /* load entropy tables */
+ { size_t const eSize = ZSTD_loadDEntropy(&dctx->entropy, dict, dictSize);
+ if (ZSTD_isError(eSize)) return ERROR(dictionary_corrupted);
+ dict = (const char*)dict + eSize;
+ dictSize -= eSize;
+ }
+ dctx->litEntropy = dctx->fseEntropy = 1;
+
+ /* reference dictionary content */
+ return ZSTD_refDictContent(dctx, dict, dictSize);
+}
+
+size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx)
+{
+ assert(dctx != NULL);
+ dctx->expected = ZSTD_startingInputLength(dctx->format); /* dctx->format must be properly set */
+ dctx->stage = ZSTDds_getFrameHeaderSize;
+ dctx->decodedSize = 0;
+ dctx->previousDstEnd = NULL;
+ dctx->prefixStart = NULL;
+ dctx->virtualStart = NULL;
+ dctx->dictEnd = NULL;
+ dctx->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */
+ dctx->litEntropy = dctx->fseEntropy = 0;
+ dctx->dictID = 0;
+ ZSTD_STATIC_ASSERT(sizeof(dctx->entropy.rep) == sizeof(repStartValue));
+ memcpy(dctx->entropy.rep, repStartValue, sizeof(repStartValue)); /* initial repcodes */
+ dctx->LLTptr = dctx->entropy.LLTable;
+ dctx->MLTptr = dctx->entropy.MLTable;
+ dctx->OFTptr = dctx->entropy.OFTable;
+ dctx->HUFptr = dctx->entropy.hufTable;
+ return 0;
+}
+
+size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize)
+{
+ CHECK_F( ZSTD_decompressBegin(dctx) );
+ if (dict && dictSize)
+ CHECK_E(ZSTD_decompress_insertDictionary(dctx, dict, dictSize), dictionary_corrupted);
+ return 0;
+}
+
+
+/* ====== ZSTD_DDict ====== */
+
+size_t ZSTD_decompressBegin_usingDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict)
+{
+ DEBUGLOG(4, "ZSTD_decompressBegin_usingDDict");
+ assert(dctx != NULL);
+ if (ddict) {
+ const char* const dictStart = (const char*)ZSTD_DDict_dictContent(ddict);
+ size_t const dictSize = ZSTD_DDict_dictSize(ddict);
+ const void* const dictEnd = dictStart + dictSize;
+ dctx->ddictIsCold = (dctx->dictEnd != dictEnd);
+ DEBUGLOG(4, "DDict is %s",
+ dctx->ddictIsCold ? "~cold~" : "hot!");
+ }
+ CHECK_F( ZSTD_decompressBegin(dctx) );
+ if (ddict) { /* NULL ddict is equivalent to no dictionary */
+ ZSTD_copyDDictParameters(dctx, ddict);
+ }
+ return 0;
+}
+
+/*! ZSTD_getDictID_fromDict() :
+ * Provides the dictID stored within dictionary.
+ * if @return == 0, the dictionary is not conformant with Zstandard specification.
+ * It can still be loaded, but as a content-only dictionary. */
+unsigned ZSTD_getDictID_fromDict(const void* dict, size_t dictSize)
+{
+ if (dictSize < 8) return 0;
+ if (MEM_readLE32(dict) != ZSTD_MAGIC_DICTIONARY) return 0;
+ return MEM_readLE32((const char*)dict + ZSTD_FRAMEIDSIZE);
+}
+
+/*! ZSTD_getDictID_fromFrame() :
+ * Provides the dictID required to decompresse frame stored within `src`.
+ * If @return == 0, the dictID could not be decoded.
+ * This could for one of the following reasons :
+ * - The frame does not require a dictionary (most common case).
+ * - The frame was built with dictID intentionally removed.
+ * Needed dictionary is a hidden information.
+ * Note : this use case also happens when using a non-conformant dictionary.
+ * - `srcSize` is too small, and as a result, frame header could not be decoded.
+ * Note : possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`.
+ * - This is not a Zstandard frame.
+ * When identifying the exact failure cause, it's possible to use
+ * ZSTD_getFrameHeader(), which will provide a more precise error code. */
+unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize)
+{
+ ZSTD_frameHeader zfp = { 0, 0, 0, ZSTD_frame, 0, 0, 0 };
+ size_t const hError = ZSTD_getFrameHeader(&zfp, src, srcSize);
+ if (ZSTD_isError(hError)) return 0;
+ return zfp.dictID;
+}
+
+
+/*! ZSTD_decompress_usingDDict() :
+* Decompression using a pre-digested Dictionary
+* Use dictionary without significant overhead. */
+size_t ZSTD_decompress_usingDDict(ZSTD_DCtx* dctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ const ZSTD_DDict* ddict)
+{
+ /* pass content and size in case legacy frames are encountered */
+ return ZSTD_decompressMultiFrame(dctx, dst, dstCapacity, src, srcSize,
+ NULL, 0,
+ ddict);
+}
+
+
+/*=====================================
+* Streaming decompression
+*====================================*/
+
+ZSTD_DStream* ZSTD_createDStream(void)
+{
+ DEBUGLOG(3, "ZSTD_createDStream");
+ return ZSTD_createDStream_advanced(ZSTD_defaultCMem);
+}
+
+ZSTD_DStream* ZSTD_initStaticDStream(void *workspace, size_t workspaceSize)
+{
+ return ZSTD_initStaticDCtx(workspace, workspaceSize);
+}
+
+ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem)
+{
+ return ZSTD_createDCtx_advanced(customMem);
+}
+
+size_t ZSTD_freeDStream(ZSTD_DStream* zds)
+{
+ return ZSTD_freeDCtx(zds);
+}
+
+
+/* *** Initialization *** */
+
+size_t ZSTD_DStreamInSize(void) { return ZSTD_BLOCKSIZE_MAX + ZSTD_blockHeaderSize; }
+size_t ZSTD_DStreamOutSize(void) { return ZSTD_BLOCKSIZE_MAX; }
+
+size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx,
+ const void* dict, size_t dictSize,
+ ZSTD_dictLoadMethod_e dictLoadMethod,
+ ZSTD_dictContentType_e dictContentType)
+{
+ if (dctx->streamStage != zdss_init) return ERROR(stage_wrong);
+ ZSTD_freeDDict(dctx->ddictLocal);
+ if (dict && dictSize >= 8) {
+ dctx->ddictLocal = ZSTD_createDDict_advanced(dict, dictSize, dictLoadMethod, dictContentType, dctx->customMem);
+ if (dctx->ddictLocal == NULL) return ERROR(memory_allocation);
+ } else {
+ dctx->ddictLocal = NULL;
+ }
+ dctx->ddict = dctx->ddictLocal;
+ return 0;
+}
+
+size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize)
+{
+ return ZSTD_DCtx_loadDictionary_advanced(dctx, dict, dictSize, ZSTD_dlm_byRef, ZSTD_dct_auto);
+}
+
+size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize)
+{
+ return ZSTD_DCtx_loadDictionary_advanced(dctx, dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto);
+}
+
+size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType)
+{
+ return ZSTD_DCtx_loadDictionary_advanced(dctx, prefix, prefixSize, ZSTD_dlm_byRef, dictContentType);
+}
+
+size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize)
+{
+ return ZSTD_DCtx_refPrefix_advanced(dctx, prefix, prefixSize, ZSTD_dct_rawContent);
+}
+
+
+/* ZSTD_initDStream_usingDict() :
+ * return : expected size, aka ZSTD_FRAMEHEADERSIZE_PREFIX.
+ * this function cannot fail */
+size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize)
+{
+ DEBUGLOG(4, "ZSTD_initDStream_usingDict");
+ zds->streamStage = zdss_init;
+ zds->noForwardProgress = 0;
+ CHECK_F( ZSTD_DCtx_loadDictionary(zds, dict, dictSize) );
+ return ZSTD_FRAMEHEADERSIZE_PREFIX;
+}
+
+/* note : this variant can't fail */
+size_t ZSTD_initDStream(ZSTD_DStream* zds)
+{
+ DEBUGLOG(4, "ZSTD_initDStream");
+ return ZSTD_initDStream_usingDict(zds, NULL, 0);
+}
+
+/* ZSTD_initDStream_usingDDict() :
+ * ddict will just be referenced, and must outlive decompression session
+ * this function cannot fail */
+size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* dctx, const ZSTD_DDict* ddict)
+{
+ size_t const initResult = ZSTD_initDStream(dctx);
+ dctx->ddict = ddict;
+ return initResult;
+}
+
+/* ZSTD_resetDStream() :
+ * return : expected size, aka ZSTD_FRAMEHEADERSIZE_PREFIX.
+ * this function cannot fail */
+size_t ZSTD_resetDStream(ZSTD_DStream* dctx)
+{
+ DEBUGLOG(4, "ZSTD_resetDStream");
+ dctx->streamStage = zdss_loadHeader;
+ dctx->lhSize = dctx->inPos = dctx->outStart = dctx->outEnd = 0;
+ dctx->legacyVersion = 0;
+ dctx->hostageByte = 0;
+ return ZSTD_FRAMEHEADERSIZE_PREFIX;
+}
+
+
+size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict)
+{
+ if (dctx->streamStage != zdss_init) return ERROR(stage_wrong);
+ dctx->ddict = ddict;
+ return 0;
+}
+
+/* ZSTD_DCtx_setMaxWindowSize() :
+ * note : no direct equivalence in ZSTD_DCtx_setParameter,
+ * since this version sets windowSize, and the other sets windowLog */
+size_t ZSTD_DCtx_setMaxWindowSize(ZSTD_DCtx* dctx, size_t maxWindowSize)
+{
+ ZSTD_bounds const bounds = ZSTD_dParam_getBounds(ZSTD_d_windowLogMax);
+ size_t const min = (size_t)1 << bounds.lowerBound;
+ size_t const max = (size_t)1 << bounds.upperBound;
+ if (dctx->streamStage != zdss_init) return ERROR(stage_wrong);
+ if (maxWindowSize < min) return ERROR(parameter_outOfBound);
+ if (maxWindowSize > max) return ERROR(parameter_outOfBound);
+ dctx->maxWindowSize = maxWindowSize;
+ return 0;
+}
+
+size_t ZSTD_DCtx_setFormat(ZSTD_DCtx* dctx, ZSTD_format_e format)
+{
+ return ZSTD_DCtx_setParameter(dctx, ZSTD_d_format, format);
+}
+
+ZSTD_bounds ZSTD_dParam_getBounds(ZSTD_dParameter dParam)
+{
+ ZSTD_bounds bounds = { 0, 0, 0 };
+ switch(dParam) {
+ case ZSTD_d_windowLogMax:
+ bounds.lowerBound = ZSTD_WINDOWLOG_ABSOLUTEMIN;
+ bounds.upperBound = ZSTD_WINDOWLOG_MAX;
+ return bounds;
+ case ZSTD_d_format:
+ bounds.lowerBound = (int)ZSTD_f_zstd1;
+ bounds.upperBound = (int)ZSTD_f_zstd1_magicless;
+ ZSTD_STATIC_ASSERT(ZSTD_f_zstd1 < ZSTD_f_zstd1_magicless);
+ return bounds;
+ default:;
+ }
+ bounds.error = ERROR(parameter_unsupported);
+ return bounds;
+}
+
+/* ZSTD_dParam_withinBounds:
+ * @return 1 if value is within dParam bounds,
+ * 0 otherwise */
+static int ZSTD_dParam_withinBounds(ZSTD_dParameter dParam, int value)
+{
+ ZSTD_bounds const bounds = ZSTD_dParam_getBounds(dParam);
+ if (ZSTD_isError(bounds.error)) return 0;
+ if (value < bounds.lowerBound) return 0;
+ if (value > bounds.upperBound) return 0;
+ return 1;
+}
+
+#define CHECK_DBOUNDS(p,v) { \
+ if (!ZSTD_dParam_withinBounds(p, v)) \
+ return ERROR(parameter_outOfBound); \
+}
+
+size_t ZSTD_DCtx_setParameter(ZSTD_DCtx* dctx, ZSTD_dParameter dParam, int value)
+{
+ if (dctx->streamStage != zdss_init) return ERROR(stage_wrong);
+ switch(dParam) {
+ case ZSTD_d_windowLogMax:
+ CHECK_DBOUNDS(ZSTD_d_windowLogMax, value);
+ dctx->maxWindowSize = ((size_t)1) << value;
+ return 0;
+ case ZSTD_d_format:
+ CHECK_DBOUNDS(ZSTD_d_format, value);
+ dctx->format = (ZSTD_format_e)value;
+ return 0;
+ default:;
+ }
+ return ERROR(parameter_unsupported);
+}
+
+size_t ZSTD_DCtx_reset(ZSTD_DCtx* dctx, ZSTD_ResetDirective reset)
+{
+ if ( (reset == ZSTD_reset_session_only)
+ || (reset == ZSTD_reset_session_and_parameters) ) {
+ (void)ZSTD_initDStream(dctx);
+ }
+ if ( (reset == ZSTD_reset_parameters)
+ || (reset == ZSTD_reset_session_and_parameters) ) {
+ if (dctx->streamStage != zdss_init)
+ return ERROR(stage_wrong);
+ dctx->format = ZSTD_f_zstd1;
+ dctx->maxWindowSize = ZSTD_MAXWINDOWSIZE_DEFAULT;
+ }
+ return 0;
+}
+
+
+size_t ZSTD_sizeof_DStream(const ZSTD_DStream* dctx)
+{
+ return ZSTD_sizeof_DCtx(dctx);
+}
+
+size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long long frameContentSize)
+{
+ size_t const blockSize = (size_t) MIN(windowSize, ZSTD_BLOCKSIZE_MAX);
+ unsigned long long const neededRBSize = windowSize + blockSize + (WILDCOPY_OVERLENGTH * 2);
+ unsigned long long const neededSize = MIN(frameContentSize, neededRBSize);
+ size_t const minRBSize = (size_t) neededSize;
+ if ((unsigned long long)minRBSize != neededSize) return ERROR(frameParameter_windowTooLarge);
+ return minRBSize;
+}
+
+size_t ZSTD_estimateDStreamSize(size_t windowSize)
+{
+ size_t const blockSize = MIN(windowSize, ZSTD_BLOCKSIZE_MAX);
+ size_t const inBuffSize = blockSize; /* no block can be larger */
+ size_t const outBuffSize = ZSTD_decodingBufferSize_min(windowSize, ZSTD_CONTENTSIZE_UNKNOWN);
+ return ZSTD_estimateDCtxSize() + inBuffSize + outBuffSize;
+}
+
+size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize)
+{
+ U32 const windowSizeMax = 1U << ZSTD_WINDOWLOG_MAX; /* note : should be user-selectable, but requires an additional parameter (or a dctx) */
+ ZSTD_frameHeader zfh;
+ size_t const err = ZSTD_getFrameHeader(&zfh, src, srcSize);
+ if (ZSTD_isError(err)) return err;
+ if (err>0) return ERROR(srcSize_wrong);
+ if (zfh.windowSize > windowSizeMax)
+ return ERROR(frameParameter_windowTooLarge);
+ return ZSTD_estimateDStreamSize((size_t)zfh.windowSize);
+}
+
+
+/* ***** Decompression ***** */
+
+MEM_STATIC size_t ZSTD_limitCopy(void* dst, size_t dstCapacity, const void* src, size_t srcSize)
+{
+ size_t const length = MIN(dstCapacity, srcSize);
+ memcpy(dst, src, length);
+ return length;
+}
+
+
+size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inBuffer* input)
+{
+ const char* const istart = (const char*)(input->src) + input->pos;
+ const char* const iend = (const char*)(input->src) + input->size;
+ const char* ip = istart;
+ char* const ostart = (char*)(output->dst) + output->pos;
+ char* const oend = (char*)(output->dst) + output->size;
+ char* op = ostart;
+ U32 someMoreWork = 1;
+
+ DEBUGLOG(5, "ZSTD_decompressStream");
+ if (input->pos > input->size) { /* forbidden */
+ DEBUGLOG(5, "in: pos: %u vs size: %u",
+ (U32)input->pos, (U32)input->size);
+ return ERROR(srcSize_wrong);
+ }
+ if (output->pos > output->size) { /* forbidden */
+ DEBUGLOG(5, "out: pos: %u vs size: %u",
+ (U32)output->pos, (U32)output->size);
+ return ERROR(dstSize_tooSmall);
+ }
+ DEBUGLOG(5, "input size : %u", (U32)(input->size - input->pos));
+
+ while (someMoreWork) {
+ switch(zds->streamStage)
+ {
+ case zdss_init :
+ DEBUGLOG(5, "stage zdss_init => transparent reset ");
+ ZSTD_resetDStream(zds); /* transparent reset on starting decoding a new frame */
+ /* fall-through */
+
+ case zdss_loadHeader :
+ DEBUGLOG(5, "stage zdss_loadHeader (srcSize : %u)", (U32)(iend - ip));
+#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1)
+ if (zds->legacyVersion) {
+ /* legacy support is incompatible with static dctx */
+ if (zds->staticSize) return ERROR(memory_allocation);
+ { size_t const hint = ZSTD_decompressLegacyStream(zds->legacyContext, zds->legacyVersion, output, input);
+ if (hint==0) zds->streamStage = zdss_init;
+ return hint;
+ } }
+#endif
+ { size_t const hSize = ZSTD_getFrameHeader_advanced(&zds->fParams, zds->headerBuffer, zds->lhSize, zds->format);
+ DEBUGLOG(5, "header size : %u", (U32)hSize);
+ if (ZSTD_isError(hSize)) {
+#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1)
+ U32 const legacyVersion = ZSTD_isLegacy(istart, iend-istart);
+ if (legacyVersion) {
+ const void* const dict = zds->ddict ? ZSTD_DDict_dictContent(zds->ddict) : NULL;
+ size_t const dictSize = zds->ddict ? ZSTD_DDict_dictSize(zds->ddict) : 0;
+ DEBUGLOG(5, "ZSTD_decompressStream: detected legacy version v0.%u", legacyVersion);
+ /* legacy support is incompatible with static dctx */
+ if (zds->staticSize) return ERROR(memory_allocation);
+ CHECK_F(ZSTD_initLegacyStream(&zds->legacyContext,
+ zds->previousLegacyVersion, legacyVersion,
+ dict, dictSize));
+ zds->legacyVersion = zds->previousLegacyVersion = legacyVersion;
+ { size_t const hint = ZSTD_decompressLegacyStream(zds->legacyContext, legacyVersion, output, input);
+ if (hint==0) zds->streamStage = zdss_init; /* or stay in stage zdss_loadHeader */
+ return hint;
+ } }
+#endif
+ return hSize; /* error */
+ }
+ if (hSize != 0) { /* need more input */
+ size_t const toLoad = hSize - zds->lhSize; /* if hSize!=0, hSize > zds->lhSize */
+ size_t const remainingInput = (size_t)(iend-ip);
+ assert(iend >= ip);
+ if (toLoad > remainingInput) { /* not enough input to load full header */
+ if (remainingInput > 0) {
+ memcpy(zds->headerBuffer + zds->lhSize, ip, remainingInput);
+ zds->lhSize += remainingInput;
+ }
+ input->pos = input->size;
+ return (MAX(ZSTD_FRAMEHEADERSIZE_MIN, hSize) - zds->lhSize) + ZSTD_blockHeaderSize; /* remaining header bytes + next block header */
+ }
+ assert(ip != NULL);
+ memcpy(zds->headerBuffer + zds->lhSize, ip, toLoad); zds->lhSize = hSize; ip += toLoad;
+ break;
+ } }
+
+ /* check for single-pass mode opportunity */
+ if (zds->fParams.frameContentSize && zds->fParams.windowSize /* skippable frame if == 0 */
+ && (U64)(size_t)(oend-op) >= zds->fParams.frameContentSize) {
+ size_t const cSize = ZSTD_findFrameCompressedSize(istart, iend-istart);
+ if (cSize <= (size_t)(iend-istart)) {
+ /* shortcut : using single-pass mode */
+ size_t const decompressedSize = ZSTD_decompress_usingDDict(zds, op, oend-op, istart, cSize, zds->ddict);
+ if (ZSTD_isError(decompressedSize)) return decompressedSize;
+ DEBUGLOG(4, "shortcut to single-pass ZSTD_decompress_usingDDict()")
+ ip = istart + cSize;
+ op += decompressedSize;
+ zds->expected = 0;
+ zds->streamStage = zdss_init;
+ someMoreWork = 0;
+ break;
+ } }
+
+ /* Consume header (see ZSTDds_decodeFrameHeader) */
+ DEBUGLOG(4, "Consume header");
+ CHECK_F(ZSTD_decompressBegin_usingDDict(zds, zds->ddict));
+
+ if ((MEM_readLE32(zds->headerBuffer) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) { /* skippable frame */
+ zds->expected = MEM_readLE32(zds->headerBuffer + ZSTD_FRAMEIDSIZE);
+ zds->stage = ZSTDds_skipFrame;
+ } else {
+ CHECK_F(ZSTD_decodeFrameHeader(zds, zds->headerBuffer, zds->lhSize));
+ zds->expected = ZSTD_blockHeaderSize;
+ zds->stage = ZSTDds_decodeBlockHeader;
+ }
+
+ /* control buffer memory usage */
+ DEBUGLOG(4, "Control max memory usage (%u KB <= max %u KB)",
+ (U32)(zds->fParams.windowSize >>10),
+ (U32)(zds->maxWindowSize >> 10) );
+ zds->fParams.windowSize = MAX(zds->fParams.windowSize, 1U << ZSTD_WINDOWLOG_ABSOLUTEMIN);
+ if (zds->fParams.windowSize > zds->maxWindowSize) return ERROR(frameParameter_windowTooLarge);
+
+ /* Adapt buffer sizes to frame header instructions */
+ { size_t const neededInBuffSize = MAX(zds->fParams.blockSizeMax, 4 /* frame checksum */);
+ size_t const neededOutBuffSize = ZSTD_decodingBufferSize_min(zds->fParams.windowSize, zds->fParams.frameContentSize);
+ if ((zds->inBuffSize < neededInBuffSize) || (zds->outBuffSize < neededOutBuffSize)) {
+ size_t const bufferSize = neededInBuffSize + neededOutBuffSize;
+ DEBUGLOG(4, "inBuff : from %u to %u",
+ (U32)zds->inBuffSize, (U32)neededInBuffSize);
+ DEBUGLOG(4, "outBuff : from %u to %u",
+ (U32)zds->outBuffSize, (U32)neededOutBuffSize);
+ if (zds->staticSize) { /* static DCtx */
+ DEBUGLOG(4, "staticSize : %u", (U32)zds->staticSize);
+ assert(zds->staticSize >= sizeof(ZSTD_DCtx)); /* controlled at init */
+ if (bufferSize > zds->staticSize - sizeof(ZSTD_DCtx))
+ return ERROR(memory_allocation);
+ } else {
+ ZSTD_free(zds->inBuff, zds->customMem);
+ zds->inBuffSize = 0;
+ zds->outBuffSize = 0;
+ zds->inBuff = (char*)ZSTD_malloc(bufferSize, zds->customMem);
+ if (zds->inBuff == NULL) return ERROR(memory_allocation);
+ }
+ zds->inBuffSize = neededInBuffSize;
+ zds->outBuff = zds->inBuff + zds->inBuffSize;
+ zds->outBuffSize = neededOutBuffSize;
+ } }
+ zds->streamStage = zdss_read;
+ /* fall-through */
+
+ case zdss_read:
+ DEBUGLOG(5, "stage zdss_read");
+ { size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds);
+ DEBUGLOG(5, "neededInSize = %u", (U32)neededInSize);
+ if (neededInSize==0) { /* end of frame */
+ zds->streamStage = zdss_init;
+ someMoreWork = 0;
+ break;
+ }
+ if ((size_t)(iend-ip) >= neededInSize) { /* decode directly from src */
+ int const isSkipFrame = ZSTD_isSkipFrame(zds);
+ size_t const decodedSize = ZSTD_decompressContinue(zds,
+ 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->streamStage = zdss_flush;
+ break;
+ } }
+ if (ip==iend) { someMoreWork = 0; break; } /* no more input */
+ zds->streamStage = zdss_load;
+ /* fall-through */
+
+ case zdss_load:
+ { size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds);
+ size_t const toLoad = neededInSize - zds->inPos;
+ int const isSkipFrame = ZSTD_isSkipFrame(zds);
+ size_t loadedSize;
+ if (isSkipFrame) {
+ loadedSize = MIN(toLoad, (size_t)(iend-ip));
+ } else {
+ if (toLoad > zds->inBuffSize - zds->inPos) return ERROR(corruption_detected); /* should never happen */
+ loadedSize = ZSTD_limitCopy(zds->inBuff + zds->inPos, toLoad, ip, iend-ip);
+ }
+ ip += loadedSize;
+ zds->inPos += loadedSize;
+ if (loadedSize < toLoad) { someMoreWork = 0; break; } /* not enough input, wait for more */
+
+ /* decode loaded input */
+ { size_t const decodedSize = ZSTD_decompressContinue(zds,
+ zds->outBuff + zds->outStart, zds->outBuffSize - zds->outStart,
+ zds->inBuff, neededInSize);
+ if (ZSTD_isError(decodedSize)) return decodedSize;
+ zds->inPos = 0; /* input is consumed */
+ if (!decodedSize && !isSkipFrame) { zds->streamStage = zdss_read; break; } /* this was just a header */
+ zds->outEnd = zds->outStart + decodedSize;
+ } }
+ zds->streamStage = zdss_flush;
+ /* fall-through */
+
+ case zdss_flush:
+ { size_t const toFlushSize = zds->outEnd - zds->outStart;
+ size_t const flushedSize = ZSTD_limitCopy(op, oend-op, zds->outBuff + zds->outStart, toFlushSize);
+ op += flushedSize;
+ zds->outStart += flushedSize;
+ if (flushedSize == toFlushSize) { /* flush completed */
+ zds->streamStage = zdss_read;
+ if ( (zds->outBuffSize < zds->fParams.frameContentSize)
+ && (zds->outStart + zds->fParams.blockSizeMax > zds->outBuffSize) ) {
+ DEBUGLOG(5, "restart filling outBuff from beginning (left:%i, needed:%u)",
+ (int)(zds->outBuffSize - zds->outStart),
+ (U32)zds->fParams.blockSizeMax);
+ zds->outStart = zds->outEnd = 0;
+ }
+ break;
+ } }
+ /* cannot complete flush */
+ someMoreWork = 0;
+ break;
+
+ default:
+ assert(0); /* impossible */
+ return ERROR(GENERIC); /* some compiler require default to do something */
+ } }
+
+ /* result */
+ input->pos = (size_t)(ip - (const char*)(input->src));
+ output->pos = (size_t)(op - (char*)(output->dst));
+ if ((ip==istart) && (op==ostart)) { /* no forward progress */
+ zds->noForwardProgress ++;
+ if (zds->noForwardProgress >= ZSTD_NO_FORWARD_PROGRESS_MAX) {
+ if (op==oend) return ERROR(dstSize_tooSmall);
+ if (ip==iend) return ERROR(srcSize_wrong);
+ assert(0);
+ }
+ } else {
+ zds->noForwardProgress = 0;
+ }
+ { size_t nextSrcSizeHint = ZSTD_nextSrcSizeToDecompress(zds);
+ if (!nextSrcSizeHint) { /* frame fully decoded */
+ if (zds->outEnd == zds->outStart) { /* output fully flushed */
+ if (zds->hostageByte) {
+ if (input->pos >= input->size) {
+ /* can't release hostage (not present) */
+ zds->streamStage = zdss_read;
+ return 1;
+ }
+ input->pos++; /* release hostage */
+ } /* zds->hostageByte */
+ return 0;
+ } /* zds->outEnd == zds->outStart */
+ 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==0 */
+ nextSrcSizeHint += ZSTD_blockHeaderSize * (ZSTD_nextInputType(zds) == ZSTDnit_block); /* preload header of next block */
+ assert(zds->inPos <= nextSrcSizeHint);
+ nextSrcSizeHint -= zds->inPos; /* part already loaded*/
+ return nextSrcSizeHint;
+ }
+}
+
+size_t ZSTD_decompressStream_simpleArgs (
+ ZSTD_DCtx* dctx,
+ void* dst, size_t dstCapacity, size_t* dstPos,
+ const void* src, size_t srcSize, size_t* srcPos)
+{
+ ZSTD_outBuffer output = { dst, dstCapacity, *dstPos };
+ ZSTD_inBuffer input = { src, srcSize, *srcPos };
+ /* ZSTD_compress_generic() will check validity of dstPos and srcPos */
+ size_t const cErr = ZSTD_decompressStream(dctx, &output, &input);
+ *dstPos = output.pos;
+ *srcPos = input.pos;
+ return cErr;
+}
diff --git a/Utilities/cmzstd/lib/decompress/zstd_decompress_block.c b/Utilities/cmzstd/lib/decompress/zstd_decompress_block.c
new file mode 100644
index 000000000..32baad9fb
--- /dev/null
+++ b/Utilities/cmzstd/lib/decompress/zstd_decompress_block.c
@@ -0,0 +1,1307 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+/* zstd_decompress_block :
+ * this module takes care of decompressing _compressed_ block */
+
+/*-*******************************************************
+* Dependencies
+*********************************************************/
+#include <string.h> /* memcpy, memmove, memset */
+#include "compiler.h" /* prefetch */
+#include "cpu.h" /* bmi2 */
+#include "mem.h" /* low level memory routines */
+#define FSE_STATIC_LINKING_ONLY
+#include "fse.h"
+#define HUF_STATIC_LINKING_ONLY
+#include "huf.h"
+#include "zstd_internal.h"
+#include "zstd_decompress_internal.h" /* ZSTD_DCtx */
+#include "zstd_ddict.h" /* ZSTD_DDictDictContent */
+#include "zstd_decompress_block.h"
+
+/*_*******************************************************
+* Macros
+**********************************************************/
+
+/* These two optional macros force the use one way or another of the two
+ * ZSTD_decompressSequences implementations. You can't force in both directions
+ * at the same time.
+ */
+#if defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \
+ defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG)
+#error "Cannot force the use of the short and the long ZSTD_decompressSequences variants!"
+#endif
+
+
+/*_*******************************************************
+* Memory operations
+**********************************************************/
+static void ZSTD_copy4(void* dst, const void* src) { memcpy(dst, src, 4); }
+
+
+/*-*************************************************************
+ * Block decoding
+ ***************************************************************/
+
+/*! ZSTD_getcBlockSize() :
+ * Provides the size of compressed block from block header `src` */
+size_t ZSTD_getcBlockSize(const void* src, size_t srcSize,
+ blockProperties_t* bpPtr)
+{
+ if (srcSize < ZSTD_blockHeaderSize) return ERROR(srcSize_wrong);
+ { U32 const cBlockHeader = MEM_readLE24(src);
+ U32 const cSize = cBlockHeader >> 3;
+ bpPtr->lastBlock = cBlockHeader & 1;
+ bpPtr->blockType = (blockType_e)((cBlockHeader >> 1) & 3);
+ bpPtr->origSize = cSize; /* only useful for RLE */
+ if (bpPtr->blockType == bt_rle) return 1;
+ if (bpPtr->blockType == bt_reserved) return ERROR(corruption_detected);
+ return cSize;
+ }
+}
+
+
+/* Hidden declaration for fullbench */
+size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx,
+ const void* src, size_t srcSize);
+/*! ZSTD_decodeLiteralsBlock() :
+ * @return : nb of bytes read from src (< srcSize )
+ * note : symbol not declared but exposed for fullbench */
+size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx,
+ const void* src, size_t srcSize) /* note : srcSize < BLOCKSIZE */
+{
+ if (srcSize < MIN_CBLOCK_SIZE) return ERROR(corruption_detected);
+
+ { const BYTE* const istart = (const BYTE*) src;
+ symbolEncodingType_e const litEncType = (symbolEncodingType_e)(istart[0] & 3);
+
+ switch(litEncType)
+ {
+ case set_repeat:
+ if (dctx->litEntropy==0) return ERROR(dictionary_corrupted);
+ /* fall-through */
+
+ case set_compressed:
+ if (srcSize < 5) return ERROR(corruption_detected); /* srcSize >= MIN_CBLOCK_SIZE == 3; here we need up to 5 for case 3 */
+ { size_t lhSize, litSize, litCSize;
+ U32 singleStream=0;
+ U32 const lhlCode = (istart[0] >> 2) & 3;
+ U32 const lhc = MEM_readLE32(istart);
+ size_t hufSuccess;
+ switch(lhlCode)
+ {
+ 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;
+ case 2:
+ /* 2 - 2 - 14 - 14 */
+ 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;
+ }
+ if (litSize > ZSTD_BLOCKSIZE_MAX) return ERROR(corruption_detected);
+ if (litCSize + lhSize > srcSize) return ERROR(corruption_detected);
+
+ /* prefetch huffman table if cold */
+ if (dctx->ddictIsCold && (litSize > 768 /* heuristic */)) {
+ PREFETCH_AREA(dctx->HUFptr, sizeof(dctx->entropy.hufTable));
+ }
+
+ if (litEncType==set_repeat) {
+ if (singleStream) {
+ hufSuccess = HUF_decompress1X_usingDTable_bmi2(
+ dctx->litBuffer, litSize, istart+lhSize, litCSize,
+ dctx->HUFptr, dctx->bmi2);
+ } else {
+ hufSuccess = HUF_decompress4X_usingDTable_bmi2(
+ dctx->litBuffer, litSize, istart+lhSize, litCSize,
+ dctx->HUFptr, dctx->bmi2);
+ }
+ } else {
+ if (singleStream) {
+#if defined(HUF_FORCE_DECOMPRESS_X2)
+ hufSuccess = HUF_decompress1X_DCtx_wksp(
+ dctx->entropy.hufTable, dctx->litBuffer, litSize,
+ istart+lhSize, litCSize, dctx->workspace,
+ sizeof(dctx->workspace));
+#else
+ hufSuccess = HUF_decompress1X1_DCtx_wksp_bmi2(
+ dctx->entropy.hufTable, dctx->litBuffer, litSize,
+ istart+lhSize, litCSize, dctx->workspace,
+ sizeof(dctx->workspace), dctx->bmi2);
+#endif
+ } else {
+ hufSuccess = HUF_decompress4X_hufOnly_wksp_bmi2(
+ dctx->entropy.hufTable, dctx->litBuffer, litSize,
+ istart+lhSize, litCSize, dctx->workspace,
+ sizeof(dctx->workspace), dctx->bmi2);
+ }
+ }
+
+ if (HUF_isError(hufSuccess)) return ERROR(corruption_detected);
+
+ dctx->litPtr = dctx->litBuffer;
+ dctx->litSize = litSize;
+ dctx->litEntropy = 1;
+ if (litEncType==set_compressed) dctx->HUFptr = dctx->entropy.hufTable;
+ memset(dctx->litBuffer + dctx->litSize, 0, WILDCOPY_OVERLENGTH);
+ return litCSize + lhSize;
+ }
+
+ case set_basic:
+ { size_t litSize, lhSize;
+ U32 const lhlCode = ((istart[0]) >> 2) & 3;
+ switch(lhlCode)
+ {
+ case 0: case 2: default: /* note : default is impossible, since lhlCode into [0..3] */
+ lhSize = 1;
+ litSize = istart[0] >> 3;
+ break;
+ case 1:
+ lhSize = 2;
+ litSize = MEM_readLE16(istart) >> 4;
+ break;
+ case 3:
+ lhSize = 3;
+ litSize = MEM_readLE24(istart) >> 4;
+ break;
+ }
+
+ if (lhSize+litSize+WILDCOPY_OVERLENGTH > srcSize) { /* risk reading beyond src buffer with wildcopy */
+ if (litSize+lhSize > srcSize) return ERROR(corruption_detected);
+ memcpy(dctx->litBuffer, istart+lhSize, litSize);
+ dctx->litPtr = dctx->litBuffer;
+ dctx->litSize = litSize;
+ memset(dctx->litBuffer + dctx->litSize, 0, WILDCOPY_OVERLENGTH);
+ return lhSize+litSize;
+ }
+ /* direct reference into compressed stream */
+ dctx->litPtr = istart+lhSize;
+ dctx->litSize = litSize;
+ return lhSize+litSize;
+ }
+
+ case set_rle:
+ { U32 const lhlCode = ((istart[0]) >> 2) & 3;
+ size_t litSize, lhSize;
+ switch(lhlCode)
+ {
+ case 0: case 2: default: /* note : default is impossible, since lhlCode into [0..3] */
+ lhSize = 1;
+ litSize = istart[0] >> 3;
+ break;
+ case 1:
+ lhSize = 2;
+ litSize = MEM_readLE16(istart) >> 4;
+ break;
+ case 3:
+ lhSize = 3;
+ litSize = MEM_readLE24(istart) >> 4;
+ if (srcSize<4) return ERROR(corruption_detected); /* srcSize >= MIN_CBLOCK_SIZE == 3; here we need lhSize+1 = 4 */
+ break;
+ }
+ if (litSize > ZSTD_BLOCKSIZE_MAX) return ERROR(corruption_detected);
+ memset(dctx->litBuffer, istart[lhSize], litSize + WILDCOPY_OVERLENGTH);
+ dctx->litPtr = dctx->litBuffer;
+ dctx->litSize = litSize;
+ return lhSize+1;
+ }
+ default:
+ return ERROR(corruption_detected); /* impossible */
+ }
+ }
+}
+
+/* Default FSE distribution tables.
+ * These are pre-calculated FSE decoding tables using default distributions as defined in specification :
+ * https://github.com/facebook/zstd/blob/master/doc/zstd_compression_format.md#default-distributions
+ * They were generated programmatically with following method :
+ * - start from default distributions, present in /lib/common/zstd_internal.h
+ * - generate tables normally, using ZSTD_buildFSETable()
+ * - printout the content of tables
+ * - pretify output, report below, test with fuzzer to ensure it's correct */
+
+/* Default FSE distribution table for Literal Lengths */
+static const ZSTD_seqSymbol LL_defaultDTable[(1<<LL_DEFAULTNORMLOG)+1] = {
+ { 1, 1, 1, LL_DEFAULTNORMLOG}, /* header : fastMode, tableLog */
+ /* nextState, nbAddBits, nbBits, baseVal */
+ { 0, 0, 4, 0}, { 16, 0, 4, 0},
+ { 32, 0, 5, 1}, { 0, 0, 5, 3},
+ { 0, 0, 5, 4}, { 0, 0, 5, 6},
+ { 0, 0, 5, 7}, { 0, 0, 5, 9},
+ { 0, 0, 5, 10}, { 0, 0, 5, 12},
+ { 0, 0, 6, 14}, { 0, 1, 5, 16},
+ { 0, 1, 5, 20}, { 0, 1, 5, 22},
+ { 0, 2, 5, 28}, { 0, 3, 5, 32},
+ { 0, 4, 5, 48}, { 32, 6, 5, 64},
+ { 0, 7, 5, 128}, { 0, 8, 6, 256},
+ { 0, 10, 6, 1024}, { 0, 12, 6, 4096},
+ { 32, 0, 4, 0}, { 0, 0, 4, 1},
+ { 0, 0, 5, 2}, { 32, 0, 5, 4},
+ { 0, 0, 5, 5}, { 32, 0, 5, 7},
+ { 0, 0, 5, 8}, { 32, 0, 5, 10},
+ { 0, 0, 5, 11}, { 0, 0, 6, 13},
+ { 32, 1, 5, 16}, { 0, 1, 5, 18},
+ { 32, 1, 5, 22}, { 0, 2, 5, 24},
+ { 32, 3, 5, 32}, { 0, 3, 5, 40},
+ { 0, 6, 4, 64}, { 16, 6, 4, 64},
+ { 32, 7, 5, 128}, { 0, 9, 6, 512},
+ { 0, 11, 6, 2048}, { 48, 0, 4, 0},
+ { 16, 0, 4, 1}, { 32, 0, 5, 2},
+ { 32, 0, 5, 3}, { 32, 0, 5, 5},
+ { 32, 0, 5, 6}, { 32, 0, 5, 8},
+ { 32, 0, 5, 9}, { 32, 0, 5, 11},
+ { 32, 0, 5, 12}, { 0, 0, 6, 15},
+ { 32, 1, 5, 18}, { 32, 1, 5, 20},
+ { 32, 2, 5, 24}, { 32, 2, 5, 28},
+ { 32, 3, 5, 40}, { 32, 4, 5, 48},
+ { 0, 16, 6,65536}, { 0, 15, 6,32768},
+ { 0, 14, 6,16384}, { 0, 13, 6, 8192},
+}; /* LL_defaultDTable */
+
+/* Default FSE distribution table for Offset Codes */
+static const ZSTD_seqSymbol OF_defaultDTable[(1<<OF_DEFAULTNORMLOG)+1] = {
+ { 1, 1, 1, OF_DEFAULTNORMLOG}, /* header : fastMode, tableLog */
+ /* nextState, nbAddBits, nbBits, baseVal */
+ { 0, 0, 5, 0}, { 0, 6, 4, 61},
+ { 0, 9, 5, 509}, { 0, 15, 5,32765},
+ { 0, 21, 5,2097149}, { 0, 3, 5, 5},
+ { 0, 7, 4, 125}, { 0, 12, 5, 4093},
+ { 0, 18, 5,262141}, { 0, 23, 5,8388605},
+ { 0, 5, 5, 29}, { 0, 8, 4, 253},
+ { 0, 14, 5,16381}, { 0, 20, 5,1048573},
+ { 0, 2, 5, 1}, { 16, 7, 4, 125},
+ { 0, 11, 5, 2045}, { 0, 17, 5,131069},
+ { 0, 22, 5,4194301}, { 0, 4, 5, 13},
+ { 16, 8, 4, 253}, { 0, 13, 5, 8189},
+ { 0, 19, 5,524285}, { 0, 1, 5, 1},
+ { 16, 6, 4, 61}, { 0, 10, 5, 1021},
+ { 0, 16, 5,65533}, { 0, 28, 5,268435453},
+ { 0, 27, 5,134217725}, { 0, 26, 5,67108861},
+ { 0, 25, 5,33554429}, { 0, 24, 5,16777213},
+}; /* OF_defaultDTable */
+
+
+/* Default FSE distribution table for Match Lengths */
+static const ZSTD_seqSymbol ML_defaultDTable[(1<<ML_DEFAULTNORMLOG)+1] = {
+ { 1, 1, 1, ML_DEFAULTNORMLOG}, /* header : fastMode, tableLog */
+ /* nextState, nbAddBits, nbBits, baseVal */
+ { 0, 0, 6, 3}, { 0, 0, 4, 4},
+ { 32, 0, 5, 5}, { 0, 0, 5, 6},
+ { 0, 0, 5, 8}, { 0, 0, 5, 9},
+ { 0, 0, 5, 11}, { 0, 0, 6, 13},
+ { 0, 0, 6, 16}, { 0, 0, 6, 19},
+ { 0, 0, 6, 22}, { 0, 0, 6, 25},
+ { 0, 0, 6, 28}, { 0, 0, 6, 31},
+ { 0, 0, 6, 34}, { 0, 1, 6, 37},
+ { 0, 1, 6, 41}, { 0, 2, 6, 47},
+ { 0, 3, 6, 59}, { 0, 4, 6, 83},
+ { 0, 7, 6, 131}, { 0, 9, 6, 515},
+ { 16, 0, 4, 4}, { 0, 0, 4, 5},
+ { 32, 0, 5, 6}, { 0, 0, 5, 7},
+ { 32, 0, 5, 9}, { 0, 0, 5, 10},
+ { 0, 0, 6, 12}, { 0, 0, 6, 15},
+ { 0, 0, 6, 18}, { 0, 0, 6, 21},
+ { 0, 0, 6, 24}, { 0, 0, 6, 27},
+ { 0, 0, 6, 30}, { 0, 0, 6, 33},
+ { 0, 1, 6, 35}, { 0, 1, 6, 39},
+ { 0, 2, 6, 43}, { 0, 3, 6, 51},
+ { 0, 4, 6, 67}, { 0, 5, 6, 99},
+ { 0, 8, 6, 259}, { 32, 0, 4, 4},
+ { 48, 0, 4, 4}, { 16, 0, 4, 5},
+ { 32, 0, 5, 7}, { 32, 0, 5, 8},
+ { 32, 0, 5, 10}, { 32, 0, 5, 11},
+ { 0, 0, 6, 14}, { 0, 0, 6, 17},
+ { 0, 0, 6, 20}, { 0, 0, 6, 23},
+ { 0, 0, 6, 26}, { 0, 0, 6, 29},
+ { 0, 0, 6, 32}, { 0, 16, 6,65539},
+ { 0, 15, 6,32771}, { 0, 14, 6,16387},
+ { 0, 13, 6, 8195}, { 0, 12, 6, 4099},
+ { 0, 11, 6, 2051}, { 0, 10, 6, 1027},
+}; /* ML_defaultDTable */
+
+
+static void ZSTD_buildSeqTable_rle(ZSTD_seqSymbol* dt, U32 baseValue, U32 nbAddBits)
+{
+ void* ptr = dt;
+ ZSTD_seqSymbol_header* const DTableH = (ZSTD_seqSymbol_header*)ptr;
+ ZSTD_seqSymbol* const cell = dt + 1;
+
+ DTableH->tableLog = 0;
+ DTableH->fastMode = 0;
+
+ cell->nbBits = 0;
+ cell->nextState = 0;
+ assert(nbAddBits < 255);
+ cell->nbAdditionalBits = (BYTE)nbAddBits;
+ cell->baseValue = baseValue;
+}
+
+
+/* ZSTD_buildFSETable() :
+ * generate FSE decoding table for one symbol (ll, ml or off)
+ * cannot fail if input is valid =>
+ * all inputs are presumed validated at this stage */
+void
+ZSTD_buildFSETable(ZSTD_seqSymbol* dt,
+ const short* normalizedCounter, unsigned maxSymbolValue,
+ const U32* baseValue, const U32* nbAdditionalBits,
+ unsigned tableLog)
+{
+ ZSTD_seqSymbol* const tableDecode = dt+1;
+ U16 symbolNext[MaxSeq+1];
+
+ U32 const maxSV1 = maxSymbolValue + 1;
+ U32 const tableSize = 1 << tableLog;
+ U32 highThreshold = tableSize-1;
+
+ /* Sanity Checks */
+ assert(maxSymbolValue <= MaxSeq);
+ assert(tableLog <= MaxFSELog);
+
+ /* Init, lay down lowprob symbols */
+ { ZSTD_seqSymbol_header DTableH;
+ DTableH.tableLog = tableLog;
+ DTableH.fastMode = 1;
+ { S16 const largeLimit= (S16)(1 << (tableLog-1));
+ U32 s;
+ for (s=0; s<maxSV1; s++) {
+ if (normalizedCounter[s]==-1) {
+ tableDecode[highThreshold--].baseValue = s;
+ symbolNext[s] = 1;
+ } else {
+ if (normalizedCounter[s] >= largeLimit) DTableH.fastMode=0;
+ symbolNext[s] = normalizedCounter[s];
+ } } }
+ memcpy(dt, &DTableH, sizeof(DTableH));
+ }
+
+ /* Spread symbols */
+ { U32 const tableMask = tableSize-1;
+ U32 const step = FSE_TABLESTEP(tableSize);
+ U32 s, position = 0;
+ for (s=0; s<maxSV1; s++) {
+ int i;
+ for (i=0; i<normalizedCounter[s]; i++) {
+ tableDecode[position].baseValue = s;
+ position = (position + step) & tableMask;
+ while (position > highThreshold) position = (position + step) & tableMask; /* lowprob area */
+ } }
+ assert(position == 0); /* position must reach all cells once, otherwise normalizedCounter is incorrect */
+ }
+
+ /* Build Decoding table */
+ { U32 u;
+ for (u=0; u<tableSize; u++) {
+ U32 const symbol = tableDecode[u].baseValue;
+ U32 const nextState = symbolNext[symbol]++;
+ tableDecode[u].nbBits = (BYTE) (tableLog - BIT_highbit32(nextState) );
+ tableDecode[u].nextState = (U16) ( (nextState << tableDecode[u].nbBits) - tableSize);
+ assert(nbAdditionalBits[symbol] < 255);
+ tableDecode[u].nbAdditionalBits = (BYTE)nbAdditionalBits[symbol];
+ tableDecode[u].baseValue = baseValue[symbol];
+ } }
+}
+
+
+/*! ZSTD_buildSeqTable() :
+ * @return : nb bytes read from src,
+ * or an error code if it fails */
+static size_t ZSTD_buildSeqTable(ZSTD_seqSymbol* DTableSpace, const ZSTD_seqSymbol** DTablePtr,
+ symbolEncodingType_e type, unsigned max, U32 maxLog,
+ const void* src, size_t srcSize,
+ const U32* baseValue, const U32* nbAdditionalBits,
+ const ZSTD_seqSymbol* defaultTable, U32 flagRepeatTable,
+ int ddictIsCold, int nbSeq)
+{
+ switch(type)
+ {
+ case set_rle :
+ if (!srcSize) return ERROR(srcSize_wrong);
+ if ( (*(const BYTE*)src) > max) return ERROR(corruption_detected);
+ { U32 const symbol = *(const BYTE*)src;
+ U32 const baseline = baseValue[symbol];
+ U32 const nbBits = nbAdditionalBits[symbol];
+ ZSTD_buildSeqTable_rle(DTableSpace, baseline, nbBits);
+ }
+ *DTablePtr = DTableSpace;
+ return 1;
+ case set_basic :
+ *DTablePtr = defaultTable;
+ return 0;
+ case set_repeat:
+ if (!flagRepeatTable) return ERROR(corruption_detected);
+ /* prefetch FSE table if used */
+ if (ddictIsCold && (nbSeq > 24 /* heuristic */)) {
+ const void* const pStart = *DTablePtr;
+ size_t const pSize = sizeof(ZSTD_seqSymbol) * (SEQSYMBOL_TABLE_SIZE(maxLog));
+ PREFETCH_AREA(pStart, pSize);
+ }
+ return 0;
+ case set_compressed :
+ { unsigned tableLog;
+ S16 norm[MaxSeq+1];
+ 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);
+ ZSTD_buildFSETable(DTableSpace, norm, max, baseValue, nbAdditionalBits, tableLog);
+ *DTablePtr = DTableSpace;
+ return headerSize;
+ }
+ default : /* impossible */
+ assert(0);
+ return ERROR(GENERIC);
+ }
+}
+
+size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr,
+ const void* src, size_t srcSize)
+{
+ const BYTE* const istart = (const BYTE* const)src;
+ const BYTE* const iend = istart + srcSize;
+ const BYTE* ip = istart;
+ int nbSeq;
+ DEBUGLOG(5, "ZSTD_decodeSeqHeaders");
+
+ /* check */
+ if (srcSize < MIN_SEQUENCES_SIZE) return ERROR(srcSize_wrong);
+
+ /* SeqHead */
+ nbSeq = *ip++;
+ if (!nbSeq) {
+ *nbSeqPtr=0;
+ if (srcSize != 1) return ERROR(srcSize_wrong);
+ return 1;
+ }
+ if (nbSeq > 0x7F) {
+ if (nbSeq == 0xFF) {
+ if (ip+2 > iend) return ERROR(srcSize_wrong);
+ nbSeq = MEM_readLE16(ip) + LONGNBSEQ, ip+=2;
+ } else {
+ if (ip >= iend) return ERROR(srcSize_wrong);
+ nbSeq = ((nbSeq-0x80)<<8) + *ip++;
+ }
+ }
+ *nbSeqPtr = nbSeq;
+
+ /* FSE table descriptors */
+ if (ip+4 > iend) return ERROR(srcSize_wrong); /* minimum possible size */
+ { symbolEncodingType_e const LLtype = (symbolEncodingType_e)(*ip >> 6);
+ symbolEncodingType_e const OFtype = (symbolEncodingType_e)((*ip >> 4) & 3);
+ symbolEncodingType_e const MLtype = (symbolEncodingType_e)((*ip >> 2) & 3);
+ ip++;
+
+ /* Build DTables */
+ { size_t const llhSize = ZSTD_buildSeqTable(dctx->entropy.LLTable, &dctx->LLTptr,
+ LLtype, MaxLL, LLFSELog,
+ ip, iend-ip,
+ LL_base, LL_bits,
+ LL_defaultDTable, dctx->fseEntropy,
+ dctx->ddictIsCold, nbSeq);
+ if (ZSTD_isError(llhSize)) return ERROR(corruption_detected);
+ ip += llhSize;
+ }
+
+ { size_t const ofhSize = ZSTD_buildSeqTable(dctx->entropy.OFTable, &dctx->OFTptr,
+ OFtype, MaxOff, OffFSELog,
+ ip, iend-ip,
+ OF_base, OF_bits,
+ OF_defaultDTable, dctx->fseEntropy,
+ dctx->ddictIsCold, nbSeq);
+ if (ZSTD_isError(ofhSize)) return ERROR(corruption_detected);
+ ip += ofhSize;
+ }
+
+ { size_t const mlhSize = ZSTD_buildSeqTable(dctx->entropy.MLTable, &dctx->MLTptr,
+ MLtype, MaxML, MLFSELog,
+ ip, iend-ip,
+ ML_base, ML_bits,
+ ML_defaultDTable, dctx->fseEntropy,
+ dctx->ddictIsCold, nbSeq);
+ if (ZSTD_isError(mlhSize)) return ERROR(corruption_detected);
+ ip += mlhSize;
+ }
+ }
+
+ return ip-istart;
+}
+
+
+typedef struct {
+ size_t litLength;
+ size_t matchLength;
+ size_t offset;
+ const BYTE* match;
+} seq_t;
+
+typedef struct {
+ size_t state;
+ const ZSTD_seqSymbol* table;
+} ZSTD_fseState;
+
+typedef struct {
+ BIT_DStream_t DStream;
+ ZSTD_fseState stateLL;
+ ZSTD_fseState stateOffb;
+ ZSTD_fseState stateML;
+ size_t prevOffset[ZSTD_REP_NUM];
+ const BYTE* prefixStart;
+ const BYTE* dictEnd;
+ size_t pos;
+} seqState_t;
+
+
+/* ZSTD_execSequenceLast7():
+ * exceptional case : decompress a match starting within last 7 bytes of output buffer.
+ * requires more careful checks, to ensure there is no overflow.
+ * performance does not matter though.
+ * note : this case is supposed to be never generated "naturally" by reference encoder,
+ * since in most cases it needs at least 8 bytes to look for a match.
+ * but it's allowed by the specification. */
+FORCE_NOINLINE
+size_t ZSTD_execSequenceLast7(BYTE* op,
+ BYTE* const oend, seq_t sequence,
+ const BYTE** litPtr, const BYTE* const litLimit,
+ const BYTE* const base, const BYTE* const vBase, const BYTE* const dictEnd)
+{
+ BYTE* const oLitEnd = op + sequence.litLength;
+ size_t const sequenceLength = sequence.litLength + sequence.matchLength;
+ BYTE* const oMatchEnd = op + sequenceLength; /* risk : address space overflow (32-bits) */
+ const BYTE* const iLitEnd = *litPtr + sequence.litLength;
+ const BYTE* match = oLitEnd - sequence.offset;
+
+ /* check */
+ if (oMatchEnd>oend) return ERROR(dstSize_tooSmall); /* last match must fit within dstBuffer */
+ if (iLitEnd > litLimit) return ERROR(corruption_detected); /* try to read beyond literal buffer */
+
+ /* copy literals */
+ while (op < oLitEnd) *op++ = *(*litPtr)++;
+
+ /* copy Match */
+ if (sequence.offset > (size_t)(oLitEnd - base)) {
+ /* offset beyond prefix */
+ if (sequence.offset > (size_t)(oLitEnd - vBase)) return ERROR(corruption_detected);
+ match = dictEnd - (base-match);
+ if (match + sequence.matchLength <= dictEnd) {
+ memmove(oLitEnd, match, sequence.matchLength);
+ return sequenceLength;
+ }
+ /* span extDict & currentPrefixSegment */
+ { size_t const length1 = dictEnd - match;
+ memmove(oLitEnd, match, length1);
+ op = oLitEnd + length1;
+ sequence.matchLength -= length1;
+ match = base;
+ } }
+ while (op < oMatchEnd) *op++ = *match++;
+ return sequenceLength;
+}
+
+
+HINT_INLINE
+size_t ZSTD_execSequence(BYTE* op,
+ BYTE* const oend, seq_t sequence,
+ const BYTE** litPtr, const BYTE* const litLimit,
+ const BYTE* const prefixStart, const BYTE* const virtualStart, const BYTE* const dictEnd)
+{
+ BYTE* const oLitEnd = op + sequence.litLength;
+ size_t const sequenceLength = sequence.litLength + sequence.matchLength;
+ BYTE* const oMatchEnd = op + sequenceLength; /* risk : address space overflow (32-bits) */
+ BYTE* const oend_w = oend - WILDCOPY_OVERLENGTH;
+ const BYTE* const iLitEnd = *litPtr + sequence.litLength;
+ const BYTE* match = oLitEnd - sequence.offset;
+
+ /* check */
+ if (oMatchEnd>oend) return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend */
+ if (iLitEnd > litLimit) return ERROR(corruption_detected); /* over-read beyond lit buffer */
+ if (oLitEnd>oend_w) return ZSTD_execSequenceLast7(op, oend, sequence, litPtr, litLimit, prefixStart, virtualStart, dictEnd);
+
+ /* copy Literals */
+ ZSTD_copy8(op, *litPtr);
+ if (sequence.litLength > 8)
+ ZSTD_wildcopy(op+8, (*litPtr)+8, sequence.litLength - 8); /* note : since oLitEnd <= oend-WILDCOPY_OVERLENGTH, no risk of overwrite beyond oend */
+ op = oLitEnd;
+ *litPtr = iLitEnd; /* update for next sequence */
+
+ /* copy Match */
+ if (sequence.offset > (size_t)(oLitEnd - prefixStart)) {
+ /* offset beyond prefix -> go into extDict */
+ if (sequence.offset > (size_t)(oLitEnd - virtualStart))
+ return ERROR(corruption_detected);
+ match = dictEnd + (match - prefixStart);
+ if (match + sequence.matchLength <= dictEnd) {
+ memmove(oLitEnd, match, sequence.matchLength);
+ return sequenceLength;
+ }
+ /* span extDict & currentPrefixSegment */
+ { size_t const length1 = dictEnd - match;
+ memmove(oLitEnd, match, length1);
+ op = oLitEnd + length1;
+ sequence.matchLength -= length1;
+ match = prefixStart;
+ if (op > oend_w || sequence.matchLength < MINMATCH) {
+ U32 i;
+ for (i = 0; i < sequence.matchLength; ++i) op[i] = match[i];
+ return sequenceLength;
+ }
+ } }
+ /* Requirement: op <= oend_w && sequence.matchLength >= MINMATCH */
+
+ /* match within prefix */
+ if (sequence.offset < 8) {
+ /* close range match, overlap */
+ static const U32 dec32table[] = { 0, 1, 2, 1, 4, 4, 4, 4 }; /* added */
+ static const int dec64table[] = { 8, 8, 8, 7, 8, 9,10,11 }; /* subtracted */
+ int const sub2 = dec64table[sequence.offset];
+ op[0] = match[0];
+ op[1] = match[1];
+ op[2] = match[2];
+ op[3] = match[3];
+ match += dec32table[sequence.offset];
+ ZSTD_copy4(op+4, match);
+ match -= sub2;
+ } else {
+ ZSTD_copy8(op, match);
+ }
+ op += 8; match += 8;
+
+ if (oMatchEnd > oend-(16-MINMATCH)) {
+ if (op < oend_w) {
+ ZSTD_wildcopy(op, match, oend_w - op);
+ match += oend_w - op;
+ op = oend_w;
+ }
+ while (op < oMatchEnd) *op++ = *match++;
+ } else {
+ ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength-8); /* works even if matchLength < 8 */
+ }
+ return sequenceLength;
+}
+
+
+HINT_INLINE
+size_t ZSTD_execSequenceLong(BYTE* op,
+ BYTE* const oend, seq_t sequence,
+ const BYTE** litPtr, const BYTE* const litLimit,
+ const BYTE* const prefixStart, const BYTE* const dictStart, const BYTE* const dictEnd)
+{
+ BYTE* const oLitEnd = op + sequence.litLength;
+ size_t const sequenceLength = sequence.litLength + sequence.matchLength;
+ BYTE* const oMatchEnd = op + sequenceLength; /* risk : address space overflow (32-bits) */
+ BYTE* const oend_w = oend - WILDCOPY_OVERLENGTH;
+ const BYTE* const iLitEnd = *litPtr + sequence.litLength;
+ const BYTE* match = sequence.match;
+
+ /* check */
+ if (oMatchEnd > oend) return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend */
+ if (iLitEnd > litLimit) return ERROR(corruption_detected); /* over-read beyond lit buffer */
+ if (oLitEnd > oend_w) return ZSTD_execSequenceLast7(op, oend, sequence, litPtr, litLimit, prefixStart, dictStart, dictEnd);
+
+ /* copy Literals */
+ ZSTD_copy8(op, *litPtr); /* note : op <= oLitEnd <= oend_w == oend - 8 */
+ if (sequence.litLength > 8)
+ ZSTD_wildcopy(op+8, (*litPtr)+8, sequence.litLength - 8); /* note : since oLitEnd <= oend-WILDCOPY_OVERLENGTH, no risk of overwrite beyond oend */
+ op = oLitEnd;
+ *litPtr = iLitEnd; /* update for next sequence */
+
+ /* copy Match */
+ if (sequence.offset > (size_t)(oLitEnd - prefixStart)) {
+ /* offset beyond prefix */
+ if (sequence.offset > (size_t)(oLitEnd - dictStart)) return ERROR(corruption_detected);
+ if (match + sequence.matchLength <= dictEnd) {
+ memmove(oLitEnd, match, sequence.matchLength);
+ return sequenceLength;
+ }
+ /* span extDict & currentPrefixSegment */
+ { size_t const length1 = dictEnd - match;
+ memmove(oLitEnd, match, length1);
+ op = oLitEnd + length1;
+ sequence.matchLength -= length1;
+ match = prefixStart;
+ if (op > oend_w || sequence.matchLength < MINMATCH) {
+ U32 i;
+ for (i = 0; i < sequence.matchLength; ++i) op[i] = match[i];
+ return sequenceLength;
+ }
+ } }
+ assert(op <= oend_w);
+ assert(sequence.matchLength >= MINMATCH);
+
+ /* match within prefix */
+ if (sequence.offset < 8) {
+ /* close range match, overlap */
+ static const U32 dec32table[] = { 0, 1, 2, 1, 4, 4, 4, 4 }; /* added */
+ static const int dec64table[] = { 8, 8, 8, 7, 8, 9,10,11 }; /* subtracted */
+ int const sub2 = dec64table[sequence.offset];
+ op[0] = match[0];
+ op[1] = match[1];
+ op[2] = match[2];
+ op[3] = match[3];
+ match += dec32table[sequence.offset];
+ ZSTD_copy4(op+4, match);
+ match -= sub2;
+ } else {
+ ZSTD_copy8(op, match);
+ }
+ op += 8; match += 8;
+
+ if (oMatchEnd > oend-(16-MINMATCH)) {
+ if (op < oend_w) {
+ ZSTD_wildcopy(op, match, oend_w - op);
+ match += oend_w - op;
+ op = oend_w;
+ }
+ while (op < oMatchEnd) *op++ = *match++;
+ } else {
+ ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength-8); /* works even if matchLength < 8 */
+ }
+ return sequenceLength;
+}
+
+static void
+ZSTD_initFseState(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD, const ZSTD_seqSymbol* dt)
+{
+ const void* ptr = dt;
+ const ZSTD_seqSymbol_header* const DTableH = (const ZSTD_seqSymbol_header*)ptr;
+ DStatePtr->state = BIT_readBits(bitD, DTableH->tableLog);
+ DEBUGLOG(6, "ZSTD_initFseState : val=%u using %u bits",
+ (U32)DStatePtr->state, DTableH->tableLog);
+ BIT_reloadDStream(bitD);
+ DStatePtr->table = dt + 1;
+}
+
+FORCE_INLINE_TEMPLATE void
+ZSTD_updateFseState(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD)
+{
+ ZSTD_seqSymbol const DInfo = DStatePtr->table[DStatePtr->state];
+ U32 const nbBits = DInfo.nbBits;
+ size_t const lowBits = BIT_readBits(bitD, nbBits);
+ DStatePtr->state = DInfo.nextState + lowBits;
+}
+
+/* We need to add at most (ZSTD_WINDOWLOG_MAX_32 - 1) bits to read the maximum
+ * offset bits. But we can only read at most (STREAM_ACCUMULATOR_MIN_32 - 1)
+ * bits before reloading. This value is the maximum number of bytes we read
+ * after reloading when we are decoding long offets.
+ */
+#define LONG_OFFSETS_MAX_EXTRA_BITS_32 \
+ (ZSTD_WINDOWLOG_MAX_32 > STREAM_ACCUMULATOR_MIN_32 \
+ ? ZSTD_WINDOWLOG_MAX_32 - STREAM_ACCUMULATOR_MIN_32 \
+ : 0)
+
+typedef enum { ZSTD_lo_isRegularOffset, ZSTD_lo_isLongOffset=1 } ZSTD_longOffset_e;
+
+#ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG
+FORCE_INLINE_TEMPLATE seq_t
+ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets)
+{
+ seq_t seq;
+ U32 const llBits = seqState->stateLL.table[seqState->stateLL.state].nbAdditionalBits;
+ U32 const mlBits = seqState->stateML.table[seqState->stateML.state].nbAdditionalBits;
+ U32 const ofBits = seqState->stateOffb.table[seqState->stateOffb.state].nbAdditionalBits;
+ U32 const totalBits = llBits+mlBits+ofBits;
+ U32 const llBase = seqState->stateLL.table[seqState->stateLL.state].baseValue;
+ U32 const mlBase = seqState->stateML.table[seqState->stateML.state].baseValue;
+ U32 const ofBase = seqState->stateOffb.table[seqState->stateOffb.state].baseValue;
+
+ /* sequence */
+ { size_t offset;
+ if (!ofBits)
+ offset = 0;
+ else {
+ ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1);
+ ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5);
+ assert(ofBits <= MaxOff);
+ if (MEM_32bits() && longOffsets && (ofBits >= STREAM_ACCUMULATOR_MIN_32)) {
+ U32 const extraBits = ofBits - MIN(ofBits, 32 - seqState->DStream.bitsConsumed);
+ offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits);
+ BIT_reloadDStream(&seqState->DStream);
+ if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits);
+ assert(extraBits <= LONG_OFFSETS_MAX_EXTRA_BITS_32); /* to avoid another reload */
+ } else {
+ offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits/*>0*/); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */
+ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream);
+ }
+ }
+
+ if (ofBits <= 1) {
+ offset += (llBase==0);
+ if (offset) {
+ size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset];
+ temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */
+ if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1];
+ seqState->prevOffset[1] = seqState->prevOffset[0];
+ seqState->prevOffset[0] = offset = temp;
+ } else { /* offset == 0 */
+ offset = seqState->prevOffset[0];
+ }
+ } else {
+ seqState->prevOffset[2] = seqState->prevOffset[1];
+ seqState->prevOffset[1] = seqState->prevOffset[0];
+ seqState->prevOffset[0] = offset;
+ }
+ seq.offset = offset;
+ }
+
+ seq.matchLength = mlBase
+ + ((mlBits>0) ? BIT_readBitsFast(&seqState->DStream, mlBits/*>0*/) : 0); /* <= 16 bits */
+ if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32))
+ BIT_reloadDStream(&seqState->DStream);
+ if (MEM_64bits() && (totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog)))
+ BIT_reloadDStream(&seqState->DStream);
+ /* Ensure there are enough bits to read the rest of data in 64-bit mode. */
+ ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64);
+
+ seq.litLength = llBase
+ + ((llBits>0) ? BIT_readBitsFast(&seqState->DStream, llBits/*>0*/) : 0); /* <= 16 bits */
+ if (MEM_32bits())
+ BIT_reloadDStream(&seqState->DStream);
+
+ DEBUGLOG(6, "seq: litL=%u, matchL=%u, offset=%u",
+ (U32)seq.litLength, (U32)seq.matchLength, (U32)seq.offset);
+
+ /* ANS state update */
+ ZSTD_updateFseState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */
+ ZSTD_updateFseState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */
+ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */
+ ZSTD_updateFseState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */
+
+ return seq;
+}
+
+FORCE_INLINE_TEMPLATE size_t
+ZSTD_decompressSequences_body( ZSTD_DCtx* dctx,
+ void* dst, size_t maxDstSize,
+ const void* seqStart, size_t seqSize, int nbSeq,
+ const ZSTD_longOffset_e isLongOffset)
+{
+ const BYTE* ip = (const BYTE*)seqStart;
+ const BYTE* const iend = ip + seqSize;
+ BYTE* const ostart = (BYTE* const)dst;
+ BYTE* const oend = ostart + maxDstSize;
+ BYTE* op = ostart;
+ const BYTE* litPtr = dctx->litPtr;
+ const BYTE* const litEnd = litPtr + dctx->litSize;
+ const BYTE* const prefixStart = (const BYTE*) (dctx->prefixStart);
+ const BYTE* const vBase = (const BYTE*) (dctx->virtualStart);
+ const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd);
+ DEBUGLOG(5, "ZSTD_decompressSequences_body");
+
+ /* Regen sequences */
+ if (nbSeq) {
+ seqState_t seqState;
+ dctx->fseEntropy = 1;
+ { U32 i; for (i=0; i<ZSTD_REP_NUM; i++) seqState.prevOffset[i] = dctx->entropy.rep[i]; }
+ CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected);
+ ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr);
+ ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr);
+ ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr);
+
+ for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && nbSeq ; ) {
+ nbSeq--;
+ { seq_t const sequence = ZSTD_decodeSequence(&seqState, isLongOffset);
+ size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litEnd, prefixStart, vBase, dictEnd);
+ DEBUGLOG(6, "regenerated sequence size : %u", (U32)oneSeqSize);
+ if (ZSTD_isError(oneSeqSize)) return oneSeqSize;
+ op += oneSeqSize;
+ } }
+
+ /* check if reached exact end */
+ DEBUGLOG(5, "ZSTD_decompressSequences_body: after decode loop, remaining nbSeq : %i", nbSeq);
+ if (nbSeq) return ERROR(corruption_detected);
+ /* save reps for next block */
+ { U32 i; for (i=0; i<ZSTD_REP_NUM; i++) dctx->entropy.rep[i] = (U32)(seqState.prevOffset[i]); }
+ }
+
+ /* last literal segment */
+ { size_t const lastLLSize = litEnd - litPtr;
+ if (lastLLSize > (size_t)(oend-op)) return ERROR(dstSize_tooSmall);
+ memcpy(op, litPtr, lastLLSize);
+ op += lastLLSize;
+ }
+
+ return op-ostart;
+}
+
+static size_t
+ZSTD_decompressSequences_default(ZSTD_DCtx* dctx,
+ void* dst, size_t maxDstSize,
+ const void* seqStart, size_t seqSize, int nbSeq,
+ const ZSTD_longOffset_e isLongOffset)
+{
+ return ZSTD_decompressSequences_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
+}
+#endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG */
+
+
+
+#ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT
+FORCE_INLINE_TEMPLATE seq_t
+ZSTD_decodeSequenceLong(seqState_t* seqState, ZSTD_longOffset_e const longOffsets)
+{
+ seq_t seq;
+ U32 const llBits = seqState->stateLL.table[seqState->stateLL.state].nbAdditionalBits;
+ U32 const mlBits = seqState->stateML.table[seqState->stateML.state].nbAdditionalBits;
+ U32 const ofBits = seqState->stateOffb.table[seqState->stateOffb.state].nbAdditionalBits;
+ U32 const totalBits = llBits+mlBits+ofBits;
+ U32 const llBase = seqState->stateLL.table[seqState->stateLL.state].baseValue;
+ U32 const mlBase = seqState->stateML.table[seqState->stateML.state].baseValue;
+ U32 const ofBase = seqState->stateOffb.table[seqState->stateOffb.state].baseValue;
+
+ /* sequence */
+ { size_t offset;
+ if (!ofBits)
+ offset = 0;
+ else {
+ ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1);
+ ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5);
+ assert(ofBits <= MaxOff);
+ if (MEM_32bits() && longOffsets) {
+ U32 const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN_32-1);
+ offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits);
+ if (MEM_32bits() || extraBits) BIT_reloadDStream(&seqState->DStream);
+ if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits);
+ } else {
+ offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */
+ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream);
+ }
+ }
+
+ if (ofBits <= 1) {
+ offset += (llBase==0);
+ if (offset) {
+ size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset];
+ temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */
+ if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1];
+ seqState->prevOffset[1] = seqState->prevOffset[0];
+ seqState->prevOffset[0] = offset = temp;
+ } else {
+ offset = seqState->prevOffset[0];
+ }
+ } else {
+ seqState->prevOffset[2] = seqState->prevOffset[1];
+ seqState->prevOffset[1] = seqState->prevOffset[0];
+ seqState->prevOffset[0] = offset;
+ }
+ seq.offset = offset;
+ }
+
+ seq.matchLength = mlBase + ((mlBits>0) ? BIT_readBitsFast(&seqState->DStream, mlBits) : 0); /* <= 16 bits */
+ if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32))
+ BIT_reloadDStream(&seqState->DStream);
+ if (MEM_64bits() && (totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog)))
+ BIT_reloadDStream(&seqState->DStream);
+ /* Verify that there is enough bits to read the rest of the data in 64-bit mode. */
+ ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64);
+
+ seq.litLength = llBase + ((llBits>0) ? BIT_readBitsFast(&seqState->DStream, llBits) : 0); /* <= 16 bits */
+ if (MEM_32bits())
+ BIT_reloadDStream(&seqState->DStream);
+
+ { size_t const pos = seqState->pos + seq.litLength;
+ const BYTE* const matchBase = (seq.offset > pos) ? seqState->dictEnd : seqState->prefixStart;
+ seq.match = matchBase + pos - seq.offset; /* note : this operation can overflow when seq.offset is really too large, which can only happen when input is corrupted.
+ * No consequence though : no memory access will occur, overly large offset will be detected in ZSTD_execSequenceLong() */
+ seqState->pos = pos + seq.matchLength;
+ }
+
+ /* ANS state update */
+ ZSTD_updateFseState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */
+ ZSTD_updateFseState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */
+ if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */
+ ZSTD_updateFseState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */
+
+ return seq;
+}
+
+FORCE_INLINE_TEMPLATE size_t
+ZSTD_decompressSequencesLong_body(
+ ZSTD_DCtx* dctx,
+ void* dst, size_t maxDstSize,
+ const void* seqStart, size_t seqSize, int nbSeq,
+ const ZSTD_longOffset_e isLongOffset)
+{
+ const BYTE* ip = (const BYTE*)seqStart;
+ const BYTE* const iend = ip + seqSize;
+ BYTE* const ostart = (BYTE* const)dst;
+ BYTE* const oend = ostart + maxDstSize;
+ BYTE* op = ostart;
+ const BYTE* litPtr = dctx->litPtr;
+ const BYTE* const litEnd = litPtr + dctx->litSize;
+ const BYTE* const prefixStart = (const BYTE*) (dctx->prefixStart);
+ const BYTE* const dictStart = (const BYTE*) (dctx->virtualStart);
+ const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd);
+
+ /* Regen sequences */
+ if (nbSeq) {
+#define STORED_SEQS 4
+#define STORED_SEQS_MASK (STORED_SEQS-1)
+#define ADVANCED_SEQS 4
+ seq_t sequences[STORED_SEQS];
+ int const seqAdvance = MIN(nbSeq, ADVANCED_SEQS);
+ seqState_t seqState;
+ int seqNb;
+ dctx->fseEntropy = 1;
+ { int i; for (i=0; i<ZSTD_REP_NUM; i++) seqState.prevOffset[i] = dctx->entropy.rep[i]; }
+ seqState.prefixStart = prefixStart;
+ seqState.pos = (size_t)(op-prefixStart);
+ seqState.dictEnd = dictEnd;
+ assert(iend >= ip);
+ CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend-ip), corruption_detected);
+ ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr);
+ ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr);
+ ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr);
+
+ /* prepare in advance */
+ for (seqNb=0; (BIT_reloadDStream(&seqState.DStream) <= BIT_DStream_completed) && (seqNb<seqAdvance); seqNb++) {
+ sequences[seqNb] = ZSTD_decodeSequenceLong(&seqState, isLongOffset);
+ PREFETCH_L1(sequences[seqNb].match); PREFETCH_L1(sequences[seqNb].match + sequences[seqNb].matchLength - 1); /* note : it's safe to invoke PREFETCH() on any memory address, including invalid ones */
+ }
+ if (seqNb<seqAdvance) return ERROR(corruption_detected);
+
+ /* decode and decompress */
+ for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && (seqNb<nbSeq) ; seqNb++) {
+ seq_t const sequence = ZSTD_decodeSequenceLong(&seqState, isLongOffset);
+ size_t const oneSeqSize = ZSTD_execSequenceLong(op, oend, sequences[(seqNb-ADVANCED_SEQS) & STORED_SEQS_MASK], &litPtr, litEnd, prefixStart, dictStart, dictEnd);
+ if (ZSTD_isError(oneSeqSize)) return oneSeqSize;
+ PREFETCH_L1(sequence.match); PREFETCH_L1(sequence.match + sequence.matchLength - 1); /* note : it's safe to invoke PREFETCH() on any memory address, including invalid ones */
+ sequences[seqNb & STORED_SEQS_MASK] = sequence;
+ op += oneSeqSize;
+ }
+ if (seqNb<nbSeq) return ERROR(corruption_detected);
+
+ /* finish queue */
+ seqNb -= seqAdvance;
+ for ( ; seqNb<nbSeq ; seqNb++) {
+ size_t const oneSeqSize = ZSTD_execSequenceLong(op, oend, sequences[seqNb&STORED_SEQS_MASK], &litPtr, litEnd, prefixStart, dictStart, dictEnd);
+ if (ZSTD_isError(oneSeqSize)) return oneSeqSize;
+ op += oneSeqSize;
+ }
+
+ /* save reps for next block */
+ { U32 i; for (i=0; i<ZSTD_REP_NUM; i++) dctx->entropy.rep[i] = (U32)(seqState.prevOffset[i]); }
+ }
+
+ /* last literal segment */
+ { size_t const lastLLSize = litEnd - litPtr;
+ if (lastLLSize > (size_t)(oend-op)) return ERROR(dstSize_tooSmall);
+ memcpy(op, litPtr, lastLLSize);
+ op += lastLLSize;
+ }
+
+ return op-ostart;
+}
+
+static size_t
+ZSTD_decompressSequencesLong_default(ZSTD_DCtx* dctx,
+ void* dst, size_t maxDstSize,
+ const void* seqStart, size_t seqSize, int nbSeq,
+ const ZSTD_longOffset_e isLongOffset)
+{
+ return ZSTD_decompressSequencesLong_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
+}
+#endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT */
+
+
+
+#if DYNAMIC_BMI2
+
+#ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG
+static TARGET_ATTRIBUTE("bmi2") size_t
+ZSTD_decompressSequences_bmi2(ZSTD_DCtx* dctx,
+ void* dst, size_t maxDstSize,
+ const void* seqStart, size_t seqSize, int nbSeq,
+ const ZSTD_longOffset_e isLongOffset)
+{
+ return ZSTD_decompressSequences_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
+}
+#endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG */
+
+#ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT
+static TARGET_ATTRIBUTE("bmi2") size_t
+ZSTD_decompressSequencesLong_bmi2(ZSTD_DCtx* dctx,
+ void* dst, size_t maxDstSize,
+ const void* seqStart, size_t seqSize, int nbSeq,
+ const ZSTD_longOffset_e isLongOffset)
+{
+ return ZSTD_decompressSequencesLong_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
+}
+#endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT */
+
+#endif /* DYNAMIC_BMI2 */
+
+typedef size_t (*ZSTD_decompressSequences_t)(
+ ZSTD_DCtx* dctx,
+ void* dst, size_t maxDstSize,
+ const void* seqStart, size_t seqSize, int nbSeq,
+ const ZSTD_longOffset_e isLongOffset);
+
+#ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG
+static size_t
+ZSTD_decompressSequences(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize,
+ const void* seqStart, size_t seqSize, int nbSeq,
+ const ZSTD_longOffset_e isLongOffset)
+{
+ DEBUGLOG(5, "ZSTD_decompressSequences");
+#if DYNAMIC_BMI2
+ if (dctx->bmi2) {
+ return ZSTD_decompressSequences_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
+ }
+#endif
+ return ZSTD_decompressSequences_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
+}
+#endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG */
+
+
+#ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT
+/* ZSTD_decompressSequencesLong() :
+ * decompression function triggered when a minimum share of offsets is considered "long",
+ * aka out of cache.
+ * note : "long" definition seems overloaded here, sometimes meaning "wider than bitstream register", and sometimes mearning "farther than memory cache distance".
+ * This function will try to mitigate main memory latency through the use of prefetching */
+static size_t
+ZSTD_decompressSequencesLong(ZSTD_DCtx* dctx,
+ void* dst, size_t maxDstSize,
+ const void* seqStart, size_t seqSize, int nbSeq,
+ const ZSTD_longOffset_e isLongOffset)
+{
+ DEBUGLOG(5, "ZSTD_decompressSequencesLong");
+#if DYNAMIC_BMI2
+ if (dctx->bmi2) {
+ return ZSTD_decompressSequencesLong_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
+ }
+#endif
+ return ZSTD_decompressSequencesLong_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset);
+}
+#endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT */
+
+
+
+#if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \
+ !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG)
+/* ZSTD_getLongOffsetsShare() :
+ * condition : offTable must be valid
+ * @return : "share" of long offsets (arbitrarily defined as > (1<<23))
+ * compared to maximum possible of (1<<OffFSELog) */
+static unsigned
+ZSTD_getLongOffsetsShare(const ZSTD_seqSymbol* offTable)
+{
+ const void* ptr = offTable;
+ U32 const tableLog = ((const ZSTD_seqSymbol_header*)ptr)[0].tableLog;
+ const ZSTD_seqSymbol* table = offTable + 1;
+ U32 const max = 1 << tableLog;
+ U32 u, total = 0;
+ DEBUGLOG(5, "ZSTD_getLongOffsetsShare: (tableLog=%u)", tableLog);
+
+ assert(max <= (1 << OffFSELog)); /* max not too large */
+ for (u=0; u<max; u++) {
+ if (table[u].nbAdditionalBits > 22) total += 1;
+ }
+
+ assert(tableLog <= OffFSELog);
+ total <<= (OffFSELog - tableLog); /* scale to OffFSELog */
+
+ return total;
+}
+#endif
+
+
+size_t
+ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize, const int frame)
+{ /* blockType == blockCompressed */
+ const BYTE* ip = (const BYTE*)src;
+ /* isLongOffset must be true if there are long offsets.
+ * Offsets are long if they are larger than 2^STREAM_ACCUMULATOR_MIN.
+ * We don't expect that to be the case in 64-bit mode.
+ * In block mode, window size is not known, so we have to be conservative.
+ * (note: but it could be evaluated from current-lowLimit)
+ */
+ ZSTD_longOffset_e const isLongOffset = (ZSTD_longOffset_e)(MEM_32bits() && (!frame || (dctx->fParams.windowSize > (1ULL << STREAM_ACCUMULATOR_MIN))));
+ DEBUGLOG(5, "ZSTD_decompressBlock_internal (size : %u)", (U32)srcSize);
+
+ if (srcSize >= ZSTD_BLOCKSIZE_MAX) return ERROR(srcSize_wrong);
+
+ /* Decode literals section */
+ { size_t const litCSize = ZSTD_decodeLiteralsBlock(dctx, src, srcSize);
+ DEBUGLOG(5, "ZSTD_decodeLiteralsBlock : %u", (U32)litCSize);
+ if (ZSTD_isError(litCSize)) return litCSize;
+ ip += litCSize;
+ srcSize -= litCSize;
+ }
+
+ /* Build Decoding Tables */
+ {
+ /* These macros control at build-time which decompressor implementation
+ * we use. If neither is defined, we do some inspection and dispatch at
+ * runtime.
+ */
+#if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \
+ !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG)
+ int usePrefetchDecoder = dctx->ddictIsCold;
+#endif
+ int nbSeq;
+ size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, srcSize);
+ if (ZSTD_isError(seqHSize)) return seqHSize;
+ ip += seqHSize;
+ srcSize -= seqHSize;
+
+#if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \
+ !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG)
+ if ( !usePrefetchDecoder
+ && (!frame || (dctx->fParams.windowSize > (1<<24)))
+ && (nbSeq>ADVANCED_SEQS) ) { /* could probably use a larger nbSeq limit */
+ U32 const shareLongOffsets = ZSTD_getLongOffsetsShare(dctx->OFTptr);
+ U32 const minShare = MEM_64bits() ? 7 : 20; /* heuristic values, correspond to 2.73% and 7.81% */
+ usePrefetchDecoder = (shareLongOffsets >= minShare);
+ }
+#endif
+
+ dctx->ddictIsCold = 0;
+
+#if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \
+ !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG)
+ if (usePrefetchDecoder)
+#endif
+#ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT
+ return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset);
+#endif
+
+#ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG
+ /* else */
+ return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset);
+#endif
+ }
+}
+
+
+size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize)
+{
+ size_t dSize;
+ ZSTD_checkContinuity(dctx, dst);
+ dSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize, /* frame */ 0);
+ dctx->previousDstEnd = (char*)dst + dSize;
+ return dSize;
+}
diff --git a/Utilities/cmzstd/lib/decompress/zstd_decompress_block.h b/Utilities/cmzstd/lib/decompress/zstd_decompress_block.h
new file mode 100644
index 000000000..7e9296041
--- /dev/null
+++ b/Utilities/cmzstd/lib/decompress/zstd_decompress_block.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+
+#ifndef ZSTD_DEC_BLOCK_H
+#define ZSTD_DEC_BLOCK_H
+
+/*-*******************************************************
+ * Dependencies
+ *********************************************************/
+#include <stddef.h> /* size_t */
+#include "zstd.h" /* DCtx, and some public functions */
+#include "zstd_internal.h" /* blockProperties_t, and some public functions */
+#include "zstd_decompress_internal.h" /* ZSTD_seqSymbol */
+
+
+/* === Prototypes === */
+
+/* note: prototypes already published within `zstd.h` :
+ * ZSTD_decompressBlock()
+ */
+
+/* note: prototypes already published within `zstd_internal.h` :
+ * ZSTD_getcBlockSize()
+ * ZSTD_decodeSeqHeaders()
+ */
+
+
+/* ZSTD_decompressBlock_internal() :
+ * decompress block, starting at `src`,
+ * into destination buffer `dst`.
+ * @return : decompressed block size,
+ * or an error code (which can be tested using ZSTD_isError())
+ */
+size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize, const int frame);
+
+/* ZSTD_buildFSETable() :
+ * generate FSE decoding table for one symbol (ll, ml or off)
+ * this function must be called with valid parameters only
+ * (dt is large enough, normalizedCounter distribution total is a power of 2, max is within range, etc.)
+ * in which case it cannot fail.
+ * Internal use only.
+ */
+void ZSTD_buildFSETable(ZSTD_seqSymbol* dt,
+ const short* normalizedCounter, unsigned maxSymbolValue,
+ const U32* baseValue, const U32* nbAdditionalBits,
+ unsigned tableLog);
+
+
+#endif /* ZSTD_DEC_BLOCK_H */
diff --git a/Utilities/cmzstd/lib/decompress/zstd_decompress_internal.h b/Utilities/cmzstd/lib/decompress/zstd_decompress_internal.h
new file mode 100644
index 000000000..abd003051
--- /dev/null
+++ b/Utilities/cmzstd/lib/decompress/zstd_decompress_internal.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+
+/* zstd_decompress_internal:
+ * objects and definitions shared within lib/decompress modules */
+
+ #ifndef ZSTD_DECOMPRESS_INTERNAL_H
+ #define ZSTD_DECOMPRESS_INTERNAL_H
+
+
+/*-*******************************************************
+ * Dependencies
+ *********************************************************/
+#include "mem.h" /* BYTE, U16, U32 */
+#include "zstd_internal.h" /* ZSTD_seqSymbol */
+
+
+
+/*-*******************************************************
+ * Constants
+ *********************************************************/
+static const U32 LL_base[MaxLL+1] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 18, 20, 22, 24, 28, 32, 40,
+ 48, 64, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000,
+ 0x2000, 0x4000, 0x8000, 0x10000 };
+
+static const U32 OF_base[MaxOff+1] = {
+ 0, 1, 1, 5, 0xD, 0x1D, 0x3D, 0x7D,
+ 0xFD, 0x1FD, 0x3FD, 0x7FD, 0xFFD, 0x1FFD, 0x3FFD, 0x7FFD,
+ 0xFFFD, 0x1FFFD, 0x3FFFD, 0x7FFFD, 0xFFFFD, 0x1FFFFD, 0x3FFFFD, 0x7FFFFD,
+ 0xFFFFFD, 0x1FFFFFD, 0x3FFFFFD, 0x7FFFFFD, 0xFFFFFFD, 0x1FFFFFFD, 0x3FFFFFFD, 0x7FFFFFFD };
+
+static const U32 OF_bits[MaxOff+1] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31 };
+
+static const U32 ML_base[MaxML+1] = {
+ 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26,
+ 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 37, 39, 41, 43, 47, 51, 59,
+ 67, 83, 99, 0x83, 0x103, 0x203, 0x403, 0x803,
+ 0x1003, 0x2003, 0x4003, 0x8003, 0x10003 };
+
+
+/*-*******************************************************
+ * Decompression types
+ *********************************************************/
+ typedef struct {
+ U32 fastMode;
+ U32 tableLog;
+ } ZSTD_seqSymbol_header;
+
+ typedef struct {
+ U16 nextState;
+ BYTE nbAdditionalBits;
+ BYTE nbBits;
+ U32 baseValue;
+ } ZSTD_seqSymbol;
+
+ #define SEQSYMBOL_TABLE_SIZE(log) (1 + (1 << (log)))
+
+typedef struct {
+ ZSTD_seqSymbol LLTable[SEQSYMBOL_TABLE_SIZE(LLFSELog)]; /* Note : Space reserved for FSE Tables */
+ ZSTD_seqSymbol OFTable[SEQSYMBOL_TABLE_SIZE(OffFSELog)]; /* is also used as temporary workspace while building hufTable during DDict creation */
+ ZSTD_seqSymbol MLTable[SEQSYMBOL_TABLE_SIZE(MLFSELog)]; /* and therefore must be at least HUF_DECOMPRESS_WORKSPACE_SIZE large */
+ HUF_DTable hufTable[HUF_DTABLE_SIZE(HufLog)]; /* can accommodate HUF_decompress4X */
+ U32 rep[ZSTD_REP_NUM];
+} ZSTD_entropyDTables_t;
+
+typedef enum { ZSTDds_getFrameHeaderSize, ZSTDds_decodeFrameHeader,
+ ZSTDds_decodeBlockHeader, ZSTDds_decompressBlock,
+ ZSTDds_decompressLastBlock, ZSTDds_checkChecksum,
+ ZSTDds_decodeSkippableHeader, ZSTDds_skipFrame } ZSTD_dStage;
+
+typedef enum { zdss_init=0, zdss_loadHeader,
+ zdss_read, zdss_load, zdss_flush } ZSTD_dStreamStage;
+
+struct ZSTD_DCtx_s
+{
+ const ZSTD_seqSymbol* LLTptr;
+ const ZSTD_seqSymbol* MLTptr;
+ const ZSTD_seqSymbol* OFTptr;
+ const HUF_DTable* HUFptr;
+ ZSTD_entropyDTables_t entropy;
+ U32 workspace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32]; /* space needed when building huffman tables */
+ const void* previousDstEnd; /* detect continuity */
+ const void* prefixStart; /* start of current segment */
+ const void* virtualStart; /* virtual start of previous segment if it was just before current one */
+ const void* dictEnd; /* end of previous segment */
+ size_t expected;
+ ZSTD_frameHeader fParams;
+ U64 decodedSize;
+ blockType_e bType; /* used in ZSTD_decompressContinue(), store blockType between block header decoding and block decompression stages */
+ ZSTD_dStage stage;
+ U32 litEntropy;
+ U32 fseEntropy;
+ XXH64_state_t xxhState;
+ size_t headerSize;
+ ZSTD_format_e format;
+ const BYTE* litPtr;
+ ZSTD_customMem customMem;
+ size_t litSize;
+ size_t rleSize;
+ size_t staticSize;
+ int bmi2; /* == 1 if the CPU supports BMI2 and 0 otherwise. CPU support is determined dynamically once per context lifetime. */
+
+ /* dictionary */
+ ZSTD_DDict* ddictLocal;
+ const ZSTD_DDict* ddict; /* set by ZSTD_initDStream_usingDDict(), or ZSTD_DCtx_refDDict() */
+ U32 dictID;
+ int ddictIsCold; /* if == 1 : dictionary is "new" for working context, and presumed "cold" (not in cpu cache) */
+
+ /* streaming */
+ ZSTD_dStreamStage streamStage;
+ char* inBuff;
+ size_t inBuffSize;
+ size_t inPos;
+ size_t maxWindowSize;
+ char* outBuff;
+ size_t outBuffSize;
+ size_t outStart;
+ size_t outEnd;
+ size_t lhSize;
+ void* legacyContext;
+ U32 previousLegacyVersion;
+ U32 legacyVersion;
+ U32 hostageByte;
+ int noForwardProgress;
+
+ /* workspace */
+ BYTE litBuffer[ZSTD_BLOCKSIZE_MAX + WILDCOPY_OVERLENGTH];
+ BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX];
+}; /* typedef'd to ZSTD_DCtx within "zstd.h" */
+
+
+/*-*******************************************************
+ * Shared internal functions
+ *********************************************************/
+
+/*! ZSTD_loadDEntropy() :
+ * dict : must point at beginning of a valid zstd dictionary.
+ * @return : size of entropy tables read */
+size_t ZSTD_loadDEntropy(ZSTD_entropyDTables_t* entropy,
+ const void* const dict, size_t const dictSize);
+
+/*! ZSTD_checkContinuity() :
+ * check if next `dst` follows previous position, where decompression ended.
+ * If yes, do nothing (continue on current segment).
+ * If not, classify previous segment as "external dictionary", and start a new segment.
+ * This function cannot fail. */
+void ZSTD_checkContinuity(ZSTD_DCtx* dctx, const void* dst);
+
+
+#endif /* ZSTD_DECOMPRESS_INTERNAL_H */
diff --git a/Utilities/cmzstd/lib/deprecated/zbuff.h b/Utilities/cmzstd/lib/deprecated/zbuff.h
new file mode 100644
index 000000000..a93115da4
--- /dev/null
+++ b/Utilities/cmzstd/lib/deprecated/zbuff.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+/* ***************************************************************
+* NOTES/WARNINGS
+******************************************************************/
+/* The streaming API defined here is deprecated.
+ * Consider migrating towards ZSTD_compressStream() API in `zstd.h`
+ * See 'lib/README.md'.
+ *****************************************************************/
+
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#ifndef ZSTD_BUFFERED_H_23987
+#define ZSTD_BUFFERED_H_23987
+
+/* *************************************
+* Dependencies
+***************************************/
+#include <stddef.h> /* size_t */
+#include "zstd.h" /* ZSTD_CStream, ZSTD_DStream, ZSTDLIB_API */
+
+
+/* ***************************************************************
+* Compiler specifics
+*****************************************************************/
+/* Deprecation warnings */
+/* Should these warnings be a problem,
+ it is generally possible to disable them,
+ typically with -Wno-deprecated-declarations for gcc
+ or _CRT_SECURE_NO_WARNINGS in Visual.
+ Otherwise, it's also possible to define ZBUFF_DISABLE_DEPRECATE_WARNINGS */
+#ifdef ZBUFF_DISABLE_DEPRECATE_WARNINGS
+# define ZBUFF_DEPRECATED(message) ZSTDLIB_API /* disable deprecation warnings */
+#else
+# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
+# define ZBUFF_DEPRECATED(message) [[deprecated(message)]] ZSTDLIB_API
+# elif (defined(__GNUC__) && (__GNUC__ >= 5)) || defined(__clang__)
+# define ZBUFF_DEPRECATED(message) ZSTDLIB_API __attribute__((deprecated(message)))
+# elif defined(__GNUC__) && (__GNUC__ >= 3)
+# define ZBUFF_DEPRECATED(message) ZSTDLIB_API __attribute__((deprecated))
+# elif defined(_MSC_VER)
+# define ZBUFF_DEPRECATED(message) ZSTDLIB_API __declspec(deprecated(message))
+# else
+# pragma message("WARNING: You need to implement ZBUFF_DEPRECATED for this compiler")
+# define ZBUFF_DEPRECATED(message) ZSTDLIB_API
+# endif
+#endif /* ZBUFF_DISABLE_DEPRECATE_WARNINGS */
+
+
+/* *************************************
+* Streaming functions
+***************************************/
+/* This is the easier "buffered" streaming API,
+* using an internal buffer to lift all restrictions on user-provided buffers
+* which can be any size, any place, for both input and output.
+* ZBUFF and ZSTD are 100% interoperable,
+* frames created by one can be decoded by the other one */
+
+typedef ZSTD_CStream ZBUFF_CCtx;
+ZBUFF_DEPRECATED("use ZSTD_createCStream") ZBUFF_CCtx* ZBUFF_createCCtx(void);
+ZBUFF_DEPRECATED("use ZSTD_freeCStream") size_t ZBUFF_freeCCtx(ZBUFF_CCtx* cctx);
+
+ZBUFF_DEPRECATED("use ZSTD_initCStream") size_t ZBUFF_compressInit(ZBUFF_CCtx* cctx, int compressionLevel);
+ZBUFF_DEPRECATED("use ZSTD_initCStream_usingDict") size_t ZBUFF_compressInitDictionary(ZBUFF_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel);
+
+ZBUFF_DEPRECATED("use ZSTD_compressStream") size_t ZBUFF_compressContinue(ZBUFF_CCtx* cctx, void* dst, size_t* dstCapacityPtr, const void* src, size_t* srcSizePtr);
+ZBUFF_DEPRECATED("use ZSTD_flushStream") size_t ZBUFF_compressFlush(ZBUFF_CCtx* cctx, void* dst, size_t* dstCapacityPtr);
+ZBUFF_DEPRECATED("use ZSTD_endStream") size_t ZBUFF_compressEnd(ZBUFF_CCtx* cctx, void* dst, size_t* dstCapacityPtr);
+
+/*-*************************************************
+* Streaming compression - howto
+*
+* A ZBUFF_CCtx object is required to track streaming operation.
+* Use ZBUFF_createCCtx() and ZBUFF_freeCCtx() to create/release resources.
+* ZBUFF_CCtx objects can be reused multiple times.
+*
+* Start by initializing ZBUF_CCtx.
+* Use ZBUFF_compressInit() to start a new compression operation.
+* Use ZBUFF_compressInitDictionary() for a compression which requires a dictionary.
+*
+* Use ZBUFF_compressContinue() repetitively to consume input stream.
+* *srcSizePtr and *dstCapacityPtr can be any size.
+* The function will report how many bytes were read or written within *srcSizePtr and *dstCapacityPtr.
+* Note that it may not consume the entire input, in which case it's up to the caller to present again remaining data.
+* The content of `dst` will be overwritten (up to *dstCapacityPtr) at each call, so save its content if it matters or change @dst .
+* @return : a hint to preferred nb of bytes to use as input for next function call (it's just a hint, to improve latency)
+* or an error code, which can be tested using ZBUFF_isError().
+*
+* At any moment, it's possible to flush whatever data remains within buffer, using ZBUFF_compressFlush().
+* The nb of bytes written into `dst` will be reported into *dstCapacityPtr.
+* Note that the function cannot output more than *dstCapacityPtr,
+* therefore, some content might still be left into internal buffer if *dstCapacityPtr is too small.
+* @return : nb of bytes still present into internal buffer (0 if it's empty)
+* or an error code, which can be tested using ZBUFF_isError().
+*
+* ZBUFF_compressEnd() instructs to finish a frame.
+* It will perform a flush and write frame epilogue.
+* The epilogue is required for decoders to consider a frame completed.
+* Similar to ZBUFF_compressFlush(), it may not be able to output the entire internal buffer content if *dstCapacityPtr is too small.
+* In which case, call again ZBUFF_compressFlush() to complete the flush.
+* @return : nb of bytes still present into internal buffer (0 if it's empty)
+* or an error code, which can be tested using ZBUFF_isError().
+*
+* Hint : _recommended buffer_ sizes (not compulsory) : ZBUFF_recommendedCInSize() / ZBUFF_recommendedCOutSize()
+* input : ZBUFF_recommendedCInSize==128 KB block size is the internal unit, use this value to reduce intermediate stages (better latency)
+* output : ZBUFF_recommendedCOutSize==ZSTD_compressBound(128 KB) + 3 + 3 : ensures it's always possible to write/flush/end a full block. Skip some buffering.
+* By using both, it ensures that input will be entirely consumed, and output will always contain the result, reducing intermediate buffering.
+* **************************************************/
+
+
+typedef ZSTD_DStream ZBUFF_DCtx;
+ZBUFF_DEPRECATED("use ZSTD_createDStream") ZBUFF_DCtx* ZBUFF_createDCtx(void);
+ZBUFF_DEPRECATED("use ZSTD_freeDStream") size_t ZBUFF_freeDCtx(ZBUFF_DCtx* dctx);
+
+ZBUFF_DEPRECATED("use ZSTD_initDStream") size_t ZBUFF_decompressInit(ZBUFF_DCtx* dctx);
+ZBUFF_DEPRECATED("use ZSTD_initDStream_usingDict") size_t ZBUFF_decompressInitDictionary(ZBUFF_DCtx* dctx, const void* dict, size_t dictSize);
+
+ZBUFF_DEPRECATED("use ZSTD_decompressStream") size_t ZBUFF_decompressContinue(ZBUFF_DCtx* dctx,
+ void* dst, size_t* dstCapacityPtr,
+ const void* src, size_t* srcSizePtr);
+
+/*-***************************************************************************
+* Streaming decompression howto
+*
+* A ZBUFF_DCtx object is required to track streaming operations.
+* Use ZBUFF_createDCtx() and ZBUFF_freeDCtx() to create/release resources.
+* Use ZBUFF_decompressInit() to start a new decompression operation,
+* or ZBUFF_decompressInitDictionary() if decompression requires a dictionary.
+* Note that ZBUFF_DCtx objects can be re-init multiple times.
+*
+* Use ZBUFF_decompressContinue() repetitively to consume your input.
+* *srcSizePtr and *dstCapacityPtr can be any size.
+* The function will report how many bytes were read or written by modifying *srcSizePtr and *dstCapacityPtr.
+* Note that it may not consume the entire input, in which case it's up to the caller to present remaining input again.
+* The content of `dst` will be overwritten (up to *dstCapacityPtr) at each function call, so save its content if it matters, or change `dst`.
+* @return : 0 when a frame is completely decoded and fully flushed,
+* 1 when there is still some data left within internal buffer to flush,
+* >1 when more data is expected, with value being a suggested next input size (it's just a hint, which helps latency),
+* or an error code, which can be tested using ZBUFF_isError().
+*
+* Hint : recommended buffer sizes (not compulsory) : ZBUFF_recommendedDInSize() and ZBUFF_recommendedDOutSize()
+* output : ZBUFF_recommendedDOutSize== 128 KB block size is the internal unit, it ensures it's always possible to write a full block when decoded.
+* input : ZBUFF_recommendedDInSize == 128KB + 3;
+* just follow indications from ZBUFF_decompressContinue() to minimize latency. It should always be <= 128 KB + 3 .
+* *******************************************************************************/
+
+
+/* *************************************
+* Tool functions
+***************************************/
+ZBUFF_DEPRECATED("use ZSTD_isError") unsigned ZBUFF_isError(size_t errorCode);
+ZBUFF_DEPRECATED("use ZSTD_getErrorName") const char* ZBUFF_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 */
+ZBUFF_DEPRECATED("use ZSTD_CStreamInSize") size_t ZBUFF_recommendedCInSize(void);
+ZBUFF_DEPRECATED("use ZSTD_CStreamOutSize") size_t ZBUFF_recommendedCOutSize(void);
+ZBUFF_DEPRECATED("use ZSTD_DStreamInSize") size_t ZBUFF_recommendedDInSize(void);
+ZBUFF_DEPRECATED("use ZSTD_DStreamOutSize") size_t ZBUFF_recommendedDOutSize(void);
+
+#endif /* ZSTD_BUFFERED_H_23987 */
+
+
+#ifdef ZBUFF_STATIC_LINKING_ONLY
+#ifndef ZBUFF_STATIC_H_30298098432
+#define ZBUFF_STATIC_H_30298098432
+
+/* ====================================================================================
+ * The definitions in this section are considered experimental.
+ * They should never be used in association with a dynamic library, as they may change in the future.
+ * They are provided for advanced usages.
+ * Use them only in association with static linking.
+ * ==================================================================================== */
+
+/*--- Dependency ---*/
+#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_parameters, ZSTD_customMem */
+#include "zstd.h"
+
+
+/*--- Custom memory allocator ---*/
+/*! ZBUFF_createCCtx_advanced() :
+ * Create a ZBUFF compression context using external alloc and free functions */
+ZBUFF_DEPRECATED("use ZSTD_createCStream_advanced") ZBUFF_CCtx* ZBUFF_createCCtx_advanced(ZSTD_customMem customMem);
+
+/*! ZBUFF_createDCtx_advanced() :
+ * Create a ZBUFF decompression context using external alloc and free functions */
+ZBUFF_DEPRECATED("use ZSTD_createDStream_advanced") ZBUFF_DCtx* ZBUFF_createDCtx_advanced(ZSTD_customMem customMem);
+
+
+/*--- Advanced Streaming Initialization ---*/
+ZBUFF_DEPRECATED("use ZSTD_initDStream_usingDict") size_t ZBUFF_compressInit_advanced(ZBUFF_CCtx* zbc,
+ const void* dict, size_t dictSize,
+ ZSTD_parameters params, unsigned long long pledgedSrcSize);
+
+
+#endif /* ZBUFF_STATIC_H_30298098432 */
+#endif /* ZBUFF_STATIC_LINKING_ONLY */
+
+
+#if defined (__cplusplus)
+}
+#endif
diff --git a/Utilities/cmzstd/lib/deprecated/zbuff_common.c b/Utilities/cmzstd/lib/deprecated/zbuff_common.c
new file mode 100644
index 000000000..661b9b0e1
--- /dev/null
+++ b/Utilities/cmzstd/lib/deprecated/zbuff_common.c
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+/*-*************************************
+* Dependencies
+***************************************/
+#include "error_private.h"
+#include "zbuff.h"
+
+/*-****************************************
+* ZBUFF Error Management (deprecated)
+******************************************/
+
+/*! ZBUFF_isError() :
+* tells if a return value is an error code */
+unsigned ZBUFF_isError(size_t errorCode) { return ERR_isError(errorCode); }
+/*! ZBUFF_getErrorName() :
+* provides error code string from function result (useful for debugging) */
+const char* ZBUFF_getErrorName(size_t errorCode) { return ERR_getErrorName(errorCode); }
diff --git a/Utilities/cmzstd/lib/deprecated/zbuff_compress.c b/Utilities/cmzstd/lib/deprecated/zbuff_compress.c
new file mode 100644
index 000000000..f39c60d89
--- /dev/null
+++ b/Utilities/cmzstd/lib/deprecated/zbuff_compress.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+
+
+/* *************************************
+* Dependencies
+***************************************/
+#define ZBUFF_STATIC_LINKING_ONLY
+#include "zbuff.h"
+
+
+/*-***********************************************************
+* Streaming compression
+*
+* A ZBUFF_CCtx object is required to track streaming operation.
+* Use ZBUFF_createCCtx() and ZBUFF_freeCCtx() to create/release resources.
+* Use ZBUFF_compressInit() to start a new compression operation.
+* ZBUFF_CCtx objects can be reused multiple times.
+*
+* Use ZBUFF_compressContinue() repetitively to consume your input.
+* *srcSizePtr and *dstCapacityPtr can be any size.
+* The function will report how many bytes were read or written by modifying *srcSizePtr and *dstCapacityPtr.
+* Note that it may not consume the entire input, in which case it's up to the caller to call again the function with remaining input.
+* The content of dst will be overwritten (up to *dstCapacityPtr) at each function call, so save its content if it matters or change dst .
+* @return : a hint to preferred nb of bytes to use as input for next function call (it's only a hint, to improve latency)
+* or an error code, which can be tested using ZBUFF_isError().
+*
+* ZBUFF_compressFlush() can be used to instruct ZBUFF to compress and output whatever remains within its buffer.
+* Note that it will not output more than *dstCapacityPtr.
+* Therefore, some content might still be left into its internal buffer if dst buffer is too small.
+* @return : nb of bytes still present into internal buffer (0 if it's empty)
+* or an error code, which can be tested using ZBUFF_isError().
+*
+* ZBUFF_compressEnd() instructs to finish a frame.
+* It will perform a flush and write frame epilogue.
+* Similar to ZBUFF_compressFlush(), it may not be able to output the entire internal buffer content if *dstCapacityPtr is too small.
+* @return : nb of bytes still present into internal buffer (0 if it's empty)
+* or an error code, which can be tested using ZBUFF_isError().
+*
+* Hint : recommended buffer sizes (not compulsory)
+* input : ZSTD_BLOCKSIZE_MAX (128 KB), internal unit size, it improves latency to use this value.
+* output : ZSTD_compressBound(ZSTD_BLOCKSIZE_MAX) + ZSTD_blockHeaderSize + ZBUFF_endFrameSize : ensures it's always possible to write/flush/end a full block at best speed.
+* ***********************************************************/
+
+ZBUFF_CCtx* ZBUFF_createCCtx(void)
+{
+ return ZSTD_createCStream();
+}
+
+ZBUFF_CCtx* ZBUFF_createCCtx_advanced(ZSTD_customMem customMem)
+{
+ return ZSTD_createCStream_advanced(customMem);
+}
+
+size_t ZBUFF_freeCCtx(ZBUFF_CCtx* zbc)
+{
+ return ZSTD_freeCStream(zbc);
+}
+
+
+/* ====== Initialization ====== */
+
+size_t ZBUFF_compressInit_advanced(ZBUFF_CCtx* zbc,
+ const void* dict, size_t dictSize,
+ ZSTD_parameters params, unsigned long long pledgedSrcSize)
+{
+ if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; /* preserve "0 == unknown" behavior */
+ return ZSTD_initCStream_advanced(zbc, dict, dictSize, params, pledgedSrcSize);
+}
+
+
+size_t ZBUFF_compressInitDictionary(ZBUFF_CCtx* zbc, const void* dict, size_t dictSize, int compressionLevel)
+{
+ return ZSTD_initCStream_usingDict(zbc, dict, dictSize, compressionLevel);
+}
+
+size_t ZBUFF_compressInit(ZBUFF_CCtx* zbc, int compressionLevel)
+{
+ return ZSTD_initCStream(zbc, compressionLevel);
+}
+
+/* ====== Compression ====== */
+
+
+size_t ZBUFF_compressContinue(ZBUFF_CCtx* zbc,
+ void* dst, size_t* dstCapacityPtr,
+ const void* src, size_t* srcSizePtr)
+{
+ size_t result;
+ ZSTD_outBuffer outBuff;
+ ZSTD_inBuffer inBuff;
+ outBuff.dst = dst;
+ outBuff.pos = 0;
+ outBuff.size = *dstCapacityPtr;
+ inBuff.src = src;
+ inBuff.pos = 0;
+ inBuff.size = *srcSizePtr;
+ result = ZSTD_compressStream(zbc, &outBuff, &inBuff);
+ *dstCapacityPtr = outBuff.pos;
+ *srcSizePtr = inBuff.pos;
+ return result;
+}
+
+
+
+/* ====== Finalize ====== */
+
+size_t ZBUFF_compressFlush(ZBUFF_CCtx* zbc, void* dst, size_t* dstCapacityPtr)
+{
+ size_t result;
+ ZSTD_outBuffer outBuff;
+ outBuff.dst = dst;
+ outBuff.pos = 0;
+ outBuff.size = *dstCapacityPtr;
+ result = ZSTD_flushStream(zbc, &outBuff);
+ *dstCapacityPtr = outBuff.pos;
+ return result;
+}
+
+
+size_t ZBUFF_compressEnd(ZBUFF_CCtx* zbc, void* dst, size_t* dstCapacityPtr)
+{
+ size_t result;
+ ZSTD_outBuffer outBuff;
+ outBuff.dst = dst;
+ outBuff.pos = 0;
+ outBuff.size = *dstCapacityPtr;
+ result = ZSTD_endStream(zbc, &outBuff);
+ *dstCapacityPtr = outBuff.pos;
+ return result;
+}
+
+
+
+/* *************************************
+* Tool functions
+***************************************/
+size_t ZBUFF_recommendedCInSize(void) { return ZSTD_CStreamInSize(); }
+size_t ZBUFF_recommendedCOutSize(void) { return ZSTD_CStreamOutSize(); }
diff --git a/Utilities/cmzstd/lib/deprecated/zbuff_decompress.c b/Utilities/cmzstd/lib/deprecated/zbuff_decompress.c
new file mode 100644
index 000000000..923c22b73
--- /dev/null
+++ b/Utilities/cmzstd/lib/deprecated/zbuff_decompress.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+
+
+/* *************************************
+* Dependencies
+***************************************/
+#define ZBUFF_STATIC_LINKING_ONLY
+#include "zbuff.h"
+
+
+ZBUFF_DCtx* ZBUFF_createDCtx(void)
+{
+ return ZSTD_createDStream();
+}
+
+ZBUFF_DCtx* ZBUFF_createDCtx_advanced(ZSTD_customMem customMem)
+{
+ return ZSTD_createDStream_advanced(customMem);
+}
+
+size_t ZBUFF_freeDCtx(ZBUFF_DCtx* zbd)
+{
+ return ZSTD_freeDStream(zbd);
+}
+
+
+/* *** Initialization *** */
+
+size_t ZBUFF_decompressInitDictionary(ZBUFF_DCtx* zbd, const void* dict, size_t dictSize)
+{
+ return ZSTD_initDStream_usingDict(zbd, dict, dictSize);
+}
+
+size_t ZBUFF_decompressInit(ZBUFF_DCtx* zbd)
+{
+ return ZSTD_initDStream(zbd);
+}
+
+
+/* *** Decompression *** */
+
+size_t ZBUFF_decompressContinue(ZBUFF_DCtx* zbd,
+ void* dst, size_t* dstCapacityPtr,
+ const void* src, size_t* srcSizePtr)
+{
+ ZSTD_outBuffer outBuff;
+ ZSTD_inBuffer inBuff;
+ size_t result;
+ outBuff.dst = dst;
+ outBuff.pos = 0;
+ outBuff.size = *dstCapacityPtr;
+ inBuff.src = src;
+ inBuff.pos = 0;
+ inBuff.size = *srcSizePtr;
+ result = ZSTD_decompressStream(zbd, &outBuff, &inBuff);
+ *dstCapacityPtr = outBuff.pos;
+ *srcSizePtr = inBuff.pos;
+ return result;
+}
+
+
+/* *************************************
+* Tool functions
+***************************************/
+size_t ZBUFF_recommendedDInSize(void) { return ZSTD_DStreamInSize(); }
+size_t ZBUFF_recommendedDOutSize(void) { return ZSTD_DStreamOutSize(); }
diff --git a/Utilities/cmzstd/lib/dictBuilder/cover.c b/Utilities/cmzstd/lib/dictBuilder/cover.c
new file mode 100644
index 000000000..b55bfb510
--- /dev/null
+++ b/Utilities/cmzstd/lib/dictBuilder/cover.c
@@ -0,0 +1,1081 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+/* *****************************************************************************
+ * Constructs a dictionary using a heuristic based on the following paper:
+ *
+ * Liao, Petri, Moffat, Wirth
+ * Effective Construction of Relative Lempel-Ziv Dictionaries
+ * Published in WWW 2016.
+ *
+ * Adapted from code originally written by @ot (Giuseppe Ottaviano).
+ ******************************************************************************/
+
+/*-*************************************
+* Dependencies
+***************************************/
+#include <stdio.h> /* fprintf */
+#include <stdlib.h> /* malloc, free, qsort */
+#include <string.h> /* memset */
+#include <time.h> /* clock */
+
+#include "mem.h" /* read */
+#include "pool.h"
+#include "threading.h"
+#include "cover.h"
+#include "zstd_internal.h" /* includes zstd.h */
+#ifndef ZDICT_STATIC_LINKING_ONLY
+#define ZDICT_STATIC_LINKING_ONLY
+#endif
+#include "zdict.h"
+
+/*-*************************************
+* Constants
+***************************************/
+#define COVER_MAX_SAMPLES_SIZE (sizeof(size_t) == 8 ? ((unsigned)-1) : ((unsigned)1 GB))
+#define DEFAULT_SPLITPOINT 1.0
+
+/*-*************************************
+* Console display
+***************************************/
+static int g_displayLevel = 2;
+#define DISPLAY(...) \
+ { \
+ fprintf(stderr, __VA_ARGS__); \
+ fflush(stderr); \
+ }
+#define LOCALDISPLAYLEVEL(displayLevel, l, ...) \
+ if (displayLevel >= l) { \
+ DISPLAY(__VA_ARGS__); \
+ } /* 0 : no display; 1: errors; 2: default; 3: details; 4: debug */
+#define DISPLAYLEVEL(l, ...) LOCALDISPLAYLEVEL(g_displayLevel, l, __VA_ARGS__)
+
+#define LOCALDISPLAYUPDATE(displayLevel, l, ...) \
+ if (displayLevel >= l) { \
+ if ((clock() - g_time > refreshRate) || (displayLevel >= 4)) { \
+ g_time = clock(); \
+ DISPLAY(__VA_ARGS__); \
+ } \
+ }
+#define DISPLAYUPDATE(l, ...) LOCALDISPLAYUPDATE(g_displayLevel, l, __VA_ARGS__)
+static const clock_t refreshRate = CLOCKS_PER_SEC * 15 / 100;
+static clock_t g_time = 0;
+
+/*-*************************************
+* Hash table
+***************************************
+* A small specialized hash map for storing activeDmers.
+* The map does not resize, so if it becomes full it will loop forever.
+* Thus, the map must be large enough to store every value.
+* The map implements linear probing and keeps its load less than 0.5.
+*/
+
+#define MAP_EMPTY_VALUE ((U32)-1)
+typedef struct COVER_map_pair_t_s {
+ U32 key;
+ U32 value;
+} COVER_map_pair_t;
+
+typedef struct COVER_map_s {
+ COVER_map_pair_t *data;
+ U32 sizeLog;
+ U32 size;
+ U32 sizeMask;
+} COVER_map_t;
+
+/**
+ * Clear the map.
+ */
+static void COVER_map_clear(COVER_map_t *map) {
+ memset(map->data, MAP_EMPTY_VALUE, map->size * sizeof(COVER_map_pair_t));
+}
+
+/**
+ * Initializes a map of the given size.
+ * Returns 1 on success and 0 on failure.
+ * The map must be destroyed with COVER_map_destroy().
+ * The map is only guaranteed to be large enough to hold size elements.
+ */
+static int COVER_map_init(COVER_map_t *map, U32 size) {
+ map->sizeLog = ZSTD_highbit32(size) + 2;
+ map->size = (U32)1 << map->sizeLog;
+ map->sizeMask = map->size - 1;
+ map->data = (COVER_map_pair_t *)malloc(map->size * sizeof(COVER_map_pair_t));
+ if (!map->data) {
+ map->sizeLog = 0;
+ map->size = 0;
+ return 0;
+ }
+ COVER_map_clear(map);
+ return 1;
+}
+
+/**
+ * Internal hash function
+ */
+static const U32 prime4bytes = 2654435761U;
+static U32 COVER_map_hash(COVER_map_t *map, U32 key) {
+ return (key * prime4bytes) >> (32 - map->sizeLog);
+}
+
+/**
+ * Helper function that returns the index that a key should be placed into.
+ */
+static U32 COVER_map_index(COVER_map_t *map, U32 key) {
+ const U32 hash = COVER_map_hash(map, key);
+ U32 i;
+ for (i = hash;; i = (i + 1) & map->sizeMask) {
+ COVER_map_pair_t *pos = &map->data[i];
+ if (pos->value == MAP_EMPTY_VALUE) {
+ return i;
+ }
+ if (pos->key == key) {
+ return i;
+ }
+ }
+}
+
+/**
+ * Returns the pointer to the value for key.
+ * If key is not in the map, it is inserted and the value is set to 0.
+ * The map must not be full.
+ */
+static U32 *COVER_map_at(COVER_map_t *map, U32 key) {
+ COVER_map_pair_t *pos = &map->data[COVER_map_index(map, key)];
+ if (pos->value == MAP_EMPTY_VALUE) {
+ pos->key = key;
+ pos->value = 0;
+ }
+ return &pos->value;
+}
+
+/**
+ * Deletes key from the map if present.
+ */
+static void COVER_map_remove(COVER_map_t *map, U32 key) {
+ U32 i = COVER_map_index(map, key);
+ COVER_map_pair_t *del = &map->data[i];
+ U32 shift = 1;
+ if (del->value == MAP_EMPTY_VALUE) {
+ return;
+ }
+ for (i = (i + 1) & map->sizeMask;; i = (i + 1) & map->sizeMask) {
+ COVER_map_pair_t *const pos = &map->data[i];
+ /* If the position is empty we are done */
+ if (pos->value == MAP_EMPTY_VALUE) {
+ del->value = MAP_EMPTY_VALUE;
+ return;
+ }
+ /* If pos can be moved to del do so */
+ if (((i - COVER_map_hash(map, pos->key)) & map->sizeMask) >= shift) {
+ del->key = pos->key;
+ del->value = pos->value;
+ del = pos;
+ shift = 1;
+ } else {
+ ++shift;
+ }
+ }
+}
+
+/**
+ * Destroys a map that is inited with COVER_map_init().
+ */
+static void COVER_map_destroy(COVER_map_t *map) {
+ if (map->data) {
+ free(map->data);
+ }
+ map->data = NULL;
+ map->size = 0;
+}
+
+/*-*************************************
+* Context
+***************************************/
+
+typedef struct {
+ const BYTE *samples;
+ size_t *offsets;
+ const size_t *samplesSizes;
+ size_t nbSamples;
+ size_t nbTrainSamples;
+ size_t nbTestSamples;
+ U32 *suffix;
+ size_t suffixSize;
+ U32 *freqs;
+ U32 *dmerAt;
+ unsigned d;
+} COVER_ctx_t;
+
+/* We need a global context for qsort... */
+static COVER_ctx_t *g_ctx = NULL;
+
+/*-*************************************
+* Helper functions
+***************************************/
+
+/**
+ * Returns the sum of the sample sizes.
+ */
+size_t COVER_sum(const size_t *samplesSizes, unsigned nbSamples) {
+ size_t sum = 0;
+ unsigned i;
+ for (i = 0; i < nbSamples; ++i) {
+ sum += samplesSizes[i];
+ }
+ return sum;
+}
+
+/**
+ * Returns -1 if the dmer at lp is less than the dmer at rp.
+ * Return 0 if the dmers at lp and rp are equal.
+ * Returns 1 if the dmer at lp is greater than the dmer at rp.
+ */
+static int COVER_cmp(COVER_ctx_t *ctx, const void *lp, const void *rp) {
+ U32 const lhs = *(U32 const *)lp;
+ U32 const rhs = *(U32 const *)rp;
+ return memcmp(ctx->samples + lhs, ctx->samples + rhs, ctx->d);
+}
+/**
+ * Faster version for d <= 8.
+ */
+static int COVER_cmp8(COVER_ctx_t *ctx, const void *lp, const void *rp) {
+ U64 const mask = (ctx->d == 8) ? (U64)-1 : (((U64)1 << (8 * ctx->d)) - 1);
+ U64 const lhs = MEM_readLE64(ctx->samples + *(U32 const *)lp) & mask;
+ U64 const rhs = MEM_readLE64(ctx->samples + *(U32 const *)rp) & mask;
+ if (lhs < rhs) {
+ return -1;
+ }
+ return (lhs > rhs);
+}
+
+/**
+ * Same as COVER_cmp() except ties are broken by pointer value
+ * NOTE: g_ctx must be set to call this function. A global is required because
+ * qsort doesn't take an opaque pointer.
+ */
+static int COVER_strict_cmp(const void *lp, const void *rp) {
+ int result = COVER_cmp(g_ctx, lp, rp);
+ if (result == 0) {
+ result = lp < rp ? -1 : 1;
+ }
+ return result;
+}
+/**
+ * Faster version for d <= 8.
+ */
+static int COVER_strict_cmp8(const void *lp, const void *rp) {
+ int result = COVER_cmp8(g_ctx, lp, rp);
+ if (result == 0) {
+ result = lp < rp ? -1 : 1;
+ }
+ return result;
+}
+
+/**
+ * Returns the first pointer in [first, last) whose element does not compare
+ * less than value. If no such element exists it returns last.
+ */
+static const size_t *COVER_lower_bound(const size_t *first, const size_t *last,
+ size_t value) {
+ size_t count = last - first;
+ while (count != 0) {
+ size_t step = count / 2;
+ const size_t *ptr = first;
+ ptr += step;
+ if (*ptr < value) {
+ first = ++ptr;
+ count -= step + 1;
+ } else {
+ count = step;
+ }
+ }
+ return first;
+}
+
+/**
+ * Generic groupBy function.
+ * Groups an array sorted by cmp into groups with equivalent values.
+ * Calls grp for each group.
+ */
+static void
+COVER_groupBy(const void *data, size_t count, size_t size, COVER_ctx_t *ctx,
+ int (*cmp)(COVER_ctx_t *, const void *, const void *),
+ void (*grp)(COVER_ctx_t *, const void *, const void *)) {
+ const BYTE *ptr = (const BYTE *)data;
+ size_t num = 0;
+ while (num < count) {
+ const BYTE *grpEnd = ptr + size;
+ ++num;
+ while (num < count && cmp(ctx, ptr, grpEnd) == 0) {
+ grpEnd += size;
+ ++num;
+ }
+ grp(ctx, ptr, grpEnd);
+ ptr = grpEnd;
+ }
+}
+
+/*-*************************************
+* Cover functions
+***************************************/
+
+/**
+ * Called on each group of positions with the same dmer.
+ * Counts the frequency of each dmer and saves it in the suffix array.
+ * Fills `ctx->dmerAt`.
+ */
+static void COVER_group(COVER_ctx_t *ctx, const void *group,
+ const void *groupEnd) {
+ /* The group consists of all the positions with the same first d bytes. */
+ const U32 *grpPtr = (const U32 *)group;
+ const U32 *grpEnd = (const U32 *)groupEnd;
+ /* The dmerId is how we will reference this dmer.
+ * This allows us to map the whole dmer space to a much smaller space, the
+ * size of the suffix array.
+ */
+ const U32 dmerId = (U32)(grpPtr - ctx->suffix);
+ /* Count the number of samples this dmer shows up in */
+ U32 freq = 0;
+ /* Details */
+ const size_t *curOffsetPtr = ctx->offsets;
+ const size_t *offsetsEnd = ctx->offsets + ctx->nbSamples;
+ /* Once *grpPtr >= curSampleEnd this occurrence of the dmer is in a
+ * different sample than the last.
+ */
+ size_t curSampleEnd = ctx->offsets[0];
+ for (; grpPtr != grpEnd; ++grpPtr) {
+ /* Save the dmerId for this position so we can get back to it. */
+ ctx->dmerAt[*grpPtr] = dmerId;
+ /* Dictionaries only help for the first reference to the dmer.
+ * After that zstd can reference the match from the previous reference.
+ * So only count each dmer once for each sample it is in.
+ */
+ if (*grpPtr < curSampleEnd) {
+ continue;
+ }
+ freq += 1;
+ /* Binary search to find the end of the sample *grpPtr is in.
+ * In the common case that grpPtr + 1 == grpEnd we can skip the binary
+ * search because the loop is over.
+ */
+ if (grpPtr + 1 != grpEnd) {
+ const size_t *sampleEndPtr =
+ COVER_lower_bound(curOffsetPtr, offsetsEnd, *grpPtr);
+ curSampleEnd = *sampleEndPtr;
+ curOffsetPtr = sampleEndPtr + 1;
+ }
+ }
+ /* At this point we are never going to look at this segment of the suffix
+ * array again. We take advantage of this fact to save memory.
+ * We store the frequency of the dmer in the first position of the group,
+ * which is dmerId.
+ */
+ ctx->suffix[dmerId] = freq;
+}
+
+
+/**
+ * Selects the best segment in an epoch.
+ * Segments of are scored according to the function:
+ *
+ * Let F(d) be the frequency of dmer d.
+ * Let S_i be the dmer at position i of segment S which has length k.
+ *
+ * Score(S) = F(S_1) + F(S_2) + ... + F(S_{k-d+1})
+ *
+ * Once the dmer d is in the dictionay we set F(d) = 0.
+ */
+static COVER_segment_t COVER_selectSegment(const COVER_ctx_t *ctx, U32 *freqs,
+ COVER_map_t *activeDmers, U32 begin,
+ U32 end,
+ ZDICT_cover_params_t parameters) {
+ /* Constants */
+ const U32 k = parameters.k;
+ const U32 d = parameters.d;
+ const U32 dmersInK = k - d + 1;
+ /* Try each segment (activeSegment) and save the best (bestSegment) */
+ COVER_segment_t bestSegment = {0, 0, 0};
+ COVER_segment_t activeSegment;
+ /* Reset the activeDmers in the segment */
+ COVER_map_clear(activeDmers);
+ /* The activeSegment starts at the beginning of the epoch. */
+ activeSegment.begin = begin;
+ activeSegment.end = begin;
+ activeSegment.score = 0;
+ /* Slide the activeSegment through the whole epoch.
+ * Save the best segment in bestSegment.
+ */
+ while (activeSegment.end < end) {
+ /* The dmerId for the dmer at the next position */
+ U32 newDmer = ctx->dmerAt[activeSegment.end];
+ /* The entry in activeDmers for this dmerId */
+ U32 *newDmerOcc = COVER_map_at(activeDmers, newDmer);
+ /* If the dmer isn't already present in the segment add its score. */
+ if (*newDmerOcc == 0) {
+ /* The paper suggest using the L-0.5 norm, but experiments show that it
+ * doesn't help.
+ */
+ activeSegment.score += freqs[newDmer];
+ }
+ /* Add the dmer to the segment */
+ activeSegment.end += 1;
+ *newDmerOcc += 1;
+
+ /* If the window is now too large, drop the first position */
+ if (activeSegment.end - activeSegment.begin == dmersInK + 1) {
+ U32 delDmer = ctx->dmerAt[activeSegment.begin];
+ U32 *delDmerOcc = COVER_map_at(activeDmers, delDmer);
+ activeSegment.begin += 1;
+ *delDmerOcc -= 1;
+ /* If this is the last occurence of the dmer, subtract its score */
+ if (*delDmerOcc == 0) {
+ COVER_map_remove(activeDmers, delDmer);
+ activeSegment.score -= freqs[delDmer];
+ }
+ }
+
+ /* If this segment is the best so far save it */
+ if (activeSegment.score > bestSegment.score) {
+ bestSegment = activeSegment;
+ }
+ }
+ {
+ /* Trim off the zero frequency head and tail from the segment. */
+ U32 newBegin = bestSegment.end;
+ U32 newEnd = bestSegment.begin;
+ U32 pos;
+ for (pos = bestSegment.begin; pos != bestSegment.end; ++pos) {
+ U32 freq = freqs[ctx->dmerAt[pos]];
+ if (freq != 0) {
+ newBegin = MIN(newBegin, pos);
+ newEnd = pos + 1;
+ }
+ }
+ bestSegment.begin = newBegin;
+ bestSegment.end = newEnd;
+ }
+ {
+ /* Zero out the frequency of each dmer covered by the chosen segment. */
+ U32 pos;
+ for (pos = bestSegment.begin; pos != bestSegment.end; ++pos) {
+ freqs[ctx->dmerAt[pos]] = 0;
+ }
+ }
+ return bestSegment;
+}
+
+/**
+ * Check the validity of the parameters.
+ * Returns non-zero if the parameters are valid and 0 otherwise.
+ */
+static int COVER_checkParameters(ZDICT_cover_params_t parameters,
+ size_t maxDictSize) {
+ /* k and d are required parameters */
+ if (parameters.d == 0 || parameters.k == 0) {
+ return 0;
+ }
+ /* k <= maxDictSize */
+ if (parameters.k > maxDictSize) {
+ return 0;
+ }
+ /* d <= k */
+ if (parameters.d > parameters.k) {
+ return 0;
+ }
+ /* 0 < splitPoint <= 1 */
+ if (parameters.splitPoint <= 0 || parameters.splitPoint > 1){
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * Clean up a context initialized with `COVER_ctx_init()`.
+ */
+static void COVER_ctx_destroy(COVER_ctx_t *ctx) {
+ if (!ctx) {
+ return;
+ }
+ if (ctx->suffix) {
+ free(ctx->suffix);
+ ctx->suffix = NULL;
+ }
+ if (ctx->freqs) {
+ free(ctx->freqs);
+ ctx->freqs = NULL;
+ }
+ if (ctx->dmerAt) {
+ free(ctx->dmerAt);
+ ctx->dmerAt = NULL;
+ }
+ if (ctx->offsets) {
+ free(ctx->offsets);
+ ctx->offsets = NULL;
+ }
+}
+
+/**
+ * Prepare a context for dictionary building.
+ * The context is only dependent on the parameter `d` and can used multiple
+ * times.
+ * Returns 1 on success or zero on error.
+ * The context must be destroyed with `COVER_ctx_destroy()`.
+ */
+static int COVER_ctx_init(COVER_ctx_t *ctx, const void *samplesBuffer,
+ const size_t *samplesSizes, unsigned nbSamples,
+ unsigned d, double splitPoint) {
+ const BYTE *const samples = (const BYTE *)samplesBuffer;
+ const size_t totalSamplesSize = COVER_sum(samplesSizes, nbSamples);
+ /* Split samples into testing and training sets */
+ const unsigned nbTrainSamples = splitPoint < 1.0 ? (unsigned)((double)nbSamples * splitPoint) : nbSamples;
+ const unsigned nbTestSamples = splitPoint < 1.0 ? nbSamples - nbTrainSamples : nbSamples;
+ const size_t trainingSamplesSize = splitPoint < 1.0 ? COVER_sum(samplesSizes, nbTrainSamples) : totalSamplesSize;
+ const size_t testSamplesSize = splitPoint < 1.0 ? COVER_sum(samplesSizes + nbTrainSamples, nbTestSamples) : totalSamplesSize;
+ /* Checks */
+ if (totalSamplesSize < MAX(d, sizeof(U64)) ||
+ totalSamplesSize >= (size_t)COVER_MAX_SAMPLES_SIZE) {
+ DISPLAYLEVEL(1, "Total samples size is too large (%u MB), maximum size is %u MB\n",
+ (unsigned)(totalSamplesSize>>20), (COVER_MAX_SAMPLES_SIZE >> 20));
+ return 0;
+ }
+ /* Check if there are at least 5 training samples */
+ if (nbTrainSamples < 5) {
+ DISPLAYLEVEL(1, "Total number of training samples is %u and is invalid.", nbTrainSamples);
+ return 0;
+ }
+ /* Check if there's testing sample */
+ if (nbTestSamples < 1) {
+ DISPLAYLEVEL(1, "Total number of testing samples is %u and is invalid.", nbTestSamples);
+ return 0;
+ }
+ /* Zero the context */
+ memset(ctx, 0, sizeof(*ctx));
+ DISPLAYLEVEL(2, "Training on %u samples of total size %u\n", nbTrainSamples,
+ (unsigned)trainingSamplesSize);
+ DISPLAYLEVEL(2, "Testing on %u samples of total size %u\n", nbTestSamples,
+ (unsigned)testSamplesSize);
+ ctx->samples = samples;
+ ctx->samplesSizes = samplesSizes;
+ ctx->nbSamples = nbSamples;
+ ctx->nbTrainSamples = nbTrainSamples;
+ ctx->nbTestSamples = nbTestSamples;
+ /* Partial suffix array */
+ ctx->suffixSize = trainingSamplesSize - MAX(d, sizeof(U64)) + 1;
+ ctx->suffix = (U32 *)malloc(ctx->suffixSize * sizeof(U32));
+ /* Maps index to the dmerID */
+ ctx->dmerAt = (U32 *)malloc(ctx->suffixSize * sizeof(U32));
+ /* The offsets of each file */
+ ctx->offsets = (size_t *)malloc((nbSamples + 1) * sizeof(size_t));
+ if (!ctx->suffix || !ctx->dmerAt || !ctx->offsets) {
+ DISPLAYLEVEL(1, "Failed to allocate scratch buffers\n");
+ COVER_ctx_destroy(ctx);
+ return 0;
+ }
+ ctx->freqs = NULL;
+ ctx->d = d;
+
+ /* Fill offsets from the samplesSizes */
+ {
+ U32 i;
+ ctx->offsets[0] = 0;
+ for (i = 1; i <= nbSamples; ++i) {
+ ctx->offsets[i] = ctx->offsets[i - 1] + samplesSizes[i - 1];
+ }
+ }
+ DISPLAYLEVEL(2, "Constructing partial suffix array\n");
+ {
+ /* suffix is a partial suffix array.
+ * It only sorts suffixes by their first parameters.d bytes.
+ * The sort is stable, so each dmer group is sorted by position in input.
+ */
+ U32 i;
+ for (i = 0; i < ctx->suffixSize; ++i) {
+ ctx->suffix[i] = i;
+ }
+ /* qsort doesn't take an opaque pointer, so pass as a global.
+ * On OpenBSD qsort() is not guaranteed to be stable, their mergesort() is.
+ */
+ g_ctx = ctx;
+#if defined(__OpenBSD__)
+ mergesort(ctx->suffix, ctx->suffixSize, sizeof(U32),
+ (ctx->d <= 8 ? &COVER_strict_cmp8 : &COVER_strict_cmp));
+#else
+ qsort(ctx->suffix, ctx->suffixSize, sizeof(U32),
+ (ctx->d <= 8 ? &COVER_strict_cmp8 : &COVER_strict_cmp));
+#endif
+ }
+ DISPLAYLEVEL(2, "Computing frequencies\n");
+ /* For each dmer group (group of positions with the same first d bytes):
+ * 1. For each position we set dmerAt[position] = dmerID. The dmerID is
+ * (groupBeginPtr - suffix). This allows us to go from position to
+ * dmerID so we can look up values in freq.
+ * 2. We calculate how many samples the dmer occurs in and save it in
+ * freqs[dmerId].
+ */
+ COVER_groupBy(ctx->suffix, ctx->suffixSize, sizeof(U32), ctx,
+ (ctx->d <= 8 ? &COVER_cmp8 : &COVER_cmp), &COVER_group);
+ ctx->freqs = ctx->suffix;
+ ctx->suffix = NULL;
+ return 1;
+}
+
+/**
+ * Given the prepared context build the dictionary.
+ */
+static size_t COVER_buildDictionary(const COVER_ctx_t *ctx, U32 *freqs,
+ COVER_map_t *activeDmers, void *dictBuffer,
+ size_t dictBufferCapacity,
+ ZDICT_cover_params_t parameters) {
+ BYTE *const dict = (BYTE *)dictBuffer;
+ size_t tail = dictBufferCapacity;
+ /* Divide the data up into epochs of equal size.
+ * We will select at least one segment from each epoch.
+ */
+ const unsigned epochs = MAX(1, (U32)(dictBufferCapacity / parameters.k / 4));
+ const unsigned epochSize = (U32)(ctx->suffixSize / epochs);
+ size_t epoch;
+ DISPLAYLEVEL(2, "Breaking content into %u epochs of size %u\n",
+ epochs, epochSize);
+ /* Loop through the epochs until there are no more segments or the dictionary
+ * is full.
+ */
+ for (epoch = 0; tail > 0; epoch = (epoch + 1) % epochs) {
+ const U32 epochBegin = (U32)(epoch * epochSize);
+ const U32 epochEnd = epochBegin + epochSize;
+ size_t segmentSize;
+ /* Select a segment */
+ COVER_segment_t segment = COVER_selectSegment(
+ ctx, freqs, activeDmers, epochBegin, epochEnd, parameters);
+ /* If the segment covers no dmers, then we are out of content */
+ if (segment.score == 0) {
+ break;
+ }
+ /* Trim the segment if necessary and if it is too small then we are done */
+ segmentSize = MIN(segment.end - segment.begin + parameters.d - 1, tail);
+ if (segmentSize < parameters.d) {
+ break;
+ }
+ /* We fill the dictionary from the back to allow the best segments to be
+ * referenced with the smallest offsets.
+ */
+ tail -= segmentSize;
+ memcpy(dict + tail, ctx->samples + segment.begin, segmentSize);
+ DISPLAYUPDATE(
+ 2, "\r%u%% ",
+ (unsigned)(((dictBufferCapacity - tail) * 100) / dictBufferCapacity));
+ }
+ DISPLAYLEVEL(2, "\r%79s\r", "");
+ return tail;
+}
+
+ZDICTLIB_API size_t ZDICT_trainFromBuffer_cover(
+ void *dictBuffer, size_t dictBufferCapacity,
+ const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples,
+ ZDICT_cover_params_t parameters)
+{
+ BYTE* const dict = (BYTE*)dictBuffer;
+ COVER_ctx_t ctx;
+ COVER_map_t activeDmers;
+ parameters.splitPoint = 1.0;
+ /* Initialize global data */
+ g_displayLevel = parameters.zParams.notificationLevel;
+ /* Checks */
+ if (!COVER_checkParameters(parameters, dictBufferCapacity)) {
+ DISPLAYLEVEL(1, "Cover parameters incorrect\n");
+ return ERROR(GENERIC);
+ }
+ if (nbSamples == 0) {
+ DISPLAYLEVEL(1, "Cover must have at least one input file\n");
+ return ERROR(GENERIC);
+ }
+ if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) {
+ DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n",
+ ZDICT_DICTSIZE_MIN);
+ return ERROR(dstSize_tooSmall);
+ }
+ /* Initialize context and activeDmers */
+ if (!COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples,
+ parameters.d, parameters.splitPoint)) {
+ return ERROR(GENERIC);
+ }
+ if (!COVER_map_init(&activeDmers, parameters.k - parameters.d + 1)) {
+ DISPLAYLEVEL(1, "Failed to allocate dmer map: out of memory\n");
+ COVER_ctx_destroy(&ctx);
+ return ERROR(GENERIC);
+ }
+
+ DISPLAYLEVEL(2, "Building dictionary\n");
+ {
+ const size_t tail =
+ COVER_buildDictionary(&ctx, ctx.freqs, &activeDmers, dictBuffer,
+ dictBufferCapacity, parameters);
+ const size_t dictionarySize = ZDICT_finalizeDictionary(
+ dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail,
+ samplesBuffer, samplesSizes, nbSamples, parameters.zParams);
+ if (!ZSTD_isError(dictionarySize)) {
+ DISPLAYLEVEL(2, "Constructed dictionary of size %u\n",
+ (unsigned)dictionarySize);
+ }
+ COVER_ctx_destroy(&ctx);
+ COVER_map_destroy(&activeDmers);
+ return dictionarySize;
+ }
+}
+
+
+
+size_t COVER_checkTotalCompressedSize(const ZDICT_cover_params_t parameters,
+ const size_t *samplesSizes, const BYTE *samples,
+ size_t *offsets,
+ size_t nbTrainSamples, size_t nbSamples,
+ BYTE *const dict, size_t dictBufferCapacity) {
+ size_t totalCompressedSize = ERROR(GENERIC);
+ /* Pointers */
+ ZSTD_CCtx *cctx;
+ ZSTD_CDict *cdict;
+ void *dst;
+ /* Local variables */
+ size_t dstCapacity;
+ size_t i;
+ /* Allocate dst with enough space to compress the maximum sized sample */
+ {
+ size_t maxSampleSize = 0;
+ i = parameters.splitPoint < 1.0 ? nbTrainSamples : 0;
+ for (; i < nbSamples; ++i) {
+ maxSampleSize = MAX(samplesSizes[i], maxSampleSize);
+ }
+ dstCapacity = ZSTD_compressBound(maxSampleSize);
+ dst = malloc(dstCapacity);
+ }
+ /* Create the cctx and cdict */
+ cctx = ZSTD_createCCtx();
+ cdict = ZSTD_createCDict(dict, dictBufferCapacity,
+ parameters.zParams.compressionLevel);
+ if (!dst || !cctx || !cdict) {
+ goto _compressCleanup;
+ }
+ /* Compress each sample and sum their sizes (or error) */
+ totalCompressedSize = dictBufferCapacity;
+ i = parameters.splitPoint < 1.0 ? nbTrainSamples : 0;
+ for (; i < nbSamples; ++i) {
+ const size_t size = ZSTD_compress_usingCDict(
+ cctx, dst, dstCapacity, samples + offsets[i],
+ samplesSizes[i], cdict);
+ if (ZSTD_isError(size)) {
+ totalCompressedSize = ERROR(GENERIC);
+ goto _compressCleanup;
+ }
+ totalCompressedSize += size;
+ }
+_compressCleanup:
+ ZSTD_freeCCtx(cctx);
+ ZSTD_freeCDict(cdict);
+ if (dst) {
+ free(dst);
+ }
+ return totalCompressedSize;
+}
+
+
+/**
+ * Initialize the `COVER_best_t`.
+ */
+void COVER_best_init(COVER_best_t *best) {
+ if (best==NULL) return; /* compatible with init on NULL */
+ (void)ZSTD_pthread_mutex_init(&best->mutex, NULL);
+ (void)ZSTD_pthread_cond_init(&best->cond, NULL);
+ best->liveJobs = 0;
+ best->dict = NULL;
+ best->dictSize = 0;
+ best->compressedSize = (size_t)-1;
+ memset(&best->parameters, 0, sizeof(best->parameters));
+}
+
+/**
+ * Wait until liveJobs == 0.
+ */
+void COVER_best_wait(COVER_best_t *best) {
+ if (!best) {
+ return;
+ }
+ ZSTD_pthread_mutex_lock(&best->mutex);
+ while (best->liveJobs != 0) {
+ ZSTD_pthread_cond_wait(&best->cond, &best->mutex);
+ }
+ ZSTD_pthread_mutex_unlock(&best->mutex);
+}
+
+/**
+ * Call COVER_best_wait() and then destroy the COVER_best_t.
+ */
+void COVER_best_destroy(COVER_best_t *best) {
+ if (!best) {
+ return;
+ }
+ COVER_best_wait(best);
+ if (best->dict) {
+ free(best->dict);
+ }
+ ZSTD_pthread_mutex_destroy(&best->mutex);
+ ZSTD_pthread_cond_destroy(&best->cond);
+}
+
+/**
+ * Called when a thread is about to be launched.
+ * Increments liveJobs.
+ */
+void COVER_best_start(COVER_best_t *best) {
+ if (!best) {
+ return;
+ }
+ ZSTD_pthread_mutex_lock(&best->mutex);
+ ++best->liveJobs;
+ ZSTD_pthread_mutex_unlock(&best->mutex);
+}
+
+/**
+ * Called when a thread finishes executing, both on error or success.
+ * Decrements liveJobs and signals any waiting threads if liveJobs == 0.
+ * If this dictionary is the best so far save it and its parameters.
+ */
+void COVER_best_finish(COVER_best_t *best, size_t compressedSize,
+ ZDICT_cover_params_t parameters, void *dict,
+ size_t dictSize) {
+ if (!best) {
+ return;
+ }
+ {
+ size_t liveJobs;
+ ZSTD_pthread_mutex_lock(&best->mutex);
+ --best->liveJobs;
+ liveJobs = best->liveJobs;
+ /* If the new dictionary is better */
+ if (compressedSize < best->compressedSize) {
+ /* Allocate space if necessary */
+ if (!best->dict || best->dictSize < dictSize) {
+ if (best->dict) {
+ free(best->dict);
+ }
+ best->dict = malloc(dictSize);
+ if (!best->dict) {
+ best->compressedSize = ERROR(GENERIC);
+ best->dictSize = 0;
+ ZSTD_pthread_cond_signal(&best->cond);
+ ZSTD_pthread_mutex_unlock(&best->mutex);
+ return;
+ }
+ }
+ /* Save the dictionary, parameters, and size */
+ memcpy(best->dict, dict, dictSize);
+ best->dictSize = dictSize;
+ best->parameters = parameters;
+ best->compressedSize = compressedSize;
+ }
+ if (liveJobs == 0) {
+ ZSTD_pthread_cond_broadcast(&best->cond);
+ }
+ ZSTD_pthread_mutex_unlock(&best->mutex);
+ }
+}
+
+/**
+ * Parameters for COVER_tryParameters().
+ */
+typedef struct COVER_tryParameters_data_s {
+ const COVER_ctx_t *ctx;
+ COVER_best_t *best;
+ size_t dictBufferCapacity;
+ ZDICT_cover_params_t parameters;
+} COVER_tryParameters_data_t;
+
+/**
+ * Tries a set of parameters and updates the COVER_best_t with the results.
+ * This function is thread safe if zstd is compiled with multithreaded support.
+ * It takes its parameters as an *OWNING* opaque pointer to support threading.
+ */
+static void COVER_tryParameters(void *opaque) {
+ /* Save parameters as local variables */
+ COVER_tryParameters_data_t *const data = (COVER_tryParameters_data_t *)opaque;
+ const COVER_ctx_t *const ctx = data->ctx;
+ const ZDICT_cover_params_t parameters = data->parameters;
+ size_t dictBufferCapacity = data->dictBufferCapacity;
+ size_t totalCompressedSize = ERROR(GENERIC);
+ /* Allocate space for hash table, dict, and freqs */
+ COVER_map_t activeDmers;
+ BYTE *const dict = (BYTE * const)malloc(dictBufferCapacity);
+ U32 *freqs = (U32 *)malloc(ctx->suffixSize * sizeof(U32));
+ if (!COVER_map_init(&activeDmers, parameters.k - parameters.d + 1)) {
+ DISPLAYLEVEL(1, "Failed to allocate dmer map: out of memory\n");
+ goto _cleanup;
+ }
+ if (!dict || !freqs) {
+ DISPLAYLEVEL(1, "Failed to allocate buffers: out of memory\n");
+ goto _cleanup;
+ }
+ /* Copy the frequencies because we need to modify them */
+ memcpy(freqs, ctx->freqs, ctx->suffixSize * sizeof(U32));
+ /* Build the dictionary */
+ {
+ const size_t tail = COVER_buildDictionary(ctx, freqs, &activeDmers, dict,
+ dictBufferCapacity, parameters);
+ dictBufferCapacity = ZDICT_finalizeDictionary(
+ dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail,
+ ctx->samples, ctx->samplesSizes, (unsigned)ctx->nbTrainSamples,
+ parameters.zParams);
+ if (ZDICT_isError(dictBufferCapacity)) {
+ DISPLAYLEVEL(1, "Failed to finalize dictionary\n");
+ goto _cleanup;
+ }
+ }
+ /* Check total compressed size */
+ totalCompressedSize = COVER_checkTotalCompressedSize(parameters, ctx->samplesSizes,
+ ctx->samples, ctx->offsets,
+ ctx->nbTrainSamples, ctx->nbSamples,
+ dict, dictBufferCapacity);
+
+_cleanup:
+ COVER_best_finish(data->best, totalCompressedSize, parameters, dict,
+ dictBufferCapacity);
+ free(data);
+ COVER_map_destroy(&activeDmers);
+ if (dict) {
+ free(dict);
+ }
+ if (freqs) {
+ free(freqs);
+ }
+}
+
+ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_cover(
+ void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer,
+ const size_t *samplesSizes, unsigned nbSamples,
+ ZDICT_cover_params_t *parameters) {
+ /* constants */
+ const unsigned nbThreads = parameters->nbThreads;
+ const double splitPoint =
+ parameters->splitPoint <= 0.0 ? DEFAULT_SPLITPOINT : parameters->splitPoint;
+ const unsigned kMinD = parameters->d == 0 ? 6 : parameters->d;
+ const unsigned kMaxD = parameters->d == 0 ? 8 : parameters->d;
+ const unsigned kMinK = parameters->k == 0 ? 50 : parameters->k;
+ const unsigned kMaxK = parameters->k == 0 ? 2000 : parameters->k;
+ const unsigned kSteps = parameters->steps == 0 ? 40 : parameters->steps;
+ const unsigned kStepSize = MAX((kMaxK - kMinK) / kSteps, 1);
+ const unsigned kIterations =
+ (1 + (kMaxD - kMinD) / 2) * (1 + (kMaxK - kMinK) / kStepSize);
+ /* Local variables */
+ const int displayLevel = parameters->zParams.notificationLevel;
+ unsigned iteration = 1;
+ unsigned d;
+ unsigned k;
+ COVER_best_t best;
+ POOL_ctx *pool = NULL;
+
+ /* Checks */
+ if (splitPoint <= 0 || splitPoint > 1) {
+ LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect parameters\n");
+ return ERROR(GENERIC);
+ }
+ if (kMinK < kMaxD || kMaxK < kMinK) {
+ LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect parameters\n");
+ return ERROR(GENERIC);
+ }
+ if (nbSamples == 0) {
+ DISPLAYLEVEL(1, "Cover must have at least one input file\n");
+ return ERROR(GENERIC);
+ }
+ if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) {
+ DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n",
+ ZDICT_DICTSIZE_MIN);
+ return ERROR(dstSize_tooSmall);
+ }
+ if (nbThreads > 1) {
+ pool = POOL_create(nbThreads, 1);
+ if (!pool) {
+ return ERROR(memory_allocation);
+ }
+ }
+ /* Initialization */
+ COVER_best_init(&best);
+ /* Turn down global display level to clean up display at level 2 and below */
+ g_displayLevel = displayLevel == 0 ? 0 : displayLevel - 1;
+ /* Loop through d first because each new value needs a new context */
+ LOCALDISPLAYLEVEL(displayLevel, 2, "Trying %u different sets of parameters\n",
+ kIterations);
+ for (d = kMinD; d <= kMaxD; d += 2) {
+ /* Initialize the context for this value of d */
+ COVER_ctx_t ctx;
+ LOCALDISPLAYLEVEL(displayLevel, 3, "d=%u\n", d);
+ if (!COVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, d, splitPoint)) {
+ LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to initialize context\n");
+ COVER_best_destroy(&best);
+ POOL_free(pool);
+ return ERROR(GENERIC);
+ }
+ /* Loop through k reusing the same context */
+ for (k = kMinK; k <= kMaxK; k += kStepSize) {
+ /* Prepare the arguments */
+ COVER_tryParameters_data_t *data = (COVER_tryParameters_data_t *)malloc(
+ sizeof(COVER_tryParameters_data_t));
+ LOCALDISPLAYLEVEL(displayLevel, 3, "k=%u\n", k);
+ if (!data) {
+ LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to allocate parameters\n");
+ COVER_best_destroy(&best);
+ COVER_ctx_destroy(&ctx);
+ POOL_free(pool);
+ return ERROR(GENERIC);
+ }
+ data->ctx = &ctx;
+ data->best = &best;
+ data->dictBufferCapacity = dictBufferCapacity;
+ data->parameters = *parameters;
+ data->parameters.k = k;
+ data->parameters.d = d;
+ data->parameters.splitPoint = splitPoint;
+ data->parameters.steps = kSteps;
+ data->parameters.zParams.notificationLevel = g_displayLevel;
+ /* Check the parameters */
+ if (!COVER_checkParameters(data->parameters, dictBufferCapacity)) {
+ DISPLAYLEVEL(1, "Cover parameters incorrect\n");
+ free(data);
+ continue;
+ }
+ /* Call the function and pass ownership of data to it */
+ COVER_best_start(&best);
+ if (pool) {
+ POOL_add(pool, &COVER_tryParameters, data);
+ } else {
+ COVER_tryParameters(data);
+ }
+ /* Print status */
+ LOCALDISPLAYUPDATE(displayLevel, 2, "\r%u%% ",
+ (unsigned)((iteration * 100) / kIterations));
+ ++iteration;
+ }
+ COVER_best_wait(&best);
+ COVER_ctx_destroy(&ctx);
+ }
+ LOCALDISPLAYLEVEL(displayLevel, 2, "\r%79s\r", "");
+ /* Fill the output buffer and parameters with output of the best parameters */
+ {
+ const size_t dictSize = best.dictSize;
+ if (ZSTD_isError(best.compressedSize)) {
+ const size_t compressedSize = best.compressedSize;
+ COVER_best_destroy(&best);
+ POOL_free(pool);
+ return compressedSize;
+ }
+ *parameters = best.parameters;
+ memcpy(dictBuffer, best.dict, dictSize);
+ COVER_best_destroy(&best);
+ POOL_free(pool);
+ return dictSize;
+ }
+}
diff --git a/Utilities/cmzstd/lib/dictBuilder/cover.h b/Utilities/cmzstd/lib/dictBuilder/cover.h
new file mode 100644
index 000000000..82e2e1cea
--- /dev/null
+++ b/Utilities/cmzstd/lib/dictBuilder/cover.h
@@ -0,0 +1,83 @@
+#include <stdio.h> /* fprintf */
+#include <stdlib.h> /* malloc, free, qsort */
+#include <string.h> /* memset */
+#include <time.h> /* clock */
+#include "mem.h" /* read */
+#include "pool.h"
+#include "threading.h"
+#include "zstd_internal.h" /* includes zstd.h */
+#ifndef ZDICT_STATIC_LINKING_ONLY
+#define ZDICT_STATIC_LINKING_ONLY
+#endif
+#include "zdict.h"
+
+/**
+ * COVER_best_t is used for two purposes:
+ * 1. Synchronizing threads.
+ * 2. Saving the best parameters and dictionary.
+ *
+ * All of the methods except COVER_best_init() are thread safe if zstd is
+ * compiled with multithreaded support.
+ */
+typedef struct COVER_best_s {
+ ZSTD_pthread_mutex_t mutex;
+ ZSTD_pthread_cond_t cond;
+ size_t liveJobs;
+ void *dict;
+ size_t dictSize;
+ ZDICT_cover_params_t parameters;
+ size_t compressedSize;
+} COVER_best_t;
+
+/**
+ * A segment is a range in the source as well as the score of the segment.
+ */
+typedef struct {
+ U32 begin;
+ U32 end;
+ U32 score;
+} COVER_segment_t;
+
+/**
+ * Checks total compressed size of a dictionary
+ */
+size_t COVER_checkTotalCompressedSize(const ZDICT_cover_params_t parameters,
+ const size_t *samplesSizes, const BYTE *samples,
+ size_t *offsets,
+ size_t nbTrainSamples, size_t nbSamples,
+ BYTE *const dict, size_t dictBufferCapacity);
+
+/**
+ * Returns the sum of the sample sizes.
+ */
+size_t COVER_sum(const size_t *samplesSizes, unsigned nbSamples) ;
+
+/**
+ * Initialize the `COVER_best_t`.
+ */
+void COVER_best_init(COVER_best_t *best);
+
+/**
+ * Wait until liveJobs == 0.
+ */
+void COVER_best_wait(COVER_best_t *best);
+
+/**
+ * Call COVER_best_wait() and then destroy the COVER_best_t.
+ */
+void COVER_best_destroy(COVER_best_t *best);
+
+/**
+ * Called when a thread is about to be launched.
+ * Increments liveJobs.
+ */
+void COVER_best_start(COVER_best_t *best);
+
+/**
+ * Called when a thread finishes executing, both on error or success.
+ * Decrements liveJobs and signals any waiting threads if liveJobs == 0.
+ * If this dictionary is the best so far save it and its parameters.
+ */
+void COVER_best_finish(COVER_best_t *best, size_t compressedSize,
+ ZDICT_cover_params_t parameters, void *dict,
+ size_t dictSize);
diff --git a/Utilities/cmzstd/lib/dictBuilder/divsufsort.c b/Utilities/cmzstd/lib/dictBuilder/divsufsort.c
new file mode 100644
index 000000000..ead922044
--- /dev/null
+++ b/Utilities/cmzstd/lib/dictBuilder/divsufsort.c
@@ -0,0 +1,1913 @@
+/*
+ * divsufsort.c for libdivsufsort-lite
+ * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*- Compiler specifics -*/
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Wshorten-64-to-32"
+#endif
+
+#if defined(_MSC_VER)
+# pragma warning(disable : 4244)
+# pragma warning(disable : 4127) /* C4127 : Condition expression is constant */
+#endif
+
+
+/*- Dependencies -*/
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "divsufsort.h"
+
+/*- Constants -*/
+#if defined(INLINE)
+# undef INLINE
+#endif
+#if !defined(INLINE)
+# define INLINE __inline
+#endif
+#if defined(ALPHABET_SIZE) && (ALPHABET_SIZE < 1)
+# undef ALPHABET_SIZE
+#endif
+#if !defined(ALPHABET_SIZE)
+# define ALPHABET_SIZE (256)
+#endif
+#define BUCKET_A_SIZE (ALPHABET_SIZE)
+#define BUCKET_B_SIZE (ALPHABET_SIZE * ALPHABET_SIZE)
+#if defined(SS_INSERTIONSORT_THRESHOLD)
+# if SS_INSERTIONSORT_THRESHOLD < 1
+# undef SS_INSERTIONSORT_THRESHOLD
+# define SS_INSERTIONSORT_THRESHOLD (1)
+# endif
+#else
+# define SS_INSERTIONSORT_THRESHOLD (8)
+#endif
+#if defined(SS_BLOCKSIZE)
+# if SS_BLOCKSIZE < 0
+# undef SS_BLOCKSIZE
+# define SS_BLOCKSIZE (0)
+# elif 32768 <= SS_BLOCKSIZE
+# undef SS_BLOCKSIZE
+# define SS_BLOCKSIZE (32767)
+# endif
+#else
+# define SS_BLOCKSIZE (1024)
+#endif
+/* minstacksize = log(SS_BLOCKSIZE) / log(3) * 2 */
+#if SS_BLOCKSIZE == 0
+# define SS_MISORT_STACKSIZE (96)
+#elif SS_BLOCKSIZE <= 4096
+# define SS_MISORT_STACKSIZE (16)
+#else
+# define SS_MISORT_STACKSIZE (24)
+#endif
+#define SS_SMERGE_STACKSIZE (32)
+#define TR_INSERTIONSORT_THRESHOLD (8)
+#define TR_STACKSIZE (64)
+
+
+/*- Macros -*/
+#ifndef SWAP
+# define SWAP(_a, _b) do { t = (_a); (_a) = (_b); (_b) = t; } while(0)
+#endif /* SWAP */
+#ifndef MIN
+# define MIN(_a, _b) (((_a) < (_b)) ? (_a) : (_b))
+#endif /* MIN */
+#ifndef MAX
+# define MAX(_a, _b) (((_a) > (_b)) ? (_a) : (_b))
+#endif /* MAX */
+#define STACK_PUSH(_a, _b, _c, _d)\
+ do {\
+ assert(ssize < STACK_SIZE);\
+ stack[ssize].a = (_a), stack[ssize].b = (_b),\
+ stack[ssize].c = (_c), stack[ssize++].d = (_d);\
+ } while(0)
+#define STACK_PUSH5(_a, _b, _c, _d, _e)\
+ do {\
+ assert(ssize < STACK_SIZE);\
+ stack[ssize].a = (_a), stack[ssize].b = (_b),\
+ stack[ssize].c = (_c), stack[ssize].d = (_d), stack[ssize++].e = (_e);\
+ } while(0)
+#define STACK_POP(_a, _b, _c, _d)\
+ do {\
+ assert(0 <= ssize);\
+ if(ssize == 0) { return; }\
+ (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\
+ (_c) = stack[ssize].c, (_d) = stack[ssize].d;\
+ } while(0)
+#define STACK_POP5(_a, _b, _c, _d, _e)\
+ do {\
+ assert(0 <= ssize);\
+ if(ssize == 0) { return; }\
+ (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\
+ (_c) = stack[ssize].c, (_d) = stack[ssize].d, (_e) = stack[ssize].e;\
+ } while(0)
+#define BUCKET_A(_c0) bucket_A[(_c0)]
+#if ALPHABET_SIZE == 256
+#define BUCKET_B(_c0, _c1) (bucket_B[((_c1) << 8) | (_c0)])
+#define BUCKET_BSTAR(_c0, _c1) (bucket_B[((_c0) << 8) | (_c1)])
+#else
+#define BUCKET_B(_c0, _c1) (bucket_B[(_c1) * ALPHABET_SIZE + (_c0)])
+#define BUCKET_BSTAR(_c0, _c1) (bucket_B[(_c0) * ALPHABET_SIZE + (_c1)])
+#endif
+
+
+/*- Private Functions -*/
+
+static const int lg_table[256]= {
+ -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
+};
+
+#if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE)
+
+static INLINE
+int
+ss_ilg(int n) {
+#if SS_BLOCKSIZE == 0
+ return (n & 0xffff0000) ?
+ ((n & 0xff000000) ?
+ 24 + lg_table[(n >> 24) & 0xff] :
+ 16 + lg_table[(n >> 16) & 0xff]) :
+ ((n & 0x0000ff00) ?
+ 8 + lg_table[(n >> 8) & 0xff] :
+ 0 + lg_table[(n >> 0) & 0xff]);
+#elif SS_BLOCKSIZE < 256
+ return lg_table[n];
+#else
+ return (n & 0xff00) ?
+ 8 + lg_table[(n >> 8) & 0xff] :
+ 0 + lg_table[(n >> 0) & 0xff];
+#endif
+}
+
+#endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */
+
+#if SS_BLOCKSIZE != 0
+
+static const int sqq_table[256] = {
+ 0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57, 59, 61,
+ 64, 65, 67, 69, 71, 73, 75, 76, 78, 80, 81, 83, 84, 86, 87, 89,
+ 90, 91, 93, 94, 96, 97, 98, 99, 101, 102, 103, 104, 106, 107, 108, 109,
+110, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,
+128, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
+143, 144, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155,
+156, 157, 158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168,
+169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 178, 179, 180,
+181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191,
+192, 192, 193, 193, 194, 195, 195, 196, 197, 197, 198, 199, 199, 200, 201, 201,
+202, 203, 203, 204, 204, 205, 206, 206, 207, 208, 208, 209, 209, 210, 211, 211,
+212, 212, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 221,
+221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227, 227, 228, 229, 229, 230,
+230, 231, 231, 232, 232, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238,
+239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247,
+247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, 255
+};
+
+static INLINE
+int
+ss_isqrt(int x) {
+ int y, e;
+
+ if(x >= (SS_BLOCKSIZE * SS_BLOCKSIZE)) { return SS_BLOCKSIZE; }
+ e = (x & 0xffff0000) ?
+ ((x & 0xff000000) ?
+ 24 + lg_table[(x >> 24) & 0xff] :
+ 16 + lg_table[(x >> 16) & 0xff]) :
+ ((x & 0x0000ff00) ?
+ 8 + lg_table[(x >> 8) & 0xff] :
+ 0 + lg_table[(x >> 0) & 0xff]);
+
+ if(e >= 16) {
+ y = sqq_table[x >> ((e - 6) - (e & 1))] << ((e >> 1) - 7);
+ if(e >= 24) { y = (y + 1 + x / y) >> 1; }
+ y = (y + 1 + x / y) >> 1;
+ } else if(e >= 8) {
+ y = (sqq_table[x >> ((e - 6) - (e & 1))] >> (7 - (e >> 1))) + 1;
+ } else {
+ return sqq_table[x] >> 4;
+ }
+
+ return (x < (y * y)) ? y - 1 : y;
+}
+
+#endif /* SS_BLOCKSIZE != 0 */
+
+
+/*---------------------------------------------------------------------------*/
+
+/* Compares two suffixes. */
+static INLINE
+int
+ss_compare(const unsigned char *T,
+ const int *p1, const int *p2,
+ int depth) {
+ const unsigned char *U1, *U2, *U1n, *U2n;
+
+ for(U1 = T + depth + *p1,
+ U2 = T + depth + *p2,
+ U1n = T + *(p1 + 1) + 2,
+ U2n = T + *(p2 + 1) + 2;
+ (U1 < U1n) && (U2 < U2n) && (*U1 == *U2);
+ ++U1, ++U2) {
+ }
+
+ return U1 < U1n ?
+ (U2 < U2n ? *U1 - *U2 : 1) :
+ (U2 < U2n ? -1 : 0);
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+#if (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1)
+
+/* Insertionsort for small size groups */
+static
+void
+ss_insertionsort(const unsigned char *T, const int *PA,
+ int *first, int *last, int depth) {
+ int *i, *j;
+ int t;
+ int r;
+
+ for(i = last - 2; first <= i; --i) {
+ for(t = *i, j = i + 1; 0 < (r = ss_compare(T, PA + t, PA + *j, depth));) {
+ do { *(j - 1) = *j; } while((++j < last) && (*j < 0));
+ if(last <= j) { break; }
+ }
+ if(r == 0) { *j = ~*j; }
+ *(j - 1) = t;
+ }
+}
+
+#endif /* (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) */
+
+
+/*---------------------------------------------------------------------------*/
+
+#if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE)
+
+static INLINE
+void
+ss_fixdown(const unsigned char *Td, const int *PA,
+ int *SA, int i, int size) {
+ int j, k;
+ int v;
+ int c, d, e;
+
+ for(v = SA[i], c = Td[PA[v]]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) {
+ d = Td[PA[SA[k = j++]]];
+ if(d < (e = Td[PA[SA[j]]])) { k = j; d = e; }
+ if(d <= c) { break; }
+ }
+ SA[i] = v;
+}
+
+/* Simple top-down heapsort. */
+static
+void
+ss_heapsort(const unsigned char *Td, const int *PA, int *SA, int size) {
+ int i, m;
+ int t;
+
+ m = size;
+ if((size % 2) == 0) {
+ m--;
+ if(Td[PA[SA[m / 2]]] < Td[PA[SA[m]]]) { SWAP(SA[m], SA[m / 2]); }
+ }
+
+ for(i = m / 2 - 1; 0 <= i; --i) { ss_fixdown(Td, PA, SA, i, m); }
+ if((size % 2) == 0) { SWAP(SA[0], SA[m]); ss_fixdown(Td, PA, SA, 0, m); }
+ for(i = m - 1; 0 < i; --i) {
+ t = SA[0], SA[0] = SA[i];
+ ss_fixdown(Td, PA, SA, 0, i);
+ SA[i] = t;
+ }
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+/* Returns the median of three elements. */
+static INLINE
+int *
+ss_median3(const unsigned char *Td, const int *PA,
+ int *v1, int *v2, int *v3) {
+ int *t;
+ if(Td[PA[*v1]] > Td[PA[*v2]]) { SWAP(v1, v2); }
+ if(Td[PA[*v2]] > Td[PA[*v3]]) {
+ if(Td[PA[*v1]] > Td[PA[*v3]]) { return v1; }
+ else { return v3; }
+ }
+ return v2;
+}
+
+/* Returns the median of five elements. */
+static INLINE
+int *
+ss_median5(const unsigned char *Td, const int *PA,
+ int *v1, int *v2, int *v3, int *v4, int *v5) {
+ int *t;
+ if(Td[PA[*v2]] > Td[PA[*v3]]) { SWAP(v2, v3); }
+ if(Td[PA[*v4]] > Td[PA[*v5]]) { SWAP(v4, v5); }
+ if(Td[PA[*v2]] > Td[PA[*v4]]) { SWAP(v2, v4); SWAP(v3, v5); }
+ if(Td[PA[*v1]] > Td[PA[*v3]]) { SWAP(v1, v3); }
+ if(Td[PA[*v1]] > Td[PA[*v4]]) { SWAP(v1, v4); SWAP(v3, v5); }
+ if(Td[PA[*v3]] > Td[PA[*v4]]) { return v4; }
+ return v3;
+}
+
+/* Returns the pivot element. */
+static INLINE
+int *
+ss_pivot(const unsigned char *Td, const int *PA, int *first, int *last) {
+ int *middle;
+ int t;
+
+ t = last - first;
+ middle = first + t / 2;
+
+ if(t <= 512) {
+ if(t <= 32) {
+ return ss_median3(Td, PA, first, middle, last - 1);
+ } else {
+ t >>= 2;
+ return ss_median5(Td, PA, first, first + t, middle, last - 1 - t, last - 1);
+ }
+ }
+ t >>= 3;
+ first = ss_median3(Td, PA, first, first + t, first + (t << 1));
+ middle = ss_median3(Td, PA, middle - t, middle, middle + t);
+ last = ss_median3(Td, PA, last - 1 - (t << 1), last - 1 - t, last - 1);
+ return ss_median3(Td, PA, first, middle, last);
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+/* Binary partition for substrings. */
+static INLINE
+int *
+ss_partition(const int *PA,
+ int *first, int *last, int depth) {
+ int *a, *b;
+ int t;
+ for(a = first - 1, b = last;;) {
+ for(; (++a < b) && ((PA[*a] + depth) >= (PA[*a + 1] + 1));) { *a = ~*a; }
+ for(; (a < --b) && ((PA[*b] + depth) < (PA[*b + 1] + 1));) { }
+ if(b <= a) { break; }
+ t = ~*b;
+ *b = *a;
+ *a = t;
+ }
+ if(first < a) { *first = ~*first; }
+ return a;
+}
+
+/* Multikey introsort for medium size groups. */
+static
+void
+ss_mintrosort(const unsigned char *T, const int *PA,
+ int *first, int *last,
+ int depth) {
+#define STACK_SIZE SS_MISORT_STACKSIZE
+ struct { int *a, *b, c; int d; } stack[STACK_SIZE];
+ const unsigned char *Td;
+ int *a, *b, *c, *d, *e, *f;
+ int s, t;
+ int ssize;
+ int limit;
+ int v, x = 0;
+
+ for(ssize = 0, limit = ss_ilg(last - first);;) {
+
+ if((last - first) <= SS_INSERTIONSORT_THRESHOLD) {
+#if 1 < SS_INSERTIONSORT_THRESHOLD
+ if(1 < (last - first)) { ss_insertionsort(T, PA, first, last, depth); }
+#endif
+ STACK_POP(first, last, depth, limit);
+ continue;
+ }
+
+ Td = T + depth;
+ if(limit-- == 0) { ss_heapsort(Td, PA, first, last - first); }
+ if(limit < 0) {
+ for(a = first + 1, v = Td[PA[*first]]; a < last; ++a) {
+ if((x = Td[PA[*a]]) != v) {
+ if(1 < (a - first)) { break; }
+ v = x;
+ first = a;
+ }
+ }
+ if(Td[PA[*first] - 1] < v) {
+ first = ss_partition(PA, first, a, depth);
+ }
+ if((a - first) <= (last - a)) {
+ if(1 < (a - first)) {
+ STACK_PUSH(a, last, depth, -1);
+ last = a, depth += 1, limit = ss_ilg(a - first);
+ } else {
+ first = a, limit = -1;
+ }
+ } else {
+ if(1 < (last - a)) {
+ STACK_PUSH(first, a, depth + 1, ss_ilg(a - first));
+ first = a, limit = -1;
+ } else {
+ last = a, depth += 1, limit = ss_ilg(a - first);
+ }
+ }
+ continue;
+ }
+
+ /* choose pivot */
+ a = ss_pivot(Td, PA, first, last);
+ v = Td[PA[*a]];
+ SWAP(*first, *a);
+
+ /* partition */
+ for(b = first; (++b < last) && ((x = Td[PA[*b]]) == v);) { }
+ if(((a = b) < last) && (x < v)) {
+ for(; (++b < last) && ((x = Td[PA[*b]]) <= v);) {
+ if(x == v) { SWAP(*b, *a); ++a; }
+ }
+ }
+ for(c = last; (b < --c) && ((x = Td[PA[*c]]) == v);) { }
+ if((b < (d = c)) && (x > v)) {
+ for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) {
+ if(x == v) { SWAP(*c, *d); --d; }
+ }
+ }
+ for(; b < c;) {
+ SWAP(*b, *c);
+ for(; (++b < c) && ((x = Td[PA[*b]]) <= v);) {
+ if(x == v) { SWAP(*b, *a); ++a; }
+ }
+ for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) {
+ if(x == v) { SWAP(*c, *d); --d; }
+ }
+ }
+
+ if(a <= d) {
+ c = b - 1;
+
+ if((s = a - first) > (t = b - a)) { s = t; }
+ for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); }
+ if((s = d - c) > (t = last - d - 1)) { s = t; }
+ for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); }
+
+ a = first + (b - a), c = last - (d - c);
+ b = (v <= Td[PA[*a] - 1]) ? a : ss_partition(PA, a, c, depth);
+
+ if((a - first) <= (last - c)) {
+ if((last - c) <= (c - b)) {
+ STACK_PUSH(b, c, depth + 1, ss_ilg(c - b));
+ STACK_PUSH(c, last, depth, limit);
+ last = a;
+ } else if((a - first) <= (c - b)) {
+ STACK_PUSH(c, last, depth, limit);
+ STACK_PUSH(b, c, depth + 1, ss_ilg(c - b));
+ last = a;
+ } else {
+ STACK_PUSH(c, last, depth, limit);
+ STACK_PUSH(first, a, depth, limit);
+ first = b, last = c, depth += 1, limit = ss_ilg(c - b);
+ }
+ } else {
+ if((a - first) <= (c - b)) {
+ STACK_PUSH(b, c, depth + 1, ss_ilg(c - b));
+ STACK_PUSH(first, a, depth, limit);
+ first = c;
+ } else if((last - c) <= (c - b)) {
+ STACK_PUSH(first, a, depth, limit);
+ STACK_PUSH(b, c, depth + 1, ss_ilg(c - b));
+ first = c;
+ } else {
+ STACK_PUSH(first, a, depth, limit);
+ STACK_PUSH(c, last, depth, limit);
+ first = b, last = c, depth += 1, limit = ss_ilg(c - b);
+ }
+ }
+ } else {
+ limit += 1;
+ if(Td[PA[*first] - 1] < v) {
+ first = ss_partition(PA, first, last, depth);
+ limit = ss_ilg(last - first);
+ }
+ depth += 1;
+ }
+ }
+#undef STACK_SIZE
+}
+
+#endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */
+
+
+/*---------------------------------------------------------------------------*/
+
+#if SS_BLOCKSIZE != 0
+
+static INLINE
+void
+ss_blockswap(int *a, int *b, int n) {
+ int t;
+ for(; 0 < n; --n, ++a, ++b) {
+ t = *a, *a = *b, *b = t;
+ }
+}
+
+static INLINE
+void
+ss_rotate(int *first, int *middle, int *last) {
+ int *a, *b, t;
+ int l, r;
+ l = middle - first, r = last - middle;
+ for(; (0 < l) && (0 < r);) {
+ if(l == r) { ss_blockswap(first, middle, l); break; }
+ if(l < r) {
+ a = last - 1, b = middle - 1;
+ t = *a;
+ do {
+ *a-- = *b, *b-- = *a;
+ if(b < first) {
+ *a = t;
+ last = a;
+ if((r -= l + 1) <= l) { break; }
+ a -= 1, b = middle - 1;
+ t = *a;
+ }
+ } while(1);
+ } else {
+ a = first, b = middle;
+ t = *a;
+ do {
+ *a++ = *b, *b++ = *a;
+ if(last <= b) {
+ *a = t;
+ first = a + 1;
+ if((l -= r + 1) <= r) { break; }
+ a += 1, b = middle;
+ t = *a;
+ }
+ } while(1);
+ }
+ }
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+static
+void
+ss_inplacemerge(const unsigned char *T, const int *PA,
+ int *first, int *middle, int *last,
+ int depth) {
+ const int *p;
+ int *a, *b;
+ int len, half;
+ int q, r;
+ int x;
+
+ for(;;) {
+ if(*(last - 1) < 0) { x = 1; p = PA + ~*(last - 1); }
+ else { x = 0; p = PA + *(last - 1); }
+ for(a = first, len = middle - first, half = len >> 1, r = -1;
+ 0 < len;
+ len = half, half >>= 1) {
+ b = a + half;
+ q = ss_compare(T, PA + ((0 <= *b) ? *b : ~*b), p, depth);
+ if(q < 0) {
+ a = b + 1;
+ half -= (len & 1) ^ 1;
+ } else {
+ r = q;
+ }
+ }
+ if(a < middle) {
+ if(r == 0) { *a = ~*a; }
+ ss_rotate(a, middle, last);
+ last -= middle - a;
+ middle = a;
+ if(first == middle) { break; }
+ }
+ --last;
+ if(x != 0) { while(*--last < 0) { } }
+ if(middle == last) { break; }
+ }
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+/* Merge-forward with internal buffer. */
+static
+void
+ss_mergeforward(const unsigned char *T, const int *PA,
+ int *first, int *middle, int *last,
+ int *buf, int depth) {
+ int *a, *b, *c, *bufend;
+ int t;
+ int r;
+
+ bufend = buf + (middle - first) - 1;
+ ss_blockswap(buf, first, middle - first);
+
+ for(t = *(a = first), b = buf, c = middle;;) {
+ r = ss_compare(T, PA + *b, PA + *c, depth);
+ if(r < 0) {
+ do {
+ *a++ = *b;
+ if(bufend <= b) { *bufend = t; return; }
+ *b++ = *a;
+ } while(*b < 0);
+ } else if(r > 0) {
+ do {
+ *a++ = *c, *c++ = *a;
+ if(last <= c) {
+ while(b < bufend) { *a++ = *b, *b++ = *a; }
+ *a = *b, *b = t;
+ return;
+ }
+ } while(*c < 0);
+ } else {
+ *c = ~*c;
+ do {
+ *a++ = *b;
+ if(bufend <= b) { *bufend = t; return; }
+ *b++ = *a;
+ } while(*b < 0);
+
+ do {
+ *a++ = *c, *c++ = *a;
+ if(last <= c) {
+ while(b < bufend) { *a++ = *b, *b++ = *a; }
+ *a = *b, *b = t;
+ return;
+ }
+ } while(*c < 0);
+ }
+ }
+}
+
+/* Merge-backward with internal buffer. */
+static
+void
+ss_mergebackward(const unsigned char *T, const int *PA,
+ int *first, int *middle, int *last,
+ int *buf, int depth) {
+ const int *p1, *p2;
+ int *a, *b, *c, *bufend;
+ int t;
+ int r;
+ int x;
+
+ bufend = buf + (last - middle) - 1;
+ ss_blockswap(buf, middle, last - middle);
+
+ x = 0;
+ if(*bufend < 0) { p1 = PA + ~*bufend; x |= 1; }
+ else { p1 = PA + *bufend; }
+ if(*(middle - 1) < 0) { p2 = PA + ~*(middle - 1); x |= 2; }
+ else { p2 = PA + *(middle - 1); }
+ for(t = *(a = last - 1), b = bufend, c = middle - 1;;) {
+ r = ss_compare(T, p1, p2, depth);
+ if(0 < r) {
+ if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; }
+ *a-- = *b;
+ if(b <= buf) { *buf = t; break; }
+ *b-- = *a;
+ if(*b < 0) { p1 = PA + ~*b; x |= 1; }
+ else { p1 = PA + *b; }
+ } else if(r < 0) {
+ if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; }
+ *a-- = *c, *c-- = *a;
+ if(c < first) {
+ while(buf < b) { *a-- = *b, *b-- = *a; }
+ *a = *b, *b = t;
+ break;
+ }
+ if(*c < 0) { p2 = PA + ~*c; x |= 2; }
+ else { p2 = PA + *c; }
+ } else {
+ if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; }
+ *a-- = ~*b;
+ if(b <= buf) { *buf = t; break; }
+ *b-- = *a;
+ if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; }
+ *a-- = *c, *c-- = *a;
+ if(c < first) {
+ while(buf < b) { *a-- = *b, *b-- = *a; }
+ *a = *b, *b = t;
+ break;
+ }
+ if(*b < 0) { p1 = PA + ~*b; x |= 1; }
+ else { p1 = PA + *b; }
+ if(*c < 0) { p2 = PA + ~*c; x |= 2; }
+ else { p2 = PA + *c; }
+ }
+ }
+}
+
+/* D&C based merge. */
+static
+void
+ss_swapmerge(const unsigned char *T, const int *PA,
+ int *first, int *middle, int *last,
+ int *buf, int bufsize, int depth) {
+#define STACK_SIZE SS_SMERGE_STACKSIZE
+#define GETIDX(a) ((0 <= (a)) ? (a) : (~(a)))
+#define MERGE_CHECK(a, b, c)\
+ do {\
+ if(((c) & 1) ||\
+ (((c) & 2) && (ss_compare(T, PA + GETIDX(*((a) - 1)), PA + *(a), depth) == 0))) {\
+ *(a) = ~*(a);\
+ }\
+ if(((c) & 4) && ((ss_compare(T, PA + GETIDX(*((b) - 1)), PA + *(b), depth) == 0))) {\
+ *(b) = ~*(b);\
+ }\
+ } while(0)
+ struct { int *a, *b, *c; int d; } stack[STACK_SIZE];
+ int *l, *r, *lm, *rm;
+ int m, len, half;
+ int ssize;
+ int check, next;
+
+ for(check = 0, ssize = 0;;) {
+ if((last - middle) <= bufsize) {
+ if((first < middle) && (middle < last)) {
+ ss_mergebackward(T, PA, first, middle, last, buf, depth);
+ }
+ MERGE_CHECK(first, last, check);
+ STACK_POP(first, middle, last, check);
+ continue;
+ }
+
+ if((middle - first) <= bufsize) {
+ if(first < middle) {
+ ss_mergeforward(T, PA, first, middle, last, buf, depth);
+ }
+ MERGE_CHECK(first, last, check);
+ STACK_POP(first, middle, last, check);
+ continue;
+ }
+
+ for(m = 0, len = MIN(middle - first, last - middle), half = len >> 1;
+ 0 < len;
+ len = half, half >>= 1) {
+ if(ss_compare(T, PA + GETIDX(*(middle + m + half)),
+ PA + GETIDX(*(middle - m - half - 1)), depth) < 0) {
+ m += half + 1;
+ half -= (len & 1) ^ 1;
+ }
+ }
+
+ if(0 < m) {
+ lm = middle - m, rm = middle + m;
+ ss_blockswap(lm, middle, m);
+ l = r = middle, next = 0;
+ if(rm < last) {
+ if(*rm < 0) {
+ *rm = ~*rm;
+ if(first < lm) { for(; *--l < 0;) { } next |= 4; }
+ next |= 1;
+ } else if(first < lm) {
+ for(; *r < 0; ++r) { }
+ next |= 2;
+ }
+ }
+
+ if((l - first) <= (last - r)) {
+ STACK_PUSH(r, rm, last, (next & 3) | (check & 4));
+ middle = lm, last = l, check = (check & 3) | (next & 4);
+ } else {
+ if((next & 2) && (r == middle)) { next ^= 6; }
+ STACK_PUSH(first, lm, l, (check & 3) | (next & 4));
+ first = r, middle = rm, check = (next & 3) | (check & 4);
+ }
+ } else {
+ if(ss_compare(T, PA + GETIDX(*(middle - 1)), PA + *middle, depth) == 0) {
+ *middle = ~*middle;
+ }
+ MERGE_CHECK(first, last, check);
+ STACK_POP(first, middle, last, check);
+ }
+ }
+#undef STACK_SIZE
+}
+
+#endif /* SS_BLOCKSIZE != 0 */
+
+
+/*---------------------------------------------------------------------------*/
+
+/* Substring sort */
+static
+void
+sssort(const unsigned char *T, const int *PA,
+ int *first, int *last,
+ int *buf, int bufsize,
+ int depth, int n, int lastsuffix) {
+ int *a;
+#if SS_BLOCKSIZE != 0
+ int *b, *middle, *curbuf;
+ int j, k, curbufsize, limit;
+#endif
+ int i;
+
+ if(lastsuffix != 0) { ++first; }
+
+#if SS_BLOCKSIZE == 0
+ ss_mintrosort(T, PA, first, last, depth);
+#else
+ if((bufsize < SS_BLOCKSIZE) &&
+ (bufsize < (last - first)) &&
+ (bufsize < (limit = ss_isqrt(last - first)))) {
+ if(SS_BLOCKSIZE < limit) { limit = SS_BLOCKSIZE; }
+ buf = middle = last - limit, bufsize = limit;
+ } else {
+ middle = last, limit = 0;
+ }
+ for(a = first, i = 0; SS_BLOCKSIZE < (middle - a); a += SS_BLOCKSIZE, ++i) {
+#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE
+ ss_mintrosort(T, PA, a, a + SS_BLOCKSIZE, depth);
+#elif 1 < SS_BLOCKSIZE
+ ss_insertionsort(T, PA, a, a + SS_BLOCKSIZE, depth);
+#endif
+ curbufsize = last - (a + SS_BLOCKSIZE);
+ curbuf = a + SS_BLOCKSIZE;
+ if(curbufsize <= bufsize) { curbufsize = bufsize, curbuf = buf; }
+ for(b = a, k = SS_BLOCKSIZE, j = i; j & 1; b -= k, k <<= 1, j >>= 1) {
+ ss_swapmerge(T, PA, b - k, b, b + k, curbuf, curbufsize, depth);
+ }
+ }
+#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE
+ ss_mintrosort(T, PA, a, middle, depth);
+#elif 1 < SS_BLOCKSIZE
+ ss_insertionsort(T, PA, a, middle, depth);
+#endif
+ for(k = SS_BLOCKSIZE; i != 0; k <<= 1, i >>= 1) {
+ if(i & 1) {
+ ss_swapmerge(T, PA, a - k, a, middle, buf, bufsize, depth);
+ a -= k;
+ }
+ }
+ if(limit != 0) {
+#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE
+ ss_mintrosort(T, PA, middle, last, depth);
+#elif 1 < SS_BLOCKSIZE
+ ss_insertionsort(T, PA, middle, last, depth);
+#endif
+ ss_inplacemerge(T, PA, first, middle, last, depth);
+ }
+#endif
+
+ if(lastsuffix != 0) {
+ /* Insert last type B* suffix. */
+ int PAi[2]; PAi[0] = PA[*(first - 1)], PAi[1] = n - 2;
+ for(a = first, i = *(first - 1);
+ (a < last) && ((*a < 0) || (0 < ss_compare(T, &(PAi[0]), PA + *a, depth)));
+ ++a) {
+ *(a - 1) = *a;
+ }
+ *(a - 1) = i;
+ }
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+static INLINE
+int
+tr_ilg(int n) {
+ return (n & 0xffff0000) ?
+ ((n & 0xff000000) ?
+ 24 + lg_table[(n >> 24) & 0xff] :
+ 16 + lg_table[(n >> 16) & 0xff]) :
+ ((n & 0x0000ff00) ?
+ 8 + lg_table[(n >> 8) & 0xff] :
+ 0 + lg_table[(n >> 0) & 0xff]);
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+/* Simple insertionsort for small size groups. */
+static
+void
+tr_insertionsort(const int *ISAd, int *first, int *last) {
+ int *a, *b;
+ int t, r;
+
+ for(a = first + 1; a < last; ++a) {
+ for(t = *a, b = a - 1; 0 > (r = ISAd[t] - ISAd[*b]);) {
+ do { *(b + 1) = *b; } while((first <= --b) && (*b < 0));
+ if(b < first) { break; }
+ }
+ if(r == 0) { *b = ~*b; }
+ *(b + 1) = t;
+ }
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+static INLINE
+void
+tr_fixdown(const int *ISAd, int *SA, int i, int size) {
+ int j, k;
+ int v;
+ int c, d, e;
+
+ for(v = SA[i], c = ISAd[v]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) {
+ d = ISAd[SA[k = j++]];
+ if(d < (e = ISAd[SA[j]])) { k = j; d = e; }
+ if(d <= c) { break; }
+ }
+ SA[i] = v;
+}
+
+/* Simple top-down heapsort. */
+static
+void
+tr_heapsort(const int *ISAd, int *SA, int size) {
+ int i, m;
+ int t;
+
+ m = size;
+ if((size % 2) == 0) {
+ m--;
+ if(ISAd[SA[m / 2]] < ISAd[SA[m]]) { SWAP(SA[m], SA[m / 2]); }
+ }
+
+ for(i = m / 2 - 1; 0 <= i; --i) { tr_fixdown(ISAd, SA, i, m); }
+ if((size % 2) == 0) { SWAP(SA[0], SA[m]); tr_fixdown(ISAd, SA, 0, m); }
+ for(i = m - 1; 0 < i; --i) {
+ t = SA[0], SA[0] = SA[i];
+ tr_fixdown(ISAd, SA, 0, i);
+ SA[i] = t;
+ }
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+/* Returns the median of three elements. */
+static INLINE
+int *
+tr_median3(const int *ISAd, int *v1, int *v2, int *v3) {
+ int *t;
+ if(ISAd[*v1] > ISAd[*v2]) { SWAP(v1, v2); }
+ if(ISAd[*v2] > ISAd[*v3]) {
+ if(ISAd[*v1] > ISAd[*v3]) { return v1; }
+ else { return v3; }
+ }
+ return v2;
+}
+
+/* Returns the median of five elements. */
+static INLINE
+int *
+tr_median5(const int *ISAd,
+ int *v1, int *v2, int *v3, int *v4, int *v5) {
+ int *t;
+ if(ISAd[*v2] > ISAd[*v3]) { SWAP(v2, v3); }
+ if(ISAd[*v4] > ISAd[*v5]) { SWAP(v4, v5); }
+ if(ISAd[*v2] > ISAd[*v4]) { SWAP(v2, v4); SWAP(v3, v5); }
+ if(ISAd[*v1] > ISAd[*v3]) { SWAP(v1, v3); }
+ if(ISAd[*v1] > ISAd[*v4]) { SWAP(v1, v4); SWAP(v3, v5); }
+ if(ISAd[*v3] > ISAd[*v4]) { return v4; }
+ return v3;
+}
+
+/* Returns the pivot element. */
+static INLINE
+int *
+tr_pivot(const int *ISAd, int *first, int *last) {
+ int *middle;
+ int t;
+
+ t = last - first;
+ middle = first + t / 2;
+
+ if(t <= 512) {
+ if(t <= 32) {
+ return tr_median3(ISAd, first, middle, last - 1);
+ } else {
+ t >>= 2;
+ return tr_median5(ISAd, first, first + t, middle, last - 1 - t, last - 1);
+ }
+ }
+ t >>= 3;
+ first = tr_median3(ISAd, first, first + t, first + (t << 1));
+ middle = tr_median3(ISAd, middle - t, middle, middle + t);
+ last = tr_median3(ISAd, last - 1 - (t << 1), last - 1 - t, last - 1);
+ return tr_median3(ISAd, first, middle, last);
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+typedef struct _trbudget_t trbudget_t;
+struct _trbudget_t {
+ int chance;
+ int remain;
+ int incval;
+ int count;
+};
+
+static INLINE
+void
+trbudget_init(trbudget_t *budget, int chance, int incval) {
+ budget->chance = chance;
+ budget->remain = budget->incval = incval;
+}
+
+static INLINE
+int
+trbudget_check(trbudget_t *budget, int size) {
+ if(size <= budget->remain) { budget->remain -= size; return 1; }
+ if(budget->chance == 0) { budget->count += size; return 0; }
+ budget->remain += budget->incval - size;
+ budget->chance -= 1;
+ return 1;
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+static INLINE
+void
+tr_partition(const int *ISAd,
+ int *first, int *middle, int *last,
+ int **pa, int **pb, int v) {
+ int *a, *b, *c, *d, *e, *f;
+ int t, s;
+ int x = 0;
+
+ for(b = middle - 1; (++b < last) && ((x = ISAd[*b]) == v);) { }
+ if(((a = b) < last) && (x < v)) {
+ for(; (++b < last) && ((x = ISAd[*b]) <= v);) {
+ if(x == v) { SWAP(*b, *a); ++a; }
+ }
+ }
+ for(c = last; (b < --c) && ((x = ISAd[*c]) == v);) { }
+ if((b < (d = c)) && (x > v)) {
+ for(; (b < --c) && ((x = ISAd[*c]) >= v);) {
+ if(x == v) { SWAP(*c, *d); --d; }
+ }
+ }
+ for(; b < c;) {
+ SWAP(*b, *c);
+ for(; (++b < c) && ((x = ISAd[*b]) <= v);) {
+ if(x == v) { SWAP(*b, *a); ++a; }
+ }
+ for(; (b < --c) && ((x = ISAd[*c]) >= v);) {
+ if(x == v) { SWAP(*c, *d); --d; }
+ }
+ }
+
+ if(a <= d) {
+ c = b - 1;
+ if((s = a - first) > (t = b - a)) { s = t; }
+ for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); }
+ if((s = d - c) > (t = last - d - 1)) { s = t; }
+ for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); }
+ first += (b - a), last -= (d - c);
+ }
+ *pa = first, *pb = last;
+}
+
+static
+void
+tr_copy(int *ISA, const int *SA,
+ int *first, int *a, int *b, int *last,
+ int depth) {
+ /* sort suffixes of middle partition
+ by using sorted order of suffixes of left and right partition. */
+ int *c, *d, *e;
+ int s, v;
+
+ v = b - SA - 1;
+ for(c = first, d = a - 1; c <= d; ++c) {
+ if((0 <= (s = *c - depth)) && (ISA[s] == v)) {
+ *++d = s;
+ ISA[s] = d - SA;
+ }
+ }
+ for(c = last - 1, e = d + 1, d = b; e < d; --c) {
+ if((0 <= (s = *c - depth)) && (ISA[s] == v)) {
+ *--d = s;
+ ISA[s] = d - SA;
+ }
+ }
+}
+
+static
+void
+tr_partialcopy(int *ISA, const int *SA,
+ int *first, int *a, int *b, int *last,
+ int depth) {
+ int *c, *d, *e;
+ int s, v;
+ int rank, lastrank, newrank = -1;
+
+ v = b - SA - 1;
+ lastrank = -1;
+ for(c = first, d = a - 1; c <= d; ++c) {
+ if((0 <= (s = *c - depth)) && (ISA[s] == v)) {
+ *++d = s;
+ rank = ISA[s + depth];
+ if(lastrank != rank) { lastrank = rank; newrank = d - SA; }
+ ISA[s] = newrank;
+ }
+ }
+
+ lastrank = -1;
+ for(e = d; first <= e; --e) {
+ rank = ISA[*e];
+ if(lastrank != rank) { lastrank = rank; newrank = e - SA; }
+ if(newrank != rank) { ISA[*e] = newrank; }
+ }
+
+ lastrank = -1;
+ for(c = last - 1, e = d + 1, d = b; e < d; --c) {
+ if((0 <= (s = *c - depth)) && (ISA[s] == v)) {
+ *--d = s;
+ rank = ISA[s + depth];
+ if(lastrank != rank) { lastrank = rank; newrank = d - SA; }
+ ISA[s] = newrank;
+ }
+ }
+}
+
+static
+void
+tr_introsort(int *ISA, const int *ISAd,
+ int *SA, int *first, int *last,
+ trbudget_t *budget) {
+#define STACK_SIZE TR_STACKSIZE
+ struct { const int *a; int *b, *c; int d, e; }stack[STACK_SIZE];
+ int *a, *b, *c;
+ int t;
+ int v, x = 0;
+ int incr = ISAd - ISA;
+ int limit, next;
+ int ssize, trlink = -1;
+
+ for(ssize = 0, limit = tr_ilg(last - first);;) {
+
+ if(limit < 0) {
+ if(limit == -1) {
+ /* tandem repeat partition */
+ tr_partition(ISAd - incr, first, first, last, &a, &b, last - SA - 1);
+
+ /* update ranks */
+ if(a < last) {
+ for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; }
+ }
+ if(b < last) {
+ for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; }
+ }
+
+ /* push */
+ if(1 < (b - a)) {
+ STACK_PUSH5(NULL, a, b, 0, 0);
+ STACK_PUSH5(ISAd - incr, first, last, -2, trlink);
+ trlink = ssize - 2;
+ }
+ if((a - first) <= (last - b)) {
+ if(1 < (a - first)) {
+ STACK_PUSH5(ISAd, b, last, tr_ilg(last - b), trlink);
+ last = a, limit = tr_ilg(a - first);
+ } else if(1 < (last - b)) {
+ first = b, limit = tr_ilg(last - b);
+ } else {
+ STACK_POP5(ISAd, first, last, limit, trlink);
+ }
+ } else {
+ if(1 < (last - b)) {
+ STACK_PUSH5(ISAd, first, a, tr_ilg(a - first), trlink);
+ first = b, limit = tr_ilg(last - b);
+ } else if(1 < (a - first)) {
+ last = a, limit = tr_ilg(a - first);
+ } else {
+ STACK_POP5(ISAd, first, last, limit, trlink);
+ }
+ }
+ } else if(limit == -2) {
+ /* tandem repeat copy */
+ a = stack[--ssize].b, b = stack[ssize].c;
+ if(stack[ssize].d == 0) {
+ tr_copy(ISA, SA, first, a, b, last, ISAd - ISA);
+ } else {
+ if(0 <= trlink) { stack[trlink].d = -1; }
+ tr_partialcopy(ISA, SA, first, a, b, last, ISAd - ISA);
+ }
+ STACK_POP5(ISAd, first, last, limit, trlink);
+ } else {
+ /* sorted partition */
+ if(0 <= *first) {
+ a = first;
+ do { ISA[*a] = a - SA; } while((++a < last) && (0 <= *a));
+ first = a;
+ }
+ if(first < last) {
+ a = first; do { *a = ~*a; } while(*++a < 0);
+ next = (ISA[*a] != ISAd[*a]) ? tr_ilg(a - first + 1) : -1;
+ if(++a < last) { for(b = first, v = a - SA - 1; b < a; ++b) { ISA[*b] = v; } }
+
+ /* push */
+ if(trbudget_check(budget, a - first)) {
+ if((a - first) <= (last - a)) {
+ STACK_PUSH5(ISAd, a, last, -3, trlink);
+ ISAd += incr, last = a, limit = next;
+ } else {
+ if(1 < (last - a)) {
+ STACK_PUSH5(ISAd + incr, first, a, next, trlink);
+ first = a, limit = -3;
+ } else {
+ ISAd += incr, last = a, limit = next;
+ }
+ }
+ } else {
+ if(0 <= trlink) { stack[trlink].d = -1; }
+ if(1 < (last - a)) {
+ first = a, limit = -3;
+ } else {
+ STACK_POP5(ISAd, first, last, limit, trlink);
+ }
+ }
+ } else {
+ STACK_POP5(ISAd, first, last, limit, trlink);
+ }
+ }
+ continue;
+ }
+
+ if((last - first) <= TR_INSERTIONSORT_THRESHOLD) {
+ tr_insertionsort(ISAd, first, last);
+ limit = -3;
+ continue;
+ }
+
+ if(limit-- == 0) {
+ tr_heapsort(ISAd, first, last - first);
+ for(a = last - 1; first < a; a = b) {
+ for(x = ISAd[*a], b = a - 1; (first <= b) && (ISAd[*b] == x); --b) { *b = ~*b; }
+ }
+ limit = -3;
+ continue;
+ }
+
+ /* choose pivot */
+ a = tr_pivot(ISAd, first, last);
+ SWAP(*first, *a);
+ v = ISAd[*first];
+
+ /* partition */
+ tr_partition(ISAd, first, first + 1, last, &a, &b, v);
+ if((last - first) != (b - a)) {
+ next = (ISA[*a] != v) ? tr_ilg(b - a) : -1;
+
+ /* update ranks */
+ for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; }
+ if(b < last) { for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } }
+
+ /* push */
+ if((1 < (b - a)) && (trbudget_check(budget, b - a))) {
+ if((a - first) <= (last - b)) {
+ if((last - b) <= (b - a)) {
+ if(1 < (a - first)) {
+ STACK_PUSH5(ISAd + incr, a, b, next, trlink);
+ STACK_PUSH5(ISAd, b, last, limit, trlink);
+ last = a;
+ } else if(1 < (last - b)) {
+ STACK_PUSH5(ISAd + incr, a, b, next, trlink);
+ first = b;
+ } else {
+ ISAd += incr, first = a, last = b, limit = next;
+ }
+ } else if((a - first) <= (b - a)) {
+ if(1 < (a - first)) {
+ STACK_PUSH5(ISAd, b, last, limit, trlink);
+ STACK_PUSH5(ISAd + incr, a, b, next, trlink);
+ last = a;
+ } else {
+ STACK_PUSH5(ISAd, b, last, limit, trlink);
+ ISAd += incr, first = a, last = b, limit = next;
+ }
+ } else {
+ STACK_PUSH5(ISAd, b, last, limit, trlink);
+ STACK_PUSH5(ISAd, first, a, limit, trlink);
+ ISAd += incr, first = a, last = b, limit = next;
+ }
+ } else {
+ if((a - first) <= (b - a)) {
+ if(1 < (last - b)) {
+ STACK_PUSH5(ISAd + incr, a, b, next, trlink);
+ STACK_PUSH5(ISAd, first, a, limit, trlink);
+ first = b;
+ } else if(1 < (a - first)) {
+ STACK_PUSH5(ISAd + incr, a, b, next, trlink);
+ last = a;
+ } else {
+ ISAd += incr, first = a, last = b, limit = next;
+ }
+ } else if((last - b) <= (b - a)) {
+ if(1 < (last - b)) {
+ STACK_PUSH5(ISAd, first, a, limit, trlink);
+ STACK_PUSH5(ISAd + incr, a, b, next, trlink);
+ first = b;
+ } else {
+ STACK_PUSH5(ISAd, first, a, limit, trlink);
+ ISAd += incr, first = a, last = b, limit = next;
+ }
+ } else {
+ STACK_PUSH5(ISAd, first, a, limit, trlink);
+ STACK_PUSH5(ISAd, b, last, limit, trlink);
+ ISAd += incr, first = a, last = b, limit = next;
+ }
+ }
+ } else {
+ if((1 < (b - a)) && (0 <= trlink)) { stack[trlink].d = -1; }
+ if((a - first) <= (last - b)) {
+ if(1 < (a - first)) {
+ STACK_PUSH5(ISAd, b, last, limit, trlink);
+ last = a;
+ } else if(1 < (last - b)) {
+ first = b;
+ } else {
+ STACK_POP5(ISAd, first, last, limit, trlink);
+ }
+ } else {
+ if(1 < (last - b)) {
+ STACK_PUSH5(ISAd, first, a, limit, trlink);
+ first = b;
+ } else if(1 < (a - first)) {
+ last = a;
+ } else {
+ STACK_POP5(ISAd, first, last, limit, trlink);
+ }
+ }
+ }
+ } else {
+ if(trbudget_check(budget, last - first)) {
+ limit = tr_ilg(last - first), ISAd += incr;
+ } else {
+ if(0 <= trlink) { stack[trlink].d = -1; }
+ STACK_POP5(ISAd, first, last, limit, trlink);
+ }
+ }
+ }
+#undef STACK_SIZE
+}
+
+
+
+/*---------------------------------------------------------------------------*/
+
+/* Tandem repeat sort */
+static
+void
+trsort(int *ISA, int *SA, int n, int depth) {
+ int *ISAd;
+ int *first, *last;
+ trbudget_t budget;
+ int t, skip, unsorted;
+
+ trbudget_init(&budget, tr_ilg(n) * 2 / 3, n);
+/* trbudget_init(&budget, tr_ilg(n) * 3 / 4, n); */
+ for(ISAd = ISA + depth; -n < *SA; ISAd += ISAd - ISA) {
+ first = SA;
+ skip = 0;
+ unsorted = 0;
+ do {
+ if((t = *first) < 0) { first -= t; skip += t; }
+ else {
+ if(skip != 0) { *(first + skip) = skip; skip = 0; }
+ last = SA + ISA[t] + 1;
+ if(1 < (last - first)) {
+ budget.count = 0;
+ tr_introsort(ISA, ISAd, SA, first, last, &budget);
+ if(budget.count != 0) { unsorted += budget.count; }
+ else { skip = first - last; }
+ } else if((last - first) == 1) {
+ skip = -1;
+ }
+ first = last;
+ }
+ } while(first < (SA + n));
+ if(skip != 0) { *(first + skip) = skip; }
+ if(unsorted == 0) { break; }
+ }
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+/* Sorts suffixes of type B*. */
+static
+int
+sort_typeBstar(const unsigned char *T, int *SA,
+ int *bucket_A, int *bucket_B,
+ int n, int openMP) {
+ int *PAb, *ISAb, *buf;
+#ifdef LIBBSC_OPENMP
+ int *curbuf;
+ int l;
+#endif
+ int i, j, k, t, m, bufsize;
+ int c0, c1;
+#ifdef LIBBSC_OPENMP
+ int d0, d1;
+#endif
+ (void)openMP;
+
+ /* Initialize bucket arrays. */
+ for(i = 0; i < BUCKET_A_SIZE; ++i) { bucket_A[i] = 0; }
+ for(i = 0; i < BUCKET_B_SIZE; ++i) { bucket_B[i] = 0; }
+
+ /* Count the number of occurrences of the first one or two characters of each
+ type A, B and B* suffix. Moreover, store the beginning position of all
+ type B* suffixes into the array SA. */
+ for(i = n - 1, m = n, c0 = T[n - 1]; 0 <= i;) {
+ /* type A suffix. */
+ do { ++BUCKET_A(c1 = c0); } while((0 <= --i) && ((c0 = T[i]) >= c1));
+ if(0 <= i) {
+ /* type B* suffix. */
+ ++BUCKET_BSTAR(c0, c1);
+ SA[--m] = i;
+ /* type B suffix. */
+ for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) {
+ ++BUCKET_B(c0, c1);
+ }
+ }
+ }
+ m = n - m;
+/*
+note:
+ A type B* suffix is lexicographically smaller than a type B suffix that
+ begins with the same first two characters.
+*/
+
+ /* Calculate the index of start/end point of each bucket. */
+ for(c0 = 0, i = 0, j = 0; c0 < ALPHABET_SIZE; ++c0) {
+ t = i + BUCKET_A(c0);
+ BUCKET_A(c0) = i + j; /* start point */
+ i = t + BUCKET_B(c0, c0);
+ for(c1 = c0 + 1; c1 < ALPHABET_SIZE; ++c1) {
+ j += BUCKET_BSTAR(c0, c1);
+ BUCKET_BSTAR(c0, c1) = j; /* end point */
+ i += BUCKET_B(c0, c1);
+ }
+ }
+
+ if(0 < m) {
+ /* Sort the type B* suffixes by their first two characters. */
+ PAb = SA + n - m; ISAb = SA + m;
+ for(i = m - 2; 0 <= i; --i) {
+ t = PAb[i], c0 = T[t], c1 = T[t + 1];
+ SA[--BUCKET_BSTAR(c0, c1)] = i;
+ }
+ t = PAb[m - 1], c0 = T[t], c1 = T[t + 1];
+ SA[--BUCKET_BSTAR(c0, c1)] = m - 1;
+
+ /* Sort the type B* substrings using sssort. */
+#ifdef LIBBSC_OPENMP
+ if (openMP)
+ {
+ buf = SA + m;
+ c0 = ALPHABET_SIZE - 2, c1 = ALPHABET_SIZE - 1, j = m;
+#pragma omp parallel default(shared) private(bufsize, curbuf, k, l, d0, d1)
+ {
+ bufsize = (n - (2 * m)) / omp_get_num_threads();
+ curbuf = buf + omp_get_thread_num() * bufsize;
+ k = 0;
+ for(;;) {
+ #pragma omp critical(sssort_lock)
+ {
+ if(0 < (l = j)) {
+ d0 = c0, d1 = c1;
+ do {
+ k = BUCKET_BSTAR(d0, d1);
+ if(--d1 <= d0) {
+ d1 = ALPHABET_SIZE - 1;
+ if(--d0 < 0) { break; }
+ }
+ } while(((l - k) <= 1) && (0 < (l = k)));
+ c0 = d0, c1 = d1, j = k;
+ }
+ }
+ if(l == 0) { break; }
+ sssort(T, PAb, SA + k, SA + l,
+ curbuf, bufsize, 2, n, *(SA + k) == (m - 1));
+ }
+ }
+ }
+ else
+ {
+ buf = SA + m, bufsize = n - (2 * m);
+ for(c0 = ALPHABET_SIZE - 2, j = m; 0 < j; --c0) {
+ for(c1 = ALPHABET_SIZE - 1; c0 < c1; j = i, --c1) {
+ i = BUCKET_BSTAR(c0, c1);
+ if(1 < (j - i)) {
+ sssort(T, PAb, SA + i, SA + j,
+ buf, bufsize, 2, n, *(SA + i) == (m - 1));
+ }
+ }
+ }
+ }
+#else
+ buf = SA + m, bufsize = n - (2 * m);
+ for(c0 = ALPHABET_SIZE - 2, j = m; 0 < j; --c0) {
+ for(c1 = ALPHABET_SIZE - 1; c0 < c1; j = i, --c1) {
+ i = BUCKET_BSTAR(c0, c1);
+ if(1 < (j - i)) {
+ sssort(T, PAb, SA + i, SA + j,
+ buf, bufsize, 2, n, *(SA + i) == (m - 1));
+ }
+ }
+ }
+#endif
+
+ /* Compute ranks of type B* substrings. */
+ for(i = m - 1; 0 <= i; --i) {
+ if(0 <= SA[i]) {
+ j = i;
+ do { ISAb[SA[i]] = i; } while((0 <= --i) && (0 <= SA[i]));
+ SA[i + 1] = i - j;
+ if(i <= 0) { break; }
+ }
+ j = i;
+ do { ISAb[SA[i] = ~SA[i]] = j; } while(SA[--i] < 0);
+ ISAb[SA[i]] = j;
+ }
+
+ /* Construct the inverse suffix array of type B* suffixes using trsort. */
+ trsort(ISAb, SA, m, 1);
+
+ /* Set the sorted order of tyoe B* suffixes. */
+ for(i = n - 1, j = m, c0 = T[n - 1]; 0 <= i;) {
+ for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) >= c1); --i, c1 = c0) { }
+ if(0 <= i) {
+ t = i;
+ for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { }
+ SA[ISAb[--j]] = ((t == 0) || (1 < (t - i))) ? t : ~t;
+ }
+ }
+
+ /* Calculate the index of start/end point of each bucket. */
+ BUCKET_B(ALPHABET_SIZE - 1, ALPHABET_SIZE - 1) = n; /* end point */
+ for(c0 = ALPHABET_SIZE - 2, k = m - 1; 0 <= c0; --c0) {
+ i = BUCKET_A(c0 + 1) - 1;
+ for(c1 = ALPHABET_SIZE - 1; c0 < c1; --c1) {
+ t = i - BUCKET_B(c0, c1);
+ BUCKET_B(c0, c1) = i; /* end point */
+
+ /* Move all type B* suffixes to the correct position. */
+ for(i = t, j = BUCKET_BSTAR(c0, c1);
+ j <= k;
+ --i, --k) { SA[i] = SA[k]; }
+ }
+ BUCKET_BSTAR(c0, c0 + 1) = i - BUCKET_B(c0, c0) + 1; /* start point */
+ BUCKET_B(c0, c0) = i; /* end point */
+ }
+ }
+
+ return m;
+}
+
+/* Constructs the suffix array by using the sorted order of type B* suffixes. */
+static
+void
+construct_SA(const unsigned char *T, int *SA,
+ int *bucket_A, int *bucket_B,
+ int n, int m) {
+ int *i, *j, *k;
+ int s;
+ int c0, c1, c2;
+
+ if(0 < m) {
+ /* Construct the sorted order of type B suffixes by using
+ the sorted order of type B* suffixes. */
+ for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) {
+ /* Scan the suffix array from right to left. */
+ for(i = SA + BUCKET_BSTAR(c1, c1 + 1),
+ j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1;
+ i <= j;
+ --j) {
+ if(0 < (s = *j)) {
+ assert(T[s] == c1);
+ assert(((s + 1) < n) && (T[s] <= T[s + 1]));
+ assert(T[s - 1] <= T[s]);
+ *j = ~s;
+ c0 = T[--s];
+ if((0 < s) && (T[s - 1] > c0)) { s = ~s; }
+ if(c0 != c2) {
+ if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; }
+ k = SA + BUCKET_B(c2 = c0, c1);
+ }
+ assert(k < j); assert(k != NULL);
+ *k-- = s;
+ } else {
+ assert(((s == 0) && (T[s] == c1)) || (s < 0));
+ *j = ~s;
+ }
+ }
+ }
+ }
+
+ /* Construct the suffix array by using
+ the sorted order of type B suffixes. */
+ k = SA + BUCKET_A(c2 = T[n - 1]);
+ *k++ = (T[n - 2] < c2) ? ~(n - 1) : (n - 1);
+ /* Scan the suffix array from left to right. */
+ for(i = SA, j = SA + n; i < j; ++i) {
+ if(0 < (s = *i)) {
+ assert(T[s - 1] >= T[s]);
+ c0 = T[--s];
+ if((s == 0) || (T[s - 1] < c0)) { s = ~s; }
+ if(c0 != c2) {
+ BUCKET_A(c2) = k - SA;
+ k = SA + BUCKET_A(c2 = c0);
+ }
+ assert(i < k);
+ *k++ = s;
+ } else {
+ assert(s < 0);
+ *i = ~s;
+ }
+ }
+}
+
+/* Constructs the burrows-wheeler transformed string directly
+ by using the sorted order of type B* suffixes. */
+static
+int
+construct_BWT(const unsigned char *T, int *SA,
+ int *bucket_A, int *bucket_B,
+ int n, int m) {
+ int *i, *j, *k, *orig;
+ int s;
+ int c0, c1, c2;
+
+ if(0 < m) {
+ /* Construct the sorted order of type B suffixes by using
+ the sorted order of type B* suffixes. */
+ for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) {
+ /* Scan the suffix array from right to left. */
+ for(i = SA + BUCKET_BSTAR(c1, c1 + 1),
+ j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1;
+ i <= j;
+ --j) {
+ if(0 < (s = *j)) {
+ assert(T[s] == c1);
+ assert(((s + 1) < n) && (T[s] <= T[s + 1]));
+ assert(T[s - 1] <= T[s]);
+ c0 = T[--s];
+ *j = ~((int)c0);
+ if((0 < s) && (T[s - 1] > c0)) { s = ~s; }
+ if(c0 != c2) {
+ if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; }
+ k = SA + BUCKET_B(c2 = c0, c1);
+ }
+ assert(k < j); assert(k != NULL);
+ *k-- = s;
+ } else if(s != 0) {
+ *j = ~s;
+#ifndef NDEBUG
+ } else {
+ assert(T[s] == c1);
+#endif
+ }
+ }
+ }
+ }
+
+ /* Construct the BWTed string by using
+ the sorted order of type B suffixes. */
+ k = SA + BUCKET_A(c2 = T[n - 1]);
+ *k++ = (T[n - 2] < c2) ? ~((int)T[n - 2]) : (n - 1);
+ /* Scan the suffix array from left to right. */
+ for(i = SA, j = SA + n, orig = SA; i < j; ++i) {
+ if(0 < (s = *i)) {
+ assert(T[s - 1] >= T[s]);
+ c0 = T[--s];
+ *i = c0;
+ if((0 < s) && (T[s - 1] < c0)) { s = ~((int)T[s - 1]); }
+ if(c0 != c2) {
+ BUCKET_A(c2) = k - SA;
+ k = SA + BUCKET_A(c2 = c0);
+ }
+ assert(i < k);
+ *k++ = s;
+ } else if(s != 0) {
+ *i = ~s;
+ } else {
+ orig = i;
+ }
+ }
+
+ return orig - SA;
+}
+
+/* Constructs the burrows-wheeler transformed string directly
+ by using the sorted order of type B* suffixes. */
+static
+int
+construct_BWT_indexes(const unsigned char *T, int *SA,
+ int *bucket_A, int *bucket_B,
+ int n, int m,
+ unsigned char * num_indexes, int * indexes) {
+ int *i, *j, *k, *orig;
+ int s;
+ int c0, c1, c2;
+
+ int mod = n / 8;
+ {
+ mod |= mod >> 1; mod |= mod >> 2;
+ mod |= mod >> 4; mod |= mod >> 8;
+ mod |= mod >> 16; mod >>= 1;
+
+ *num_indexes = (unsigned char)((n - 1) / (mod + 1));
+ }
+
+ if(0 < m) {
+ /* Construct the sorted order of type B suffixes by using
+ the sorted order of type B* suffixes. */
+ for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) {
+ /* Scan the suffix array from right to left. */
+ for(i = SA + BUCKET_BSTAR(c1, c1 + 1),
+ j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1;
+ i <= j;
+ --j) {
+ if(0 < (s = *j)) {
+ assert(T[s] == c1);
+ assert(((s + 1) < n) && (T[s] <= T[s + 1]));
+ assert(T[s - 1] <= T[s]);
+
+ if ((s & mod) == 0) indexes[s / (mod + 1) - 1] = j - SA;
+
+ c0 = T[--s];
+ *j = ~((int)c0);
+ if((0 < s) && (T[s - 1] > c0)) { s = ~s; }
+ if(c0 != c2) {
+ if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; }
+ k = SA + BUCKET_B(c2 = c0, c1);
+ }
+ assert(k < j); assert(k != NULL);
+ *k-- = s;
+ } else if(s != 0) {
+ *j = ~s;
+#ifndef NDEBUG
+ } else {
+ assert(T[s] == c1);
+#endif
+ }
+ }
+ }
+ }
+
+ /* Construct the BWTed string by using
+ the sorted order of type B suffixes. */
+ k = SA + BUCKET_A(c2 = T[n - 1]);
+ if (T[n - 2] < c2) {
+ if (((n - 1) & mod) == 0) indexes[(n - 1) / (mod + 1) - 1] = k - SA;
+ *k++ = ~((int)T[n - 2]);
+ }
+ else {
+ *k++ = n - 1;
+ }
+
+ /* Scan the suffix array from left to right. */
+ for(i = SA, j = SA + n, orig = SA; i < j; ++i) {
+ if(0 < (s = *i)) {
+ assert(T[s - 1] >= T[s]);
+
+ if ((s & mod) == 0) indexes[s / (mod + 1) - 1] = i - SA;
+
+ c0 = T[--s];
+ *i = c0;
+ if(c0 != c2) {
+ BUCKET_A(c2) = k - SA;
+ k = SA + BUCKET_A(c2 = c0);
+ }
+ assert(i < k);
+ if((0 < s) && (T[s - 1] < c0)) {
+ if ((s & mod) == 0) indexes[s / (mod + 1) - 1] = k - SA;
+ *k++ = ~((int)T[s - 1]);
+ } else
+ *k++ = s;
+ } else if(s != 0) {
+ *i = ~s;
+ } else {
+ orig = i;
+ }
+ }
+
+ return orig - SA;
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+/*- Function -*/
+
+int
+divsufsort(const unsigned char *T, int *SA, int n, int openMP) {
+ int *bucket_A, *bucket_B;
+ int m;
+ int err = 0;
+
+ /* Check arguments. */
+ if((T == NULL) || (SA == NULL) || (n < 0)) { return -1; }
+ else if(n == 0) { return 0; }
+ else if(n == 1) { SA[0] = 0; return 0; }
+ else if(n == 2) { m = (T[0] < T[1]); SA[m ^ 1] = 0, SA[m] = 1; return 0; }
+
+ bucket_A = (int *)malloc(BUCKET_A_SIZE * sizeof(int));
+ bucket_B = (int *)malloc(BUCKET_B_SIZE * sizeof(int));
+
+ /* Suffixsort. */
+ if((bucket_A != NULL) && (bucket_B != NULL)) {
+ m = sort_typeBstar(T, SA, bucket_A, bucket_B, n, openMP);
+ construct_SA(T, SA, bucket_A, bucket_B, n, m);
+ } else {
+ err = -2;
+ }
+
+ free(bucket_B);
+ free(bucket_A);
+
+ return err;
+}
+
+int
+divbwt(const unsigned char *T, unsigned char *U, int *A, int n, unsigned char * num_indexes, int * indexes, int openMP) {
+ int *B;
+ int *bucket_A, *bucket_B;
+ int m, pidx, i;
+
+ /* Check arguments. */
+ if((T == NULL) || (U == NULL) || (n < 0)) { return -1; }
+ else if(n <= 1) { if(n == 1) { U[0] = T[0]; } return n; }
+
+ if((B = A) == NULL) { B = (int *)malloc((size_t)(n + 1) * sizeof(int)); }
+ bucket_A = (int *)malloc(BUCKET_A_SIZE * sizeof(int));
+ bucket_B = (int *)malloc(BUCKET_B_SIZE * sizeof(int));
+
+ /* Burrows-Wheeler Transform. */
+ if((B != NULL) && (bucket_A != NULL) && (bucket_B != NULL)) {
+ m = sort_typeBstar(T, B, bucket_A, bucket_B, n, openMP);
+
+ if (num_indexes == NULL || indexes == NULL) {
+ pidx = construct_BWT(T, B, bucket_A, bucket_B, n, m);
+ } else {
+ pidx = construct_BWT_indexes(T, B, bucket_A, bucket_B, n, m, num_indexes, indexes);
+ }
+
+ /* Copy to output string. */
+ U[0] = T[n - 1];
+ for(i = 0; i < pidx; ++i) { U[i + 1] = (unsigned char)B[i]; }
+ for(i += 1; i < n; ++i) { U[i] = (unsigned char)B[i]; }
+ pidx += 1;
+ } else {
+ pidx = -2;
+ }
+
+ free(bucket_B);
+ free(bucket_A);
+ if(A == NULL) { free(B); }
+
+ return pidx;
+}
diff --git a/Utilities/cmzstd/lib/dictBuilder/divsufsort.h b/Utilities/cmzstd/lib/dictBuilder/divsufsort.h
new file mode 100644
index 000000000..5440994af
--- /dev/null
+++ b/Utilities/cmzstd/lib/dictBuilder/divsufsort.h
@@ -0,0 +1,67 @@
+/*
+ * divsufsort.h for libdivsufsort-lite
+ * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _DIVSUFSORT_H
+#define _DIVSUFSORT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/*- Prototypes -*/
+
+/**
+ * Constructs the suffix array of a given string.
+ * @param T [0..n-1] The input string.
+ * @param SA [0..n-1] The output array of suffixes.
+ * @param n The length of the given string.
+ * @param openMP enables OpenMP optimization.
+ * @return 0 if no error occurred, -1 or -2 otherwise.
+ */
+int
+divsufsort(const unsigned char *T, int *SA, int n, int openMP);
+
+/**
+ * Constructs the burrows-wheeler transformed string of a given string.
+ * @param T [0..n-1] The input string.
+ * @param U [0..n-1] The output string. (can be T)
+ * @param A [0..n-1] The temporary array. (can be NULL)
+ * @param n The length of the given string.
+ * @param num_indexes The length of secondary indexes array. (can be NULL)
+ * @param indexes The secondary indexes array. (can be NULL)
+ * @param openMP enables OpenMP optimization.
+ * @return The primary index if no error occurred, -1 or -2 otherwise.
+ */
+int
+divbwt(const unsigned char *T, unsigned char *U, int *A, int n, unsigned char * num_indexes, int * indexes, int openMP);
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
+
+#endif /* _DIVSUFSORT_H */
diff --git a/Utilities/cmzstd/lib/dictBuilder/fastcover.c b/Utilities/cmzstd/lib/dictBuilder/fastcover.c
new file mode 100644
index 000000000..c289c0690
--- /dev/null
+++ b/Utilities/cmzstd/lib/dictBuilder/fastcover.c
@@ -0,0 +1,728 @@
+/*-*************************************
+* Dependencies
+***************************************/
+#include <stdio.h> /* fprintf */
+#include <stdlib.h> /* malloc, free, qsort */
+#include <string.h> /* memset */
+#include <time.h> /* clock */
+
+#include "mem.h" /* read */
+#include "pool.h"
+#include "threading.h"
+#include "cover.h"
+#include "zstd_internal.h" /* includes zstd.h */
+#ifndef ZDICT_STATIC_LINKING_ONLY
+#define ZDICT_STATIC_LINKING_ONLY
+#endif
+#include "zdict.h"
+
+
+/*-*************************************
+* Constants
+***************************************/
+#define FASTCOVER_MAX_SAMPLES_SIZE (sizeof(size_t) == 8 ? ((unsigned)-1) : ((unsigned)1 GB))
+#define FASTCOVER_MAX_F 31
+#define FASTCOVER_MAX_ACCEL 10
+#define DEFAULT_SPLITPOINT 0.75
+#define DEFAULT_F 20
+#define DEFAULT_ACCEL 1
+
+
+/*-*************************************
+* Console display
+***************************************/
+static int g_displayLevel = 2;
+#define DISPLAY(...) \
+ { \
+ fprintf(stderr, __VA_ARGS__); \
+ fflush(stderr); \
+ }
+#define LOCALDISPLAYLEVEL(displayLevel, l, ...) \
+ if (displayLevel >= l) { \
+ DISPLAY(__VA_ARGS__); \
+ } /* 0 : no display; 1: errors; 2: default; 3: details; 4: debug */
+#define DISPLAYLEVEL(l, ...) LOCALDISPLAYLEVEL(g_displayLevel, l, __VA_ARGS__)
+
+#define LOCALDISPLAYUPDATE(displayLevel, l, ...) \
+ if (displayLevel >= l) { \
+ if ((clock() - g_time > refreshRate) || (displayLevel >= 4)) { \
+ g_time = clock(); \
+ DISPLAY(__VA_ARGS__); \
+ } \
+ }
+#define DISPLAYUPDATE(l, ...) LOCALDISPLAYUPDATE(g_displayLevel, l, __VA_ARGS__)
+static const clock_t refreshRate = CLOCKS_PER_SEC * 15 / 100;
+static clock_t g_time = 0;
+
+
+/*-*************************************
+* Hash Functions
+***************************************/
+static const U64 prime6bytes = 227718039650203ULL;
+static size_t ZSTD_hash6(U64 u, U32 h) { return (size_t)(((u << (64-48)) * prime6bytes) >> (64-h)) ; }
+static size_t ZSTD_hash6Ptr(const void* p, U32 h) { return ZSTD_hash6(MEM_readLE64(p), h); }
+
+static const U64 prime8bytes = 0xCF1BBCDCB7A56463ULL;
+static size_t ZSTD_hash8(U64 u, U32 h) { return (size_t)(((u) * prime8bytes) >> (64-h)) ; }
+static size_t ZSTD_hash8Ptr(const void* p, U32 h) { return ZSTD_hash8(MEM_readLE64(p), h); }
+
+
+/**
+ * Hash the d-byte value pointed to by p and mod 2^f
+ */
+static size_t FASTCOVER_hashPtrToIndex(const void* p, U32 h, unsigned d) {
+ if (d == 6) {
+ return ZSTD_hash6Ptr(p, h) & ((1 << h) - 1);
+ }
+ return ZSTD_hash8Ptr(p, h) & ((1 << h) - 1);
+}
+
+
+/*-*************************************
+* Acceleration
+***************************************/
+typedef struct {
+ unsigned finalize; /* Percentage of training samples used for ZDICT_finalizeDictionary */
+ unsigned skip; /* Number of dmer skipped between each dmer counted in computeFrequency */
+} FASTCOVER_accel_t;
+
+
+static const FASTCOVER_accel_t FASTCOVER_defaultAccelParameters[FASTCOVER_MAX_ACCEL+1] = {
+ { 100, 0 }, /* accel = 0, should not happen because accel = 0 defaults to accel = 1 */
+ { 100, 0 }, /* accel = 1 */
+ { 50, 1 }, /* accel = 2 */
+ { 34, 2 }, /* accel = 3 */
+ { 25, 3 }, /* accel = 4 */
+ { 20, 4 }, /* accel = 5 */
+ { 17, 5 }, /* accel = 6 */
+ { 14, 6 }, /* accel = 7 */
+ { 13, 7 }, /* accel = 8 */
+ { 11, 8 }, /* accel = 9 */
+ { 10, 9 }, /* accel = 10 */
+};
+
+
+/*-*************************************
+* Context
+***************************************/
+typedef struct {
+ const BYTE *samples;
+ size_t *offsets;
+ const size_t *samplesSizes;
+ size_t nbSamples;
+ size_t nbTrainSamples;
+ size_t nbTestSamples;
+ size_t nbDmers;
+ U32 *freqs;
+ unsigned d;
+ unsigned f;
+ FASTCOVER_accel_t accelParams;
+} FASTCOVER_ctx_t;
+
+
+/*-*************************************
+* Helper functions
+***************************************/
+/**
+ * Selects the best segment in an epoch.
+ * Segments of are scored according to the function:
+ *
+ * Let F(d) be the frequency of all dmers with hash value d.
+ * Let S_i be hash value of the dmer at position i of segment S which has length k.
+ *
+ * Score(S) = F(S_1) + F(S_2) + ... + F(S_{k-d+1})
+ *
+ * Once the dmer with hash value d is in the dictionay we set F(d) = 0.
+ */
+static COVER_segment_t FASTCOVER_selectSegment(const FASTCOVER_ctx_t *ctx,
+ U32 *freqs, U32 begin, U32 end,
+ ZDICT_cover_params_t parameters,
+ U16* segmentFreqs) {
+ /* Constants */
+ const U32 k = parameters.k;
+ const U32 d = parameters.d;
+ const U32 f = ctx->f;
+ const U32 dmersInK = k - d + 1;
+
+ /* Try each segment (activeSegment) and save the best (bestSegment) */
+ COVER_segment_t bestSegment = {0, 0, 0};
+ COVER_segment_t activeSegment;
+
+ /* Reset the activeDmers in the segment */
+ /* The activeSegment starts at the beginning of the epoch. */
+ activeSegment.begin = begin;
+ activeSegment.end = begin;
+ activeSegment.score = 0;
+
+ /* Slide the activeSegment through the whole epoch.
+ * Save the best segment in bestSegment.
+ */
+ while (activeSegment.end < end) {
+ /* Get hash value of current dmer */
+ const size_t idx = FASTCOVER_hashPtrToIndex(ctx->samples + activeSegment.end, f, d);
+
+ /* Add frequency of this index to score if this is the first occurence of index in active segment */
+ if (segmentFreqs[idx] == 0) {
+ activeSegment.score += freqs[idx];
+ }
+ /* Increment end of segment and segmentFreqs*/
+ activeSegment.end += 1;
+ segmentFreqs[idx] += 1;
+ /* If the window is now too large, drop the first position */
+ if (activeSegment.end - activeSegment.begin == dmersInK + 1) {
+ /* Get hash value of the dmer to be eliminated from active segment */
+ const size_t delIndex = FASTCOVER_hashPtrToIndex(ctx->samples + activeSegment.begin, f, d);
+ segmentFreqs[delIndex] -= 1;
+ /* Subtract frequency of this index from score if this is the last occurrence of this index in active segment */
+ if (segmentFreqs[delIndex] == 0) {
+ activeSegment.score -= freqs[delIndex];
+ }
+ /* Increment start of segment */
+ activeSegment.begin += 1;
+ }
+
+ /* If this segment is the best so far save it */
+ if (activeSegment.score > bestSegment.score) {
+ bestSegment = activeSegment;
+ }
+ }
+
+ /* Zero out rest of segmentFreqs array */
+ while (activeSegment.begin < end) {
+ const size_t delIndex = FASTCOVER_hashPtrToIndex(ctx->samples + activeSegment.begin, f, d);
+ segmentFreqs[delIndex] -= 1;
+ activeSegment.begin += 1;
+ }
+
+ {
+ /* Zero the frequency of hash value of each dmer covered by the chosen segment. */
+ U32 pos;
+ for (pos = bestSegment.begin; pos != bestSegment.end; ++pos) {
+ const size_t i = FASTCOVER_hashPtrToIndex(ctx->samples + pos, f, d);
+ freqs[i] = 0;
+ }
+ }
+
+ return bestSegment;
+}
+
+
+static int FASTCOVER_checkParameters(ZDICT_cover_params_t parameters,
+ size_t maxDictSize, unsigned f,
+ unsigned accel) {
+ /* k, d, and f are required parameters */
+ if (parameters.d == 0 || parameters.k == 0) {
+ return 0;
+ }
+ /* d has to be 6 or 8 */
+ if (parameters.d != 6 && parameters.d != 8) {
+ return 0;
+ }
+ /* k <= maxDictSize */
+ if (parameters.k > maxDictSize) {
+ return 0;
+ }
+ /* d <= k */
+ if (parameters.d > parameters.k) {
+ return 0;
+ }
+ /* 0 < f <= FASTCOVER_MAX_F*/
+ if (f > FASTCOVER_MAX_F || f == 0) {
+ return 0;
+ }
+ /* 0 < splitPoint <= 1 */
+ if (parameters.splitPoint <= 0 || parameters.splitPoint > 1) {
+ return 0;
+ }
+ /* 0 < accel <= 10 */
+ if (accel > 10 || accel == 0) {
+ return 0;
+ }
+ return 1;
+}
+
+
+/**
+ * Clean up a context initialized with `FASTCOVER_ctx_init()`.
+ */
+static void
+FASTCOVER_ctx_destroy(FASTCOVER_ctx_t* ctx)
+{
+ if (!ctx) return;
+
+ free(ctx->freqs);
+ ctx->freqs = NULL;
+
+ free(ctx->offsets);
+ ctx->offsets = NULL;
+}
+
+
+/**
+ * Calculate for frequency of hash value of each dmer in ctx->samples
+ */
+static void
+FASTCOVER_computeFrequency(U32* freqs, const FASTCOVER_ctx_t* ctx)
+{
+ const unsigned f = ctx->f;
+ const unsigned d = ctx->d;
+ const unsigned skip = ctx->accelParams.skip;
+ const unsigned readLength = MAX(d, 8);
+ size_t i;
+ assert(ctx->nbTrainSamples >= 5);
+ assert(ctx->nbTrainSamples <= ctx->nbSamples);
+ for (i = 0; i < ctx->nbTrainSamples; i++) {
+ size_t start = ctx->offsets[i]; /* start of current dmer */
+ size_t const currSampleEnd = ctx->offsets[i+1];
+ while (start + readLength <= currSampleEnd) {
+ const size_t dmerIndex = FASTCOVER_hashPtrToIndex(ctx->samples + start, f, d);
+ freqs[dmerIndex]++;
+ start = start + skip + 1;
+ }
+ }
+}
+
+
+/**
+ * Prepare a context for dictionary building.
+ * The context is only dependent on the parameter `d` and can used multiple
+ * times.
+ * Returns 1 on success or zero on error.
+ * The context must be destroyed with `FASTCOVER_ctx_destroy()`.
+ */
+static int
+FASTCOVER_ctx_init(FASTCOVER_ctx_t* ctx,
+ const void* samplesBuffer,
+ const size_t* samplesSizes, unsigned nbSamples,
+ unsigned d, double splitPoint, unsigned f,
+ FASTCOVER_accel_t accelParams)
+{
+ const BYTE* const samples = (const BYTE*)samplesBuffer;
+ const size_t totalSamplesSize = COVER_sum(samplesSizes, nbSamples);
+ /* Split samples into testing and training sets */
+ const unsigned nbTrainSamples = splitPoint < 1.0 ? (unsigned)((double)nbSamples * splitPoint) : nbSamples;
+ const unsigned nbTestSamples = splitPoint < 1.0 ? nbSamples - nbTrainSamples : nbSamples;
+ const size_t trainingSamplesSize = splitPoint < 1.0 ? COVER_sum(samplesSizes, nbTrainSamples) : totalSamplesSize;
+ const size_t testSamplesSize = splitPoint < 1.0 ? COVER_sum(samplesSizes + nbTrainSamples, nbTestSamples) : totalSamplesSize;
+
+ /* Checks */
+ if (totalSamplesSize < MAX(d, sizeof(U64)) ||
+ totalSamplesSize >= (size_t)FASTCOVER_MAX_SAMPLES_SIZE) {
+ DISPLAYLEVEL(1, "Total samples size is too large (%u MB), maximum size is %u MB\n",
+ (unsigned)(totalSamplesSize >> 20), (FASTCOVER_MAX_SAMPLES_SIZE >> 20));
+ return 0;
+ }
+
+ /* Check if there are at least 5 training samples */
+ if (nbTrainSamples < 5) {
+ DISPLAYLEVEL(1, "Total number of training samples is %u and is invalid\n", nbTrainSamples);
+ return 0;
+ }
+
+ /* Check if there's testing sample */
+ if (nbTestSamples < 1) {
+ DISPLAYLEVEL(1, "Total number of testing samples is %u and is invalid.\n", nbTestSamples);
+ return 0;
+ }
+
+ /* Zero the context */
+ memset(ctx, 0, sizeof(*ctx));
+ DISPLAYLEVEL(2, "Training on %u samples of total size %u\n", nbTrainSamples,
+ (unsigned)trainingSamplesSize);
+ DISPLAYLEVEL(2, "Testing on %u samples of total size %u\n", nbTestSamples,
+ (unsigned)testSamplesSize);
+
+ ctx->samples = samples;
+ ctx->samplesSizes = samplesSizes;
+ ctx->nbSamples = nbSamples;
+ ctx->nbTrainSamples = nbTrainSamples;
+ ctx->nbTestSamples = nbTestSamples;
+ ctx->nbDmers = trainingSamplesSize - MAX(d, sizeof(U64)) + 1;
+ ctx->d = d;
+ ctx->f = f;
+ ctx->accelParams = accelParams;
+
+ /* The offsets of each file */
+ ctx->offsets = (size_t*)calloc((nbSamples + 1), sizeof(size_t));
+ if (ctx->offsets == NULL) {
+ DISPLAYLEVEL(1, "Failed to allocate scratch buffers \n");
+ FASTCOVER_ctx_destroy(ctx);
+ return 0;
+ }
+
+ /* Fill offsets from the samplesSizes */
+ { U32 i;
+ ctx->offsets[0] = 0;
+ assert(nbSamples >= 5);
+ for (i = 1; i <= nbSamples; ++i) {
+ ctx->offsets[i] = ctx->offsets[i - 1] + samplesSizes[i - 1];
+ }
+ }
+
+ /* Initialize frequency array of size 2^f */
+ ctx->freqs = (U32*)calloc(((U64)1 << f), sizeof(U32));
+ if (ctx->freqs == NULL) {
+ DISPLAYLEVEL(1, "Failed to allocate frequency table \n");
+ FASTCOVER_ctx_destroy(ctx);
+ return 0;
+ }
+
+ DISPLAYLEVEL(2, "Computing frequencies\n");
+ FASTCOVER_computeFrequency(ctx->freqs, ctx);
+
+ return 1;
+}
+
+
+/**
+ * Given the prepared context build the dictionary.
+ */
+static size_t
+FASTCOVER_buildDictionary(const FASTCOVER_ctx_t* ctx,
+ U32* freqs,
+ void* dictBuffer, size_t dictBufferCapacity,
+ ZDICT_cover_params_t parameters,
+ U16* segmentFreqs)
+{
+ BYTE *const dict = (BYTE *)dictBuffer;
+ size_t tail = dictBufferCapacity;
+ /* Divide the data up into epochs of equal size.
+ * We will select at least one segment from each epoch.
+ */
+ const unsigned epochs = MAX(1, (U32)(dictBufferCapacity / parameters.k));
+ const unsigned epochSize = (U32)(ctx->nbDmers / epochs);
+ size_t epoch;
+ DISPLAYLEVEL(2, "Breaking content into %u epochs of size %u\n",
+ epochs, epochSize);
+ /* Loop through the epochs until there are no more segments or the dictionary
+ * is full.
+ */
+ for (epoch = 0; tail > 0; epoch = (epoch + 1) % epochs) {
+ const U32 epochBegin = (U32)(epoch * epochSize);
+ const U32 epochEnd = epochBegin + epochSize;
+ size_t segmentSize;
+ /* Select a segment */
+ COVER_segment_t segment = FASTCOVER_selectSegment(
+ ctx, freqs, epochBegin, epochEnd, parameters, segmentFreqs);
+
+ /* If the segment covers no dmers, then we are out of content */
+ if (segment.score == 0) {
+ break;
+ }
+
+ /* Trim the segment if necessary and if it is too small then we are done */
+ segmentSize = MIN(segment.end - segment.begin + parameters.d - 1, tail);
+ if (segmentSize < parameters.d) {
+ break;
+ }
+
+ /* We fill the dictionary from the back to allow the best segments to be
+ * referenced with the smallest offsets.
+ */
+ tail -= segmentSize;
+ memcpy(dict + tail, ctx->samples + segment.begin, segmentSize);
+ DISPLAYUPDATE(
+ 2, "\r%u%% ",
+ (unsigned)(((dictBufferCapacity - tail) * 100) / dictBufferCapacity));
+ }
+ DISPLAYLEVEL(2, "\r%79s\r", "");
+ return tail;
+}
+
+
+/**
+ * Parameters for FASTCOVER_tryParameters().
+ */
+typedef struct FASTCOVER_tryParameters_data_s {
+ const FASTCOVER_ctx_t* ctx;
+ COVER_best_t* best;
+ size_t dictBufferCapacity;
+ ZDICT_cover_params_t parameters;
+} FASTCOVER_tryParameters_data_t;
+
+
+/**
+ * Tries a set of parameters and updates the COVER_best_t with the results.
+ * This function is thread safe if zstd is compiled with multithreaded support.
+ * It takes its parameters as an *OWNING* opaque pointer to support threading.
+ */
+static void FASTCOVER_tryParameters(void *opaque)
+{
+ /* Save parameters as local variables */
+ FASTCOVER_tryParameters_data_t *const data = (FASTCOVER_tryParameters_data_t *)opaque;
+ const FASTCOVER_ctx_t *const ctx = data->ctx;
+ const ZDICT_cover_params_t parameters = data->parameters;
+ size_t dictBufferCapacity = data->dictBufferCapacity;
+ size_t totalCompressedSize = ERROR(GENERIC);
+ /* Initialize array to keep track of frequency of dmer within activeSegment */
+ U16* segmentFreqs = (U16 *)calloc(((U64)1 << ctx->f), sizeof(U16));
+ /* Allocate space for hash table, dict, and freqs */
+ BYTE *const dict = (BYTE * const)malloc(dictBufferCapacity);
+ U32 *freqs = (U32*) malloc(((U64)1 << ctx->f) * sizeof(U32));
+ if (!segmentFreqs || !dict || !freqs) {
+ DISPLAYLEVEL(1, "Failed to allocate buffers: out of memory\n");
+ goto _cleanup;
+ }
+ /* Copy the frequencies because we need to modify them */
+ memcpy(freqs, ctx->freqs, ((U64)1 << ctx->f) * sizeof(U32));
+ /* Build the dictionary */
+ { const size_t tail = FASTCOVER_buildDictionary(ctx, freqs, dict, dictBufferCapacity,
+ parameters, segmentFreqs);
+ const unsigned nbFinalizeSamples = (unsigned)(ctx->nbTrainSamples * ctx->accelParams.finalize / 100);
+ dictBufferCapacity = ZDICT_finalizeDictionary(
+ dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail,
+ ctx->samples, ctx->samplesSizes, nbFinalizeSamples, parameters.zParams);
+ if (ZDICT_isError(dictBufferCapacity)) {
+ DISPLAYLEVEL(1, "Failed to finalize dictionary\n");
+ goto _cleanup;
+ }
+ }
+ /* Check total compressed size */
+ totalCompressedSize = COVER_checkTotalCompressedSize(parameters, ctx->samplesSizes,
+ ctx->samples, ctx->offsets,
+ ctx->nbTrainSamples, ctx->nbSamples,
+ dict, dictBufferCapacity);
+_cleanup:
+ COVER_best_finish(data->best, totalCompressedSize, parameters, dict,
+ dictBufferCapacity);
+ free(data);
+ free(segmentFreqs);
+ free(dict);
+ free(freqs);
+}
+
+
+static void
+FASTCOVER_convertToCoverParams(ZDICT_fastCover_params_t fastCoverParams,
+ ZDICT_cover_params_t* coverParams)
+{
+ coverParams->k = fastCoverParams.k;
+ coverParams->d = fastCoverParams.d;
+ coverParams->steps = fastCoverParams.steps;
+ coverParams->nbThreads = fastCoverParams.nbThreads;
+ coverParams->splitPoint = fastCoverParams.splitPoint;
+ coverParams->zParams = fastCoverParams.zParams;
+}
+
+
+static void
+FASTCOVER_convertToFastCoverParams(ZDICT_cover_params_t coverParams,
+ ZDICT_fastCover_params_t* fastCoverParams,
+ unsigned f, unsigned accel)
+{
+ fastCoverParams->k = coverParams.k;
+ fastCoverParams->d = coverParams.d;
+ fastCoverParams->steps = coverParams.steps;
+ fastCoverParams->nbThreads = coverParams.nbThreads;
+ fastCoverParams->splitPoint = coverParams.splitPoint;
+ fastCoverParams->f = f;
+ fastCoverParams->accel = accel;
+ fastCoverParams->zParams = coverParams.zParams;
+}
+
+
+ZDICTLIB_API size_t
+ZDICT_trainFromBuffer_fastCover(void* dictBuffer, size_t dictBufferCapacity,
+ const void* samplesBuffer,
+ const size_t* samplesSizes, unsigned nbSamples,
+ ZDICT_fastCover_params_t parameters)
+{
+ BYTE* const dict = (BYTE*)dictBuffer;
+ FASTCOVER_ctx_t ctx;
+ ZDICT_cover_params_t coverParams;
+ FASTCOVER_accel_t accelParams;
+ /* Initialize global data */
+ g_displayLevel = parameters.zParams.notificationLevel;
+ /* Assign splitPoint and f if not provided */
+ parameters.splitPoint = 1.0;
+ parameters.f = parameters.f == 0 ? DEFAULT_F : parameters.f;
+ parameters.accel = parameters.accel == 0 ? DEFAULT_ACCEL : parameters.accel;
+ /* Convert to cover parameter */
+ memset(&coverParams, 0 , sizeof(coverParams));
+ FASTCOVER_convertToCoverParams(parameters, &coverParams);
+ /* Checks */
+ if (!FASTCOVER_checkParameters(coverParams, dictBufferCapacity, parameters.f,
+ parameters.accel)) {
+ DISPLAYLEVEL(1, "FASTCOVER parameters incorrect\n");
+ return ERROR(GENERIC);
+ }
+ if (nbSamples == 0) {
+ DISPLAYLEVEL(1, "FASTCOVER must have at least one input file\n");
+ return ERROR(GENERIC);
+ }
+ if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) {
+ DISPLAYLEVEL(1, "dictBufferCapacity must be at least %u\n",
+ ZDICT_DICTSIZE_MIN);
+ return ERROR(dstSize_tooSmall);
+ }
+ /* Assign corresponding FASTCOVER_accel_t to accelParams*/
+ accelParams = FASTCOVER_defaultAccelParameters[parameters.accel];
+ /* Initialize context */
+ if (!FASTCOVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples,
+ coverParams.d, parameters.splitPoint, parameters.f,
+ accelParams)) {
+ DISPLAYLEVEL(1, "Failed to initialize context\n");
+ return ERROR(GENERIC);
+ }
+ /* Build the dictionary */
+ DISPLAYLEVEL(2, "Building dictionary\n");
+ {
+ /* Initialize array to keep track of frequency of dmer within activeSegment */
+ U16* segmentFreqs = (U16 *)calloc(((U64)1 << parameters.f), sizeof(U16));
+ const size_t tail = FASTCOVER_buildDictionary(&ctx, ctx.freqs, dictBuffer,
+ dictBufferCapacity, coverParams, segmentFreqs);
+ const unsigned nbFinalizeSamples = (unsigned)(ctx.nbTrainSamples * ctx.accelParams.finalize / 100);
+ const size_t dictionarySize = ZDICT_finalizeDictionary(
+ dict, dictBufferCapacity, dict + tail, dictBufferCapacity - tail,
+ samplesBuffer, samplesSizes, nbFinalizeSamples, coverParams.zParams);
+ if (!ZSTD_isError(dictionarySize)) {
+ DISPLAYLEVEL(2, "Constructed dictionary of size %u\n",
+ (unsigned)dictionarySize);
+ }
+ FASTCOVER_ctx_destroy(&ctx);
+ free(segmentFreqs);
+ return dictionarySize;
+ }
+}
+
+
+ZDICTLIB_API size_t
+ZDICT_optimizeTrainFromBuffer_fastCover(
+ void* dictBuffer, size_t dictBufferCapacity,
+ const void* samplesBuffer,
+ const size_t* samplesSizes, unsigned nbSamples,
+ ZDICT_fastCover_params_t* parameters)
+{
+ ZDICT_cover_params_t coverParams;
+ FASTCOVER_accel_t accelParams;
+ /* constants */
+ const unsigned nbThreads = parameters->nbThreads;
+ const double splitPoint =
+ parameters->splitPoint <= 0.0 ? DEFAULT_SPLITPOINT : parameters->splitPoint;
+ const unsigned kMinD = parameters->d == 0 ? 6 : parameters->d;
+ const unsigned kMaxD = parameters->d == 0 ? 8 : parameters->d;
+ const unsigned kMinK = parameters->k == 0 ? 50 : parameters->k;
+ const unsigned kMaxK = parameters->k == 0 ? 2000 : parameters->k;
+ const unsigned kSteps = parameters->steps == 0 ? 40 : parameters->steps;
+ const unsigned kStepSize = MAX((kMaxK - kMinK) / kSteps, 1);
+ const unsigned kIterations =
+ (1 + (kMaxD - kMinD) / 2) * (1 + (kMaxK - kMinK) / kStepSize);
+ const unsigned f = parameters->f == 0 ? DEFAULT_F : parameters->f;
+ const unsigned accel = parameters->accel == 0 ? DEFAULT_ACCEL : parameters->accel;
+ /* Local variables */
+ const int displayLevel = parameters->zParams.notificationLevel;
+ unsigned iteration = 1;
+ unsigned d;
+ unsigned k;
+ COVER_best_t best;
+ POOL_ctx *pool = NULL;
+ /* Checks */
+ if (splitPoint <= 0 || splitPoint > 1) {
+ LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect splitPoint\n");
+ return ERROR(GENERIC);
+ }
+ if (accel == 0 || accel > FASTCOVER_MAX_ACCEL) {
+ LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect accel\n");
+ return ERROR(GENERIC);
+ }
+ if (kMinK < kMaxD || kMaxK < kMinK) {
+ LOCALDISPLAYLEVEL(displayLevel, 1, "Incorrect k\n");
+ return ERROR(GENERIC);
+ }
+ if (nbSamples == 0) {
+ LOCALDISPLAYLEVEL(displayLevel, 1, "FASTCOVER must have at least one input file\n");
+ return ERROR(GENERIC);
+ }
+ if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) {
+ LOCALDISPLAYLEVEL(displayLevel, 1, "dictBufferCapacity must be at least %u\n",
+ ZDICT_DICTSIZE_MIN);
+ return ERROR(dstSize_tooSmall);
+ }
+ if (nbThreads > 1) {
+ pool = POOL_create(nbThreads, 1);
+ if (!pool) {
+ return ERROR(memory_allocation);
+ }
+ }
+ /* Initialization */
+ COVER_best_init(&best);
+ memset(&coverParams, 0 , sizeof(coverParams));
+ FASTCOVER_convertToCoverParams(*parameters, &coverParams);
+ accelParams = FASTCOVER_defaultAccelParameters[accel];
+ /* Turn down global display level to clean up display at level 2 and below */
+ g_displayLevel = displayLevel == 0 ? 0 : displayLevel - 1;
+ /* Loop through d first because each new value needs a new context */
+ LOCALDISPLAYLEVEL(displayLevel, 2, "Trying %u different sets of parameters\n",
+ kIterations);
+ for (d = kMinD; d <= kMaxD; d += 2) {
+ /* Initialize the context for this value of d */
+ FASTCOVER_ctx_t ctx;
+ LOCALDISPLAYLEVEL(displayLevel, 3, "d=%u\n", d);
+ if (!FASTCOVER_ctx_init(&ctx, samplesBuffer, samplesSizes, nbSamples, d, splitPoint, f, accelParams)) {
+ LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to initialize context\n");
+ COVER_best_destroy(&best);
+ POOL_free(pool);
+ return ERROR(GENERIC);
+ }
+ /* Loop through k reusing the same context */
+ for (k = kMinK; k <= kMaxK; k += kStepSize) {
+ /* Prepare the arguments */
+ FASTCOVER_tryParameters_data_t *data = (FASTCOVER_tryParameters_data_t *)malloc(
+ sizeof(FASTCOVER_tryParameters_data_t));
+ LOCALDISPLAYLEVEL(displayLevel, 3, "k=%u\n", k);
+ if (!data) {
+ LOCALDISPLAYLEVEL(displayLevel, 1, "Failed to allocate parameters\n");
+ COVER_best_destroy(&best);
+ FASTCOVER_ctx_destroy(&ctx);
+ POOL_free(pool);
+ return ERROR(GENERIC);
+ }
+ data->ctx = &ctx;
+ data->best = &best;
+ data->dictBufferCapacity = dictBufferCapacity;
+ data->parameters = coverParams;
+ data->parameters.k = k;
+ data->parameters.d = d;
+ data->parameters.splitPoint = splitPoint;
+ data->parameters.steps = kSteps;
+ data->parameters.zParams.notificationLevel = g_displayLevel;
+ /* Check the parameters */
+ if (!FASTCOVER_checkParameters(data->parameters, dictBufferCapacity,
+ data->ctx->f, accel)) {
+ DISPLAYLEVEL(1, "FASTCOVER parameters incorrect\n");
+ free(data);
+ continue;
+ }
+ /* Call the function and pass ownership of data to it */
+ COVER_best_start(&best);
+ if (pool) {
+ POOL_add(pool, &FASTCOVER_tryParameters, data);
+ } else {
+ FASTCOVER_tryParameters(data);
+ }
+ /* Print status */
+ LOCALDISPLAYUPDATE(displayLevel, 2, "\r%u%% ",
+ (unsigned)((iteration * 100) / kIterations));
+ ++iteration;
+ }
+ COVER_best_wait(&best);
+ FASTCOVER_ctx_destroy(&ctx);
+ }
+ LOCALDISPLAYLEVEL(displayLevel, 2, "\r%79s\r", "");
+ /* Fill the output buffer and parameters with output of the best parameters */
+ {
+ const size_t dictSize = best.dictSize;
+ if (ZSTD_isError(best.compressedSize)) {
+ const size_t compressedSize = best.compressedSize;
+ COVER_best_destroy(&best);
+ POOL_free(pool);
+ return compressedSize;
+ }
+ FASTCOVER_convertToFastCoverParams(best.parameters, parameters, f, accel);
+ memcpy(dictBuffer, best.dict, dictSize);
+ COVER_best_destroy(&best);
+ POOL_free(pool);
+ return dictSize;
+ }
+
+}
diff --git a/Utilities/cmzstd/lib/dictBuilder/zdict.c b/Utilities/cmzstd/lib/dictBuilder/zdict.c
new file mode 100644
index 000000000..c753da0db
--- /dev/null
+++ b/Utilities/cmzstd/lib/dictBuilder/zdict.c
@@ -0,0 +1,1111 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+
+/*-**************************************
+* Tuning parameters
+****************************************/
+#define MINRATIO 4 /* minimum nb of apparition to be selected in dictionary */
+#define ZDICT_MAX_SAMPLES_SIZE (2000U << 20)
+#define ZDICT_MIN_SAMPLES_SIZE (ZDICT_CONTENTSIZE_MIN * MINRATIO)
+
+
+/*-**************************************
+* Compiler Options
+****************************************/
+/* Unix Large Files support (>4GB) */
+#define _FILE_OFFSET_BITS 64
+#if (defined(__sun__) && (!defined(__LP64__))) /* Sun Solaris 32-bits requires specific definitions */
+# define _LARGEFILE_SOURCE
+#elif ! defined(__LP64__) /* No point defining Large file for 64 bit */
+# define _LARGEFILE64_SOURCE
+#endif
+
+
+/*-*************************************
+* Dependencies
+***************************************/
+#include <stdlib.h> /* malloc, free */
+#include <string.h> /* memset */
+#include <stdio.h> /* fprintf, fopen, ftello64 */
+#include <time.h> /* clock */
+
+#include "mem.h" /* read */
+#include "fse.h" /* FSE_normalizeCount, FSE_writeNCount */
+#define HUF_STATIC_LINKING_ONLY
+#include "huf.h" /* HUF_buildCTable, HUF_writeCTable */
+#include "zstd_internal.h" /* includes zstd.h */
+#include "xxhash.h" /* XXH64 */
+#include "divsufsort.h"
+#ifndef ZDICT_STATIC_LINKING_ONLY
+# define ZDICT_STATIC_LINKING_ONLY
+#endif
+#include "zdict.h"
+
+
+/*-*************************************
+* Constants
+***************************************/
+#define KB *(1 <<10)
+#define MB *(1 <<20)
+#define GB *(1U<<30)
+
+#define DICTLISTSIZE_DEFAULT 10000
+
+#define NOISELENGTH 32
+
+static const int g_compressionLevel_default = 3;
+static const U32 g_selectivity_default = 9;
+
+
+/*-*************************************
+* Console display
+***************************************/
+#define DISPLAY(...) { fprintf(stderr, __VA_ARGS__); fflush( stderr ); }
+#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(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 */
+ DISPLAY("%c", c);
+ }
+}
+
+
+/*-********************************************************
+* Helper functions
+**********************************************************/
+unsigned ZDICT_isError(size_t errorCode) { return ERR_isError(errorCode); }
+
+const char* ZDICT_getErrorName(size_t errorCode) { return ERR_getErrorName(errorCode); }
+
+unsigned ZDICT_getDictID(const void* dictBuffer, size_t dictSize)
+{
+ if (dictSize < 8) return 0;
+ if (MEM_readLE32(dictBuffer) != ZSTD_MAGIC_DICTIONARY) return 0;
+ return MEM_readLE32((const char*)dictBuffer + 4);
+}
+
+
+/*-********************************************************
+* Dictionary training functions
+**********************************************************/
+static unsigned ZDICT_NbCommonBytes (size_t val)
+{
+ if (MEM_isLittleEndian()) {
+ if (MEM_64bits()) {
+# if defined(_MSC_VER) && defined(_WIN64)
+ unsigned long r = 0;
+ _BitScanForward64( &r, (U64)val );
+ return (unsigned)(r>>3);
+# elif defined(__GNUC__) && (__GNUC__ >= 3)
+ return (__builtin_ctzll((U64)val) >> 3);
+# else
+ static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 };
+ return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
+# endif
+ } else { /* 32 bits */
+# if defined(_MSC_VER)
+ unsigned long r=0;
+ _BitScanForward( &r, (U32)val );
+ return (unsigned)(r>>3);
+# elif defined(__GNUC__) && (__GNUC__ >= 3)
+ return (__builtin_ctz((U32)val) >> 3);
+# else
+ static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 };
+ return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
+# endif
+ }
+ } else { /* Big Endian CPU */
+ if (MEM_64bits()) {
+# if defined(_MSC_VER) && defined(_WIN64)
+ unsigned long r = 0;
+ _BitScanReverse64( &r, val );
+ return (unsigned)(r>>3);
+# elif defined(__GNUC__) && (__GNUC__ >= 3)
+ return (__builtin_clzll(val) >> 3);
+# else
+ unsigned r;
+ const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */
+ if (!(val>>n32)) { r=4; } else { r=0; val>>=n32; }
+ if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
+ r += (!val);
+ return r;
+# endif
+ } else { /* 32 bits */
+# if defined(_MSC_VER)
+ unsigned long r = 0;
+ _BitScanReverse( &r, (unsigned long)val );
+ return (unsigned)(r>>3);
+# elif defined(__GNUC__) && (__GNUC__ >= 3)
+ return (__builtin_clz((U32)val) >> 3);
+# else
+ unsigned r;
+ if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
+ r += (!val);
+ return r;
+# endif
+ } }
+}
+
+
+/*! ZDICT_count() :
+ Count the nb of common bytes between 2 pointers.
+ Note : this function presumes end of buffer followed by noisy guard band.
+*/
+static size_t ZDICT_count(const void* pIn, const void* pMatch)
+{
+ const char* const pStart = (const char*)pIn;
+ for (;;) {
+ size_t const diff = MEM_readST(pMatch) ^ MEM_readST(pIn);
+ if (!diff) {
+ pIn = (const char*)pIn+sizeof(size_t);
+ pMatch = (const char*)pMatch+sizeof(size_t);
+ continue;
+ }
+ pIn = (const char*)pIn+ZDICT_NbCommonBytes(diff);
+ return (size_t)((const char*)pIn - pStart);
+ }
+}
+
+
+typedef struct {
+ U32 pos;
+ U32 length;
+ U32 savings;
+} dictItem;
+
+static void ZDICT_initDictItem(dictItem* d)
+{
+ d->pos = 1;
+ d->length = 0;
+ d->savings = (U32)(-1);
+}
+
+
+#define LLIMIT 64 /* heuristic determined experimentally */
+#define MINMATCHLENGTH 7 /* heuristic determined experimentally */
+static dictItem ZDICT_analyzePos(
+ BYTE* doneMarks,
+ const int* suffix, U32 start,
+ const void* buffer, U32 minRatio, U32 notificationLevel)
+{
+ U32 lengthList[LLIMIT] = {0};
+ U32 cumulLength[LLIMIT] = {0};
+ U32 savings[LLIMIT] = {0};
+ const BYTE* b = (const BYTE*)buffer;
+ size_t maxLength = LLIMIT;
+ size_t pos = suffix[start];
+ U32 end = start;
+ dictItem solution;
+
+ /* init */
+ memset(&solution, 0, sizeof(solution));
+ doneMarks[pos] = 1;
+
+ /* trivial repetition cases */
+ if ( (MEM_read16(b+pos+0) == MEM_read16(b+pos+2))
+ ||(MEM_read16(b+pos+1) == MEM_read16(b+pos+3))
+ ||(MEM_read16(b+pos+2) == MEM_read16(b+pos+4)) ) {
+ /* skip and mark segment */
+ U16 const pattern16 = MEM_read16(b+pos+4);
+ U32 u, patternEnd = 6;
+ while (MEM_read16(b+pos+patternEnd) == pattern16) patternEnd+=2 ;
+ if (b[pos+patternEnd] == b[pos+patternEnd-1]) patternEnd++;
+ for (u=1; u<patternEnd; u++)
+ doneMarks[pos+u] = 1;
+ return solution;
+ }
+
+ /* look forward */
+ { size_t length;
+ do {
+ end++;
+ length = ZDICT_count(b + pos, b + suffix[end]);
+ } while (length >= MINMATCHLENGTH);
+ }
+
+ /* look backward */
+ { size_t length;
+ do {
+ length = ZDICT_count(b + pos, b + *(suffix+start-1));
+ if (length >=MINMATCHLENGTH) start--;
+ } while(length >= MINMATCHLENGTH);
+ }
+
+ /* exit if not found a minimum nb of repetitions */
+ if (end-start < minRatio) {
+ U32 idx;
+ for(idx=start; idx<end; idx++)
+ doneMarks[suffix[idx]] = 1;
+ return solution;
+ }
+
+ { int i;
+ U32 mml;
+ U32 refinedStart = start;
+ U32 refinedEnd = end;
+
+ DISPLAYLEVEL(4, "\n");
+ DISPLAYLEVEL(4, "found %3u matches of length >= %i at pos %7u ", (unsigned)(end-start), MINMATCHLENGTH, (unsigned)pos);
+ DISPLAYLEVEL(4, "\n");
+
+ for (mml = MINMATCHLENGTH ; ; mml++) {
+ BYTE currentChar = 0;
+ U32 currentCount = 0;
+ U32 currentID = refinedStart;
+ U32 id;
+ U32 selectedCount = 0;
+ U32 selectedID = currentID;
+ for (id =refinedStart; id < refinedEnd; id++) {
+ if (b[suffix[id] + mml] != currentChar) {
+ if (currentCount > selectedCount) {
+ selectedCount = currentCount;
+ selectedID = currentID;
+ }
+ currentID = id;
+ currentChar = b[ suffix[id] + mml];
+ currentCount = 0;
+ }
+ currentCount ++;
+ }
+ if (currentCount > selectedCount) { /* for last */
+ selectedCount = currentCount;
+ selectedID = currentID;
+ }
+
+ if (selectedCount < minRatio)
+ break;
+ refinedStart = selectedID;
+ refinedEnd = refinedStart + selectedCount;
+ }
+
+ /* evaluate gain based on new dict */
+ start = refinedStart;
+ pos = suffix[refinedStart];
+ end = start;
+ memset(lengthList, 0, sizeof(lengthList));
+
+ /* look forward */
+ { size_t length;
+ do {
+ end++;
+ length = ZDICT_count(b + pos, b + suffix[end]);
+ if (length >= LLIMIT) length = LLIMIT-1;
+ lengthList[length]++;
+ } while (length >=MINMATCHLENGTH);
+ }
+
+ /* look backward */
+ { size_t length = MINMATCHLENGTH;
+ while ((length >= MINMATCHLENGTH) & (start > 0)) {
+ length = ZDICT_count(b + pos, b + suffix[start - 1]);
+ if (length >= LLIMIT) length = LLIMIT - 1;
+ lengthList[length]++;
+ if (length >= MINMATCHLENGTH) start--;
+ }
+ }
+
+ /* largest useful length */
+ memset(cumulLength, 0, sizeof(cumulLength));
+ cumulLength[maxLength-1] = lengthList[maxLength-1];
+ for (i=(int)(maxLength-2); i>=0; i--)
+ cumulLength[i] = cumulLength[i+1] + lengthList[i];
+
+ for (i=LLIMIT-1; i>=MINMATCHLENGTH; i--) if (cumulLength[i]>=minRatio) break;
+ maxLength = i;
+
+ /* reduce maxLength in case of final into repetitive data */
+ { U32 l = (U32)maxLength;
+ BYTE const c = b[pos + maxLength-1];
+ while (b[pos+l-2]==c) l--;
+ maxLength = l;
+ }
+ if (maxLength < MINMATCHLENGTH) return solution; /* skip : no long-enough solution */
+
+ /* calculate savings */
+ savings[5] = 0;
+ for (i=MINMATCHLENGTH; i<=(int)maxLength; i++)
+ savings[i] = savings[i-1] + (lengthList[i] * (i-3));
+
+ DISPLAYLEVEL(4, "Selected dict at position %u, of length %u : saves %u (ratio: %.2f) \n",
+ (unsigned)pos, (unsigned)maxLength, (unsigned)savings[maxLength], (double)savings[maxLength] / maxLength);
+
+ solution.pos = (U32)pos;
+ solution.length = (U32)maxLength;
+ solution.savings = savings[maxLength];
+
+ /* mark positions done */
+ { U32 id;
+ for (id=start; id<end; id++) {
+ U32 p, pEnd, length;
+ U32 const testedPos = suffix[id];
+ if (testedPos == pos)
+ length = solution.length;
+ else {
+ length = (U32)ZDICT_count(b+pos, b+testedPos);
+ if (length > solution.length) length = solution.length;
+ }
+ pEnd = (U32)(testedPos + length);
+ for (p=testedPos; p<pEnd; p++)
+ doneMarks[p] = 1;
+ } } }
+
+ return solution;
+}
+
+
+static int isIncluded(const void* in, const void* container, size_t length)
+{
+ const char* const ip = (const char*) in;
+ const char* const into = (const char*) container;
+ size_t u;
+
+ for (u=0; u<length; u++) { /* works because end of buffer is a noisy guard band */
+ if (ip[u] != into[u]) break;
+ }
+
+ return u==length;
+}
+
+/*! ZDICT_tryMerge() :
+ check if dictItem can be merged, do it if possible
+ @return : id of destination elt, 0 if not merged
+*/
+static U32 ZDICT_tryMerge(dictItem* table, dictItem elt, U32 eltNbToSkip, const void* buffer)
+{
+ const U32 tableSize = table->pos;
+ const U32 eltEnd = elt.pos + elt.length;
+ const char* const buf = (const char*) buffer;
+
+ /* tail overlap */
+ U32 u; for (u=1; u<tableSize; u++) {
+ if (u==eltNbToSkip) continue;
+ if ((table[u].pos > elt.pos) && (table[u].pos <= eltEnd)) { /* overlap, existing > new */
+ /* append */
+ U32 const addedLength = table[u].pos - elt.pos;
+ table[u].length += addedLength;
+ table[u].pos = elt.pos;
+ table[u].savings += elt.savings * addedLength / elt.length; /* rough approx */
+ table[u].savings += elt.length / 8; /* rough approx bonus */
+ elt = table[u];
+ /* sort : improve rank */
+ while ((u>1) && (table[u-1].savings < elt.savings))
+ table[u] = table[u-1], u--;
+ table[u] = elt;
+ return u;
+ } }
+
+ /* front overlap */
+ for (u=1; u<tableSize; u++) {
+ if (u==eltNbToSkip) continue;
+
+ if ((table[u].pos + table[u].length >= elt.pos) && (table[u].pos < elt.pos)) { /* overlap, existing < new */
+ /* append */
+ int const addedLength = (int)eltEnd - (table[u].pos + table[u].length);
+ table[u].savings += elt.length / 8; /* rough approx bonus */
+ if (addedLength > 0) { /* otherwise, elt fully included into existing */
+ table[u].length += addedLength;
+ table[u].savings += elt.savings * addedLength / elt.length; /* rough approx */
+ }
+ /* sort : improve rank */
+ elt = table[u];
+ while ((u>1) && (table[u-1].savings < elt.savings))
+ table[u] = table[u-1], u--;
+ table[u] = elt;
+ return u;
+ }
+
+ if (MEM_read64(buf + table[u].pos) == MEM_read64(buf + elt.pos + 1)) {
+ if (isIncluded(buf + table[u].pos, buf + elt.pos + 1, table[u].length)) {
+ size_t const addedLength = MAX( (int)elt.length - (int)table[u].length , 1 );
+ table[u].pos = elt.pos;
+ table[u].savings += (U32)(elt.savings * addedLength / elt.length);
+ table[u].length = MIN(elt.length, table[u].length + 1);
+ return u;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static void ZDICT_removeDictItem(dictItem* table, U32 id)
+{
+ /* convention : table[0].pos stores nb of elts */
+ U32 const max = table[0].pos;
+ U32 u;
+ if (!id) return; /* protection, should never happen */
+ for (u=id; u<max-1; u++)
+ table[u] = table[u+1];
+ table->pos--;
+}
+
+
+static void ZDICT_insertDictItem(dictItem* table, U32 maxSize, dictItem elt, const void* buffer)
+{
+ /* merge if possible */
+ U32 mergeId = ZDICT_tryMerge(table, elt, 0, buffer);
+ if (mergeId) {
+ U32 newMerge = 1;
+ while (newMerge) {
+ newMerge = ZDICT_tryMerge(table, table[mergeId], mergeId, buffer);
+ if (newMerge) ZDICT_removeDictItem(table, mergeId);
+ mergeId = newMerge;
+ }
+ return;
+ }
+
+ /* insert */
+ { U32 current;
+ U32 nextElt = table->pos;
+ if (nextElt >= maxSize) nextElt = maxSize-1;
+ current = nextElt-1;
+ while (table[current].savings < elt.savings) {
+ table[current+1] = table[current];
+ current--;
+ }
+ table[current+1] = elt;
+ table->pos = nextElt+1;
+ }
+}
+
+
+static U32 ZDICT_dictSize(const dictItem* dictList)
+{
+ U32 u, dictSize = 0;
+ for (u=1; u<dictList[0].pos; u++)
+ dictSize += dictList[u].length;
+ return dictSize;
+}
+
+
+static size_t ZDICT_trainBuffer_legacy(dictItem* dictList, U32 dictListSize,
+ const void* const buffer, size_t bufferSize, /* buffer must end with noisy guard band */
+ const size_t* fileSizes, unsigned nbFiles,
+ unsigned minRatio, U32 notificationLevel)
+{
+ int* const suffix0 = (int*)malloc((bufferSize+2)*sizeof(*suffix0));
+ int* const suffix = suffix0+1;
+ U32* reverseSuffix = (U32*)malloc((bufferSize)*sizeof(*reverseSuffix));
+ BYTE* doneMarks = (BYTE*)malloc((bufferSize+16)*sizeof(*doneMarks)); /* +16 for overflow security */
+ U32* filePos = (U32*)malloc(nbFiles * sizeof(*filePos));
+ 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(stderr); } }
+
+ /* init */
+ DISPLAYLEVEL(2, "\r%70s\r", ""); /* clean display line */
+ if (!suffix0 || !reverseSuffix || !doneMarks || !filePos) {
+ result = ERROR(memory_allocation);
+ goto _cleanup;
+ }
+ if (minRatio < MINRATIO) minRatio = MINRATIO;
+ memset(doneMarks, 0, bufferSize+16);
+
+ /* limit sample set size (divsufsort limitation)*/
+ if (bufferSize > ZDICT_MAX_SAMPLES_SIZE) DISPLAYLEVEL(3, "sample set too large : reduced to %u MB ...\n", (unsigned)(ZDICT_MAX_SAMPLES_SIZE>>20));
+ while (bufferSize > ZDICT_MAX_SAMPLES_SIZE) bufferSize -= fileSizes[--nbFiles];
+
+ /* sort */
+ DISPLAYLEVEL(2, "sorting %u files of total size %u MB ...\n", nbFiles, (unsigned)(bufferSize>>20));
+ { int const divSuftSortResult = divsufsort((const unsigned char*)buffer, suffix, (int)bufferSize, 0);
+ if (divSuftSortResult != 0) { result = ERROR(GENERIC); goto _cleanup; }
+ }
+ suffix[bufferSize] = (int)bufferSize; /* leads into noise */
+ suffix0[0] = (int)bufferSize; /* leads into noise */
+ /* build reverse suffix sort */
+ { size_t pos;
+ for (pos=0; pos < bufferSize; pos++)
+ reverseSuffix[suffix[pos]] = (U32)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]);
+ }
+
+ DISPLAYLEVEL(2, "finding patterns ... \n");
+ DISPLAYLEVEL(3, "minimum ratio : %u \n", minRatio);
+
+ { U32 cursor; for (cursor=0; cursor < bufferSize; ) {
+ dictItem solution;
+ if (doneMarks[cursor]) { cursor++; continue; }
+ solution = ZDICT_analyzePos(doneMarks, suffix, reverseSuffix[cursor], buffer, minRatio, notificationLevel);
+ if (solution.length==0) { cursor++; continue; }
+ ZDICT_insertDictItem(dictList, dictListSize, solution, buffer);
+ cursor += solution.length;
+ DISPLAYUPDATE(2, "\r%4.2f %% \r", (double)cursor / bufferSize * 100);
+ } }
+
+_cleanup:
+ free(suffix0);
+ free(reverseSuffix);
+ free(doneMarks);
+ free(filePos);
+ return result;
+}
+
+
+static void ZDICT_fillNoise(void* buffer, size_t length)
+{
+ unsigned const prime1 = 2654435761U;
+ unsigned const prime2 = 2246822519U;
+ unsigned acc = prime1;
+ size_t p=0;;
+ for (p=0; p<length; p++) {
+ acc *= prime2;
+ ((unsigned char*)buffer)[p] = (unsigned char)(acc >> 21);
+ }
+}
+
+
+typedef struct
+{
+ ZSTD_CDict* dict; /* dictionary */
+ ZSTD_CCtx* zc; /* working context */
+ void* workPlace; /* must be ZSTD_BLOCKSIZE_MAX allocated */
+} EStats_ress_t;
+
+#define MAXREPOFFSET 1024
+
+static void ZDICT_countEStats(EStats_ress_t esr, ZSTD_parameters params,
+ unsigned* countLit, unsigned* offsetcodeCount, unsigned* matchlengthCount, unsigned* litlengthCount, U32* repOffsets,
+ const void* src, size_t srcSize,
+ U32 notificationLevel)
+{
+ size_t const blockSizeMax = MIN (ZSTD_BLOCKSIZE_MAX, 1 << params.cParams.windowLog);
+ size_t cSize;
+
+ if (srcSize > blockSizeMax) srcSize = blockSizeMax; /* protection vs large samples */
+ { size_t const errorCode = ZSTD_compressBegin_usingCDict(esr.zc, esr.dict);
+ if (ZSTD_isError(errorCode)) { DISPLAYLEVEL(1, "warning : ZSTD_compressBegin_usingCDict failed \n"); return; }
+
+ }
+ cSize = ZSTD_compressBlock(esr.zc, esr.workPlace, ZSTD_BLOCKSIZE_MAX, src, srcSize);
+ if (ZSTD_isError(cSize)) { DISPLAYLEVEL(3, "warning : could not compress sample size %u \n", (unsigned)srcSize); return; }
+
+ if (cSize) { /* if == 0; block is not compressible */
+ const seqStore_t* const seqStorePtr = ZSTD_getSeqStore(esr.zc);
+
+ /* literals stats */
+ { const BYTE* bytePtr;
+ for(bytePtr = seqStorePtr->litStart; bytePtr < seqStorePtr->lit; bytePtr++)
+ countLit[*bytePtr]++;
+ }
+
+ /* seqStats */
+ { U32 const nbSeq = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart);
+ ZSTD_seqToCodes(seqStorePtr);
+
+ { const BYTE* codePtr = seqStorePtr->ofCode;
+ U32 u;
+ for (u=0; u<nbSeq; u++) offsetcodeCount[codePtr[u]]++;
+ }
+
+ { const BYTE* codePtr = seqStorePtr->mlCode;
+ U32 u;
+ for (u=0; u<nbSeq; u++) matchlengthCount[codePtr[u]]++;
+ }
+
+ { const BYTE* codePtr = seqStorePtr->llCode;
+ U32 u;
+ for (u=0; u<nbSeq; u++) litlengthCount[codePtr[u]]++;
+ }
+
+ if (nbSeq >= 2) { /* rep offsets */
+ const seqDef* const seq = seqStorePtr->sequencesStart;
+ U32 offset1 = seq[0].offset - 3;
+ U32 offset2 = seq[1].offset - 3;
+ if (offset1 >= MAXREPOFFSET) offset1 = 0;
+ if (offset2 >= MAXREPOFFSET) offset2 = 0;
+ repOffsets[offset1] += 3;
+ repOffsets[offset2] += 1;
+ } } }
+}
+
+static size_t ZDICT_totalSampleSize(const size_t* fileSizes, unsigned nbFiles)
+{
+ size_t total=0;
+ unsigned u;
+ for (u=0; u<nbFiles; u++) total += fileSizes[u];
+ return total;
+}
+
+typedef struct { U32 offset; U32 count; } offsetCount_t;
+
+static void ZDICT_insertSortCount(offsetCount_t table[ZSTD_REP_NUM+1], U32 val, U32 count)
+{
+ U32 u;
+ table[ZSTD_REP_NUM].offset = val;
+ table[ZSTD_REP_NUM].count = count;
+ for (u=ZSTD_REP_NUM; u>0; u--) {
+ offsetCount_t tmp;
+ if (table[u-1].count >= table[u].count) break;
+ tmp = table[u-1];
+ table[u-1] = table[u];
+ table[u] = tmp;
+ }
+}
+
+/* ZDICT_flatLit() :
+ * rewrite `countLit` to contain a mostly flat but still compressible distribution of literals.
+ * necessary to avoid generating a non-compressible distribution that HUF_writeCTable() cannot encode.
+ */
+static void ZDICT_flatLit(unsigned* countLit)
+{
+ int u;
+ for (u=1; u<256; u++) countLit[u] = 2;
+ countLit[0] = 4;
+ countLit[253] = 1;
+ countLit[254] = 1;
+}
+
+#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 notificationLevel)
+{
+ unsigned countLit[256];
+ HUF_CREATE_STATIC_CTABLE(hufTable, 255);
+ unsigned offcodeCount[OFFCODE_MAX+1];
+ short offcodeNCount[OFFCODE_MAX+1];
+ U32 offcodeMax = ZSTD_highbit32((U32)(dictBufferSize + 128 KB));
+ unsigned matchLengthCount[MaxML+1];
+ short matchLengthNCount[MaxML+1];
+ unsigned litLengthCount[MaxLL+1];
+ short litLengthNCount[MaxLL+1];
+ U32 repOffset[MAXREPOFFSET];
+ offsetCount_t bestRepOffset[ZSTD_REP_NUM+1];
+ EStats_ress_t esr = { NULL, NULL, NULL };
+ ZSTD_parameters params;
+ U32 u, huffLog = 11, Offlog = OffFSELog, mlLog = MLFSELog, llLog = LLFSELog, total;
+ size_t pos = 0, errorCode;
+ size_t eSize = 0;
+ size_t const totalSrcSize = ZDICT_totalSampleSize(fileSizes, nbFiles);
+ size_t const averageSampleSize = totalSrcSize / (nbFiles + !nbFiles);
+ BYTE* dstPtr = (BYTE*)dstBuffer;
+
+ /* init */
+ DEBUGLOG(4, "ZDICT_analyzeEntropy");
+ if (offcodeMax>OFFCODE_MAX) { eSize = ERROR(dictionaryCreation_failed); goto _cleanup; } /* too large dictionary */
+ for (u=0; u<256; u++) countLit[u] = 1; /* any character must be described */
+ for (u=0; u<=offcodeMax; u++) offcodeCount[u] = 1;
+ for (u=0; u<=MaxML; u++) matchLengthCount[u] = 1;
+ for (u=0; u<=MaxLL; u++) litLengthCount[u] = 1;
+ memset(repOffset, 0, sizeof(repOffset));
+ repOffset[1] = repOffset[4] = repOffset[8] = 1;
+ memset(bestRepOffset, 0, sizeof(bestRepOffset));
+ if (compressionLevel==0) compressionLevel = g_compressionLevel_default;
+ params = ZSTD_getParams(compressionLevel, averageSampleSize, dictBufferSize);
+
+ esr.dict = ZSTD_createCDict_advanced(dictBuffer, dictBufferSize, ZSTD_dlm_byRef, ZSTD_dct_rawContent, params.cParams, ZSTD_defaultCMem);
+ esr.zc = ZSTD_createCCtx();
+ esr.workPlace = malloc(ZSTD_BLOCKSIZE_MAX);
+ if (!esr.dict || !esr.zc || !esr.workPlace) {
+ eSize = ERROR(memory_allocation);
+ DISPLAYLEVEL(1, "Not enough memory \n");
+ goto _cleanup;
+ }
+
+ /* collect stats on all samples */
+ for (u=0; u<nbFiles; u++) {
+ ZDICT_countEStats(esr, params,
+ countLit, offcodeCount, matchLengthCount, litLengthCount, repOffset,
+ (const char*)srcBuffer + pos, fileSizes[u],
+ notificationLevel);
+ pos += fileSizes[u];
+ }
+
+ /* analyze, build stats, starting with literals */
+ { size_t maxNbBits = HUF_buildCTable (hufTable, countLit, 255, huffLog);
+ if (HUF_isError(maxNbBits)) {
+ eSize = ERROR(GENERIC);
+ DISPLAYLEVEL(1, " HUF_buildCTable error \n");
+ goto _cleanup;
+ }
+ if (maxNbBits==8) { /* not compressible : will fail on HUF_writeCTable() */
+ DISPLAYLEVEL(2, "warning : pathological dataset : literals are not compressible : samples are noisy or too regular \n");
+ ZDICT_flatLit(countLit); /* replace distribution by a fake "mostly flat but still compressible" distribution, that HUF_writeCTable() can encode */
+ maxNbBits = HUF_buildCTable (hufTable, countLit, 255, huffLog);
+ assert(maxNbBits==9);
+ }
+ huffLog = (U32)maxNbBits;
+ }
+
+ /* looking for most common first offsets */
+ { U32 offset;
+ for (offset=1; offset<MAXREPOFFSET; offset++)
+ ZDICT_insertSortCount(bestRepOffset, offset, repOffset[offset]);
+ }
+ /* note : the result of this phase should be used to better appreciate the impact on statistics */
+
+ total=0; for (u=0; u<=offcodeMax; u++) total+=offcodeCount[u];
+ errorCode = FSE_normalizeCount(offcodeNCount, Offlog, offcodeCount, total, offcodeMax);
+ if (FSE_isError(errorCode)) {
+ eSize = ERROR(GENERIC);
+ DISPLAYLEVEL(1, "FSE_normalizeCount error with offcodeCount \n");
+ goto _cleanup;
+ }
+ Offlog = (U32)errorCode;
+
+ total=0; for (u=0; u<=MaxML; u++) total+=matchLengthCount[u];
+ errorCode = FSE_normalizeCount(matchLengthNCount, mlLog, matchLengthCount, total, MaxML);
+ if (FSE_isError(errorCode)) {
+ eSize = ERROR(GENERIC);
+ DISPLAYLEVEL(1, "FSE_normalizeCount error with matchLengthCount \n");
+ goto _cleanup;
+ }
+ mlLog = (U32)errorCode;
+
+ total=0; for (u=0; u<=MaxLL; u++) total+=litLengthCount[u];
+ errorCode = FSE_normalizeCount(litLengthNCount, llLog, litLengthCount, total, MaxLL);
+ if (FSE_isError(errorCode)) {
+ eSize = ERROR(GENERIC);
+ DISPLAYLEVEL(1, "FSE_normalizeCount error with litLengthCount \n");
+ goto _cleanup;
+ }
+ llLog = (U32)errorCode;
+
+ /* write result to buffer */
+ { size_t const hhSize = HUF_writeCTable(dstPtr, maxDstSize, hufTable, 255, huffLog);
+ if (HUF_isError(hhSize)) {
+ eSize = ERROR(GENERIC);
+ DISPLAYLEVEL(1, "HUF_writeCTable error \n");
+ goto _cleanup;
+ }
+ dstPtr += hhSize;
+ maxDstSize -= hhSize;
+ eSize += hhSize;
+ }
+
+ { size_t const ohSize = FSE_writeNCount(dstPtr, maxDstSize, offcodeNCount, OFFCODE_MAX, Offlog);
+ if (FSE_isError(ohSize)) {
+ eSize = ERROR(GENERIC);
+ DISPLAYLEVEL(1, "FSE_writeNCount error with offcodeNCount \n");
+ goto _cleanup;
+ }
+ dstPtr += ohSize;
+ maxDstSize -= ohSize;
+ eSize += ohSize;
+ }
+
+ { size_t const mhSize = FSE_writeNCount(dstPtr, maxDstSize, matchLengthNCount, MaxML, mlLog);
+ if (FSE_isError(mhSize)) {
+ eSize = ERROR(GENERIC);
+ DISPLAYLEVEL(1, "FSE_writeNCount error with matchLengthNCount \n");
+ goto _cleanup;
+ }
+ dstPtr += mhSize;
+ maxDstSize -= mhSize;
+ eSize += mhSize;
+ }
+
+ { size_t const lhSize = FSE_writeNCount(dstPtr, maxDstSize, litLengthNCount, MaxLL, llLog);
+ if (FSE_isError(lhSize)) {
+ eSize = ERROR(GENERIC);
+ DISPLAYLEVEL(1, "FSE_writeNCount error with litlengthNCount \n");
+ goto _cleanup;
+ }
+ dstPtr += lhSize;
+ maxDstSize -= lhSize;
+ eSize += lhSize;
+ }
+
+ if (maxDstSize<12) {
+ eSize = ERROR(GENERIC);
+ DISPLAYLEVEL(1, "not enough space to write RepOffsets \n");
+ goto _cleanup;
+ }
+# if 0
+ MEM_writeLE32(dstPtr+0, bestRepOffset[0].offset);
+ MEM_writeLE32(dstPtr+4, bestRepOffset[1].offset);
+ MEM_writeLE32(dstPtr+8, bestRepOffset[2].offset);
+#else
+ /* at this stage, we don't use the result of "most common first offset",
+ as the impact of statistics is not properly evaluated */
+ MEM_writeLE32(dstPtr+0, repStartValue[0]);
+ MEM_writeLE32(dstPtr+4, repStartValue[1]);
+ MEM_writeLE32(dstPtr+8, repStartValue[2]);
+#endif
+ eSize += 12;
+
+_cleanup:
+ ZSTD_freeCDict(esr.dict);
+ ZSTD_freeCCtx(esr.zc);
+ free(esr.workPlace);
+
+ return eSize;
+}
+
+
+
+size_t ZDICT_finalizeDictionary(void* dictBuffer, size_t dictBufferCapacity,
+ const void* customDictContent, size_t dictContentSize,
+ const void* samplesBuffer, const size_t* samplesSizes,
+ unsigned nbSamples, ZDICT_params_t params)
+{
+ size_t hSize;
+#define HBUFFSIZE 256 /* should prove large enough for all entropy headers */
+ BYTE header[HBUFFSIZE];
+ int const compressionLevel = (params.compressionLevel == 0) ? g_compressionLevel_default : params.compressionLevel;
+ U32 const notificationLevel = params.notificationLevel;
+
+ /* check conditions */
+ DEBUGLOG(4, "ZDICT_finalizeDictionary");
+ if (dictBufferCapacity < dictContentSize) return ERROR(dstSize_tooSmall);
+ if (dictContentSize < ZDICT_CONTENTSIZE_MIN) return ERROR(srcSize_wrong);
+ if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) return ERROR(dstSize_tooSmall);
+
+ /* dictionary header */
+ MEM_writeLE32(header, ZSTD_MAGIC_DICTIONARY);
+ { U64 const randomID = XXH64(customDictContent, dictContentSize, 0);
+ U32 const compliantID = (randomID % ((1U<<31)-32768)) + 32768;
+ U32 const dictID = params.dictID ? params.dictID : compliantID;
+ MEM_writeLE32(header+4, dictID);
+ }
+ hSize = 8;
+
+ /* entropy tables */
+ DISPLAYLEVEL(2, "\r%70s\r", ""); /* clean display line */
+ DISPLAYLEVEL(2, "statistics ... \n");
+ { size_t const eSize = ZDICT_analyzeEntropy(header+hSize, HBUFFSIZE-hSize,
+ compressionLevel,
+ samplesBuffer, samplesSizes, nbSamples,
+ customDictContent, dictContentSize,
+ notificationLevel);
+ if (ZDICT_isError(eSize)) return eSize;
+ hSize += eSize;
+ }
+
+ /* copy elements in final buffer ; note : src and dst buffer can overlap */
+ if (hSize + dictContentSize > dictBufferCapacity) dictContentSize = dictBufferCapacity - hSize;
+ { size_t const dictSize = hSize + dictContentSize;
+ char* dictEnd = (char*)dictBuffer + dictSize;
+ memmove(dictEnd - dictContentSize, customDictContent, dictContentSize);
+ memcpy(dictBuffer, header, hSize);
+ return dictSize;
+ }
+}
+
+
+static size_t ZDICT_addEntropyTablesFromBuffer_advanced(
+ void* dictBuffer, size_t dictContentSize, size_t dictBufferCapacity,
+ const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples,
+ ZDICT_params_t params)
+{
+ int const compressionLevel = (params.compressionLevel == 0) ? g_compressionLevel_default : params.compressionLevel;
+ U32 const notificationLevel = params.notificationLevel;
+ size_t hSize = 8;
+
+ /* calculate entropy tables */
+ DISPLAYLEVEL(2, "\r%70s\r", ""); /* clean display line */
+ DISPLAYLEVEL(2, "statistics ... \n");
+ { size_t const eSize = ZDICT_analyzeEntropy((char*)dictBuffer+hSize, dictBufferCapacity-hSize,
+ compressionLevel,
+ samplesBuffer, samplesSizes, nbSamples,
+ (char*)dictBuffer + dictBufferCapacity - dictContentSize, dictContentSize,
+ notificationLevel);
+ if (ZDICT_isError(eSize)) return eSize;
+ hSize += eSize;
+ }
+
+ /* add dictionary header (after entropy tables) */
+ MEM_writeLE32(dictBuffer, ZSTD_MAGIC_DICTIONARY);
+ { U64 const randomID = XXH64((char*)dictBuffer + dictBufferCapacity - dictContentSize, dictContentSize, 0);
+ U32 const compliantID = (randomID % ((1U<<31)-32768)) + 32768;
+ U32 const dictID = params.dictID ? params.dictID : compliantID;
+ MEM_writeLE32((char*)dictBuffer+4, dictID);
+ }
+
+ if (hSize + dictContentSize < dictBufferCapacity)
+ memmove((char*)dictBuffer + hSize, (char*)dictBuffer + dictBufferCapacity - dictContentSize, dictContentSize);
+ return MIN(dictBufferCapacity, hSize+dictContentSize);
+}
+
+/* Hidden declaration for dbio.c */
+size_t ZDICT_trainFromBuffer_unsafe_legacy(
+ void* dictBuffer, size_t maxDictSize,
+ const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples,
+ ZDICT_legacy_params_t params);
+/*! ZDICT_trainFromBuffer_unsafe_legacy() :
+* Warning : `samplesBuffer` must be followed by noisy guard band.
+* @return : size of dictionary, or an error code which can be tested with ZDICT_isError()
+*/
+size_t ZDICT_trainFromBuffer_unsafe_legacy(
+ void* dictBuffer, size_t maxDictSize,
+ const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples,
+ ZDICT_legacy_params_t params)
+{
+ U32 const dictListSize = MAX(MAX(DICTLISTSIZE_DEFAULT, nbSamples), (U32)(maxDictSize/16));
+ dictItem* const dictList = (dictItem*)malloc(dictListSize * sizeof(*dictList));
+ unsigned const selectivity = params.selectivityLevel == 0 ? g_selectivity_default : params.selectivityLevel;
+ unsigned const minRep = (selectivity > 30) ? MINRATIO : nbSamples >> selectivity;
+ size_t const targetDictSize = maxDictSize;
+ size_t const samplesBuffSize = ZDICT_totalSampleSize(samplesSizes, nbSamples);
+ size_t dictSize = 0;
+ U32 const notificationLevel = params.zParams.notificationLevel;
+
+ /* checks */
+ if (!dictList) return ERROR(memory_allocation);
+ if (maxDictSize < ZDICT_DICTSIZE_MIN) { free(dictList); return ERROR(dstSize_tooSmall); } /* requested dictionary size is too small */
+ if (samplesBuffSize < ZDICT_MIN_SAMPLES_SIZE) { free(dictList); return ERROR(dictionaryCreation_failed); } /* not enough source to create dictionary */
+
+ /* init */
+ ZDICT_initDictItem(dictList);
+
+ /* build dictionary */
+ ZDICT_trainBuffer_legacy(dictList, dictListSize,
+ samplesBuffer, samplesBuffSize,
+ samplesSizes, nbSamples,
+ minRep, notificationLevel);
+
+ /* display best matches */
+ if (params.zParams.notificationLevel>= 3) {
+ unsigned const nb = MIN(25, dictList[0].pos);
+ unsigned const dictContentSize = ZDICT_dictSize(dictList);
+ unsigned u;
+ DISPLAYLEVEL(3, "\n %u segments found, of total size %u \n", (unsigned)dictList[0].pos-1, dictContentSize);
+ DISPLAYLEVEL(3, "list %u best segments \n", nb-1);
+ for (u=1; u<nb; u++) {
+ unsigned const pos = dictList[u].pos;
+ unsigned const length = dictList[u].length;
+ U32 const printedLength = MIN(40, length);
+ if ((pos > samplesBuffSize) || ((pos + length) > samplesBuffSize)) {
+ free(dictList);
+ return ERROR(GENERIC); /* should never happen */
+ }
+ DISPLAYLEVEL(3, "%3u:%3u bytes at pos %8u, savings %7u bytes |",
+ u, length, pos, (unsigned)dictList[u].savings);
+ ZDICT_printHex((const char*)samplesBuffer+pos, printedLength);
+ DISPLAYLEVEL(3, "| \n");
+ } }
+
+
+ /* create dictionary */
+ { unsigned dictContentSize = ZDICT_dictSize(dictList);
+ if (dictContentSize < ZDICT_CONTENTSIZE_MIN) { free(dictList); return ERROR(dictionaryCreation_failed); } /* dictionary content too small */
+ if (dictContentSize < targetDictSize/4) {
+ DISPLAYLEVEL(2, "! warning : selected content significantly smaller than requested (%u < %u) \n", dictContentSize, (unsigned)maxDictSize);
+ if (samplesBuffSize < 10 * targetDictSize)
+ DISPLAYLEVEL(2, "! consider increasing the number of samples (total size : %u MB)\n", (unsigned)(samplesBuffSize>>20));
+ if (minRep > MINRATIO) {
+ DISPLAYLEVEL(2, "! consider increasing selectivity to produce larger dictionary (-s%u) \n", selectivity+1);
+ DISPLAYLEVEL(2, "! note : larger dictionaries are not necessarily better, test its efficiency on samples \n");
+ }
+ }
+
+ if ((dictContentSize > targetDictSize*3) && (nbSamples > 2*MINRATIO) && (selectivity>1)) {
+ unsigned proposedSelectivity = selectivity-1;
+ while ((nbSamples >> proposedSelectivity) <= MINRATIO) { proposedSelectivity--; }
+ DISPLAYLEVEL(2, "! note : calculated dictionary significantly larger than requested (%u > %u) \n", dictContentSize, (unsigned)maxDictSize);
+ DISPLAYLEVEL(2, "! consider increasing dictionary size, or produce denser dictionary (-s%u) \n", proposedSelectivity);
+ DISPLAYLEVEL(2, "! always test dictionary efficiency on real samples \n");
+ }
+
+ /* limit dictionary size */
+ { U32 const max = dictList->pos; /* convention : nb of useful elts within dictList */
+ U32 currentSize = 0;
+ U32 n; for (n=1; n<max; n++) {
+ currentSize += dictList[n].length;
+ if (currentSize > targetDictSize) { currentSize -= dictList[n].length; break; }
+ }
+ dictList->pos = n;
+ dictContentSize = currentSize;
+ }
+
+ /* build dict content */
+ { U32 u;
+ BYTE* ptr = (BYTE*)dictBuffer + maxDictSize;
+ for (u=1; u<dictList->pos; u++) {
+ U32 l = dictList[u].length;
+ ptr -= l;
+ if (ptr<(BYTE*)dictBuffer) { free(dictList); return ERROR(GENERIC); } /* should not happen */
+ memcpy(ptr, (const char*)samplesBuffer+dictList[u].pos, l);
+ } }
+
+ dictSize = ZDICT_addEntropyTablesFromBuffer_advanced(dictBuffer, dictContentSize, maxDictSize,
+ samplesBuffer, samplesSizes, nbSamples,
+ params.zParams);
+ }
+
+ /* clean up */
+ free(dictList);
+ return dictSize;
+}
+
+
+/* ZDICT_trainFromBuffer_legacy() :
+ * issue : samplesBuffer need to be followed by a noisy guard band.
+ * work around : duplicate the buffer, and add the noise */
+size_t ZDICT_trainFromBuffer_legacy(void* dictBuffer, size_t dictBufferCapacity,
+ const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples,
+ ZDICT_legacy_params_t params)
+{
+ size_t result;
+ void* newBuff;
+ size_t const sBuffSize = ZDICT_totalSampleSize(samplesSizes, nbSamples);
+ if (sBuffSize < ZDICT_MIN_SAMPLES_SIZE) return 0; /* not enough content => no dictionary */
+
+ newBuff = malloc(sBuffSize + NOISELENGTH);
+ if (!newBuff) return ERROR(memory_allocation);
+
+ memcpy(newBuff, samplesBuffer, sBuffSize);
+ ZDICT_fillNoise((char*)newBuff + sBuffSize, NOISELENGTH); /* guard band, for end of buffer condition */
+
+ result =
+ ZDICT_trainFromBuffer_unsafe_legacy(dictBuffer, dictBufferCapacity, newBuff,
+ samplesSizes, nbSamples, params);
+ free(newBuff);
+ return result;
+}
+
+
+size_t ZDICT_trainFromBuffer(void* dictBuffer, size_t dictBufferCapacity,
+ const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples)
+{
+ ZDICT_fastCover_params_t params;
+ DEBUGLOG(3, "ZDICT_trainFromBuffer");
+ memset(&params, 0, sizeof(params));
+ params.d = 8;
+ params.steps = 4;
+ /* Default to level 6 since no compression level information is available */
+ params.zParams.compressionLevel = 3;
+#if defined(DEBUGLEVEL) && (DEBUGLEVEL>=1)
+ params.zParams.notificationLevel = DEBUGLEVEL;
+#endif
+ return ZDICT_optimizeTrainFromBuffer_fastCover(dictBuffer, dictBufferCapacity,
+ samplesBuffer, samplesSizes, nbSamples,
+ &params);
+}
+
+size_t ZDICT_addEntropyTablesFromBuffer(void* dictBuffer, size_t dictContentSize, size_t dictBufferCapacity,
+ const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples)
+{
+ ZDICT_params_t params;
+ memset(&params, 0, sizeof(params));
+ return ZDICT_addEntropyTablesFromBuffer_advanced(dictBuffer, dictContentSize, dictBufferCapacity,
+ samplesBuffer, samplesSizes, nbSamples,
+ params);
+}
diff --git a/Utilities/cmzstd/lib/dictBuilder/zdict.h b/Utilities/cmzstd/lib/dictBuilder/zdict.h
new file mode 100644
index 000000000..d57d59f01
--- /dev/null
+++ b/Utilities/cmzstd/lib/dictBuilder/zdict.h
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#ifndef DICTBUILDER_H_001
+#define DICTBUILDER_H_001
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+
+/*====== Dependencies ======*/
+#include <stddef.h> /* size_t */
+
+
+/* ===== ZDICTLIB_API : control library symbols visibility ===== */
+#ifndef ZDICTLIB_VISIBILITY
+# if defined(__GNUC__) && (__GNUC__ >= 4)
+# define ZDICTLIB_VISIBILITY __attribute__ ((visibility ("default")))
+# else
+# define ZDICTLIB_VISIBILITY
+# endif
+#endif
+#if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1)
+# define ZDICTLIB_API __declspec(dllexport) ZDICTLIB_VISIBILITY
+#elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1)
+# define ZDICTLIB_API __declspec(dllimport) ZDICTLIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
+#else
+# define ZDICTLIB_API ZDICTLIB_VISIBILITY
+#endif
+
+
+/*! ZDICT_trainFromBuffer():
+ * Train a dictionary from an array of samples.
+ * Redirect towards ZDICT_optimizeTrainFromBuffer_fastCover() single-threaded, with d=8, steps=4,
+ * f=20, and accel=1.
+ * Samples must be stored concatenated in a single flat buffer `samplesBuffer`,
+ * supplied with an array of sizes `samplesSizes`, providing the size of each sample, in order.
+ * The resulting dictionary will be saved into `dictBuffer`.
+ * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`)
+ * or an error code, which can be tested with ZDICT_isError().
+ * Note: ZDICT_trainFromBuffer() requires about 9 bytes of memory for each input byte.
+ * Tips: In general, a reasonable dictionary has a size of ~ 100 KB.
+ * It's possible to select smaller or larger size, just by specifying `dictBufferCapacity`.
+ * In general, it's recommended to provide a few thousands samples, though this can vary a lot.
+ * It's recommended that total size of all samples be about ~x100 times the target size of dictionary.
+ */
+ZDICTLIB_API size_t ZDICT_trainFromBuffer(void* dictBuffer, size_t dictBufferCapacity,
+ const void* samplesBuffer,
+ const size_t* samplesSizes, unsigned nbSamples);
+
+
+/*====== Helper functions ======*/
+ZDICTLIB_API unsigned ZDICT_getDictID(const void* dictBuffer, size_t dictSize); /**< extracts dictID; @return zero if error (not a valid dictionary) */
+ZDICTLIB_API unsigned ZDICT_isError(size_t errorCode);
+ZDICTLIB_API const char* ZDICT_getErrorName(size_t errorCode);
+
+
+
+#ifdef ZDICT_STATIC_LINKING_ONLY
+
+/* ====================================================================================
+ * The definitions in this section are considered experimental.
+ * They should never be used with a dynamic library, as they may change in the future.
+ * They are provided for advanced usages.
+ * Use them only in association with static linking.
+ * ==================================================================================== */
+
+typedef struct {
+ int compressionLevel; /* optimize for a specific zstd compression level; 0 means default */
+ unsigned notificationLevel; /* Write log to stderr; 0 = none (default); 1 = errors; 2 = progression; 3 = details; 4 = debug; */
+ unsigned dictID; /* force dictID value; 0 means auto mode (32-bits random value) */
+} ZDICT_params_t;
+
+/*! ZDICT_cover_params_t:
+ * k and d are the only required parameters.
+ * For others, value 0 means default.
+ */
+typedef struct {
+ unsigned k; /* Segment size : constraint: 0 < k : Reasonable range [16, 2048+] */
+ unsigned d; /* dmer size : constraint: 0 < d <= k : Reasonable range [6, 16] */
+ unsigned steps; /* Number of steps : Only used for optimization : 0 means default (40) : Higher means more parameters checked */
+ unsigned nbThreads; /* Number of threads : constraint: 0 < nbThreads : 1 means single-threaded : Only used for optimization : Ignored if ZSTD_MULTITHREAD is not defined */
+ double splitPoint; /* Percentage of samples used for training: Only used for optimization : the first nbSamples * splitPoint samples will be used to training, the last nbSamples * (1 - splitPoint) samples will be used for testing, 0 means default (1.0), 1.0 when all samples are used for both training and testing */
+ ZDICT_params_t zParams;
+} ZDICT_cover_params_t;
+
+typedef struct {
+ unsigned k; /* Segment size : constraint: 0 < k : Reasonable range [16, 2048+] */
+ unsigned d; /* dmer size : constraint: 0 < d <= k : Reasonable range [6, 16] */
+ unsigned f; /* log of size of frequency array : constraint: 0 < f <= 31 : 1 means default(20)*/
+ unsigned steps; /* Number of steps : Only used for optimization : 0 means default (40) : Higher means more parameters checked */
+ unsigned nbThreads; /* Number of threads : constraint: 0 < nbThreads : 1 means single-threaded : Only used for optimization : Ignored if ZSTD_MULTITHREAD is not defined */
+ double splitPoint; /* Percentage of samples used for training: Only used for optimization : the first nbSamples * splitPoint samples will be used to training, the last nbSamples * (1 - splitPoint) samples will be used for testing, 0 means default (0.75), 1.0 when all samples are used for both training and testing */
+ unsigned accel; /* Acceleration level: constraint: 0 < accel <= 10, higher means faster and less accurate, 0 means default(1) */
+ ZDICT_params_t zParams;
+} ZDICT_fastCover_params_t;
+
+/*! ZDICT_trainFromBuffer_cover():
+ * Train a dictionary from an array of samples using the COVER algorithm.
+ * Samples must be stored concatenated in a single flat buffer `samplesBuffer`,
+ * supplied with an array of sizes `samplesSizes`, providing the size of each sample, in order.
+ * The resulting dictionary will be saved into `dictBuffer`.
+ * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`)
+ * or an error code, which can be tested with ZDICT_isError().
+ * Note: ZDICT_trainFromBuffer_cover() requires about 9 bytes of memory for each input byte.
+ * Tips: In general, a reasonable dictionary has a size of ~ 100 KB.
+ * It's possible to select smaller or larger size, just by specifying `dictBufferCapacity`.
+ * In general, it's recommended to provide a few thousands samples, though this can vary a lot.
+ * It's recommended that total size of all samples be about ~x100 times the target size of dictionary.
+ */
+ZDICTLIB_API size_t ZDICT_trainFromBuffer_cover(
+ void *dictBuffer, size_t dictBufferCapacity,
+ const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples,
+ ZDICT_cover_params_t parameters);
+
+/*! ZDICT_optimizeTrainFromBuffer_cover():
+ * The same requirements as above hold for all the parameters except `parameters`.
+ * This function tries many parameter combinations and picks the best parameters.
+ * `*parameters` is filled with the best parameters found,
+ * dictionary constructed with those parameters is stored in `dictBuffer`.
+ *
+ * All of the parameters d, k, steps are optional.
+ * If d is non-zero then we don't check multiple values of d, otherwise we check d = {6, 8}.
+ * if steps is zero it defaults to its default value.
+ * If k is non-zero then we don't check multiple values of k, otherwise we check steps values in [50, 2000].
+ *
+ * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`)
+ * or an error code, which can be tested with ZDICT_isError().
+ * On success `*parameters` contains the parameters selected.
+ * Note: ZDICT_optimizeTrainFromBuffer_cover() requires about 8 bytes of memory for each input byte and additionally another 5 bytes of memory for each byte of memory for each thread.
+ */
+ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_cover(
+ void* dictBuffer, size_t dictBufferCapacity,
+ const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples,
+ ZDICT_cover_params_t* parameters);
+
+/*! ZDICT_trainFromBuffer_fastCover():
+ * Train a dictionary from an array of samples using a modified version of COVER algorithm.
+ * Samples must be stored concatenated in a single flat buffer `samplesBuffer`,
+ * supplied with an array of sizes `samplesSizes`, providing the size of each sample, in order.
+ * d and k are required.
+ * All other parameters are optional, will use default values if not provided
+ * The resulting dictionary will be saved into `dictBuffer`.
+ * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`)
+ * or an error code, which can be tested with ZDICT_isError().
+ * Note: ZDICT_trainFromBuffer_fastCover() requires about 1 bytes of memory for each input byte and additionally another 6 * 2^f bytes of memory .
+ * Tips: In general, a reasonable dictionary has a size of ~ 100 KB.
+ * It's possible to select smaller or larger size, just by specifying `dictBufferCapacity`.
+ * In general, it's recommended to provide a few thousands samples, though this can vary a lot.
+ * It's recommended that total size of all samples be about ~x100 times the target size of dictionary.
+ */
+ZDICTLIB_API size_t ZDICT_trainFromBuffer_fastCover(void *dictBuffer,
+ size_t dictBufferCapacity, const void *samplesBuffer,
+ const size_t *samplesSizes, unsigned nbSamples,
+ ZDICT_fastCover_params_t parameters);
+
+/*! ZDICT_optimizeTrainFromBuffer_fastCover():
+ * The same requirements as above hold for all the parameters except `parameters`.
+ * This function tries many parameter combinations (specifically, k and d combinations)
+ * and picks the best parameters. `*parameters` is filled with the best parameters found,
+ * dictionary constructed with those parameters is stored in `dictBuffer`.
+ * All of the parameters d, k, steps, f, and accel are optional.
+ * If d is non-zero then we don't check multiple values of d, otherwise we check d = {6, 8}.
+ * if steps is zero it defaults to its default value.
+ * If k is non-zero then we don't check multiple values of k, otherwise we check steps values in [50, 2000].
+ * If f is zero, default value of 20 is used.
+ * If accel is zero, default value of 1 is used.
+ *
+ * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`)
+ * or an error code, which can be tested with ZDICT_isError().
+ * On success `*parameters` contains the parameters selected.
+ * Note: ZDICT_optimizeTrainFromBuffer_fastCover() requires about 1 byte of memory for each input byte and additionally another 6 * 2^f bytes of memory for each thread.
+ */
+ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_fastCover(void* dictBuffer,
+ size_t dictBufferCapacity, const void* samplesBuffer,
+ const size_t* samplesSizes, unsigned nbSamples,
+ ZDICT_fastCover_params_t* parameters);
+
+/*! ZDICT_finalizeDictionary():
+ * Given a custom content as a basis for dictionary, and a set of samples,
+ * finalize dictionary by adding headers and statistics.
+ *
+ * Samples must be stored concatenated in a flat buffer `samplesBuffer`,
+ * supplied with an array of sizes `samplesSizes`, providing the size of each sample in order.
+ *
+ * dictContentSize must be >= ZDICT_CONTENTSIZE_MIN bytes.
+ * maxDictSize must be >= dictContentSize, and must be >= ZDICT_DICTSIZE_MIN bytes.
+ *
+ * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`),
+ * or an error code, which can be tested by ZDICT_isError().
+ * Note: ZDICT_finalizeDictionary() will push notifications into stderr if instructed to, using notificationLevel>0.
+ * Note 2: dictBuffer and dictContent can overlap
+ */
+#define ZDICT_CONTENTSIZE_MIN 128
+#define ZDICT_DICTSIZE_MIN 256
+ZDICTLIB_API size_t ZDICT_finalizeDictionary(void* dictBuffer, size_t dictBufferCapacity,
+ const void* dictContent, size_t dictContentSize,
+ const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples,
+ ZDICT_params_t parameters);
+
+typedef struct {
+ unsigned selectivityLevel; /* 0 means default; larger => select more => larger dictionary */
+ ZDICT_params_t zParams;
+} ZDICT_legacy_params_t;
+
+/*! ZDICT_trainFromBuffer_legacy():
+ * Train a dictionary from an array of samples.
+ * Samples must be stored concatenated in a single flat buffer `samplesBuffer`,
+ * supplied with an array of sizes `samplesSizes`, providing the size of each sample, in order.
+ * The resulting dictionary will be saved into `dictBuffer`.
+ * `parameters` is optional and can be provided with values set to 0 to mean "default".
+ * @return: size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`)
+ * or an error code, which can be tested with ZDICT_isError().
+ * Tips: In general, a reasonable dictionary has a size of ~ 100 KB.
+ * It's possible to select smaller or larger size, just by specifying `dictBufferCapacity`.
+ * In general, it's recommended to provide a few thousands samples, though this can vary a lot.
+ * It's recommended that total size of all samples be about ~x100 times the target size of dictionary.
+ * Note: ZDICT_trainFromBuffer_legacy() will send notifications into stderr if instructed to, using notificationLevel>0.
+ */
+ZDICTLIB_API size_t ZDICT_trainFromBuffer_legacy(
+ void *dictBuffer, size_t dictBufferCapacity,
+ const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples,
+ ZDICT_legacy_params_t parameters);
+
+/* Deprecation warnings */
+/* It is generally possible to disable deprecation warnings from compiler,
+ for example with -Wno-deprecated-declarations for gcc
+ or _CRT_SECURE_NO_WARNINGS in Visual.
+ Otherwise, it's also possible to manually define ZDICT_DISABLE_DEPRECATE_WARNINGS */
+#ifdef ZDICT_DISABLE_DEPRECATE_WARNINGS
+# define ZDICT_DEPRECATED(message) ZDICTLIB_API /* disable deprecation warnings */
+#else
+# define ZDICT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
+# define ZDICT_DEPRECATED(message) [[deprecated(message)]] ZDICTLIB_API
+# elif (ZDICT_GCC_VERSION >= 405) || defined(__clang__)
+# define ZDICT_DEPRECATED(message) ZDICTLIB_API __attribute__((deprecated(message)))
+# elif (ZDICT_GCC_VERSION >= 301)
+# define ZDICT_DEPRECATED(message) ZDICTLIB_API __attribute__((deprecated))
+# elif defined(_MSC_VER)
+# define ZDICT_DEPRECATED(message) ZDICTLIB_API __declspec(deprecated(message))
+# else
+# pragma message("WARNING: You need to implement ZDICT_DEPRECATED for this compiler")
+# define ZDICT_DEPRECATED(message) ZDICTLIB_API
+# endif
+#endif /* ZDICT_DISABLE_DEPRECATE_WARNINGS */
+
+ZDICT_DEPRECATED("use ZDICT_finalizeDictionary() instead")
+size_t ZDICT_addEntropyTablesFromBuffer(void* dictBuffer, size_t dictContentSize, size_t dictBufferCapacity,
+ const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples);
+
+
+#endif /* ZDICT_STATIC_LINKING_ONLY */
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* DICTBUILDER_H_001 */
diff --git a/Utilities/cmzstd/lib/zstd.h b/Utilities/cmzstd/lib/zstd.h
new file mode 100644
index 000000000..b18fc8a44
--- /dev/null
+++ b/Utilities/cmzstd/lib/zstd.h
@@ -0,0 +1,1766 @@
+/*
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#ifndef ZSTD_H_235446
+#define ZSTD_H_235446
+
+/* ====== Dependency ======*/
+#include <stddef.h> /* size_t */
+
+
+/* ===== ZSTDLIB_API : control library symbols visibility ===== */
+#ifndef ZSTDLIB_VISIBILITY
+# if defined(__GNUC__) && (__GNUC__ >= 4)
+# define ZSTDLIB_VISIBILITY __attribute__ ((visibility ("default")))
+# else
+# define ZSTDLIB_VISIBILITY
+# endif
+#endif
+#if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1)
+# define ZSTDLIB_API __declspec(dllexport) ZSTDLIB_VISIBILITY
+#elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1)
+# define ZSTDLIB_API __declspec(dllimport) ZSTDLIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
+#else
+# define ZSTDLIB_API ZSTDLIB_VISIBILITY
+#endif
+
+
+/*******************************************************************************
+ Introduction
+
+ zstd, short for Zstandard, is a fast lossless compression algorithm, targeting
+ real-time compression scenarios at zlib-level and better compression ratios.
+ The zstd compression library provides in-memory compression and decompression
+ functions.
+
+ The library supports regular compression levels from 1 up to ZSTD_maxCLevel(),
+ which is currently 22. Levels >= 20, labeled `--ultra`, should be used with
+ caution, as they require more memory. The library also offers negative
+ compression levels, which extend the range of speed vs. ratio preferences.
+ The lower the level, the faster the speed (at the cost of compression).
+
+ Compression can be done in:
+ - a single step (described as Simple API)
+ - a single step, reusing a context (described as Explicit context)
+ - unbounded multiple steps (described as Streaming compression)
+
+ The compression ratio achievable on small data can be highly improved using
+ a dictionary. Dictionary compression can be performed in:
+ - a single step (described as Simple dictionary API)
+ - a single step, reusing a dictionary (described as Bulk-processing
+ dictionary API)
+
+ Advanced experimental functions can be accessed using
+ `#define ZSTD_STATIC_LINKING_ONLY` before including zstd.h.
+
+ Advanced experimental APIs should never be used with a dynamically-linked
+ library. They are not "stable"; their definitions or signatures may change in
+ the future. Only static linking is allowed.
+*******************************************************************************/
+
+/*------ Version ------*/
+#define ZSTD_VERSION_MAJOR 1
+#define ZSTD_VERSION_MINOR 3
+#define ZSTD_VERSION_RELEASE 8
+
+#define ZSTD_VERSION_NUMBER (ZSTD_VERSION_MAJOR *100*100 + ZSTD_VERSION_MINOR *100 + ZSTD_VERSION_RELEASE)
+ZSTDLIB_API unsigned ZSTD_versionNumber(void); /**< to check runtime library version */
+
+#define ZSTD_LIB_VERSION ZSTD_VERSION_MAJOR.ZSTD_VERSION_MINOR.ZSTD_VERSION_RELEASE
+#define ZSTD_QUOTE(str) #str
+#define ZSTD_EXPAND_AND_QUOTE(str) ZSTD_QUOTE(str)
+#define ZSTD_VERSION_STRING ZSTD_EXPAND_AND_QUOTE(ZSTD_LIB_VERSION)
+ZSTDLIB_API const char* ZSTD_versionString(void); /* requires v1.3.0+ */
+
+/***************************************
+* Default constant
+***************************************/
+#ifndef ZSTD_CLEVEL_DEFAULT
+# define ZSTD_CLEVEL_DEFAULT 3
+#endif
+
+/***************************************
+* Simple API
+***************************************/
+/*! ZSTD_compress() :
+ * Compresses `src` content as a single zstd compressed frame into already allocated `dst`.
+ * Hint : compression runs faster if `dstCapacity` >= `ZSTD_compressBound(srcSize)`.
+ * @return : compressed size written into `dst` (<= `dstCapacity),
+ * 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_decompress() :
+ * `compressedSize` : must be the _exact_ size of some number of compressed and/or skippable frames.
+ * `dstCapacity` is an upper bound of originalSize to regenerate.
+ * 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_getFrameContentSize() : requires v1.3.0+
+ * `src` should point to the start of a ZSTD encoded frame.
+ * `srcSize` must be at least as large as the frame header.
+ * hint : any size >= `ZSTD_frameHeaderSize_max` is large enough.
+ * @return : - decompressed size of `src` frame content, if known
+ * - ZSTD_CONTENTSIZE_UNKNOWN if the size cannot be determined
+ * - ZSTD_CONTENTSIZE_ERROR if an error occurred (e.g. invalid magic number, srcSize too small)
+ * note 1 : a 0 return value means the frame is valid but "empty".
+ * note 2 : decompressed size is an optional field, it may not be present, typically in streaming mode.
+ * When `return==ZSTD_CONTENTSIZE_UNKNOWN`, data to decompress could be any size.
+ * In which case, it's necessary to use streaming mode to decompress data.
+ * Optionally, application can rely on some implicit limit,
+ * as ZSTD_decompress() only needs an upper bound of decompressed size.
+ * (For example, data could be necessarily cut into blocks <= 16 KB).
+ * note 3 : decompressed size is always present when compression is completed using single-pass functions,
+ * such as ZSTD_compress(), ZSTD_compressCCtx() ZSTD_compress_usingDict() or ZSTD_compress_usingCDict().
+ * note 4 : 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 5 : If source is untrusted, decompressed size could be wrong or intentionally modified.
+ * Always ensure return value fits within application's authorized limits.
+ * Each application can set its own limits.
+ * note 6 : This function replaces ZSTD_getDecompressedSize() */
+#define ZSTD_CONTENTSIZE_UNKNOWN (0ULL - 1)
+#define ZSTD_CONTENTSIZE_ERROR (0ULL - 2)
+ZSTDLIB_API unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize);
+
+/*! ZSTD_getDecompressedSize() :
+ * NOTE: This function is now obsolete, in favor of ZSTD_getFrameContentSize().
+ * Both functions work the same way, but ZSTD_getDecompressedSize() blends
+ * "empty", "unknown" and "error" results to the same return value (0),
+ * while ZSTD_getFrameContentSize() gives them separate return values.
+ * @return : decompressed size of `src` frame content _if known and not empty_, 0 otherwise. */
+ZSTDLIB_API unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize);
+
+
+/*====== Helper functions ======*/
+#define ZSTD_COMPRESSBOUND(srcSize) ((srcSize) + ((srcSize)>>8) + (((srcSize) < (128<<10)) ? (((128<<10) - (srcSize)) >> 11) /* margin, from 64 to 0 */ : 0)) /* this formula ensures that bound(A) + bound(B) <= bound(A+B) as long as A and B >= 128 KB */
+ZSTDLIB_API size_t ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case single-pass scenario */
+ZSTDLIB_API unsigned ZSTD_isError(size_t code); /*!< tells if a `size_t` function result is an error code */
+ZSTDLIB_API const char* ZSTD_getErrorName(size_t code); /*!< provides readable string from an error code */
+ZSTDLIB_API int ZSTD_maxCLevel(void); /*!< maximum compression level available */
+
+
+/***************************************
+* Explicit context
+***************************************/
+/*= Compression context
+ * When compressing many times,
+ * it is recommended to allocate a context just once, and re-use it for each successive compression operation.
+ * This will make workload friendlier for system's memory.
+ * Use one context per thread for parallel execution in multi-threaded environments. */
+typedef struct ZSTD_CCtx_s ZSTD_CCtx;
+ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx(void);
+ZSTDLIB_API size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx);
+
+/*! ZSTD_compressCCtx() :
+ * Same as ZSTD_compress(), using an explicit ZSTD_CCtx
+ * The function will compress at requested compression level,
+ * ignoring any other parameter */
+ZSTDLIB_API size_t ZSTD_compressCCtx(ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ int compressionLevel);
+
+/*= Decompression context
+ * When decompressing many times,
+ * it is recommended to allocate a context only once,
+ * and re-use it for each successive compression operation.
+ * This will make workload friendlier for system's memory.
+ * Use one context per thread for parallel execution. */
+typedef struct ZSTD_DCtx_s ZSTD_DCtx;
+ZSTDLIB_API ZSTD_DCtx* ZSTD_createDCtx(void);
+ZSTDLIB_API size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx);
+
+/*! ZSTD_decompressDCtx() :
+ * Same as ZSTD_decompress(),
+ * requires an allocated ZSTD_DCtx.
+ * Compatible with sticky parameters.
+ */
+ZSTDLIB_API size_t ZSTD_decompressDCtx(ZSTD_DCtx* dctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize);
+
+
+/**************************
+* Simple dictionary API
+***************************/
+/*! ZSTD_compress_usingDict() :
+ * Compression at an explicit compression level using a Dictionary.
+ * A dictionary can be any arbitrary data segment (also called a prefix),
+ * or a buffer with specified information (see dictBuilder/zdict.h).
+ * Note : This function loads the dictionary, resulting in significant startup delay.
+ * It's intended for a dictionary used only once.
+ * Note 2 : When `dict == NULL || dictSize < 8` no dictionary is used. */
+ZSTDLIB_API size_t ZSTD_compress_usingDict(ZSTD_CCtx* ctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ const void* dict,size_t dictSize,
+ int compressionLevel);
+
+/*! ZSTD_decompress_usingDict() :
+ * Decompression using a known Dictionary.
+ * Dictionary must be identical to the one used during compression.
+ * Note : This function loads the dictionary, resulting in significant startup delay.
+ * It's intended for a dictionary used only once.
+ * Note : When `dict == NULL || dictSize < 8` no dictionary is used. */
+ZSTDLIB_API 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);
+
+
+/***********************************
+ * Bulk processing dictionary API
+ **********************************/
+typedef struct ZSTD_CDict_s ZSTD_CDict;
+
+/*! ZSTD_createCDict() :
+ * When compressing multiple messages / blocks using the same dictionary, it's recommended to load it only once.
+ * ZSTD_createCDict() will create a digested dictionary, ready to start future compression operations without startup cost.
+ * ZSTD_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only.
+ * `dictBuffer` can be released after ZSTD_CDict creation, because its content is copied within CDict.
+ * Consider experimental function `ZSTD_createCDict_byReference()` if you prefer to not duplicate `dictBuffer` content.
+ * Note : A ZSTD_CDict can be created from an empty dictBuffer, but it is inefficient when used to compress small data. */
+ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict(const void* dictBuffer, size_t dictSize,
+ int compressionLevel);
+
+/*! ZSTD_freeCDict() :
+ * Function frees memory allocated by ZSTD_createCDict(). */
+ZSTDLIB_API size_t ZSTD_freeCDict(ZSTD_CDict* CDict);
+
+/*! ZSTD_compress_usingCDict() :
+ * Compression using a digested Dictionary.
+ * Recommended when same dictionary is used multiple times.
+ * Note : compression level is _decided at dictionary creation time_,
+ * and frame parameters are hardcoded (dictID=yes, contentSize=yes, checksum=no) */
+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);
+
+
+typedef struct ZSTD_DDict_s ZSTD_DDict;
+
+/*! ZSTD_createDDict() :
+ * Create a digested dictionary, ready to start decompression operation without startup delay.
+ * dictBuffer can be released after DDict creation, as its content is copied inside DDict. */
+ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict(const void* dictBuffer, size_t dictSize);
+
+/*! ZSTD_freeDDict() :
+ * Function frees memory allocated with ZSTD_createDDict() */
+ZSTDLIB_API size_t ZSTD_freeDDict(ZSTD_DDict* ddict);
+
+/*! ZSTD_decompress_usingDDict() :
+ * Decompression using a digested Dictionary.
+ * Recommended when same dictionary is used multiple times. */
+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);
+
+
+/****************************
+* Streaming
+****************************/
+
+typedef struct ZSTD_inBuffer_s {
+ const void* src; /**< start of input buffer */
+ size_t size; /**< size of input buffer */
+ size_t pos; /**< position where reading stopped. Will be updated. Necessarily 0 <= pos <= size */
+} ZSTD_inBuffer;
+
+typedef struct ZSTD_outBuffer_s {
+ void* dst; /**< start of output buffer */
+ size_t size; /**< size of output buffer */
+ size_t pos; /**< position where writing stopped. Will be updated. Necessarily 0 <= pos <= size */
+} ZSTD_outBuffer;
+
+
+
+/*-***********************************************************************
+* Streaming compression - HowTo
+*
+* A ZSTD_CStream object is required to track streaming operation.
+* Use ZSTD_createCStream() and ZSTD_freeCStream() to create/release resources.
+* ZSTD_CStream objects can be reused multiple times on consecutive compression operations.
+* It is recommended to re-use ZSTD_CStream since it will play nicer with system's memory, by re-using already allocated memory.
+*
+* For parallel execution, use one separate ZSTD_CStream per thread.
+*
+* note : since v1.3.0, ZSTD_CStream and ZSTD_CCtx are the same thing.
+*
+* Parameters are sticky : when starting a new compression on the same context,
+* it will re-use the same sticky parameters as previous compression session.
+* When in doubt, it's recommended to fully initialize the context before usage.
+* Use ZSTD_initCStream() to set the parameter to a selected compression level.
+* Use advanced API (ZSTD_CCtx_setParameter(), etc.) to set more specific parameters.
+*
+* Use ZSTD_compressStream() as many times as necessary to consume input stream.
+* The function will automatically update both `pos` fields within `input` and `output`.
+* Note that the function may not consume the entire input,
+* for example, because the output buffer is already full,
+* in which case `input.pos < input.size`.
+* The caller must check if input has been entirely consumed.
+* If not, the caller must make some room to receive more compressed data,
+* and then present again remaining input data.
+* @return : a size hint, preferred nb of bytes to use as input for next function call
+* or an error code, which can be tested using ZSTD_isError().
+* Note 1 : it's just a hint, to help latency a little, any value will work fine.
+* Note 2 : size hint is guaranteed to be <= ZSTD_CStreamInSize()
+*
+* At any moment, it's possible to flush whatever data might remain stuck within internal buffer,
+* using ZSTD_flushStream(). `output->pos` will be updated.
+* Note that, if `output->size` is too small, a single invocation of ZSTD_flushStream() might not be enough (return code > 0).
+* In which case, make some room to receive more compressed data, and call again ZSTD_flushStream().
+* @return : 0 if internal buffers are entirely flushed,
+* >0 if some data still present within internal buffer (the value is minimal estimation of remaining size),
+* or an error code, which can be tested using ZSTD_isError().
+*
+* ZSTD_endStream() instructs to finish a frame.
+* It will perform a flush and write frame epilogue.
+* The epilogue is required for decoders to consider a frame completed.
+* flush() operation is the same, and follows same rules as ZSTD_flushStream().
+* @return : 0 if frame fully completed and fully flushed,
+* >0 if some data still present within internal buffer (the value is minimal estimation of remaining size),
+* or an error code, which can be tested using ZSTD_isError().
+*
+* *******************************************************************/
+
+typedef ZSTD_CCtx ZSTD_CStream; /**< CCtx and CStream are now effectively same object (>= v1.3.0) */
+ /* Continue to distinguish them for compatibility with older versions <= v1.2.0 */
+/*===== ZSTD_CStream management functions =====*/
+ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream(void);
+ZSTDLIB_API size_t ZSTD_freeCStream(ZSTD_CStream* zcs);
+
+/*===== Streaming compression functions =====*/
+ZSTDLIB_API size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel);
+ZSTDLIB_API size_t ZSTD_compressStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuffer* input);
+ZSTDLIB_API size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output);
+ZSTDLIB_API size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output);
+
+ZSTDLIB_API size_t ZSTD_CStreamInSize(void); /**< recommended size for input 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. */
+
+
+
+/*-***************************************************************************
+* Streaming decompression - HowTo
+*
+* A ZSTD_DStream object is required to track streaming operations.
+* Use ZSTD_createDStream() and ZSTD_freeDStream() to create/release resources.
+* ZSTD_DStream objects can be re-used multiple times.
+*
+* Use ZSTD_initDStream() to start a new decompression operation.
+* @return : recommended first input size
+* Alternatively, use advanced API to set specific properties.
+*
+* Use ZSTD_decompressStream() repetitively to consume your input.
+* 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.
+* The function tries to flush all data decoded immediately, respecting output buffer size.
+* If `output.pos < output.size`, decoder has flushed everything it could.
+* But if `output.pos == output.size`, there might be some data left within internal buffers.,
+* In which case, call ZSTD_decompressStream() again to flush whatever remains in the buffer.
+* Note : with no additional input provided, amount of data flushed is necessarily <= ZSTD_BLOCKSIZE_MAX.
+* @return : 0 when a frame is completely decoded and fully flushed,
+* or an error code, which can be tested using ZSTD_isError(),
+* or any other value > 0, which means there is still some decoding or flushing to do to complete current frame :
+* the return value is a suggested next input size (just a hint for better latency)
+* that will never request more than the remaining frame size.
+* *******************************************************************************/
+
+typedef ZSTD_DCtx ZSTD_DStream; /**< DCtx and DStream are now effectively same object (>= v1.3.0) */
+ /* For compatibility with versions <= v1.2.0, prefer differentiating them. */
+/*===== ZSTD_DStream management functions =====*/
+ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream(void);
+ZSTDLIB_API size_t ZSTD_freeDStream(ZSTD_DStream* zds);
+
+/*===== Streaming decompression functions =====*/
+ZSTDLIB_API size_t ZSTD_initDStream(ZSTD_DStream* zds);
+ZSTDLIB_API size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inBuffer* input);
+
+ZSTDLIB_API size_t ZSTD_DStreamInSize(void); /*!< recommended size for input 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. */
+
+#endif /* ZSTD_H_235446 */
+
+
+
+
+/****************************************************************************************
+ * ADVANCED AND EXPERIMENTAL FUNCTIONS
+ ****************************************************************************************
+ * The definitions in the following section are considered experimental.
+ * They are provided for advanced scenarios.
+ * They should never be used with a dynamic library, as prototypes may change in the future.
+ * Use them only in association with static linking.
+ * ***************************************************************************************/
+
+#if defined(ZSTD_STATIC_LINKING_ONLY) && !defined(ZSTD_H_ZSTD_STATIC_LINKING_ONLY)
+#define ZSTD_H_ZSTD_STATIC_LINKING_ONLY
+
+
+/****************************************************************************************
+ * Candidate API for promotion to stable status
+ ****************************************************************************************
+ * The following symbols and constants form the "staging area" :
+ * they are considered to join "stable API" by v1.4.0.
+ * The proposal is written so that it can be made stable "as is",
+ * though it's still possible to suggest improvements.
+ * Staging is in fact last chance for changes,
+ * the API is locked once reaching "stable" status.
+ * ***************************************************************************************/
+
+
+/* === Constants === */
+
+/* all magic numbers are supposed read/written to/from files/memory using little-endian convention */
+#define ZSTD_MAGICNUMBER 0xFD2FB528 /* valid since v0.8.0 */
+#define ZSTD_MAGIC_DICTIONARY 0xEC30A437 /* valid since v0.7.0 */
+#define ZSTD_MAGIC_SKIPPABLE_START 0x184D2A50 /* all 16 values, from 0x184D2A50 to 0x184D2A5F, signal the beginning of a skippable frame */
+#define ZSTD_MAGIC_SKIPPABLE_MASK 0xFFFFFFF0
+
+#define ZSTD_BLOCKSIZELOG_MAX 17
+#define ZSTD_BLOCKSIZE_MAX (1<<ZSTD_BLOCKSIZELOG_MAX)
+
+
+/* === query limits === */
+
+ZSTDLIB_API int ZSTD_minCLevel(void); /*!< minimum negative compression level allowed */
+
+
+/* === frame size === */
+
+/*! ZSTD_findFrameCompressedSize() :
+ * `src` should point to the start of a ZSTD frame or skippable frame.
+ * `srcSize` must be >= first frame size
+ * @return : the compressed size of the first frame starting at `src`,
+ * suitable to pass as `srcSize` to `ZSTD_decompress` or similar,
+ * or an error code if input is invalid */
+ZSTDLIB_API size_t ZSTD_findFrameCompressedSize(const void* src, size_t srcSize);
+
+
+/* === Memory management === */
+
+/*! ZSTD_sizeof_*() :
+ * These functions give the _current_ memory usage of selected object.
+ * Note that object memory usage can evolve (increase or decrease) over time. */
+ZSTDLIB_API size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx);
+ZSTDLIB_API size_t ZSTD_sizeof_DCtx(const ZSTD_DCtx* dctx);
+ZSTDLIB_API size_t ZSTD_sizeof_CStream(const ZSTD_CStream* zcs);
+ZSTDLIB_API size_t ZSTD_sizeof_DStream(const ZSTD_DStream* zds);
+ZSTDLIB_API size_t ZSTD_sizeof_CDict(const ZSTD_CDict* cdict);
+ZSTDLIB_API size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict);
+
+
+/***************************************
+* Advanced compression API
+***************************************/
+
+/* API design :
+ * Parameters are pushed one by one into an existing context,
+ * using ZSTD_CCtx_set*() functions.
+ * Pushed parameters are sticky : they are valid for next compressed frame, and any subsequent frame.
+ * "sticky" parameters are applicable to `ZSTD_compress2()` and `ZSTD_compressStream*()` !
+ * They do not apply to "simple" one-shot variants such as ZSTD_compressCCtx()
+ *
+ * It's possible to reset all parameters to "default" using ZSTD_CCtx_reset().
+ *
+ * This API supercedes all other "advanced" API entry points in the experimental section.
+ * In the future, we expect to remove from experimental API entry points which are redundant with this API.
+ */
+
+
+/* Compression strategies, listed from fastest to strongest */
+typedef enum { ZSTD_fast=1,
+ ZSTD_dfast=2,
+ ZSTD_greedy=3,
+ ZSTD_lazy=4,
+ ZSTD_lazy2=5,
+ ZSTD_btlazy2=6,
+ ZSTD_btopt=7,
+ ZSTD_btultra=8,
+ ZSTD_btultra2=9
+ /* note : new strategies _might_ be added in the future.
+ Only the order (from fast to strong) is guaranteed */
+} ZSTD_strategy;
+
+
+typedef enum {
+
+ /* compression parameters */
+ ZSTD_c_compressionLevel=100, /* Update all compression parameters according to pre-defined cLevel table
+ * Default level is ZSTD_CLEVEL_DEFAULT==3.
+ * Special: value 0 means default, which is controlled by ZSTD_CLEVEL_DEFAULT.
+ * Note 1 : it's possible to pass a negative compression level.
+ * Note 2 : setting a level sets all default values of other compression parameters */
+ ZSTD_c_windowLog=101, /* Maximum allowed back-reference distance, expressed as power of 2.
+ * Must be clamped between ZSTD_WINDOWLOG_MIN and ZSTD_WINDOWLOG_MAX.
+ * Special: value 0 means "use default windowLog".
+ * Note: Using a windowLog greater than ZSTD_WINDOWLOG_LIMIT_DEFAULT
+ * requires explicitly allowing such window size at decompression stage if using streaming. */
+ ZSTD_c_hashLog=102, /* Size of the initial probe table, as a power of 2.
+ * Resulting memory usage is (1 << (hashLog+2)).
+ * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX.
+ * Larger tables improve compression ratio of strategies <= dFast,
+ * and improve speed of strategies > dFast.
+ * Special: value 0 means "use default hashLog". */
+ ZSTD_c_chainLog=103, /* Size of the multi-probe search table, as a power of 2.
+ * Resulting memory usage is (1 << (chainLog+2)).
+ * Must be clamped between ZSTD_CHAINLOG_MIN and ZSTD_CHAINLOG_MAX.
+ * Larger tables result in better and slower compression.
+ * This parameter is useless when using "fast" strategy.
+ * It's still useful when using "dfast" strategy,
+ * in which case it defines a secondary probe table.
+ * Special: value 0 means "use default chainLog". */
+ ZSTD_c_searchLog=104, /* Number of search attempts, as a power of 2.
+ * More attempts result in better and slower compression.
+ * This parameter is useless when using "fast" and "dFast" strategies.
+ * Special: value 0 means "use default searchLog". */
+ ZSTD_c_minMatch=105, /* Minimum size of searched matches.
+ * Note that Zstandard can still find matches of smaller size,
+ * it just tweaks its search algorithm to look for this size and larger.
+ * Larger values increase compression and decompression speed, but decrease ratio.
+ * Must be clamped between ZSTD_MINMATCH_MIN and ZSTD_MINMATCH_MAX.
+ * Note that currently, for all strategies < btopt, effective minimum is 4.
+ * , for all strategies > fast, effective maximum is 6.
+ * Special: value 0 means "use default minMatchLength". */
+ ZSTD_c_targetLength=106, /* Impact of this field depends on strategy.
+ * For strategies btopt, btultra & btultra2:
+ * Length of Match considered "good enough" to stop search.
+ * Larger values make compression stronger, and slower.
+ * For strategy fast:
+ * Distance between match sampling.
+ * Larger values make compression faster, and weaker.
+ * Special: value 0 means "use default targetLength". */
+ ZSTD_c_strategy=107, /* See ZSTD_strategy enum definition.
+ * The higher the value of selected strategy, the more complex it is,
+ * resulting in stronger and slower compression.
+ * Special: value 0 means "use default strategy". */
+
+ /* LDM mode parameters */
+ ZSTD_c_enableLongDistanceMatching=160, /* Enable long distance matching.
+ * This parameter is designed to improve compression ratio
+ * for large inputs, by finding large matches at long distance.
+ * It increases memory usage and window size.
+ * Note: enabling this parameter increases default ZSTD_c_windowLog to 128 MB
+ * except when expressly set to a different value. */
+ ZSTD_c_ldmHashLog=161, /* Size of the table for long distance matching, as a power of 2.
+ * Larger values increase memory usage and compression ratio,
+ * but decrease compression speed.
+ * Must be clamped between ZSTD_HASHLOG_MIN and ZSTD_HASHLOG_MAX
+ * default: windowlog - 7.
+ * Special: value 0 means "automatically determine hashlog". */
+ ZSTD_c_ldmMinMatch=162, /* Minimum match size for long distance matcher.
+ * Larger/too small values usually decrease compression ratio.
+ * Must be clamped between ZSTD_LDM_MINMATCH_MIN and ZSTD_LDM_MINMATCH_MAX.
+ * Special: value 0 means "use default value" (default: 64). */
+ ZSTD_c_ldmBucketSizeLog=163, /* Log size of each bucket in the LDM hash table for collision resolution.
+ * Larger values improve collision resolution but decrease compression speed.
+ * The maximum value is ZSTD_LDM_BUCKETSIZELOG_MAX.
+ * Special: value 0 means "use default value" (default: 3). */
+ ZSTD_c_ldmHashRateLog=164, /* Frequency of inserting/looking up entries into the LDM hash table.
+ * Must be clamped between 0 and (ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN).
+ * Default is MAX(0, (windowLog - ldmHashLog)), optimizing hash table usage.
+ * Larger values improve compression speed.
+ * Deviating far from default value will likely result in a compression ratio decrease.
+ * Special: value 0 means "automatically determine hashRateLog". */
+
+ /* frame parameters */
+ ZSTD_c_contentSizeFlag=200, /* Content size will be written into frame header _whenever known_ (default:1)
+ * Content size must be known at the beginning of compression.
+ * This is automatically the case when using ZSTD_compress2(),
+ * For streaming variants, content size must be provided with ZSTD_CCtx_setPledgedSrcSize() */
+ ZSTD_c_checksumFlag=201, /* A 32-bits checksum of content is written at end of frame (default:0) */
+ ZSTD_c_dictIDFlag=202, /* When applicable, dictionary's ID is written into frame header (default:1) */
+
+ /* multi-threading parameters */
+ /* These parameters are only useful if multi-threading is enabled (compiled with build macro ZSTD_MULTITHREAD).
+ * They return an error otherwise. */
+ ZSTD_c_nbWorkers=400, /* Select how many threads will be spawned to compress in parallel.
+ * When nbWorkers >= 1, triggers asynchronous mode when used with ZSTD_compressStream*() :
+ * ZSTD_compressStream*() consumes input and flush output if possible, but immediately gives back control to caller,
+ * while compression work is performed in parallel, within worker threads.
+ * (note : a strong exception to this rule is when first invocation of ZSTD_compressStream2() sets ZSTD_e_end :
+ * in which case, ZSTD_compressStream2() delegates to ZSTD_compress2(), which is always a blocking call).
+ * More workers improve speed, but also increase memory usage.
+ * Default value is `0`, aka "single-threaded mode" : no worker is spawned, compression is performed inside Caller's thread, all invocations are blocking */
+ ZSTD_c_jobSize=401, /* Size of a compression job. This value is enforced only when nbWorkers >= 1.
+ * Each compression job is completed in parallel, so this value can indirectly impact the nb of active threads.
+ * 0 means default, which is dynamically determined based on compression parameters.
+ * Job size must be a minimum of overlap size, or 1 MB, whichever is largest.
+ * The minimum size is automatically and transparently enforced */
+ ZSTD_c_overlapLog=402, /* Control the overlap size, as a fraction of window size.
+ * The overlap size is an amount of data reloaded from previous job at the beginning of a new job.
+ * It helps preserve compression ratio, while each job is compressed in parallel.
+ * This value is enforced only when nbWorkers >= 1.
+ * Larger values increase compression ratio, but decrease speed.
+ * Possible values range from 0 to 9 :
+ * - 0 means "default" : value will be determined by the library, depending on strategy
+ * - 1 means "no overlap"
+ * - 9 means "full overlap", using a full window size.
+ * Each intermediate rank increases/decreases load size by a factor 2 :
+ * 9: full window; 8: w/2; 7: w/4; 6: w/8; 5:w/16; 4: w/32; 3:w/64; 2:w/128; 1:no overlap; 0:default
+ * default value varies between 6 and 9, depending on strategy */
+
+ /* note : additional experimental parameters are also available
+ * within the experimental section of the API.
+ * At the time of this writing, they include :
+ * ZSTD_c_rsyncable
+ * ZSTD_c_format
+ * ZSTD_c_forceMaxWindow
+ * ZSTD_c_forceAttachDict
+ * Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them.
+ * note : never ever use experimentalParam? names directly;
+ * also, the enums values themselves are unstable and can still change.
+ */
+ ZSTD_c_experimentalParam1=500,
+ ZSTD_c_experimentalParam2=10,
+ ZSTD_c_experimentalParam3=1000,
+ ZSTD_c_experimentalParam4=1001
+} ZSTD_cParameter;
+
+
+typedef struct {
+ size_t error;
+ int lowerBound;
+ int upperBound;
+} ZSTD_bounds;
+
+/*! ZSTD_cParam_getBounds() :
+ * All parameters must belong to an interval with lower and upper bounds,
+ * otherwise they will either trigger an error or be automatically clamped.
+ * @return : a structure, ZSTD_bounds, which contains
+ * - an error status field, which must be tested using ZSTD_isError()
+ * - lower and upper bounds, both inclusive
+ */
+ZSTDLIB_API ZSTD_bounds ZSTD_cParam_getBounds(ZSTD_cParameter cParam);
+
+/*! ZSTD_CCtx_setParameter() :
+ * Set one compression parameter, selected by enum ZSTD_cParameter.
+ * All parameters have valid bounds. Bounds can be queried using ZSTD_cParam_getBounds().
+ * Providing a value beyond bound will either clamp it, or trigger an error (depending on parameter).
+ * Setting a parameter is generally only possible during frame initialization (before starting compression).
+ * Exception : when using multi-threading mode (nbWorkers >= 1),
+ * the following parameters can be updated _during_ compression (within same frame):
+ * => compressionLevel, hashLog, chainLog, searchLog, minMatch, targetLength and strategy.
+ * new parameters will be active for next job only (after a flush()).
+ * @return : an error code (which can be tested using ZSTD_isError()).
+ */
+ZSTDLIB_API size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int value);
+
+/*! ZSTD_CCtx_setPledgedSrcSize() :
+ * Total input data size to be compressed as a single frame.
+ * Value will be written in frame header, unless if explicitly forbidden using ZSTD_c_contentSizeFlag.
+ * This value will also be controlled at end of frame, and trigger an error if not respected.
+ * @result : 0, or an error code (which can be tested with ZSTD_isError()).
+ * Note 1 : pledgedSrcSize==0 actually means zero, aka an empty frame.
+ * In order to mean "unknown content size", pass constant ZSTD_CONTENTSIZE_UNKNOWN.
+ * ZSTD_CONTENTSIZE_UNKNOWN is default value for any new frame.
+ * Note 2 : pledgedSrcSize is only valid once, for the next frame.
+ * It's discarded at the end of the frame, and replaced by ZSTD_CONTENTSIZE_UNKNOWN.
+ * Note 3 : Whenever all input data is provided and consumed in a single round,
+ * for example with ZSTD_compress2(),
+ * or invoking immediately ZSTD_compressStream2(,,,ZSTD_e_end),
+ * this value is automatically overriden by srcSize instead.
+ */
+ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize);
+
+/*! ZSTD_CCtx_loadDictionary() :
+ * Create an internal CDict from `dict` buffer.
+ * Decompression will have to use same dictionary.
+ * @result : 0, or an error code (which can be tested with ZSTD_isError()).
+ * Special: Loading a NULL (or 0-size) dictionary invalidates previous dictionary,
+ * meaning "return to no-dictionary mode".
+ * Note 1 : Dictionary is sticky, it will be used for all future compressed frames.
+ * To return to "no-dictionary" situation, load a NULL dictionary (or reset parameters).
+ * Note 2 : Loading a dictionary involves building tables.
+ * It's also a CPU consuming operation, with non-negligible impact on latency.
+ * Tables are dependent on compression parameters, and for this reason,
+ * compression parameters can no longer be changed after loading a dictionary.
+ * Note 3 :`dict` content will be copied internally.
+ * Use experimental ZSTD_CCtx_loadDictionary_byReference() to reference content instead.
+ * In such a case, dictionary buffer must outlive its users.
+ * Note 4 : Use ZSTD_CCtx_loadDictionary_advanced()
+ * to precisely select how dictionary content must be interpreted. */
+ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize);
+
+/*! ZSTD_CCtx_refCDict() :
+ * Reference a prepared dictionary, to be used for all next compressed frames.
+ * Note that compression parameters are enforced from within CDict,
+ * and supercede any compression parameter previously set within CCtx.
+ * The dictionary will remain valid for future compressed frames using same CCtx.
+ * @result : 0, or an error code (which can be tested with ZSTD_isError()).
+ * Special : Referencing a NULL CDict means "return to no-dictionary mode".
+ * Note 1 : Currently, only one dictionary can be managed.
+ * Referencing a new dictionary effectively "discards" any previous one.
+ * Note 2 : CDict is just referenced, its lifetime must outlive its usage within CCtx. */
+ZSTDLIB_API size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict);
+
+/*! ZSTD_CCtx_refPrefix() :
+ * Reference a prefix (single-usage dictionary) for next compressed frame.
+ * A prefix is **only used once**. Tables are discarded at end of frame (ZSTD_e_end).
+ * Decompression will need same prefix to properly regenerate data.
+ * Compressing with a prefix is similar in outcome as performing a diff and compressing it,
+ * but performs much faster, especially during decompression (compression speed is tunable with compression level).
+ * @result : 0, or an error code (which can be tested with ZSTD_isError()).
+ * Special: Adding any prefix (including NULL) invalidates any previous prefix or dictionary
+ * Note 1 : Prefix buffer is referenced. It **must** outlive compression.
+ * Its content must remain unmodified during compression.
+ * Note 2 : If the intention is to diff some large src data blob with some prior version of itself,
+ * ensure that the window size is large enough to contain the entire source.
+ * See ZSTD_c_windowLog.
+ * Note 3 : Referencing a prefix involves building tables, which are dependent on compression parameters.
+ * It's a CPU consuming operation, with non-negligible impact on latency.
+ * If there is a need to use the same prefix multiple times, consider loadDictionary instead.
+ * Note 4 : By default, the prefix is interpreted as raw content (ZSTD_dm_rawContent).
+ * Use experimental ZSTD_CCtx_refPrefix_advanced() to alter dictionary interpretation. */
+ZSTDLIB_API size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx,
+ const void* prefix, size_t prefixSize);
+
+
+typedef enum {
+ ZSTD_reset_session_only = 1,
+ ZSTD_reset_parameters = 2,
+ ZSTD_reset_session_and_parameters = 3
+} ZSTD_ResetDirective;
+
+/*! ZSTD_CCtx_reset() :
+ * There are 2 different things that can be reset, independently or jointly :
+ * - The session : will stop compressing current frame, and make CCtx ready to start a new one.
+ * Useful after an error, or to interrupt any ongoing compression.
+ * Any internal data not yet flushed is cancelled.
+ * Compression parameters and dictionary remain unchanged.
+ * They will be used to compress next frame.
+ * Resetting session never fails.
+ * - The parameters : changes all parameters back to "default".
+ * This removes any reference to any dictionary too.
+ * Parameters can only be changed between 2 sessions (i.e. no compression is currently ongoing)
+ * otherwise the reset fails, and function returns an error value (which can be tested using ZSTD_isError())
+ * - Both : similar to resetting the session, followed by resetting parameters.
+ */
+ZSTDLIB_API size_t ZSTD_CCtx_reset(ZSTD_CCtx* cctx, ZSTD_ResetDirective reset);
+
+
+
+/*! ZSTD_compress2() :
+ * Behave the same as ZSTD_compressCCtx(), but compression parameters are set using the advanced API.
+ * ZSTD_compress2() always starts a new frame.
+ * Should cctx hold data from a previously unfinished frame, everything about it is forgotten.
+ * - Compression parameters are pushed into CCtx before starting compression, using ZSTD_CCtx_set*()
+ * - The function is always blocking, returns when compression is completed.
+ * Hint : compression runs faster if `dstCapacity` >= `ZSTD_compressBound(srcSize)`.
+ * @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_compress2( ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize);
+
+typedef enum {
+ ZSTD_e_continue=0, /* collect more data, encoder decides when to output compressed result, for optimal compression ratio */
+ ZSTD_e_flush=1, /* flush any data provided so far,
+ * it creates (at least) one new block, that can be decoded immediately on reception;
+ * frame will continue: any future data can still reference previously compressed data, improving compression. */
+ ZSTD_e_end=2 /* flush any remaining data _and_ close current frame.
+ * note that frame is only closed after compressed data is fully flushed (return value == 0).
+ * After that point, any additional data starts a new frame.
+ * note : each frame is independent (does not reference any content from previous frame). */
+} ZSTD_EndDirective;
+
+/*! ZSTD_compressStream2() :
+ * Behaves about the same as ZSTD_compressStream, with additional control on end directive.
+ * - Compression parameters are pushed into CCtx before starting compression, using ZSTD_CCtx_set*()
+ * - Compression parameters cannot be changed once compression is started (save a list of exceptions in multi-threading mode)
+ * - outpot->pos must be <= dstCapacity, input->pos must be <= srcSize
+ * - outpot->pos and input->pos will be updated. They are guaranteed to remain below their respective limit.
+ * - When nbWorkers==0 (default), function is blocking : it completes its job before returning to caller.
+ * - When nbWorkers>=1, function is non-blocking : it just acquires a copy of input, and distributes jobs to internal worker threads, flush whatever is available,
+ * and then immediately returns, just indicating that there is some data remaining to be flushed.
+ * The function nonetheless guarantees forward progress : it will return only after it reads or write at least 1+ byte.
+ * - Exception : if the first call requests a ZSTD_e_end directive and provides enough dstCapacity, the function delegates to ZSTD_compress2() which is always blocking.
+ * - @return provides a minimum amount of data remaining to be flushed from internal buffers
+ * or an error code, which can be tested using ZSTD_isError().
+ * if @return != 0, flush is not fully completed, there is still some data left within internal buffers.
+ * This is useful for ZSTD_e_flush, since in this case more flushes are necessary to empty all buffers.
+ * For ZSTD_e_end, @return == 0 when internal buffers are fully flushed and frame is completed.
+ * - after a ZSTD_e_end directive, if internal buffer is not fully flushed (@return != 0),
+ * only ZSTD_e_end or ZSTD_e_flush operations are allowed.
+ * Before starting a new compression job, or changing compression parameters,
+ * it is required to fully flush internal buffers.
+ */
+ZSTDLIB_API size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,
+ ZSTD_outBuffer* output,
+ ZSTD_inBuffer* input,
+ ZSTD_EndDirective endOp);
+
+
+
+/* ============================== */
+/* Advanced decompression API */
+/* ============================== */
+
+/* The advanced API pushes parameters one by one into an existing DCtx context.
+ * Parameters are sticky, and remain valid for all following frames
+ * using the same DCtx context.
+ * It's possible to reset parameters to default values using ZSTD_DCtx_reset().
+ * Note : This API is compatible with existing ZSTD_decompressDCtx() and ZSTD_decompressStream().
+ * Therefore, no new decompression function is necessary.
+ */
+
+
+typedef enum {
+
+ ZSTD_d_windowLogMax=100, /* Select a size limit (in power of 2) beyond which
+ * the streaming API will refuse to allocate memory buffer
+ * in order to protect the host from unreasonable memory requirements.
+ * This parameter is only useful in streaming mode, since no internal buffer is allocated in single-pass mode.
+ * By default, a decompression context accepts window sizes <= (1 << ZSTD_WINDOWLOG_LIMIT_DEFAULT) */
+
+ /* note : additional experimental parameters are also available
+ * within the experimental section of the API.
+ * At the time of this writing, they include :
+ * ZSTD_c_format
+ * Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them.
+ * note : never ever use experimentalParam? names directly
+ */
+ ZSTD_d_experimentalParam1=1000
+
+} ZSTD_dParameter;
+
+
+/*! ZSTD_dParam_getBounds() :
+ * All parameters must belong to an interval with lower and upper bounds,
+ * otherwise they will either trigger an error or be automatically clamped.
+ * @return : a structure, ZSTD_bounds, which contains
+ * - an error status field, which must be tested using ZSTD_isError()
+ * - both lower and upper bounds, inclusive
+ */
+ZSTDLIB_API ZSTD_bounds ZSTD_dParam_getBounds(ZSTD_dParameter dParam);
+
+/*! ZSTD_DCtx_setParameter() :
+ * Set one compression parameter, selected by enum ZSTD_dParameter.
+ * All parameters have valid bounds. Bounds can be queried using ZSTD_dParam_getBounds().
+ * Providing a value beyond bound will either clamp it, or trigger an error (depending on parameter).
+ * Setting a parameter is only possible during frame initialization (before starting decompression).
+ * @return : 0, or an error code (which can be tested using ZSTD_isError()).
+ */
+ZSTDLIB_API size_t ZSTD_DCtx_setParameter(ZSTD_DCtx* dctx, ZSTD_dParameter param, int value);
+
+
+/*! ZSTD_DCtx_loadDictionary() :
+ * Create an internal DDict from dict buffer,
+ * to be used to decompress next frames.
+ * The dictionary remains valid for all future frames, until explicitly invalidated.
+ * @result : 0, or an error code (which can be tested with ZSTD_isError()).
+ * Special : Adding a NULL (or 0-size) dictionary invalidates any previous dictionary,
+ * meaning "return to no-dictionary mode".
+ * Note 1 : Loading a dictionary involves building tables,
+ * which has a non-negligible impact on CPU usage and latency.
+ * It's recommended to "load once, use many times", to amortize the cost
+ * Note 2 :`dict` content will be copied internally, so `dict` can be released after loading.
+ * Use ZSTD_DCtx_loadDictionary_byReference() to reference dictionary content instead.
+ * Note 3 : Use ZSTD_DCtx_loadDictionary_advanced() to take control of
+ * how dictionary content is loaded and interpreted.
+ */
+ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
+
+/*! ZSTD_DCtx_refDDict() :
+ * Reference a prepared dictionary, to be used to decompress next frames.
+ * The dictionary remains active for decompression of future frames using same DCtx.
+ * @result : 0, or an error code (which can be tested with ZSTD_isError()).
+ * Note 1 : Currently, only one dictionary can be managed.
+ * Referencing a new dictionary effectively "discards" any previous one.
+ * Special: referencing a NULL DDict means "return to no-dictionary mode".
+ * Note 2 : DDict is just referenced, its lifetime must outlive its usage from DCtx.
+ */
+ZSTDLIB_API size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict);
+
+/*! ZSTD_DCtx_refPrefix() :
+ * Reference a prefix (single-usage dictionary) to decompress next frame.
+ * This is the reverse operation of ZSTD_CCtx_refPrefix(),
+ * and must use the same prefix as the one used during compression.
+ * Prefix is **only used once**. Reference is discarded at end of frame.
+ * End of frame is reached when ZSTD_decompressStream() returns 0.
+ * @result : 0, or an error code (which can be tested with ZSTD_isError()).
+ * Note 1 : Adding any prefix (including NULL) invalidates any previously set prefix or dictionary
+ * Note 2 : Prefix buffer is referenced. It **must** outlive decompression.
+ * Prefix buffer must remain unmodified up to the end of frame,
+ * reached when ZSTD_decompressStream() returns 0.
+ * Note 3 : By default, the prefix is treated as raw content (ZSTD_dm_rawContent).
+ * Use ZSTD_CCtx_refPrefix_advanced() to alter dictMode (Experimental section)
+ * Note 4 : Referencing a raw content prefix has almost no cpu nor memory cost.
+ * A full dictionary is more costly, as it requires building tables.
+ */
+ZSTDLIB_API size_t ZSTD_DCtx_refPrefix(ZSTD_DCtx* dctx,
+ const void* prefix, size_t prefixSize);
+
+/*! ZSTD_DCtx_reset() :
+ * Return a DCtx to clean state.
+ * Session and parameters can be reset jointly or separately.
+ * Parameters can only be reset when no active frame is being decompressed.
+ * @return : 0, or an error code, which can be tested with ZSTD_isError()
+ */
+ZSTDLIB_API size_t ZSTD_DCtx_reset(ZSTD_DCtx* dctx, ZSTD_ResetDirective reset);
+
+
+
+/****************************************************************************************
+ * experimental API (static linking only)
+ ****************************************************************************************
+ * The following symbols and constants
+ * are not planned to join "stable API" status in the near future.
+ * They can still change in future versions.
+ * Some of them are planned to remain in the static_only section indefinitely.
+ * Some of them might be removed in the future (especially when redundant with existing stable functions)
+ * ***************************************************************************************/
+
+#define ZSTD_FRAMEHEADERSIZE_PREFIX 5 /* minimum input size required to query frame header size */
+#define ZSTD_FRAMEHEADERSIZE_MIN 6
+#define ZSTD_FRAMEHEADERSIZE_MAX 18 /* can be useful for static allocation */
+#define ZSTD_SKIPPABLEHEADERSIZE 8
+
+/* compression parameter bounds */
+#define ZSTD_WINDOWLOG_MAX_32 30
+#define ZSTD_WINDOWLOG_MAX_64 31
+#define ZSTD_WINDOWLOG_MAX ((int)(sizeof(size_t) == 4 ? ZSTD_WINDOWLOG_MAX_32 : ZSTD_WINDOWLOG_MAX_64))
+#define ZSTD_WINDOWLOG_MIN 10
+#define ZSTD_HASHLOG_MAX ((ZSTD_WINDOWLOG_MAX < 30) ? ZSTD_WINDOWLOG_MAX : 30)
+#define ZSTD_HASHLOG_MIN 6
+#define ZSTD_CHAINLOG_MAX_32 29
+#define ZSTD_CHAINLOG_MAX_64 30
+#define ZSTD_CHAINLOG_MAX ((int)(sizeof(size_t) == 4 ? ZSTD_CHAINLOG_MAX_32 : ZSTD_CHAINLOG_MAX_64))
+#define ZSTD_CHAINLOG_MIN ZSTD_HASHLOG_MIN
+#define ZSTD_SEARCHLOG_MAX (ZSTD_WINDOWLOG_MAX-1)
+#define ZSTD_SEARCHLOG_MIN 1
+#define ZSTD_MINMATCH_MAX 7 /* only for ZSTD_fast, other strategies are limited to 6 */
+#define ZSTD_MINMATCH_MIN 3 /* only for ZSTD_btopt+, faster strategies are limited to 4 */
+#define ZSTD_TARGETLENGTH_MAX ZSTD_BLOCKSIZE_MAX
+#define ZSTD_TARGETLENGTH_MIN 0 /* note : comparing this constant to an unsigned results in a tautological test */
+#define ZSTD_STRATEGY_MIN ZSTD_fast
+#define ZSTD_STRATEGY_MAX ZSTD_btultra2
+
+
+#define ZSTD_OVERLAPLOG_MIN 0
+#define ZSTD_OVERLAPLOG_MAX 9
+
+#define ZSTD_WINDOWLOG_LIMIT_DEFAULT 27 /* by default, the streaming decoder will refuse any frame
+ * requiring larger than (1<<ZSTD_WINDOWLOG_LIMIT_DEFAULT) window size,
+ * to preserve host's memory from unreasonable requirements.
+ * This limit can be overriden using ZSTD_DCtx_setParameter(,ZSTD_d_windowLogMax,).
+ * The limit does not apply for one-pass decoders (such as ZSTD_decompress()), since no additional memory is allocated */
+
+
+/* LDM parameter bounds */
+#define ZSTD_LDM_HASHLOG_MIN ZSTD_HASHLOG_MIN
+#define ZSTD_LDM_HASHLOG_MAX ZSTD_HASHLOG_MAX
+#define ZSTD_LDM_MINMATCH_MIN 4
+#define ZSTD_LDM_MINMATCH_MAX 4096
+#define ZSTD_LDM_BUCKETSIZELOG_MIN 1
+#define ZSTD_LDM_BUCKETSIZELOG_MAX 8
+#define ZSTD_LDM_HASHRATELOG_MIN 0
+#define ZSTD_LDM_HASHRATELOG_MAX (ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN)
+
+/* internal */
+#define ZSTD_HASHLOG3_MAX 17
+
+
+/* --- Advanced types --- */
+
+typedef struct ZSTD_CCtx_params_s ZSTD_CCtx_params;
+
+typedef struct {
+ unsigned windowLog; /**< largest match distance : larger == more compression, more memory needed during decompression */
+ unsigned chainLog; /**< fully searched segment : larger == more compression, slower, more memory (useless for fast) */
+ unsigned hashLog; /**< dispatch table : larger == faster, more memory */
+ unsigned searchLog; /**< nb of searches : larger == more compression, slower */
+ unsigned minMatch; /**< match length searched : larger == faster decompression, sometimes less compression */
+ unsigned targetLength; /**< acceptable match size for optimal parser (only) : larger == more compression, slower */
+ ZSTD_strategy strategy; /**< see ZSTD_strategy definition above */
+} ZSTD_compressionParameters;
+
+typedef struct {
+ int contentSizeFlag; /**< 1: content size will be in frame header (when known) */
+ int checksumFlag; /**< 1: generate a 32-bits checksum using XXH64 algorithm at end of frame, for error detection */
+ int noDictIDFlag; /**< 1: no dictID will be saved into frame header (dictID is only useful for dictionary compression) */
+} ZSTD_frameParameters;
+
+typedef struct {
+ ZSTD_compressionParameters cParams;
+ ZSTD_frameParameters fParams;
+} ZSTD_parameters;
+
+typedef enum {
+ ZSTD_dct_auto = 0, /* dictionary is "full" when starting with ZSTD_MAGIC_DICTIONARY, otherwise it is "rawContent" */
+ ZSTD_dct_rawContent = 1, /* ensures dictionary is always loaded as rawContent, even if it starts with ZSTD_MAGIC_DICTIONARY */
+ ZSTD_dct_fullDict = 2 /* refuses to load a dictionary if it does not respect Zstandard's specification, starting with ZSTD_MAGIC_DICTIONARY */
+} ZSTD_dictContentType_e;
+
+typedef enum {
+ ZSTD_dlm_byCopy = 0, /**< Copy dictionary content internally */
+ ZSTD_dlm_byRef = 1, /**< Reference dictionary content -- the dictionary buffer must outlive its users. */
+} ZSTD_dictLoadMethod_e;
+
+typedef enum {
+ /* Opened question : should we have a format ZSTD_f_auto ?
+ * Today, it would mean exactly the same as ZSTD_f_zstd1.
+ * But, in the future, should several formats become supported,
+ * on the compression side, it would mean "default format".
+ * On the decompression side, it would mean "automatic format detection",
+ * so that ZSTD_f_zstd1 would mean "accept *only* zstd frames".
+ * Since meaning is a little different, another option could be to define different enums for compression and decompression.
+ * This question could be kept for later, when there are actually multiple formats to support,
+ * but there is also the question of pinning enum values, and pinning value `0` is especially important */
+ ZSTD_f_zstd1 = 0, /* zstd frame format, specified in zstd_compression_format.md (default) */
+ ZSTD_f_zstd1_magicless = 1, /* Variant of zstd frame format, without initial 4-bytes magic number.
+ * Useful to save 4 bytes per generated frame.
+ * Decoder cannot recognise automatically this format, requiring this instruction. */
+} ZSTD_format_e;
+
+typedef enum {
+ /* Note: this enum and the behavior it controls are effectively internal
+ * implementation details of the compressor. They are expected to continue
+ * to evolve and should be considered only in the context of extremely
+ * advanced performance tuning.
+ *
+ * Zstd currently supports the use of a CDict in two ways:
+ *
+ * - The contents of the CDict can be copied into the working context. This
+ * means that the compression can search both the dictionary and input
+ * while operating on a single set of internal tables. This makes
+ * the compression faster per-byte of input. However, the initial copy of
+ * the CDict's tables incurs a fixed cost at the beginning of the
+ * compression. For small compressions (< 8 KB), that copy can dominate
+ * the cost of the compression.
+ *
+ * - The CDict's tables can be used in-place. In this model, compression is
+ * slower per input byte, because the compressor has to search two sets of
+ * tables. However, this model incurs no start-up cost (as long as the
+ * working context's tables can be reused). For small inputs, this can be
+ * faster than copying the CDict's tables.
+ *
+ * Zstd has a simple internal heuristic that selects which strategy to use
+ * at the beginning of a compression. However, if experimentation shows that
+ * Zstd is making poor choices, it is possible to override that choice with
+ * this enum.
+ */
+ ZSTD_dictDefaultAttach = 0, /* Use the default heuristic. */
+ ZSTD_dictForceAttach = 1, /* Never copy the dictionary. */
+ ZSTD_dictForceCopy = 2, /* Always copy the dictionary. */
+} ZSTD_dictAttachPref_e;
+
+
+/***************************************
+* Frame size functions
+***************************************/
+
+/*! ZSTD_findDecompressedSize() :
+ * `src` should point the start of a series of ZSTD encoded and/or skippable frames
+ * `srcSize` must be the _exact_ size of this series
+ * (i.e. there should be a frame boundary exactly at `srcSize` bytes after `src`)
+ * @return : - decompressed size of all data in all successive frames
+ * - if the decompressed size cannot be determined: ZSTD_CONTENTSIZE_UNKNOWN
+ * - if an error occurred: ZSTD_CONTENTSIZE_ERROR
+ *
+ * note 1 : decompressed size is an optional field, that may not be present, especially in streaming mode.
+ * When `return==ZSTD_CONTENTSIZE_UNKNOWN`, data to decompress could be any size.
+ * In which case, it's necessary to use streaming mode to decompress data.
+ * 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 : ZSTD_findDecompressedSize handles multiple frames, and so it must traverse the input to
+ * read each contained frame header. This is fast as most of the data is skipped,
+ * however it does mean that all frame data must be present and valid. */
+ZSTDLIB_API unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize);
+
+/*! ZSTD_frameHeaderSize() :
+ * srcSize must be >= ZSTD_FRAMEHEADERSIZE_PREFIX.
+ * @return : size of the Frame Header,
+ * or an error code (if srcSize is too small) */
+ZSTDLIB_API size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize);
+
+
+/***************************************
+* Memory management
+***************************************/
+
+/*! ZSTD_estimate*() :
+ * These functions make it possible to estimate memory usage
+ * of a future {D,C}Ctx, before its creation.
+ * ZSTD_estimateCCtxSize() will provide a budget large enough for any compression level up to selected one.
+ * It will also consider src size to be arbitrarily "large", which is worst case.
+ * If srcSize is known to always be small, ZSTD_estimateCCtxSize_usingCParams() can provide a tighter estimation.
+ * ZSTD_estimateCCtxSize_usingCParams() can be used in tandem with ZSTD_getCParams() to create cParams from compressionLevel.
+ * ZSTD_estimateCCtxSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_c_nbWorkers is >= 1.
+ * Note : CCtx size estimation is only correct for single-threaded compression. */
+ZSTDLIB_API size_t ZSTD_estimateCCtxSize(int compressionLevel);
+ZSTDLIB_API size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams);
+ZSTDLIB_API size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params);
+ZSTDLIB_API size_t ZSTD_estimateDCtxSize(void);
+
+/*! ZSTD_estimateCStreamSize() :
+ * ZSTD_estimateCStreamSize() will provide a budget large enough for any compression level up to selected one.
+ * It will also consider src size to be arbitrarily "large", which is worst case.
+ * If srcSize is known to always be small, ZSTD_estimateCStreamSize_usingCParams() can provide a tighter estimation.
+ * ZSTD_estimateCStreamSize_usingCParams() can be used in tandem with ZSTD_getCParams() to create cParams from compressionLevel.
+ * ZSTD_estimateCStreamSize_usingCCtxParams() can be used in tandem with ZSTD_CCtxParam_setParameter(). Only single-threaded compression is supported. This function will return an error code if ZSTD_c_nbWorkers is >= 1.
+ * Note : CStream size estimation is only correct for single-threaded compression.
+ * ZSTD_DStream memory budget depends on window Size.
+ * This information can be passed manually, using ZSTD_estimateDStreamSize,
+ * or deducted from a valid frame Header, using ZSTD_estimateDStreamSize_fromFrame();
+ * Note : if streaming is init with function ZSTD_init?Stream_usingDict(),
+ * an internal ?Dict will be created, which additional size is not estimated here.
+ * In this case, get total size by adding ZSTD_estimate?DictSize */
+ZSTDLIB_API size_t ZSTD_estimateCStreamSize(int compressionLevel);
+ZSTDLIB_API size_t ZSTD_estimateCStreamSize_usingCParams(ZSTD_compressionParameters cParams);
+ZSTDLIB_API size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_params* params);
+ZSTDLIB_API size_t ZSTD_estimateDStreamSize(size_t windowSize);
+ZSTDLIB_API size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize);
+
+/*! ZSTD_estimate?DictSize() :
+ * ZSTD_estimateCDictSize() will bet that src size is relatively "small", and content is copied, like ZSTD_createCDict().
+ * ZSTD_estimateCDictSize_advanced() makes it possible to control compression parameters precisely, like ZSTD_createCDict_advanced().
+ * Note : dictionaries created by reference (`ZSTD_dlm_byRef`) are logically smaller.
+ */
+ZSTDLIB_API size_t ZSTD_estimateCDictSize(size_t dictSize, int compressionLevel);
+ZSTDLIB_API size_t ZSTD_estimateCDictSize_advanced(size_t dictSize, ZSTD_compressionParameters cParams, ZSTD_dictLoadMethod_e dictLoadMethod);
+ZSTDLIB_API size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod);
+
+/*! ZSTD_initStatic*() :
+ * Initialize an object using a pre-allocated fixed-size buffer.
+ * workspace: The memory area to emplace the object into.
+ * Provided pointer *must be 8-bytes aligned*.
+ * Buffer must outlive object.
+ * workspaceSize: Use ZSTD_estimate*Size() to determine
+ * how large workspace must be to support target scenario.
+ * @return : pointer to object (same address as workspace, just different type),
+ * or NULL if error (size too small, incorrect alignment, etc.)
+ * Note : zstd will never resize nor malloc() when using a static buffer.
+ * If the object requires more memory than available,
+ * zstd will just error out (typically ZSTD_error_memory_allocation).
+ * Note 2 : there is no corresponding "free" function.
+ * Since workspace is allocated externally, it must be freed externally too.
+ * Note 3 : cParams : use ZSTD_getCParams() to convert a compression level
+ * into its associated cParams.
+ * Limitation 1 : currently not compatible with internal dictionary creation, triggered by
+ * ZSTD_CCtx_loadDictionary(), ZSTD_initCStream_usingDict() or ZSTD_initDStream_usingDict().
+ * Limitation 2 : static cctx currently not compatible with multi-threading.
+ * Limitation 3 : static dctx is incompatible with legacy support.
+ */
+ZSTDLIB_API ZSTD_CCtx* ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize);
+ZSTDLIB_API ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticCCtx() */
+
+ZSTDLIB_API ZSTD_DCtx* ZSTD_initStaticDCtx(void* workspace, size_t workspaceSize);
+ZSTDLIB_API ZSTD_DStream* ZSTD_initStaticDStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticDCtx() */
+
+ZSTDLIB_API const ZSTD_CDict* ZSTD_initStaticCDict(
+ void* workspace, size_t workspaceSize,
+ const void* dict, size_t dictSize,
+ ZSTD_dictLoadMethod_e dictLoadMethod,
+ ZSTD_dictContentType_e dictContentType,
+ ZSTD_compressionParameters cParams);
+
+ZSTDLIB_API const ZSTD_DDict* ZSTD_initStaticDDict(
+ void* workspace, size_t workspaceSize,
+ const void* dict, size_t dictSize,
+ ZSTD_dictLoadMethod_e dictLoadMethod,
+ ZSTD_dictContentType_e dictContentType);
+
+
+/*! Custom memory allocation :
+ * These prototypes make it possible to pass your own allocation/free functions.
+ * ZSTD_customMem is provided at creation time, using ZSTD_create*_advanced() variants listed below.
+ * All allocation/free operations will be completed using these custom variants instead of regular <stdlib.h> ones.
+ */
+typedef void* (*ZSTD_allocFunction) (void* opaque, size_t size);
+typedef void (*ZSTD_freeFunction) (void* opaque, void* address);
+typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; void* opaque; } ZSTD_customMem;
+static ZSTD_customMem const ZSTD_defaultCMem = { NULL, NULL, NULL }; /**< this constant defers to stdlib's functions */
+
+ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem);
+ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem);
+ZSTDLIB_API ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem);
+ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem);
+
+ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize,
+ ZSTD_dictLoadMethod_e dictLoadMethod,
+ ZSTD_dictContentType_e dictContentType,
+ ZSTD_compressionParameters cParams,
+ ZSTD_customMem customMem);
+
+ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize,
+ ZSTD_dictLoadMethod_e dictLoadMethod,
+ ZSTD_dictContentType_e dictContentType,
+ ZSTD_customMem customMem);
+
+
+
+/***************************************
+* Advanced compression functions
+***************************************/
+
+/*! ZSTD_createCDict_byReference() :
+ * Create a digested dictionary for compression
+ * Dictionary content is just referenced, not duplicated.
+ * As a consequence, `dictBuffer` **must** outlive CDict,
+ * and its content must remain unmodified throughout the lifetime of CDict. */
+ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_byReference(const void* dictBuffer, size_t dictSize, int compressionLevel);
+
+/*! ZSTD_getCParams() :
+* @return ZSTD_compressionParameters structure for a selected compression level and estimated srcSize.
+* `estimatedSrcSize` value is optional, select 0 if not known */
+ZSTDLIB_API ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize);
+
+/*! ZSTD_getParams() :
+* same as ZSTD_getCParams(), but @return a full `ZSTD_parameters` object instead of sub-component `ZSTD_compressionParameters`.
+* All fields of `ZSTD_frameParameters` are set to default : contentSize=1, checksum=0, noDictID=0 */
+ZSTDLIB_API ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize);
+
+/*! ZSTD_checkCParams() :
+* Ensure param values remain within authorized range */
+ZSTDLIB_API size_t ZSTD_checkCParams(ZSTD_compressionParameters params);
+
+/*! ZSTD_adjustCParams() :
+ * optimize params for a given `srcSize` and `dictSize`.
+ * both values are optional, select `0` if unknown. */
+ZSTDLIB_API ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize);
+
+/*! ZSTD_compress_advanced() :
+ * Same as ZSTD_compress_usingDict(), with fine-tune control over compression parameters (by structure) */
+ZSTDLIB_API size_t ZSTD_compress_advanced(ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ const void* dict,size_t dictSize,
+ ZSTD_parameters params);
+
+/*! ZSTD_compress_usingCDict_advanced() :
+ * Same as ZSTD_compress_usingCDict(), with fine-tune control over frame parameters */
+ZSTDLIB_API size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
+ const void* src, size_t srcSize,
+ const ZSTD_CDict* cdict,
+ ZSTD_frameParameters fParams);
+
+
+/*! ZSTD_CCtx_loadDictionary_byReference() :
+ * Same as ZSTD_CCtx_loadDictionary(), but dictionary content is referenced, instead of being copied into CCtx.
+ * It saves some memory, but also requires that `dict` outlives its usage within `cctx` */
+ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_byReference(ZSTD_CCtx* cctx, const void* dict, size_t dictSize);
+
+/*! ZSTD_CCtx_loadDictionary_advanced() :
+ * Same as ZSTD_CCtx_loadDictionary(), but gives finer control over
+ * how to load the dictionary (by copy ? by reference ?)
+ * and how to interpret it (automatic ? force raw mode ? full mode only ?) */
+ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType);
+
+/*! ZSTD_CCtx_refPrefix_advanced() :
+ * Same as ZSTD_CCtx_refPrefix(), but gives finer control over
+ * how to interpret prefix content (automatic ? force raw mode (default) ? full mode only ?) */
+ZSTDLIB_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType);
+
+/* === experimental parameters === */
+/* these parameters can be used with ZSTD_setParameter()
+ * they are not guaranteed to remain supported in the future */
+
+ /* Enables rsyncable mode,
+ * which makes compressed files more rsync friendly
+ * by adding periodic synchronization points to the compressed data.
+ * The target average block size is ZSTD_c_jobSize / 2.
+ * It's possible to modify the job size to increase or decrease
+ * the granularity of the synchronization point.
+ * Once the jobSize is smaller than the window size,
+ * it will result in compression ratio degradation.
+ * NOTE 1: rsyncable mode only works when multithreading is enabled.
+ * NOTE 2: rsyncable performs poorly in combination with long range mode,
+ * since it will decrease the effectiveness of synchronization points,
+ * though mileage may vary.
+ * NOTE 3: Rsyncable mode limits maximum compression speed to ~400 MB/s.
+ * If the selected compression level is already running significantly slower,
+ * the overall speed won't be significantly impacted.
+ */
+ #define ZSTD_c_rsyncable ZSTD_c_experimentalParam1
+
+/* Select a compression format.
+ * The value must be of type ZSTD_format_e.
+ * See ZSTD_format_e enum definition for details */
+#define ZSTD_c_format ZSTD_c_experimentalParam2
+
+/* Force back-reference distances to remain < windowSize,
+ * even when referencing into Dictionary content (default:0) */
+#define ZSTD_c_forceMaxWindow ZSTD_c_experimentalParam3
+
+/* Controls whether the contents of a CDict
+ * are used in place, or copied into the working context.
+ * Accepts values from the ZSTD_dictAttachPref_e enum.
+ * See the comments on that enum for an explanation of the feature. */
+#define ZSTD_c_forceAttachDict ZSTD_c_experimentalParam4
+
+/*! ZSTD_CCtx_getParameter() :
+ * Get the requested compression parameter value, selected by enum ZSTD_cParameter,
+ * and store it into int* value.
+ * @return : 0, or an error code (which can be tested with ZSTD_isError()).
+ */
+ZSTDLIB_API size_t ZSTD_CCtx_getParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int* value);
+
+
+/*! ZSTD_CCtx_params :
+ * Quick howto :
+ * - ZSTD_createCCtxParams() : Create a ZSTD_CCtx_params structure
+ * - ZSTD_CCtxParam_setParameter() : Push parameters one by one into
+ * an existing ZSTD_CCtx_params structure.
+ * This is similar to
+ * ZSTD_CCtx_setParameter().
+ * - ZSTD_CCtx_setParametersUsingCCtxParams() : Apply parameters to
+ * an existing CCtx.
+ * These parameters will be applied to
+ * all subsequent frames.
+ * - ZSTD_compressStream2() : Do compression using the CCtx.
+ * - ZSTD_freeCCtxParams() : Free the memory.
+ *
+ * This can be used with ZSTD_estimateCCtxSize_advanced_usingCCtxParams()
+ * for static allocation of CCtx for single-threaded compression.
+ */
+ZSTDLIB_API ZSTD_CCtx_params* ZSTD_createCCtxParams(void);
+ZSTDLIB_API size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params);
+
+/*! ZSTD_CCtxParams_reset() :
+ * Reset params to default values.
+ */
+ZSTDLIB_API size_t ZSTD_CCtxParams_reset(ZSTD_CCtx_params* params);
+
+/*! ZSTD_CCtxParams_init() :
+ * Initializes the compression parameters of cctxParams according to
+ * compression level. All other parameters are reset to their default values.
+ */
+ZSTDLIB_API size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel);
+
+/*! ZSTD_CCtxParams_init_advanced() :
+ * Initializes the compression and frame parameters of cctxParams according to
+ * params. All other parameters are reset to their default values.
+ */
+ZSTDLIB_API size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params);
+
+/*! ZSTD_CCtxParam_setParameter() :
+ * Similar to ZSTD_CCtx_setParameter.
+ * Set one compression parameter, selected by enum ZSTD_cParameter.
+ * Parameters must be applied to a ZSTD_CCtx using ZSTD_CCtx_setParametersUsingCCtxParams().
+ * @result : 0, or an error code (which can be tested with ZSTD_isError()).
+ */
+ZSTDLIB_API size_t ZSTD_CCtxParam_setParameter(ZSTD_CCtx_params* params, ZSTD_cParameter param, int value);
+
+/*! ZSTD_CCtxParam_getParameter() :
+ * Similar to ZSTD_CCtx_getParameter.
+ * Get the requested value of one compression parameter, selected by enum ZSTD_cParameter.
+ * @result : 0, or an error code (which can be tested with ZSTD_isError()).
+ */
+ZSTDLIB_API size_t ZSTD_CCtxParam_getParameter(ZSTD_CCtx_params* params, ZSTD_cParameter param, int* value);
+
+/*! ZSTD_CCtx_setParametersUsingCCtxParams() :
+ * Apply a set of ZSTD_CCtx_params to the compression context.
+ * This can be done even after compression is started,
+ * if nbWorkers==0, this will have no impact until a new compression is started.
+ * if nbWorkers>=1, new parameters will be picked up at next job,
+ * with a few restrictions (windowLog, pledgedSrcSize, nbWorkers, jobSize, and overlapLog are not updated).
+ */
+ZSTDLIB_API size_t ZSTD_CCtx_setParametersUsingCCtxParams(
+ ZSTD_CCtx* cctx, const ZSTD_CCtx_params* params);
+
+/*! ZSTD_compressStream2_simpleArgs() :
+ * Same as ZSTD_compressStream2(),
+ * but using only integral types as arguments.
+ * This variant might be helpful for binders from dynamic languages
+ * which have troubles handling structures containing memory pointers.
+ */
+ZSTDLIB_API size_t ZSTD_compressStream2_simpleArgs (
+ ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity, size_t* dstPos,
+ const void* src, size_t srcSize, size_t* srcPos,
+ ZSTD_EndDirective endOp);
+
+
+/***************************************
+* Advanced decompression functions
+***************************************/
+
+/*! ZSTD_isFrame() :
+ * Tells if the content of `buffer` starts with a valid Frame Identifier.
+ * Note : Frame Identifier is 4 bytes. If `size < 4`, @return will always be 0.
+ * Note 2 : Legacy Frame Identifiers are considered valid only if Legacy Support is enabled.
+ * Note 3 : Skippable Frame Identifiers are considered valid. */
+ZSTDLIB_API unsigned ZSTD_isFrame(const void* buffer, size_t size);
+
+/*! ZSTD_createDDict_byReference() :
+ * Create a digested dictionary, ready to start decompression operation without startup delay.
+ * Dictionary content is referenced, and therefore stays in dictBuffer.
+ * It is important that dictBuffer outlives DDict,
+ * it must remain read accessible throughout the lifetime of DDict */
+ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize);
+
+
+/*! ZSTD_getDictID_fromDict() :
+ * Provides the dictID stored within dictionary.
+ * if @return == 0, the dictionary is not conformant with Zstandard specification.
+ * It can still be loaded, but as a content-only dictionary. */
+ZSTDLIB_API unsigned ZSTD_getDictID_fromDict(const void* dict, size_t dictSize);
+
+/*! ZSTD_getDictID_fromDDict() :
+ * Provides the dictID of the dictionary loaded into `ddict`.
+ * If @return == 0, the dictionary is not conformant to Zstandard specification, or empty.
+ * Non-conformant dictionaries can still be loaded, but as content-only dictionaries. */
+ZSTDLIB_API unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict);
+
+/*! ZSTD_getDictID_fromFrame() :
+ * Provides the dictID required to decompressed the frame stored within `src`.
+ * If @return == 0, the dictID could not be decoded.
+ * This could for one of the following reasons :
+ * - The frame does not require a dictionary to be decoded (most common case).
+ * - The frame was built with dictID intentionally removed. Whatever dictionary is necessary is a hidden information.
+ * Note : this use case also happens when using a non-conformant dictionary.
+ * - `srcSize` is too small, and as a result, the frame header could not be decoded (only possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`).
+ * - This is not a Zstandard frame.
+ * When identifying the exact failure cause, it's possible to use ZSTD_getFrameHeader(), which will provide a more precise error code. */
+ZSTDLIB_API unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize);
+
+/*! ZSTD_DCtx_loadDictionary_byReference() :
+ * Same as ZSTD_DCtx_loadDictionary(),
+ * but references `dict` content instead of copying it into `dctx`.
+ * This saves memory if `dict` remains around.,
+ * However, it's imperative that `dict` remains accessible (and unmodified) while being used, so it must outlive decompression. */
+ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
+
+/*! ZSTD_DCtx_loadDictionary_advanced() :
+ * Same as ZSTD_DCtx_loadDictionary(),
+ * but gives direct control over
+ * how to load the dictionary (by copy ? by reference ?)
+ * and how to interpret it (automatic ? force raw mode ? full mode only ?). */
+ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType);
+
+/*! ZSTD_DCtx_refPrefix_advanced() :
+ * Same as ZSTD_DCtx_refPrefix(), but gives finer control over
+ * how to interpret prefix content (automatic ? force raw mode (default) ? full mode only ?) */
+ZSTDLIB_API size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType);
+
+/*! ZSTD_DCtx_setMaxWindowSize() :
+ * Refuses allocating internal buffers for frames requiring a window size larger than provided limit.
+ * This protects a decoder context from reserving too much memory for itself (potential attack scenario).
+ * This parameter is only useful in streaming mode, since no internal buffer is allocated in single-pass mode.
+ * By default, a decompression context accepts all window sizes <= (1 << ZSTD_WINDOWLOG_LIMIT_DEFAULT)
+ * @return : 0, or an error code (which can be tested using ZSTD_isError()).
+ */
+ZSTDLIB_API size_t ZSTD_DCtx_setMaxWindowSize(ZSTD_DCtx* dctx, size_t maxWindowSize);
+
+/* ZSTD_d_format
+ * experimental parameter,
+ * allowing selection between ZSTD_format_e input compression formats
+ */
+#define ZSTD_d_format ZSTD_d_experimentalParam1
+
+/*! ZSTD_DCtx_setFormat() :
+ * Instruct the decoder context about what kind of data to decode next.
+ * This instruction is mandatory to decode data without a fully-formed header,
+ * such ZSTD_f_zstd1_magicless for example.
+ * @return : 0, or an error code (which can be tested using ZSTD_isError()). */
+ZSTDLIB_API size_t ZSTD_DCtx_setFormat(ZSTD_DCtx* dctx, ZSTD_format_e format);
+
+/*! ZSTD_decompressStream_simpleArgs() :
+ * Same as ZSTD_decompressStream(),
+ * but using only integral types as arguments.
+ * This can be helpful for binders from dynamic languages
+ * which have troubles handling structures containing memory pointers.
+ */
+ZSTDLIB_API size_t ZSTD_decompressStream_simpleArgs (
+ ZSTD_DCtx* dctx,
+ void* dst, size_t dstCapacity, size_t* dstPos,
+ const void* src, size_t srcSize, size_t* srcPos);
+
+
+/********************************************************************
+* Advanced streaming functions
+* Warning : most of these functions are now redundant with the Advanced API.
+* Once Advanced API reaches "stable" status,
+* redundant functions will be deprecated, and then at some point removed.
+********************************************************************/
+
+/*===== Advanced Streaming compression functions =====*/
+ZSTDLIB_API size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); /**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, "0" also disables frame content size field. It may be enabled in the future. */
+ZSTDLIB_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /**< creates of an internal CDict (incompatible with static CCtx), except if dict == NULL or dictSize < 8, in which case no dict is used. Note: dict is loaded with ZSTD_dm_auto (treated as a full zstd dictionary if it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.*/
+ZSTDLIB_API size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize,
+ ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize must be correct. If srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. */
+ZSTDLIB_API size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict); /**< note : cdict will just be referenced, and must outlive compression session */
+ZSTDLIB_API size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams, unsigned long long pledgedSrcSize); /**< same as ZSTD_initCStream_usingCDict(), with control over frame parameters. pledgedSrcSize must be correct. If srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. */
+
+/*! ZSTD_resetCStream() :
+ * start a new frame, using same parameters from previous frame.
+ * This is typically useful to skip dictionary loading stage, since it will re-use it in-place.
+ * Note that zcs must be init at least once before using ZSTD_resetCStream().
+ * If pledgedSrcSize is not known at reset time, use macro ZSTD_CONTENTSIZE_UNKNOWN.
+ * If pledgedSrcSize > 0, its value must be correct, as it will be written in header, and controlled at the end.
+ * For the time being, pledgedSrcSize==0 is interpreted as "srcSize unknown" for compatibility with older programs,
+ * but it will change to mean "empty" in future version, so use macro ZSTD_CONTENTSIZE_UNKNOWN instead.
+ * @return : 0, or an error code (which can be tested using ZSTD_isError())
+ */
+ZSTDLIB_API size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize);
+
+
+typedef struct {
+ unsigned long long ingested; /* nb input bytes read and buffered */
+ unsigned long long consumed; /* nb input bytes actually compressed */
+ unsigned long long produced; /* nb of compressed bytes generated and buffered */
+ unsigned long long flushed; /* nb of compressed bytes flushed : not provided; can be tracked from caller side */
+ unsigned currentJobID; /* MT only : latest started job nb */
+ unsigned nbActiveWorkers; /* MT only : nb of workers actively compressing at probe time */
+} ZSTD_frameProgression;
+
+/* ZSTD_getFrameProgression() :
+ * tells how much data has been ingested (read from input)
+ * consumed (input actually compressed) and produced (output) for current frame.
+ * Note : (ingested - consumed) is amount of input data buffered internally, not yet compressed.
+ * Aggregates progression inside active worker threads.
+ */
+ZSTDLIB_API ZSTD_frameProgression ZSTD_getFrameProgression(const ZSTD_CCtx* cctx);
+
+/*! ZSTD_toFlushNow() :
+ * Tell how many bytes are ready to be flushed immediately.
+ * Useful for multithreading scenarios (nbWorkers >= 1).
+ * Probe the oldest active job, defined as oldest job not yet entirely flushed,
+ * and check its output buffer.
+ * @return : amount of data stored in oldest job and ready to be flushed immediately.
+ * if @return == 0, it means either :
+ * + there is no active job (could be checked with ZSTD_frameProgression()), or
+ * + oldest job is still actively compressing data,
+ * but everything it has produced has also been flushed so far,
+ * therefore flush speed is limited by production speed of oldest job
+ * irrespective of the speed of concurrent (and newer) jobs.
+ */
+ZSTDLIB_API size_t ZSTD_toFlushNow(ZSTD_CCtx* cctx);
+
+
+/*===== Advanced Streaming decompression functions =====*/
+ZSTDLIB_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize); /**< note: no dictionary will be used if dict == NULL or dictSize < 8 */
+ZSTDLIB_API size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict); /**< note : ddict is referenced, it must outlive decompression session */
+ZSTDLIB_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); /**< re-use decompression parameters from previous init; saves dictionary loading */
+
+
+/*********************************************************************
+* Buffer-less and synchronous inner streaming functions
+*
+* This is an advanced API, giving full control over buffer management, for users which need direct control over memory.
+* But it's also a complex one, with several restrictions, documented below.
+* Prefer normal streaming API for an easier experience.
+********************************************************************* */
+
+/**
+ Buffer-less streaming compression (synchronous mode)
+
+ A ZSTD_CCtx object is required to track streaming operations.
+ Use ZSTD_createCCtx() / ZSTD_freeCCtx() to manage resource.
+ ZSTD_CCtx object can be re-used multiple times within successive compression operations.
+
+ Start by initializing a context.
+ Use ZSTD_compressBegin(), or ZSTD_compressBegin_usingDict() for dictionary compression,
+ or ZSTD_compressBegin_advanced(), for finer parameter control.
+ It's also possible to duplicate a reference context which has already been initialized, using ZSTD_copyCCtx()
+
+ Then, consume your input using ZSTD_compressContinue().
+ There are some important considerations to keep in mind when using this advanced function :
+ - ZSTD_compressContinue() has no internal buffer. It uses externally provided buffers only.
+ - Interface is synchronous : input is consumed entirely and produces 1+ compressed blocks.
+ - Caller must ensure there is enough space in `dst` to store compressed data under worst case scenario.
+ Worst case evaluation is provided by ZSTD_compressBound().
+ ZSTD_compressContinue() doesn't guarantee recover after a failed compression.
+ - ZSTD_compressContinue() presumes prior input ***is still accessible and unmodified*** (up to maximum distance size, see WindowLog).
+ It remembers all previous contiguous blocks, plus one separated memory segment (which can itself consists of multiple contiguous blocks)
+ - ZSTD_compressContinue() detects that prior input has been overwritten when `src` buffer overlaps.
+ In which case, it will "discard" the relevant memory section from its history.
+
+ Finish a frame with ZSTD_compressEnd(), which will write the last block(s) and optional checksum.
+ It's possible to use srcSize==0, in which case, it will write a final empty block to end the frame.
+ Without last block mark, frames are considered unfinished (hence corrupted) by compliant decoders.
+
+ `ZSTD_CCtx` object can be re-used (ZSTD_compressBegin()) to compress again.
+*/
+
+/*===== Buffer-less streaming compression functions =====*/
+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); /**< pledgedSrcSize : If srcSize is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN */
+ZSTDLIB_API size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); /**< note: fails if cdict==NULL */
+ZSTDLIB_API size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize); /* compression parameters are already set within cdict. pledgedSrcSize must be correct. If srcSize is not known, use macro ZSTD_CONTENTSIZE_UNKNOWN */
+ZSTDLIB_API size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**< note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */
+
+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);
+
+
+/*-
+ Buffer-less streaming decompression (synchronous mode)
+
+ A ZSTD_DCtx object is required to track streaming operations.
+ Use ZSTD_createDCtx() / ZSTD_freeDCtx() to manage it.
+ A ZSTD_DCtx object can be re-used multiple times.
+
+ First typical operation is to retrieve frame parameters, using ZSTD_getFrameHeader().
+ Frame header is extracted from the beginning of compressed frame, so providing only the frame's beginning is enough.
+ Data fragment must be large enough to ensure successful decoding.
+ `ZSTD_frameHeaderSize_max` bytes is guaranteed to always be large enough.
+ @result : 0 : successful decoding, the `ZSTD_frameHeader` structure is correctly filled.
+ >0 : `srcSize` is too small, please provide at least @result bytes on next attempt.
+ errorCode, which can be tested using ZSTD_isError().
+
+ It fills a ZSTD_frameHeader structure with important information to correctly decode the frame,
+ such as the dictionary ID, content size, or maximum back-reference distance (`windowSize`).
+ Note that these values could be wrong, either because of data corruption, or because a 3rd party deliberately spoofs false information.
+ As a consequence, check that values remain within valid application range.
+ For example, do not allocate memory blindly, check that `windowSize` is within expectation.
+ Each application can set its own limits, depending on local restrictions.
+ For extended interoperability, it is recommended to support `windowSize` of at least 8 MB.
+
+ ZSTD_decompressContinue() needs previous data blocks during decompression, up to `windowSize` bytes.
+ ZSTD_decompressContinue() is very sensitive to contiguity,
+ if 2 blocks don't follow each other, make sure that either the compressor breaks contiguity at the same place,
+ or that previous contiguous segment is large enough to properly handle maximum back-reference distance.
+ There are multiple ways to guarantee this condition.
+
+ The most memory efficient way is to use a round buffer of sufficient size.
+ Sufficient size is determined by invoking ZSTD_decodingBufferSize_min(),
+ which can @return an error code if required value is too large for current system (in 32-bits mode).
+ In a round buffer methodology, ZSTD_decompressContinue() decompresses each block next to previous one,
+ up to the moment there is not enough room left in the buffer to guarantee decoding another full block,
+ which maximum size is provided in `ZSTD_frameHeader` structure, field `blockSizeMax`.
+ At which point, decoding can resume from the beginning of the buffer.
+ Note that already decoded data stored in the buffer should be flushed before being overwritten.
+
+ There are alternatives possible, for example using two or more buffers of size `windowSize` each, though they consume more memory.
+
+ Finally, if you control the compression process, you can also ignore all buffer size rules,
+ as long as the encoder and decoder progress in "lock-step",
+ aka use exactly the same buffer sizes, break contiguity at the same place, etc.
+
+ Once buffers are setup, start decompression, with ZSTD_decompressBegin().
+ If decompression requires a dictionary, use ZSTD_decompressBegin_usingDict() or ZSTD_decompressBegin_usingDDict().
+
+ Then use ZSTD_nextSrcSizeToDecompress() and ZSTD_decompressContinue() alternatively.
+ ZSTD_nextSrcSizeToDecompress() tells how many bytes to provide as 'srcSize' to ZSTD_decompressContinue().
+ ZSTD_decompressContinue() requires this _exact_ amount of bytes, or it will fail.
+
+ @result of ZSTD_decompressContinue() is the number of bytes regenerated within 'dst' (necessarily <= dstCapacity).
+ It can be zero : it just means ZSTD_decompressContinue() has decoded some metadata item.
+ It can also be an error code, which can be tested with ZSTD_isError().
+
+ A frame is fully decoded when ZSTD_nextSrcSizeToDecompress() returns zero.
+ Context can then be reset to start a new decompression.
+
+ Note : it's possible to know if next input to present is a header or a block, using ZSTD_nextInputType().
+ This information is not required to properly decode a frame.
+
+ == Special case : skippable frames ==
+
+ Skippable frames allow integration of user-defined data into a flow of concatenated frames.
+ Skippable frames will be ignored (skipped) by decompressor.
+ The format of skippable frames is as follows :
+ a) Skippable frame ID - 4 Bytes, Little endian format, any value from 0x184D2A50 to 0x184D2A5F
+ b) Frame Size - 4 Bytes, Little endian format, unsigned 32-bits
+ c) Frame Content - any content (User Data) of length equal to Frame Size
+ For skippable frames ZSTD_getFrameHeader() returns zfhPtr->frameType==ZSTD_skippableFrame.
+ For skippable frames ZSTD_decompressContinue() always returns 0 : it only skips the content.
+*/
+
+/*===== Buffer-less streaming decompression functions =====*/
+typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e;
+typedef struct {
+ unsigned long long frameContentSize; /* if == ZSTD_CONTENTSIZE_UNKNOWN, it means this field is not available. 0 means "empty" */
+ unsigned long long windowSize; /* can be very large, up to <= frameContentSize */
+ unsigned blockSizeMax;
+ ZSTD_frameType_e frameType; /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */
+ unsigned headerSize;
+ unsigned dictID;
+ unsigned checksumFlag;
+} ZSTD_frameHeader;
+
+/** ZSTD_getFrameHeader() :
+ * decode Frame Header, or requires larger `srcSize`.
+ * @return : 0, `zfhPtr` is correctly filled,
+ * >0, `srcSize` is too small, value is wanted `srcSize` amount,
+ * or an error code, which can be tested using ZSTD_isError() */
+ZSTDLIB_API size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize); /**< doesn't consume input */
+/*! ZSTD_getFrameHeader_advanced() :
+ * same as ZSTD_getFrameHeader(),
+ * with added capability to select a format (like ZSTD_f_zstd1_magicless) */
+ZSTDLIB_API size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize, ZSTD_format_e format);
+ZSTDLIB_API size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long long frameContentSize); /**< when frame content size is not known, pass in frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN */
+
+ZSTDLIB_API size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx);
+ZSTDLIB_API size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
+ZSTDLIB_API size_t ZSTD_decompressBegin_usingDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict);
+
+ZSTDLIB_API size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx);
+ZSTDLIB_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
+
+/* misc */
+ZSTDLIB_API void ZSTD_copyDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx);
+typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e;
+ZSTDLIB_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx);
+
+
+
+
+/* ============================ */
+/** Block level API */
+/* ============================ */
+
+/*!
+ Block functions produce and decode raw zstd blocks, without frame metadata.
+ Frame metadata cost is typically ~18 bytes, which can be non-negligible for very small blocks (< 100 bytes).
+ User will have to take in charge required information to regenerate data, such as compressed and content sizes.
+
+ A few rules to respect :
+ - Compressing and decompressing require a context structure
+ + Use ZSTD_createCCtx() and ZSTD_createDCtx()
+ - It is necessary to init context before starting
+ + compression : any ZSTD_compressBegin*() variant, including with dictionary
+ + decompression : any ZSTD_decompressBegin*() variant, including with dictionary
+ + copyCCtx() and copyDCtx() can be used too
+ - Block size is limited, it must be <= ZSTD_getBlockSize() <= ZSTD_BLOCKSIZE_MAX == 128 KB
+ + If input is larger than a block size, it's necessary to split input data into multiple blocks
+ + For inputs larger than a single block, really consider using regular ZSTD_compress() instead.
+ Frame metadata is not that costly, and quickly becomes negligible as source size grows larger.
+ - When a block is considered not compressible enough, ZSTD_compressBlock() result will be zero.
+ In which case, nothing is produced into `dst` !
+ + User must test for such outcome and deal directly with uncompressed data
+ + ZSTD_decompressBlock() doesn't accept uncompressed data as input !!!
+ + In case of multiple successive blocks, should some of them be uncompressed,
+ decoder must be informed of their existence in order to follow proper history.
+ Use ZSTD_insertBlock() for such a case.
+*/
+
+/*===== Raw zstd block functions =====*/
+ZSTDLIB_API size_t ZSTD_getBlockSize (const ZSTD_CCtx* cctx);
+ZSTDLIB_API size_t ZSTD_compressBlock (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
+ZSTDLIB_API size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
+ZSTDLIB_API size_t ZSTD_insertBlock (ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize); /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression. */
+
+
+#endif /* ZSTD_H_ZSTD_STATIC_LINKING_ONLY */
+
+#if defined (__cplusplus)
+}
+#endif