From ec538b77d1573aaecb95feb1d39f7f3add547e45 Mon Sep 17 00:00:00 2001 From: JinWang An Date: Tue, 3 Aug 2021 16:30:09 +0900 Subject: [PATCH] Imported Upstream version 4.0 --- .clang-format | 38 + .dockerignore | 2 + .editorconfig | 17 + .git-blame-ignore-revs | 2 + .github/ISSUE_TEMPLATE/bug_report.md | 32 + .github/ISSUE_TEMPLATE/feature_request.md | 10 + .github/ISSUE_TEMPLATE/improvement.md | 11 + .github/ISSUE_TEMPLATE/support.md | 10 + .github/PULL_REQUEST_TEMPLATE.md | 10 + .github/no-response.yml | 12 + .github/workflows/build.yaml | 241 + .gitignore | 19 + .mailmap | 29 + CMakeLists.txt | 147 + CONTRIBUTING.md | 122 +- GPL-3.0.txt | 8 +- INSTALL.md | 52 - LGPL-3.0.txt | 165 + LICENSE.adoc | 979 ++- LICENSE.html | 1244 --- Makefile.in | 182 - README.md | 17 +- autogen.sh | 20 - ci/build | 32 + ci/build-and-verify-package | 21 + ci/build-and-verify-source-package | 24 + ci/collect-testdir | 12 + ci/install-cuda | 32 + cmake/.gitattributes | 1 + cmake/CIBuildType.cmake | 31 + cmake/CcachePackConfig.cmake | 24 + cmake/CcacheVersion.cmake | 64 + cmake/CheckAsmCompilerFlag.cmake | 62 + cmake/CodeAnalysis.cmake | 38 + cmake/DefaultBuildType.cmake | 27 + cmake/Findzstd.cmake | 66 + cmake/GenerateConfigurationFile.cmake | 76 + cmake/GenerateVersionFile.cmake | 6 + cmake/StandardSettings.cmake | 52 + cmake/StandardWarnings.cmake | 160 + cmake/config.h.in | 162 + cmake/version.cpp.in | 2 + config.guess | 1476 ---- config.h.in | 259 - config.sub | 1800 ---- configure | 7773 ----------------- configure.ac | 266 - dev.mk.in | 246 - dev_mode_disabled | 1 - doc/AUTHORS.adoc | 22 +- doc/AUTHORS.html | 1295 --- doc/CMakeLists.txt | 66 + doc/DEVELOPER.md | 33 + doc/INSTALL.md | 68 + doc/MANUAL.adoc | 935 +- doc/MANUAL.html | 2728 ------ doc/NEWS.adoc | 260 +- doc/NEWS.html | 3948 --------- doc/ccache.1 | 2258 ----- dockerfiles/README | 11 + dockerfiles/alpine-3.12/Dockerfile | 17 + dockerfiles/alpine-3.4/Dockerfile | 16 + dockerfiles/centos-7/Dockerfile | 21 + dockerfiles/centos-8/Dockerfile | 17 + dockerfiles/debian-10/Dockerfile | 17 + dockerfiles/debian-9/Dockerfile | 17 + dockerfiles/fedora-32/Dockerfile | 16 + dockerfiles/ubuntu-14.04/Dockerfile | 23 + dockerfiles/ubuntu-16.04/Dockerfile | 18 + dockerfiles/ubuntu-20.04/Dockerfile | 19 + install-sh | 238 - m4/clang.m4 | 19 - m4/feature_macros.m4 | 147 - m4/snprintf.m4 | 224 - misc/build-in-docker | 57 + misc/codespell-allowlist.txt | 8 + misc/combine-trace-files | 27 + misc/cppcheck-suppressions.txt | 5 + misc/format-files | 59 + misc/performance | 353 + misc/rsyslog.d/00-ccache.conf | 2 + misc/shellcheck-excludes.txt | 8 + misc/summarize-trace-files | 93 + misc/test-all-systems | 51 + misc/update-authors | 14 + src/.clang-tidy | 77 + src/Args.cpp | 218 + src/Args.hpp | 129 + src/ArgsInfo.hpp | 115 + src/AtomicFile.cpp | 69 + src/AtomicFile.hpp | 56 + src/CMakeLists.txt | 74 + src/CacheEntryReader.cpp | 93 + src/CacheEntryReader.hpp | 145 + src/CacheEntryWriter.cpp | 60 + src/CacheEntryWriter.hpp | 86 + src/CacheFile.cpp | 45 + src/CacheFile.hpp | 57 + src/Checksum.hpp | 69 + src/Compression.cpp | 68 + src/Compression.hpp | 42 + src/Compressor.cpp | 40 + src/Compressor.hpp | 67 + src/Config.cpp | 843 ++ src/Config.hpp | 477 + src/Context.cpp | 90 + src/Context.hpp | 221 + src/Counters.cpp | 96 + src/Counters.hpp | 50 + src/Decompressor.cpp | 38 + src/Decompressor.hpp | 54 + src/Digest.hpp | 95 + src/Fd.hpp | 104 + src/File.hpp | 104 + src/Finalizer.hpp | 43 + src/FormatNonstdStringView.hpp | 47 + src/Hash.cpp | 140 + src/Hash.hpp | 105 + src/InodeCache.cpp | 488 ++ src/InodeCache.hpp | 110 + src/Lockfile.cpp | 224 + src/Lockfile.hpp | 55 + src/Logging.cpp | 189 + src/Logging.hpp | 77 + src/Manifest.cpp | 623 ++ src/Manifest.hpp | 47 + src/MiniTrace.cpp | 90 + unittest/util.c => src/MiniTrace.hpp | 46 +- src/NonCopyable.hpp | 29 + src/NullCompressor.cpp | 47 + src/NullCompressor.hpp | 40 + src/NullDecompressor.cpp | 41 + src/NullDecompressor.hpp | 39 + src/ProgressBar.cpp | 93 + src/ProgressBar.hpp | 41 + src/Result.cpp | 432 + src/Result.hpp | 138 + src/ResultDumper.cpp | 59 + src/ResultDumper.hpp | 41 + src/ResultExtractor.cpp | 84 + src/ResultExtractor.hpp | 46 + src/ResultRetriever.cpp | 180 + src/ResultRetriever.hpp | 59 + src/SignalHandler.cpp | 145 + src/SignalHandler.hpp | 46 + src/Stat.cpp | 45 + src/Stat.hpp | 227 + src/Statistics.cpp | 360 + src/Statistics.hpp | 96 + src/{counters.h => StdMakeUnique.hpp} | 28 +- src/TemporaryFile.cpp | 75 + src/TemporaryFile.hpp | 48 + src/ThreadPool.cpp | 84 + src/ThreadPool.hpp | 51 + src/UmaskScope.hpp | 55 + src/Util.cpp | 1560 ++++ src/Util.hpp | 496 ++ src/Win32Util.cpp | 168 + src/Win32Util.hpp | 38 + src/ZstdCompressor.cpp | 115 + src/ZstdCompressor.hpp | 51 + src/ZstdDecompressor.cpp | 83 + src/ZstdDecompressor.hpp | 50 + src/argprocessing.cpp | 1216 +++ src/argprocessing.hpp | 62 + src/args.c | 313 - src/assertions.cpp | 38 + src/assertions.hpp | 50 + src/ccache.c | 4447 ---------- src/ccache.cpp | 2772 ++++++ src/ccache.h | 325 - src/ccache.hpp | 64 + src/cleanup.c | 270 - src/cleanup.cpp | 237 + src/cleanup.hpp | 44 + src/compopt.c | 274 - src/compopt.cpp | 276 + src/compopt.h | 17 - src/compopt.hpp | 35 + src/compress.cpp | 369 + src/compress.hpp | 42 + src/conf.c | 447 - src/conf.h | 57 - src/confitems.c | 289 - src/confitems.gperf | 55 - src/confitems.h | 49 - src/confitems_lookup.c | 163 - src/counters.c | 63 - src/envtoconfitems.gperf | 47 - src/envtoconfitems.h | 14 - src/envtoconfitems_lookup.c | 171 - src/exceptions.hpp | 117 + src/execute.c | 394 - src/execute.cpp | 279 + src/execute.hpp | 48 + src/exitfn.c | 100 - src/hash.c | 188 - src/hash.h | 100 - src/hashtable.c | 309 - src/hashtable.h | 230 - src/hashtable_itr.c | 189 - src/hashtable_itr.h | 122 - src/hashtable_private.h | 87 - src/hashutil.c | 316 - src/hashutil.cpp | 522 ++ src/hashutil.h | 51 - src/hashutil.hpp | 73 + src/language.c | 151 - src/language.cpp | 148 + src/language.h | 12 - src/language.hpp | 41 + src/lockfile.c | 253 - src/macroskip.h | 61 - src/macroskip.hpp | 72 + src/{main.c => main.cpp} | 11 +- src/manifest.c | 821 -- src/manifest.h | 15 - src/mdfour.c | 213 - src/mdfour.h | 18 - src/murmurhashneutral2.c | 44 - src/murmurhashneutral2.h | 6 - src/snprintf.c | 2116 ----- src/stats.c | 721 -- src/system.h | 94 - src/system.hpp | 190 + src/third_party/.clang-tidy | 9 + src/third_party/CMakeLists.txt | 76 + src/third_party/base32hex.c | 79 + src/third_party/base32hex.h | 23 + src/third_party/blake3/CMakeLists.txt | 41 + src/third_party/blake3/blake3.c | 603 ++ src/third_party/blake3/blake3.h | 58 + src/third_party/blake3/blake3_avx2.c | 325 + .../blake3/blake3_avx2_x86-64_unix.S | 1815 ++++ .../blake3/blake3_avx2_x86-64_windows_gnu.S | 1817 ++++ src/third_party/blake3/blake3_avx512.c | 1204 +++ .../blake3/blake3_avx512_x86-64_unix.S | 2585 ++++++ .../blake3/blake3_avx512_x86-64_windows_gnu.S | 2615 ++++++ src/third_party/blake3/blake3_dispatch.c | 270 + src/third_party/blake3/blake3_impl.h | 269 + src/third_party/blake3/blake3_neon.c | 346 + src/third_party/blake3/blake3_portable.c | 160 + src/third_party/blake3/blake3_sse2.c | 565 ++ .../blake3/blake3_sse2_x86-64_unix.S | 2291 +++++ .../blake3/blake3_sse2_x86-64_windows_gnu.S | 2332 +++++ .../blake3_sse2_x86-64_windows_msvc.asm | 2350 +++++ src/third_party/blake3/blake3_sse41.c | 559 ++ .../blake3/blake3_sse41_x86-64_unix.S | 2028 +++++ .../blake3/blake3_sse41_x86-64_windows_gnu.S | 2069 +++++ src/third_party/doctest.h | 6205 +++++++++++++ src/third_party/fmt/core.h | 1882 ++++ src/third_party/fmt/format-inl.h | 1453 +++ src/third_party/fmt/format.h | 3729 ++++++++ src/third_party/format.cpp | 69 + src/{ => third_party}/getopt_long.c | 0 src/{ => third_party}/getopt_long.h | 0 src/{ => third_party}/minitrace.c | 0 src/{ => third_party}/minitrace.h | 0 src/third_party/nonstd/optional.hpp | 1698 ++++ src/third_party/nonstd/string_view.hpp | 1626 ++++ src/third_party/win32/getopt.c | 975 +++ src/third_party/win32/getopt.h | 136 + src/third_party/xxh_x86dispatch.c | 749 ++ src/third_party/xxh_x86dispatch.h | 86 + src/third_party/xxhash.c | 43 + src/third_party/xxhash.h | 4766 ++++++++++ src/util.c | 1732 ---- src/version.c | 1 - src/zlib/adler32.c | 186 - src/zlib/crc32.c | 442 - src/zlib/crc32.h | 441 - src/zlib/deflate.c | 2163 ----- src/zlib/deflate.h | 349 - src/zlib/gzclose.c | 25 - src/zlib/gzguts.h | 218 - src/zlib/gzlib.c | 637 -- src/zlib/gzread.c | 654 -- src/zlib/gzwrite.c | 665 -- src/zlib/inffast.c | 323 - src/zlib/inffast.h | 11 - src/zlib/inffixed.h | 94 - src/zlib/inflate.c | 1561 ---- src/zlib/inflate.h | 125 - src/zlib/inftrees.c | 304 - src/zlib/inftrees.h | 62 - src/zlib/trees.c | 1203 --- src/zlib/trees.h | 128 - src/zlib/zconf.h | 534 -- src/zlib/zlib.h | 1912 ---- src/zlib/zutil.c | 325 - src/zlib/zutil.h | 271 - test/CMakeLists.txt | 69 + test/run | 296 +- test/suites/base.bash | 337 +- test/suites/basedir.bash | 177 +- test/suites/cache_levels.bash | 124 + test/suites/cleanup.bash | 160 +- test/suites/color_diagnostics.bash | 140 + test/suites/compression.bash | 23 - test/suites/debug_prefix_map.bash | 45 +- test/suites/depend.bash | 60 +- test/suites/direct.bash | 307 +- test/suites/direct_gcc.bash | 24 +- test/suites/fileclone.bash | 57 + test/suites/hardlink.bash | 51 +- test/suites/inode_cache.bash | 96 + test/suites/masquerading.bash | 41 +- test/suites/modules.bash | 83 + test/suites/no_compression.bash | 80 + test/suites/nvcc.bash | 64 +- test/suites/nvcc_direct.bash | 40 +- test/suites/nvcc_ldir.bash | 8 +- test/suites/pch.bash | 71 +- test/suites/profiling.bash | 36 +- test/suites/profiling_clang.bash | 3 +- test/suites/profiling_gcc.bash | 8 +- test/suites/profiling_hip_clang.bash | 55 + test/suites/readonly.bash | 13 +- test/suites/readonly_direct.bash | 9 + test/suites/serialize_diagnostics.bash | 16 +- test/suites/split_dwarf.bash | 45 +- test/suites/upgrade.bash | 121 +- unittest/.clang-tidy | 27 + unittest/CMakeLists.txt | 40 + unittest/TestUtil.cpp | 57 + unittest/TestUtil.hpp | 47 + unittest/framework.c | 300 - unittest/framework.h | 152 - unittest/main.c | 89 - unittest/main.cpp | 54 + unittest/test_Args.cpp | 294 + unittest/test_AtomicFile.cpp | 52 + unittest/{test_stats.c => test_Checksum.cpp} | 41 +- unittest/test_Compression.cpp | 51 + unittest/test_Config.cpp | 468 + unittest/test_Counters.cpp | 92 + unittest/test_FormatNonstdStringView.cpp | 50 + unittest/test_Hash.cpp | 86 + unittest/test_InodeCache.cpp | 197 + unittest/test_Lockfile.cpp | 60 + unittest/test_NullCompression.cpp | 71 + unittest/test_Stat.cpp | 220 + unittest/test_Statistics.cpp | 101 + unittest/test_Util.cpp | 994 +++ unittest/test_Win32Util.cpp | 43 + unittest/test_ZstdCompression.cpp | 123 + unittest/test_argprocessing.cpp | 655 ++ unittest/test_args.c | 209 - unittest/test_argument_processing.c | 489 -- unittest/test_ccache.cpp | 184 + unittest/test_compopt.c | 138 - unittest/test_compopt.cpp | 94 + unittest/test_conf.c | 566 -- unittest/test_counters.c | 60 - unittest/test_hash.c | 80 - unittest/test_hashutil.c | 216 - unittest/test_hashutil.cpp | 236 + unittest/test_lockfile.c | 65 - unittest/test_util.c | 210 - unittest/util.h | 10 - 360 files changed, 76256 insertions(+), 56262 deletions(-) create mode 100644 .clang-format create mode 100644 .dockerignore create mode 100644 .editorconfig create mode 100644 .git-blame-ignore-revs create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/improvement.md create mode 100644 .github/ISSUE_TEMPLATE/support.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/no-response.yml create mode 100644 .github/workflows/build.yaml create mode 100644 .gitignore create mode 100644 .mailmap create mode 100644 CMakeLists.txt delete mode 100644 INSTALL.md create mode 100644 LGPL-3.0.txt delete mode 100644 LICENSE.html delete mode 100644 Makefile.in delete mode 100755 autogen.sh create mode 100755 ci/build create mode 100755 ci/build-and-verify-package create mode 100755 ci/build-and-verify-source-package create mode 100755 ci/collect-testdir create mode 100755 ci/install-cuda create mode 100644 cmake/.gitattributes create mode 100644 cmake/CIBuildType.cmake create mode 100644 cmake/CcachePackConfig.cmake create mode 100644 cmake/CcacheVersion.cmake create mode 100644 cmake/CheckAsmCompilerFlag.cmake create mode 100644 cmake/CodeAnalysis.cmake create mode 100644 cmake/DefaultBuildType.cmake create mode 100644 cmake/Findzstd.cmake create mode 100644 cmake/GenerateConfigurationFile.cmake create mode 100644 cmake/GenerateVersionFile.cmake create mode 100644 cmake/StandardSettings.cmake create mode 100644 cmake/StandardWarnings.cmake create mode 100644 cmake/config.h.in create mode 100644 cmake/version.cpp.in delete mode 100644 config.guess delete mode 100644 config.h.in delete mode 100644 config.sub delete mode 100755 configure delete mode 100644 configure.ac delete mode 100644 dev.mk.in delete mode 100644 dev_mode_disabled delete mode 100644 doc/AUTHORS.html create mode 100644 doc/CMakeLists.txt create mode 100644 doc/DEVELOPER.md create mode 100644 doc/INSTALL.md delete mode 100644 doc/MANUAL.html delete mode 100644 doc/NEWS.html delete mode 100644 doc/ccache.1 create mode 100644 dockerfiles/README create mode 100644 dockerfiles/alpine-3.12/Dockerfile create mode 100644 dockerfiles/alpine-3.4/Dockerfile create mode 100644 dockerfiles/centos-7/Dockerfile create mode 100644 dockerfiles/centos-8/Dockerfile create mode 100644 dockerfiles/debian-10/Dockerfile create mode 100644 dockerfiles/debian-9/Dockerfile create mode 100644 dockerfiles/fedora-32/Dockerfile create mode 100644 dockerfiles/ubuntu-14.04/Dockerfile create mode 100644 dockerfiles/ubuntu-16.04/Dockerfile create mode 100644 dockerfiles/ubuntu-20.04/Dockerfile delete mode 100755 install-sh delete mode 100644 m4/clang.m4 delete mode 100644 m4/feature_macros.m4 delete mode 100644 m4/snprintf.m4 create mode 100755 misc/build-in-docker create mode 100644 misc/codespell-allowlist.txt create mode 100755 misc/combine-trace-files create mode 100644 misc/cppcheck-suppressions.txt create mode 100755 misc/format-files create mode 100755 misc/performance create mode 100644 misc/rsyslog.d/00-ccache.conf create mode 100644 misc/shellcheck-excludes.txt create mode 100755 misc/summarize-trace-files create mode 100755 misc/test-all-systems create mode 100755 misc/update-authors create mode 100644 src/.clang-tidy create mode 100644 src/Args.cpp create mode 100644 src/Args.hpp create mode 100644 src/ArgsInfo.hpp create mode 100644 src/AtomicFile.cpp create mode 100644 src/AtomicFile.hpp create mode 100644 src/CMakeLists.txt create mode 100644 src/CacheEntryReader.cpp create mode 100644 src/CacheEntryReader.hpp create mode 100644 src/CacheEntryWriter.cpp create mode 100644 src/CacheEntryWriter.hpp create mode 100644 src/CacheFile.cpp create mode 100644 src/CacheFile.hpp create mode 100644 src/Checksum.hpp create mode 100644 src/Compression.cpp create mode 100644 src/Compression.hpp create mode 100644 src/Compressor.cpp create mode 100644 src/Compressor.hpp create mode 100644 src/Config.cpp create mode 100644 src/Config.hpp create mode 100644 src/Context.cpp create mode 100644 src/Context.hpp create mode 100644 src/Counters.cpp create mode 100644 src/Counters.hpp create mode 100644 src/Decompressor.cpp create mode 100644 src/Decompressor.hpp create mode 100644 src/Digest.hpp create mode 100644 src/Fd.hpp create mode 100644 src/File.hpp create mode 100644 src/Finalizer.hpp create mode 100644 src/FormatNonstdStringView.hpp create mode 100644 src/Hash.cpp create mode 100644 src/Hash.hpp create mode 100644 src/InodeCache.cpp create mode 100644 src/InodeCache.hpp create mode 100644 src/Lockfile.cpp create mode 100644 src/Lockfile.hpp create mode 100644 src/Logging.cpp create mode 100644 src/Logging.hpp create mode 100644 src/Manifest.cpp create mode 100644 src/Manifest.hpp create mode 100644 src/MiniTrace.cpp rename unittest/util.c => src/MiniTrace.hpp (62%) create mode 100644 src/NonCopyable.hpp create mode 100644 src/NullCompressor.cpp create mode 100644 src/NullCompressor.hpp create mode 100644 src/NullDecompressor.cpp create mode 100644 src/NullDecompressor.hpp create mode 100644 src/ProgressBar.cpp create mode 100644 src/ProgressBar.hpp create mode 100644 src/Result.cpp create mode 100644 src/Result.hpp create mode 100644 src/ResultDumper.cpp create mode 100644 src/ResultDumper.hpp create mode 100644 src/ResultExtractor.cpp create mode 100644 src/ResultExtractor.hpp create mode 100644 src/ResultRetriever.cpp create mode 100644 src/ResultRetriever.hpp create mode 100644 src/SignalHandler.cpp create mode 100644 src/SignalHandler.hpp create mode 100644 src/Stat.cpp create mode 100644 src/Stat.hpp create mode 100644 src/Statistics.cpp create mode 100644 src/Statistics.hpp rename src/{counters.h => StdMakeUnique.hpp} (65%) create mode 100644 src/TemporaryFile.cpp create mode 100644 src/TemporaryFile.hpp create mode 100644 src/ThreadPool.cpp create mode 100644 src/ThreadPool.hpp create mode 100644 src/UmaskScope.hpp create mode 100644 src/Util.cpp create mode 100644 src/Util.hpp create mode 100644 src/Win32Util.cpp create mode 100644 src/Win32Util.hpp create mode 100644 src/ZstdCompressor.cpp create mode 100644 src/ZstdCompressor.hpp create mode 100644 src/ZstdDecompressor.cpp create mode 100644 src/ZstdDecompressor.hpp create mode 100644 src/argprocessing.cpp create mode 100644 src/argprocessing.hpp delete mode 100644 src/args.c create mode 100644 src/assertions.cpp create mode 100644 src/assertions.hpp delete mode 100644 src/ccache.c create mode 100644 src/ccache.cpp delete mode 100644 src/ccache.h create mode 100644 src/ccache.hpp delete mode 100644 src/cleanup.c create mode 100644 src/cleanup.cpp create mode 100644 src/cleanup.hpp delete mode 100644 src/compopt.c create mode 100644 src/compopt.cpp delete mode 100644 src/compopt.h create mode 100644 src/compopt.hpp create mode 100644 src/compress.cpp create mode 100644 src/compress.hpp delete mode 100644 src/conf.c delete mode 100644 src/conf.h delete mode 100644 src/confitems.c delete mode 100644 src/confitems.gperf delete mode 100644 src/confitems.h delete mode 100644 src/confitems_lookup.c delete mode 100644 src/counters.c delete mode 100644 src/envtoconfitems.gperf delete mode 100644 src/envtoconfitems.h delete mode 100644 src/envtoconfitems_lookup.c create mode 100644 src/exceptions.hpp delete mode 100644 src/execute.c create mode 100644 src/execute.cpp create mode 100644 src/execute.hpp delete mode 100644 src/exitfn.c delete mode 100644 src/hash.c delete mode 100644 src/hash.h delete mode 100644 src/hashtable.c delete mode 100644 src/hashtable.h delete mode 100644 src/hashtable_itr.c delete mode 100644 src/hashtable_itr.h delete mode 100644 src/hashtable_private.h delete mode 100644 src/hashutil.c create mode 100644 src/hashutil.cpp delete mode 100644 src/hashutil.h create mode 100644 src/hashutil.hpp delete mode 100644 src/language.c create mode 100644 src/language.cpp delete mode 100644 src/language.h create mode 100644 src/language.hpp delete mode 100644 src/lockfile.c delete mode 100644 src/macroskip.h create mode 100644 src/macroskip.hpp rename src/{main.c => main.cpp} (74%) delete mode 100644 src/manifest.c delete mode 100644 src/manifest.h delete mode 100644 src/mdfour.c delete mode 100644 src/mdfour.h delete mode 100644 src/murmurhashneutral2.c delete mode 100644 src/murmurhashneutral2.h delete mode 100644 src/snprintf.c delete mode 100644 src/stats.c delete mode 100644 src/system.h create mode 100644 src/system.hpp create mode 100644 src/third_party/.clang-tidy create mode 100644 src/third_party/CMakeLists.txt create mode 100644 src/third_party/base32hex.c create mode 100644 src/third_party/base32hex.h create mode 100644 src/third_party/blake3/CMakeLists.txt create mode 100644 src/third_party/blake3/blake3.c create mode 100644 src/third_party/blake3/blake3.h create mode 100644 src/third_party/blake3/blake3_avx2.c create mode 100644 src/third_party/blake3/blake3_avx2_x86-64_unix.S create mode 100644 src/third_party/blake3/blake3_avx2_x86-64_windows_gnu.S create mode 100644 src/third_party/blake3/blake3_avx512.c create mode 100644 src/third_party/blake3/blake3_avx512_x86-64_unix.S create mode 100644 src/third_party/blake3/blake3_avx512_x86-64_windows_gnu.S create mode 100644 src/third_party/blake3/blake3_dispatch.c create mode 100644 src/third_party/blake3/blake3_impl.h create mode 100644 src/third_party/blake3/blake3_neon.c create mode 100644 src/third_party/blake3/blake3_portable.c create mode 100644 src/third_party/blake3/blake3_sse2.c create mode 100644 src/third_party/blake3/blake3_sse2_x86-64_unix.S create mode 100644 src/third_party/blake3/blake3_sse2_x86-64_windows_gnu.S create mode 100644 src/third_party/blake3/blake3_sse2_x86-64_windows_msvc.asm create mode 100644 src/third_party/blake3/blake3_sse41.c create mode 100644 src/third_party/blake3/blake3_sse41_x86-64_unix.S create mode 100644 src/third_party/blake3/blake3_sse41_x86-64_windows_gnu.S create mode 100644 src/third_party/doctest.h create mode 100644 src/third_party/fmt/core.h create mode 100644 src/third_party/fmt/format-inl.h create mode 100644 src/third_party/fmt/format.h create mode 100644 src/third_party/format.cpp rename src/{ => third_party}/getopt_long.c (100%) rename src/{ => third_party}/getopt_long.h (100%) rename src/{ => third_party}/minitrace.c (100%) rename src/{ => third_party}/minitrace.h (100%) create mode 100644 src/third_party/nonstd/optional.hpp create mode 100644 src/third_party/nonstd/string_view.hpp create mode 100644 src/third_party/win32/getopt.c create mode 100644 src/third_party/win32/getopt.h create mode 100644 src/third_party/xxh_x86dispatch.c create mode 100644 src/third_party/xxh_x86dispatch.h create mode 100644 src/third_party/xxhash.c create mode 100644 src/third_party/xxhash.h delete mode 100644 src/util.c delete mode 100644 src/version.c delete mode 100644 src/zlib/adler32.c delete mode 100644 src/zlib/crc32.c delete mode 100644 src/zlib/crc32.h delete mode 100644 src/zlib/deflate.c delete mode 100644 src/zlib/deflate.h delete mode 100644 src/zlib/gzclose.c delete mode 100644 src/zlib/gzguts.h delete mode 100644 src/zlib/gzlib.c delete mode 100644 src/zlib/gzread.c delete mode 100644 src/zlib/gzwrite.c delete mode 100644 src/zlib/inffast.c delete mode 100644 src/zlib/inffast.h delete mode 100644 src/zlib/inffixed.h delete mode 100644 src/zlib/inflate.c delete mode 100644 src/zlib/inflate.h delete mode 100644 src/zlib/inftrees.c delete mode 100644 src/zlib/inftrees.h delete mode 100644 src/zlib/trees.c delete mode 100644 src/zlib/trees.h delete mode 100644 src/zlib/zconf.h delete mode 100644 src/zlib/zlib.h delete mode 100644 src/zlib/zutil.c delete mode 100644 src/zlib/zutil.h create mode 100644 test/CMakeLists.txt create mode 100644 test/suites/cache_levels.bash create mode 100644 test/suites/color_diagnostics.bash delete mode 100644 test/suites/compression.bash create mode 100644 test/suites/fileclone.bash create mode 100644 test/suites/inode_cache.bash create mode 100644 test/suites/modules.bash create mode 100644 test/suites/no_compression.bash create mode 100644 test/suites/profiling_hip_clang.bash create mode 100644 unittest/.clang-tidy create mode 100644 unittest/CMakeLists.txt create mode 100644 unittest/TestUtil.cpp create mode 100644 unittest/TestUtil.hpp delete mode 100644 unittest/framework.c delete mode 100644 unittest/framework.h delete mode 100644 unittest/main.c create mode 100644 unittest/main.cpp create mode 100644 unittest/test_Args.cpp create mode 100644 unittest/test_AtomicFile.cpp rename unittest/{test_stats.c => test_Checksum.cpp} (52%) create mode 100644 unittest/test_Compression.cpp create mode 100644 unittest/test_Config.cpp create mode 100644 unittest/test_Counters.cpp create mode 100644 unittest/test_FormatNonstdStringView.cpp create mode 100644 unittest/test_Hash.cpp create mode 100644 unittest/test_InodeCache.cpp create mode 100644 unittest/test_Lockfile.cpp create mode 100644 unittest/test_NullCompression.cpp create mode 100644 unittest/test_Stat.cpp create mode 100644 unittest/test_Statistics.cpp create mode 100644 unittest/test_Util.cpp create mode 100644 unittest/test_Win32Util.cpp create mode 100644 unittest/test_ZstdCompression.cpp create mode 100644 unittest/test_argprocessing.cpp delete mode 100644 unittest/test_args.c delete mode 100644 unittest/test_argument_processing.c create mode 100644 unittest/test_ccache.cpp delete mode 100644 unittest/test_compopt.c create mode 100644 unittest/test_compopt.cpp delete mode 100644 unittest/test_conf.c delete mode 100644 unittest/test_counters.c delete mode 100644 unittest/test_hash.c delete mode 100644 unittest/test_hashutil.c create mode 100644 unittest/test_hashutil.cpp delete mode 100644 unittest/test_lockfile.c delete mode 100644 unittest/test_util.c delete mode 100644 unittest/util.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..f40eea4 --- /dev/null +++ b/.clang-format @@ -0,0 +1,38 @@ +# This configuration should work with Clang-Format 6.0 and higher. +--- +Language: Cpp +BasedOnStyle: LLVM + +AllowShortFunctionsOnASingleLine: None +AlwaysBreakAfterReturnType: AllDefinitions +AlwaysBreakBeforeMultilineStrings: true +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: true + AfterFunction: true + AfterStruct: true + AfterUnion: true + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^"system.hpp"$' + Priority: 1 + - Regex: '^"third_party/' + Priority: 3 + - Regex: '^"' + Priority: 2 + - Regex: '.*' + Priority: 4 +IndentPPDirectives: AfterHash +KeepEmptyLinesAtTheStartOfBlocks: false +PointerAlignment: Left +SpaceAfterTemplateKeyword: false +Standard: Cpp11 diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..13937aa --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +.git +**/.vagrant diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..00635a5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +[*.{c,cpp,h,hpp}] +indent_style = space +indent_size = 2 + +[*.{bash,py,sh}] +indent_style = space +indent_size = 4 + +[Makefile] +indent_style = tab diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..4156854 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Run Clang-Format on all code to follow the new code style. +f5795cdbc0703d80ab21f39c49bb2384ea2429ba diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..d5d0167 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,32 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- +### How to reproduce ### + + +1. +2. +3. + +### Actual behavior ### + + + + +### Expected behavior ### + + + + +### Environment ### + + +ccache version X.Y.Z + + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..38887e1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,10 @@ +--- +name: Feature request +about: Suggest a new feature for this project +title: '' +labels: feature +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/improvement.md b/.github/ISSUE_TEMPLATE/improvement.md new file mode 100644 index 0000000..de24208 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/improvement.md @@ -0,0 +1,11 @@ +--- +name: Improvement +about: Suggest an improvement that is neither a bug fix nor a new feature +title: '' +labels: improvement +assignees: '' + +--- +### Description ### + + diff --git a/.github/ISSUE_TEMPLATE/support.md b/.github/ISSUE_TEMPLATE/support.md new file mode 100644 index 0000000..9d10686 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/support.md @@ -0,0 +1,10 @@ +--- +name: Question +about: Ask for support or make an enquiry +title: '' +labels: support +assignees: '' + +--- +### Question ### + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..20d76da --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,10 @@ + diff --git a/.github/no-response.yml b/.github/no-response.yml new file mode 100644 index 0000000..4a5ab95 --- /dev/null +++ b/.github/no-response.yml @@ -0,0 +1,12 @@ +# Configuration for probot-no-response - https://github.com/probot/no-response + +# Number of days of inactivity before an issue is closed for lack of response. +daysUntilClose: 30 +# Label requiring a response. +responseRequiredLabel: awaiting feedback +# Comment to post when closing an issue for lack of response. Set to `false` to +# disable. +closeComment: > + This issue has been automatically closed due to no response to our request for + more information from the original author. Please feel free to reopen the + issue or leave a comment if you have more information. diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..5bd2fec --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,241 @@ +name: Build +on: + push: + pull_request: + +jobs: + # These test the standard build on several systems with GCC + Clang. + standard_tests: + name: ${{ matrix.os }} & ${{ matrix.compiler.CC }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-16.04, ubuntu-18.04, ubuntu-20.04, macos-10.15] + compiler: + - CC: gcc + CXX: g++ + - CC: clang + CXX: clang++ + + steps: + - name: Get source + uses: actions/checkout@v2 + + - name: Install dependencies (Ubuntu 16 & 18) + if: startsWith(matrix.os, 'ubuntu') && matrix.os != 'ubuntu-20.04' + run: sudo apt-get install elfutils libzstd1-dev + + - name: Install dependencies (Ubuntu 20) + if: matrix.os == 'ubuntu-20.04' + run: sudo apt-get install elfutils libzstd-dev + + - name: Build and test + run: ci/build + env: + CC: ${{ matrix.compiler.CC }} + CMAKE_PARAMS: -DCMAKE_BUILD_TYPE=CI + CTEST_OUTPUT_ON_FAILURE: ON + CXX: ${{ matrix.compiler.CXX }} + ENABLE_CACHE_CLEANUP_TESTS: true + VERBOSE: 1 + + - name: Collect testdir from failed tests + if: failure() + run: ci/collect-testdir + + - name: Upload testdir from failed tests + if: failure() + uses: actions/upload-artifact@v2 + with: + name: ${{ matrix.os }} - ${{ matrix.compiler.CC }} - testdir.tar.xz + path: testdir.tar.xz + + specific_tests: + name: ${{ matrix.config.name }} + runs-on: ${{ matrix.config.os }} + strategy: + fail-fast: false + matrix: + config: + - name: Linux GCC debug + in source + tracing + os: ubuntu-18.04 + CC: gcc + CXX: g++ + ENABLE_CACHE_CLEANUP_TESTS: 1 + BUILDDIR: . + CCACHE_LOC: . + CMAKE_PARAMS: -DCMAKE_BUILD_TYPE=Debug -DENABLE_TRACING=1 + apt_get: elfutils libzstd1-dev + + - name: Linux GCC 32-bit + os: ubuntu-18.04 + CC: gcc + CXX: g++ + CFLAGS: -m32 -g -O2 + CXXFLAGS: -m32 -g -O2 + LDFLAGS: -m32 + CMAKE_PARAMS: -DCMAKE_BUILD_TYPE=CI -DZSTD_FROM_INTERNET=ON + ENABLE_CACHE_CLEANUP_TESTS: 1 + apt_get: elfutils gcc-multilib g++-multilib lib32stdc++-5-dev + + - name: Linux GCC CUDA + os: ubuntu-18.04 + CC: gcc + CXX: g++ + CMAKE_PARAMS: -DCMAKE_BUILD_TYPE=CI -DZSTD_FROM_INTERNET=ON + ENABLE_CACHE_CLEANUP_TESTS: 1 + CUDA: 10.1.243-1 + apt_get: elfutils libzstd1-dev + + - name: Linux MinGW 32-bit + os: ubuntu-18.04 + CC: i686-w64-mingw32-gcc-posix + CXX: i686-w64-mingw32-g++-posix + CMAKE_PARAMS: -DCMAKE_BUILD_TYPE=CI -DCMAKE_SYSTEM_NAME=Windows -DZSTD_FROM_INTERNET=ON + RUN_TESTS: none + apt_get: elfutils mingw-w64 + + - name: Linux MinGW 64-bit + os: ubuntu-20.04 + CC: x86_64-w64-mingw32-gcc-posix + CXX: x86_64-w64-mingw32-g++-posix + ENABLE_CACHE_CLEANUP_TESTS: 1 + CMAKE_PARAMS: -DCMAKE_BUILD_TYPE=CI -DCMAKE_SYSTEM_NAME=Windows -DZSTD_FROM_INTERNET=ON + RUN_TESTS: unittest-in-wine + apt_get: elfutils mingw-w64 wine + + - name: Linux GCC 4.8.5 + os: ubuntu-16.04 + CC: gcc-4.8 + CXX: g++-4.8 + ENABLE_CACHE_CLEANUP_TESTS: 1 + CMAKE_PARAMS: -DCMAKE_BUILD_TYPE=CI -DWARNINGS_AS_ERRORS=OFF + apt_get: elfutils libzstd1-dev g++-4.8 + + - name: Clang address & UB sanitizer + os: ubuntu-20.04 + CC: clang + CXX: clang++ + ENABLE_CACHE_CLEANUP_TESTS: 1 + CMAKE_PARAMS: -DCMAKE_BUILD_TYPE=CI -DENABLE_SANITIZER_ADDRESS=ON -DENABLE_SANITIZER_UNDEFINED_BEHAVIOR=ON + ASAN_OPTIONS: detect_leaks=0 + apt_get: elfutils libzstd-dev + + - name: Clang static analyzer + os: ubuntu-20.04 + CC: clang + CXX: clang++ + ENABLE_CACHE_CLEANUP_TESTS: 1 + CMAKE_PREFIX: scan-build + RUN_TESTS: none + apt_get: libzstd-dev + + - name: Linux binary + os: ubuntu-20.04 + CC: gcc + CXX: g++ + SPECIAL: build-and-verify-package + CMAKE_PARAMS: -DCMAKE_BUILD_TYPE=Release + apt_get: elfutils libzstd-dev ninja-build + + - name: Source package + os: ubuntu-20.04 + CC: gcc + CXX: g++ + SPECIAL: build-and-verify-source-package + apt_get: elfutils libzstd-dev ninja-build asciidoc xsltproc + + - name: HTML documentation + os: ubuntu-18.04 + EXTRA_CMAKE_BUILD_FLAGS: --target doc-html + RUN_TESTS: none + apt_get: libzstd1-dev asciidoc + + - name: Manual page + os: ubuntu-18.04 + EXTRA_CMAKE_BUILD_FLAGS: --target doc-man-page + RUN_TESTS: none + apt_get: libzstd1-dev asciidoc xsltproc + + - name: Clang-Tidy + os: ubuntu-18.04 + CC: clang + CXX: clang++ + RUN_TESTS: none + CMAKE_PARAMS: -DENABLE_CLANG_TIDY=ON + apt_get: libzstd-dev clang-tidy + + steps: + - name: Get source + uses: actions/checkout@v2 + + - name: Install CUDA + if: matrix.config.CUDA != '' + run: sudo --preserve-env=CUDA,GITHUB_PATH ci/install-cuda + env: + CUDA: ${{ matrix.config.CUDA }} + + - name: Run apt-get + if: matrix.config.apt_get != '' + run: sudo apt-get install ${{ matrix.config.apt_get }} + + - name: Build and test + env: + ASAN_OPTIONS: ${{ matrix.config.ASAN_OPTIONS }} + BUILDDIR: ${{ matrix.config.BUILDDIR }} + CC: ${{ matrix.config.CC }} + CCACHE_LOC: ${{ matrix.config.CCACHE_LOC }} + CFLAGS: ${{ matrix.config.CFLAGS }} + CMAKE_PARAMS: ${{ matrix.config.CMAKE_PARAMS }} + CTEST_OUTPUT_ON_FAILURE: ON + CXX: ${{ matrix.config.CXX }} + CXXFLAGS: ${{ matrix.config.CXXFLAGS }} + ENABLE_CACHE_CLEANUP_TESTS: ${{ matrix.config.ENABLE_CACHE_CLEANUP_TESTS }} + EXTRA_CMAKE_BUILD_FLAGS: ${{ matrix.config.EXTRA_CMAKE_BUILD_FLAGS }} + LDFLAGS: ${{ matrix.config.LDFLAGS }} + RUN_TESTS: ${{ matrix.config.RUN_TESTS }} + SPECIAL: ${{ matrix.config.SPECIAL }} + VERBOSE: 1 + run: ci/build + + - name: Collect testdir from failed tests + if: failure() + run: ci/collect-testdir + # TODO: in case of build-and-verify-*package the BUILDDIR is set within those scripts. + + - name: Upload testdir from failed tests + if: failure() + uses: actions/upload-artifact@v2 + with: + name: ${{ matrix.config.name }} - testdir.tar.xz + path: testdir.tar.xz + + check_format: + name: Code formatting + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + steps: + - name: Get source + uses: actions/checkout@v2 + + - name: Run Clang-Format in check mode + run: misc/format-files --all --check + env: + VERBOSE: 1 + + codespell: + name: Spelling + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + steps: + - name: Get source + uses: actions/checkout@v2 + + - name: Install codespell + run: sudo apt-get install codespell + + - name: Run codespell + run: codespell -q 7 -S ".git,LICENSE.adoc,./src/third_party/*" -I misc/codespell-allowlist.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9b69afd --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# Typical build directories +/build*/ + +# Emacs save files +*~ + +# Vim save files +.*.sw? +.*.un~ + +# Visual Studio settings and build output +/.vs/ +/out/build/ + +# Visual Studio Code settings +/.vscode/ + +# macOS custom attributes +.DS_Store/ diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000..0f2fe15 --- /dev/null +++ b/.mailmap @@ -0,0 +1,29 @@ +Anders F Björklund +Anders F Björklund +Anders F Björklund +Andrew Boie +Arne Hasselbring +Bernhard Bauer +Chiaki Ishikawa +Clemens Rabe +Clemens Rabe +Doug Anderson +Erik Flodin +Hongli Lai +Jonny Yu +Kona Blend +Leanid Chaika +Luboš Luňák +Martin Ettl +Mizuha Himuraki +Paul Bunch +Per Nordlöw +Peter Budai +Ramiro Polla +Ramiro Polla +Ryan Brown +Thomas Otto <39962140+totph@users.noreply.github.com> +Thomas Röfer +Tor Arne Vestbø +Ville Skyttä +Xavier René-Corail diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..77fbd2c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,147 @@ +cmake_minimum_required(VERSION 3.4.3) + +project(ccache LANGUAGES C CXX ASM) +set(CMAKE_PROJECT_DESCRIPTION "a fast C/C++ compiler cache") + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED YES) +set(CMAKE_CXX_EXTENSIONS NO) + +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED YES) +set(CMAKE_C_EXTENSIONS NO) + +# Always export compile_commands.json since it's useful for many tools. +set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +# +# Settings +# +include(StandardSettings) +include(StandardWarnings) +include(CIBuildType) +include(DefaultBuildType) + +if(NOT ${CMAKE_VERSION} VERSION_LESS "3.9") + cmake_policy(SET CMP0069 NEW) + option(ENABLE_IPO "Enable interprocedural (link time, LTO) optimization" OFF) + if(ENABLE_IPO) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) + endif() +endif() + +# +# Configuration +# +include(GNUInstallDirs) +include(GenerateConfigurationFile) +include(GenerateVersionFile) + +if(HAVE_SYS_MMAN_H) + set(INODE_CACHE_SUPPORTED 1) +endif() + +# +# Third party +# +set(ZSTD_FROM_INTERNET_DEFAULT OFF) + +# Default to downloading deps for Visual Studio, unless using a package manager. +if(MSVC AND NOT CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg|conan") + set(ZSTD_FROM_INTERNET_DEFAULT ON) +endif() + +option(ZSTD_FROM_INTERNET "Download and use libzstd from the Internet" ${ZSTD_FROM_INTERNET_DEFAULT}) +find_package(zstd 1.1.2 REQUIRED) + +# +# Special flags +# +# Note: Cppcheck will scan everything after this point. zstd is above so it +# doesn't get scanned. +# +include(CodeAnalysis) +option(ENABLE_TRACING "Enable possibility to use internal ccache tracing" OFF) + +# +# Source code +# +add_subdirectory(src) + +# +# ccache executable +# +add_executable(ccache src/main.cpp) +target_link_libraries(ccache PRIVATE standard_settings standard_warnings ccache_lib) + +# +# Documentation +# +add_subdirectory(doc) + +# +# Installation +# +install(TARGETS ccache DESTINATION ${CMAKE_INSTALL_BINDIR}) + +# +# Packaging +# +include(CcachePackConfig) + +# +# Tests +# +option(ENABLE_TESTING "Enable tests" ON) +if(ENABLE_TESTING) + enable_testing() + add_subdirectory(unittest) + add_subdirectory(test) + + # Note: VERSION_GREATER_EQUAL requires CMake 3.17 + if(NOT ${CMAKE_VERSION} VERSION_LESS "3.17") + list(APPEND CMAKE_CTEST_ARGUMENTS "--output-on-failure") + endif() + + # Add "check" target which compiles and runs tests. + set( + check_command + ${CMAKE_CTEST_COMMAND} --force-new-ctest-process --output-on-failure) + if(CMAKE_CONFIGURATION_TYPES) + list(APPEND check_command --build-config "$") + endif() + add_custom_target( + check + COMMAND ${check_command} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + DEPENDS ccache unittest) +endif() + +# +# Special formatting targets +# +find_program( + CLANG_FORMAT_EXE + NAMES "clang-format" + DOC "Path to clang-format executable.") +mark_as_advanced(CLANG_FORMAT_EXE) # Don't show in CMake UIs + +if(NOT CLANG_FORMAT_EXE) + message(STATUS "clang-format not found") +else() + add_custom_target( + format + COMMAND misc/format-files --all + COMMENT "Formatting code" + USES_TERMINAL + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + + add_custom_target( + check_format + COMMAND misc/format-files --all --check + COMMENT "Checking code formatting" + USES_TERMINAL + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +endif() diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 763be4f..46315c4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,11 +4,13 @@ Want to contribute to ccache? Awesome! ## Asking a question? -It's OK to ask questions in the issue tracker for now. +There are several options: -However, please consider posting your question to the -[mailing list](https://lists.samba.org/mailman/listinfo/ccache/) instead, since -you are more likely to get an answer there. +1. Ask a question in the [issue + tracker](https://github.com/ccache/ccache/issues/new/choose). +2. Post your question to the [mailing + list](https://lists.samba.org/mailman/listinfo/ccache/). +3. Chat in the [Gitter room](https://gitter.im/ccache/ccache). ## Reporting an issue? @@ -29,77 +31,45 @@ https://www.snoyman.com/blog/2017/10/effective-ways-help-from-maintainers). The preferred way is to create one or several pull request with your proposal(s) on [GitHub](https://github.com/ccache/ccache). -If you plan to implement major changes it is wise to open an issue on GitHub -(or send a mail to the mailing list) asking for comments on your plans before -doing the bulk of the work. That way you can avoid potentially wasting time on -doing something that might not end up being accepted. - -### How to write commit messages - -* Write a summary (short description) on the first line. -* Start the summary with a capital letter. Optional: prefix the short - description with a context followed by a colon. -* The summary should be in imperative mood (see examples below). -* The summary should not end with a period. It's a title and titles don't end - with a period. -* If a longer description is wanted, add a second line empty and write the - longer description on line three and below. -* Keep lines in the message at most 72 characters wide. - -Example 1: - - Hash a delimiter string between parts to separate them - - Previously, "gcc -I-O2 -c file.c" and "gcc -I -O2 -c file.c" would hash - to the same sum. - -Example 2: - - win32: Add a space between filename and error string in x_fmmap() - -See also: - -* http://stopwritingramblingcommitmessages.com -* http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html -* https://github.com/erlang/otp/wiki/Writing-good-commit-messages - -### Code style - -#### Formatting - -* Use tabs for indenting and spaces for aligning C code. -* Use 4 spaces for indenting other code (and spaces for aligning). -* Put the opening curly brace on a new line when defining a function, otherwise - at the end of the same line. -* Put no space between function name and the following parenthesis. -* Put one space between if/switch/for/while/do and opening curly brace. +Here are some hints to make the process smoother: + +* If you plan to implement major changes it is wise to open an issue on GitHub + (or ask in the Gitter room, or send a mail to the mailing list) asking for + comments on your plans before doing the bulk of the work. That way you can + avoid potentially wasting time on doing something that may need major rework + to be accepted, or maybe doesn't end up being accepted at all. +* Is your pull request "work in progress", i.e. you don't think that it's ready + for merging yet but you want early comments and CI test results? Then create + a draft pull request as described in [this Github blog + post](https://github.blog/2019-02-14-introducing-draft-pull-requests/). +* Please follow the ccache's code style (see the section below). +* Consider [A Note About Git Commit + Messages](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) + when writing commit messages. + +## Code style + +Ccache was written in C99 until 2019 when it started being converted to C++11. +The conversion is a slow work in progress, which is why there is a lot of +C-style code left. Please refrain from doing large C to C++ conversions; do it +little by little. + +Source code formatting is defined by `.clang-format` in the root directory. The +format is loosely based on [LLVM's code formatting +style](https://llvm.org/docs/CodingStandards.html) with some exceptions. It's +highly recommended to install +[Clang-Format](https://clang.llvm.org/docs/ClangFormat.html) 6.0 or newer and +run `make format` to format changes according to ccache's code style. Or even +better: set up your editor to run Clang-Format automatically when saving. If +you don't run Clang-Format then the ccache authors have to do it for you. + +Please follow these conventions: + +* Use `UpperCamelCase` for types (e.g. classes and structs) and namespaces. +* Use `UPPER_CASE` names for macros and (non-class )enum values. +* Use `snake_case` for other names (functions, variables, enum class values, etc.). +* Use an `m_` prefix for non-public member variables. +* Use a `g_` prefix for global mutable variables. +* Use a `k_` prefix for global constants. * Always use curly braces around if/for/while/do bodies, even if they only contain one statement. -* If possible, keep lines at most 80 character wide for a 2 character tab - width. -* Use only lowercase names for functions and variables. -* Use only uppercase names for enum items and (with some exceptions) macros. -* Don't use typedefs for structs and enums. -* Use //-style comments. - -Tip: Install the tool uncrustify and then -run "make uncrustify" to fix up source code formatting. - -#### Idioms - -* Declare variables as late as convenient, not necessarily at the beginning of - the scope. -* Use NULL to initialize null pointers. -* Don't use NULL when comparing pointers. -* Use format(), x_malloc() and friends instead of checking for memory - allocation failure explicitly. -* Use str_eq() instead of strcmp() when testing for string (in)equality. -* Consider using str_startswith() instead of strncmp(). -* Use bool, true and false for boolean values. -* Use tmp_unlink() or x_unlink() instead of unlink(). -* Use x_rename() instead of rename(). - -#### Other - -* Strive to minimize use of global variables. -* Write test cases for new code. diff --git a/GPL-3.0.txt b/GPL-3.0.txt index 99a4d62..a662cb1 100644 --- a/GPL-3.0.txt +++ b/GPL-3.0.txt @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found. GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. @@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. diff --git a/INSTALL.md b/INSTALL.md deleted file mode 100644 index a72599e..0000000 --- a/INSTALL.md +++ /dev/null @@ -1,52 +0,0 @@ -ccache installation from release archive -======================================== - -Prerequisites -------------- - -To build ccache from a -[release archive](https://ccache.dev/download.html), you need: - -- A C compiler (for instance GCC) - -It is also recommended that you have: - -- [zlib](http://www.zlib.net) (if you don't have zlib installed, ccache will - use a bundled copy) - - -Installation ------------- - -To compile and install ccache, run these commands: - - ./configure - make - make install - -You may set the installation directory and other parameters by options to -`./configure`. To see them, run `./configure --help`. - -There are two ways to use ccache. You can either prefix your compilation -commands with `ccache` or you can create a symbolic link (named as your -compiler) to ccache. The first method is most convenient if you just want to -try out ccache or wish to use it for some specific projects. The second method -is most useful for when you wish to use ccache for all your compilations. - -To install for usage by the first method just copy ccache to somewhere in your -path. - -To install for the second method, do something like this: - - cp ccache /usr/local/bin/ - ln -s ccache /usr/local/bin/gcc - ln -s ccache /usr/local/bin/g++ - ln -s ccache /usr/local/bin/cc - ln -s ccache /usr/local/bin/c++ - -And so forth. This will work as long as `/usr/local/bin` comes before the path -to the compiler (which is usually in `/usr/bin`). After installing you may wish -to run `which gcc` to make sure that the correct link is being used. - -NOTE: Do not use a hard link, use a symbolic link. A hard link will cause -"interesting" problems. diff --git a/LGPL-3.0.txt b/LGPL-3.0.txt new file mode 100644 index 0000000..0a04128 --- /dev/null +++ b/LGPL-3.0.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/LICENSE.adoc b/LICENSE.adoc index 2cc656e..9052403 100644 --- a/LICENSE.adoc +++ b/LICENSE.adoc @@ -1,4 +1,4 @@ -ccache copyright and license +Ccache copyright and license ============================ Overall license @@ -7,28 +7,28 @@ Overall license The license for ccache as a whole is as follows: ------------------------------------------------------------------------------- - This program is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free Software - Foundation; either version 3 of the License, or (at your option) any later - version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - this program; if not, write to the Free Software Foundation, Inc., 51 Franklin - Street, Fifth Floor, Boston, MA 02110-1301 USA +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 51 Franklin +Street, Fifth Floor, Boston, MA 02110-1301 USA ------------------------------------------------------------------------------- The full license text can be found in GPL-3.0.txt and at -http://www.gnu.org/licenses/gpl-3.0.html. +https://www.gnu.org/licenses/gpl-3.0.html. Copyright and authors --------------------- -ccache is a collective work with contributions from many people, listed in +Ccache is a collective work with contributions from many people, listed in AUTHORS.adoc and at https://ccache.dev/credits.html. Subsequent additions by contributing authors are implicitly licensed to the public under the same terms (GNU GPL version 3 or later), but the contributing authors retain copyrights on @@ -37,8 +37,8 @@ their portions of the work. The copyright for ccache as a whole is as follows: ------------------------------------------------------------------------------- - Copyright (C) 2002-2007 Andrew Tridgell - Copyright (C) 2009-2020 Joel Rosdahl +Copyright (C) 2002-2007 Andrew Tridgell +Copyright (C) 2009-2020 Joel Rosdahl and other contributors ------------------------------------------------------------------------------- @@ -52,376 +52,480 @@ the GPL: that is, if separated from the ccache sources, they may be usable under less restrictive terms. -src/getopt_long.[hc] -~~~~~~~~~~~~~~~~~~~~ +src/third_party/base32hex.[hc] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This implementation of `getopt_long()` was copied from -http://www.postgresql.org[PostgreSQL] and has the following license text: +This base32hex implementation comes from +. ------------------------------------------------------------------------------- - Portions Copyright (c) 1987, 1993, 1994 - The Regents of the University of California. All rights reserved. - - Portions Copyright (c) 2003 - PostgreSQL Global Development Group - - 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. 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. +(C) 2012 Peter Conrad + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License version 3 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . ------------------------------------------------------------------------------- -src/hashtable*.[hc] -~~~~~~~~~~~~~~~~~~~ +src/third_party/blake3/*.[hcS] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This is a subset of https://github.com/BLAKE3-team/BLAKE3[BLAKE3] 0.3.7 with +the following license: + +------------------------------------------------------------------------------- +This work is released into the public domain with CC0 1.0. Alternatively, it is +licensed under the Apache License 2.0. + +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- -This code comes from http://www.cl.cam.ac.uk/~cwc22/hashtable/ with the -following license: +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. ------------------------------------------------------------------------------- - Copyright (c) 2002, 2004, Christopher Clark - 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 of the original author; nor the names of any - 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 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. +------------------------------------------------------------------------------- + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 Jack O'Connor and Samuel Neves + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +------------------------------------------------------------------------------- + + +src/third_party/doctest.h +~~~~~~~~~~~~~~~~~~~~~~~~~ + +This is the single header version of https://github.com/onqtam/doctest[doctest] +2.4.0 with the following license: + +------------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2016-2019 Viktor Kirilov + +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. ------------------------------------------------------------------------------- -m4/feature_macros.m4 -~~~~~~~~~~~~~~~~~~~~ +src/third_party/fmt/*.h and src/third_party/format.cpp +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This Autoconf M4 snippet comes from http://www.python.org[Python] 2.6's -`configure.in` with the following license: +This is a subset of https://fmt.dev[fmt] 7.0.3 with the following license: ------------------------------------------------------------------------------- - A. HISTORY OF THE SOFTWARE - ========================== - - Python was created in the early 1990s by Guido van Rossum at Stichting - Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands - as a successor of a language called ABC. Guido remains Python's - principal author, although it includes many contributions from others. - - In 1995, Guido continued his work on Python at the Corporation for - National Research Initiatives (CNRI, see http://www.cnri.reston.va.us) - in Reston, Virginia where he released several versions of the - software. - - In May 2000, Guido and the Python core development team moved to - BeOpen.com to form the BeOpen PythonLabs team. In October of the same - year, the PythonLabs team moved to Digital Creations (now Zope - Corporation, see http://www.zope.com). In 2001, the Python Software - Foundation (PSF, see http://www.python.org/psf/) was formed, a - non-profit organization created specifically to own Python-related - Intellectual Property. Zope Corporation is a sponsoring member of - the PSF. - - All Python releases are Open Source (see http://www.opensource.org for - the Open Source Definition). Historically, most, but not all, Python - releases have also been GPL-compatible; the table below summarizes - the various releases. - - Release Derived Year Owner GPL- - from compatible? (1) - - 0.9.0 thru 1.2 1991-1995 CWI yes - 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes - 1.6 1.5.2 2000 CNRI no - 2.0 1.6 2000 BeOpen.com no - 1.6.1 1.6 2001 CNRI yes (2) - 2.1 2.0+1.6.1 2001 PSF no - 2.0.1 2.0+1.6.1 2001 PSF yes - 2.1.1 2.1+2.0.1 2001 PSF yes - 2.2 2.1.1 2001 PSF yes - 2.1.2 2.1.1 2002 PSF yes - 2.1.3 2.1.2 2002 PSF yes - 2.2.1 2.2 2002 PSF yes - 2.2.2 2.2.1 2002 PSF yes - 2.2.3 2.2.2 2003 PSF yes - 2.3 2.2.2 2002-2003 PSF yes - 2.3.1 2.3 2002-2003 PSF yes - 2.3.2 2.3.1 2002-2003 PSF yes - 2.3.3 2.3.2 2002-2003 PSF yes - 2.3.4 2.3.3 2004 PSF yes - 2.3.5 2.3.4 2005 PSF yes - 2.4 2.3 2004 PSF yes - 2.4.1 2.4 2005 PSF yes - 2.4.2 2.4.1 2005 PSF yes - 2.4.3 2.4.2 2006 PSF yes - 2.4.4 2.4.3 2006 PSF yes - 2.5 2.4 2006 PSF yes - 2.5.1 2.5 2007 PSF yes - 2.5.2 2.5.1 2008 PSF yes - 2.5.3 2.5.2 2008 PSF yes - 2.6 2.5 2008 PSF yes - 2.6.1 2.6 2008 PSF yes - - Footnotes: - - (1) GPL-compatible doesn't mean that we're distributing Python under - the GPL. All Python licenses, unlike the GPL, let you distribute - a modified version without making your changes open source. The - GPL-compatible licenses make it possible to combine Python with - other software that is released under the GPL; the others don't. - - (2) According to Richard Stallman, 1.6.1 is not GPL-compatible, - because its license has a choice of law clause. According to - CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 - is "not incompatible" with the GPL. - - Thanks to the many outside volunteers who have worked under Guido's - direction to make these releases possible. - - - B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON - =============================================================== - - PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 - -------------------------------------------- - - 1. This LICENSE AGREEMENT is between the Python Software Foundation - ("PSF"), and the Individual or Organization ("Licensee") accessing and - otherwise using this software ("Python") in source or binary form and - its associated documentation. - - 2. Subject to the terms and conditions of this License Agreement, PSF hereby - grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, - analyze, test, perform and/or display publicly, prepare derivative works, - distribute, and otherwise use Python alone or in any derivative version, - provided, however, that PSF's License Agreement and PSF's notice of copyright, - i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Python - Software Foundation; All Rights Reserved" are retained in Python alone or in any - derivative version prepared by Licensee. - - 3. In the event Licensee prepares a derivative work that is based on - or incorporates Python or any part thereof, and wants to make - the derivative work available to others as provided herein, then - Licensee hereby agrees to include in any such work a brief summary of - the changes made to Python. - - 4. PSF is making Python available to Licensee on an "AS IS" - basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR - IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND - DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS - FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT - INFRINGE ANY THIRD PARTY RIGHTS. - - 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON - FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS - A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, - OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - - 6. This License Agreement will automatically terminate upon a material - breach of its terms and conditions. - - 7. Nothing in this License Agreement shall be deemed to create any - relationship of agency, partnership, or joint venture between PSF and - Licensee. This License Agreement does not grant permission to use PSF - trademarks or trade name in a trademark sense to endorse or promote - products or services of Licensee, or any third party. - - 8. By copying, installing or otherwise using Python, Licensee - agrees to be bound by the terms and conditions of this License - Agreement. - - - BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 - ------------------------------------------- - - BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 - - 1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an - office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the - Individual or Organization ("Licensee") accessing and otherwise using - this software in source or binary form and its associated - documentation ("the Software"). - - 2. Subject to the terms and conditions of this BeOpen Python License - Agreement, BeOpen hereby grants Licensee a non-exclusive, - royalty-free, world-wide license to reproduce, analyze, test, perform - and/or display publicly, prepare derivative works, distribute, and - otherwise use the Software alone or in any derivative version, - provided, however, that the BeOpen Python License is retained in the - Software, alone or in any derivative version prepared by Licensee. - - 3. BeOpen is making the Software available to Licensee on an "AS IS" - basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR - IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND - DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS - FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT - INFRINGE ANY THIRD PARTY RIGHTS. - - 4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE - SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS - AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY - DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - - 5. This License Agreement will automatically terminate upon a material - breach of its terms and conditions. - - 6. This License Agreement shall be governed by and interpreted in all - respects by the law of the State of California, excluding conflict of - law provisions. Nothing in this License Agreement shall be deemed to - create any relationship of agency, partnership, or joint venture - between BeOpen and Licensee. This License Agreement does not grant - permission to use BeOpen trademarks or trade names in a trademark - sense to endorse or promote products or services of Licensee, or any - third party. As an exception, the "BeOpen Python" logos available at - http://www.pythonlabs.com/logos.html may be used according to the - permissions granted on that web page. - - 7. By copying, installing or otherwise using the software, Licensee - agrees to be bound by the terms and conditions of this License - Agreement. - - - CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 - --------------------------------------- - - 1. This LICENSE AGREEMENT is between the Corporation for National - Research Initiatives, having an office at 1895 Preston White Drive, - Reston, VA 20191 ("CNRI"), and the Individual or Organization - ("Licensee") accessing and otherwise using Python 1.6.1 software in - source or binary form and its associated documentation. - - 2. Subject to the terms and conditions of this License Agreement, CNRI - hereby grants Licensee a nonexclusive, royalty-free, world-wide - license to reproduce, analyze, test, perform and/or display publicly, - prepare derivative works, distribute, and otherwise use Python 1.6.1 - alone or in any derivative version, provided, however, that CNRI's - License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) - 1995-2001 Corporation for National Research Initiatives; All Rights - Reserved" are retained in Python 1.6.1 alone or in any derivative - version prepared by Licensee. Alternately, in lieu of CNRI's License - Agreement, Licensee may substitute the following text (omitting the - quotes): "Python 1.6.1 is made available subject to the terms and - conditions in CNRI's License Agreement. This Agreement together with - Python 1.6.1 may be located on the Internet using the following - unique, persistent identifier (known as a handle): 1895.22/1013. This - Agreement may also be obtained from a proxy server on the Internet - using the following URL: http://hdl.handle.net/1895.22/1013". - - 3. In the event Licensee prepares a derivative work that is based on - or incorporates Python 1.6.1 or any part thereof, and wants to make - the derivative work available to others as provided herein, then - Licensee hereby agrees to include in any such work a brief summary of - the changes made to Python 1.6.1. - - 4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" - basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR - IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND - DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS - FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT - INFRINGE ANY THIRD PARTY RIGHTS. - - 5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON - 1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS - A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, - OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - - 6. This License Agreement will automatically terminate upon a material - breach of its terms and conditions. - - 7. This License Agreement shall be governed by the federal - intellectual property law of the United States, including without - limitation the federal copyright law, and, to the extent such - U.S. federal law does not apply, by the law of the Commonwealth of - Virginia, excluding Virginia's conflict of law provisions. - Notwithstanding the foregoing, with regard to derivative works based - on Python 1.6.1 that incorporate non-separable material that was - previously distributed under the GNU General Public License (GPL), the - law of the Commonwealth of Virginia shall govern this License - Agreement only as to issues arising under or with respect to - Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this - License Agreement shall be deemed to create any relationship of - agency, partnership, or joint venture between CNRI and Licensee. This - License Agreement does not grant permission to use CNRI trademarks or - trade name in a trademark sense to endorse or promote products or - services of Licensee, or any third party. - - 8. By clicking on the "ACCEPT" button where indicated, or by copying, - installing or otherwise using Python 1.6.1, Licensee agrees to be - bound by the terms and conditions of this License Agreement. - - ACCEPT - - - CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 - -------------------------------------------------- - - Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, - The Netherlands. All rights reserved. - - Permission to use, copy, modify, and distribute this software and its - documentation for any purpose and without fee is hereby granted, - provided that the above copyright notice appear in all copies and that - both that copyright notice and this permission notice appear in - supporting documentation, and that the name of Stichting Mathematisch - Centrum or CWI not be used in advertising or publicity pertaining to - distribution of the software without specific, written prior - permission. - - STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO - THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE - FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT - OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +Formatting library for C++ + +Copyright (c) 2012 - present, Victor Zverovich + +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. + +--- Optional exception to the license --- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into a machine-executable object form of such +source code, you may redistribute such embedded portions in such object form +without including the above copyright and permission notices. ------------------------------------------------------------------------------- -src/murmurhashneutral2.[hc] -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +src/third_party/getopt_long.[hc] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This implementation of `getopt_long()` was copied from +https://www.postgresql.org[PostgreSQL] and has the following license text: -This fast hash implementation is released to the public domain by Austin -Appleby. See http://murmurhash.googlepages.com. +------------------------------------------------------------------------------- +Portions Copyright (c) 1987, 1993, 1994 +The Regents of the University of California. All rights reserved. + +Portions Copyright (c) 2003 +PostgreSQL Global Development Group + +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. 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. +------------------------------------------------------------------------------- -src/minitrace.[hc] -~~~~~~~~~~~~~~~~~~ +src/third_party/minitrace.[hc] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A library for producing JSON traces suitable for Chrome's built-in trace viewer (chrome://tracing). Downloaded from . @@ -450,55 +554,118 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -src/snprintf.c and m4/snprintf.m4 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This implementation of `snprintf()` and similar functions was downloaded from -http://www.jhweiss.de/software/snprintf.html and has the following license: +src/third_party/nonstd/optional.hpp +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This alternative implementation of `std::optional` was downloaded from + and has the following license +text: + +------------------------------------------------------------------------------- +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- - Copyright (c) 1995 Patrick Powell. - This code is based on code written by Patrick Powell . - It may be used for any purpose as long as this notice remains intact on all - source code distributions. - Copyright (c) 2008 Holger Weiss. +src/third_party/nonstd/string_view.hpp +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This alternative implementation of `std::string_view` was downloaded from + and has the following license +text: - This version of the code is maintained by Holger Weiss . - My changes to the code may freely be used, modified and/or redistributed for - any purpose. It would be nice if additions and fixes to this file (including - trivial code cleanups) would be sent back in order to let me include them in - the version available at . - However, this is not a requirement for using or redistributing (possibly - modified) versions of this file, nor is leaving this notice intact mandatory. ------------------------------------------------------------------------------- +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------- + + +src/third_party/win32/getopt.[hc] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This implementation of `getopt_long()` for Win32 was taken from +https://www.codeproject.com/Articles/157001/Full-getopt-Port-for-Unicode-and-Multibyte-Microso +and is licensed under the LGPL. + +The full license text can be found in LGPL-3.0.txt and at +https://www.gnu.org/licenses/lgpl-3.0.html. + -src/zlib/*.[hc] -~~~~~~~~~~~~~~~ +src/third_party/xxh(ash|_x86dispatch).[hc] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This is a bundled subset of zlib 1.2.11 from with the -following license: +xxHash - Extremely Fast Hash algorithm. Copied from xxHash v0.8.0 downloaded +from . ------------------------------------------------------------------------------- - Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jean-loup Gailly Mark Adler - jloup@gzip.org madler@alumni.caltech.edu +Copyright (c) 2012-2020 Yann Collet +All rights reserved. + +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. ------------------------------------------------------------------------------- diff --git a/LICENSE.html b/LICENSE.html deleted file mode 100644 index b7543bf..0000000 --- a/LICENSE.html +++ /dev/null @@ -1,1244 +0,0 @@ - - - - - - -ccache copyright and license - - - - - -
-
-

Overall license

-
-

The license for ccache as a whole is as follows:

-
-
-
  This program is free software; you can redistribute it and/or modify it under
-  the terms of the GNU General Public License as published by the Free Software
-  Foundation; either version 3 of the License, or (at your option) any later
-  version.
-
-  This program is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-  PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
-  Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-

The full license text can be found in GPL-3.0.txt and at -http://www.gnu.org/licenses/gpl-3.0.html.

-
-
-
- -
-

ccache is a collective work with contributions from many people, listed in -AUTHORS.adoc and at https://ccache.dev/credits.html. Subsequent additions by -contributing authors are implicitly licensed to the public under the same terms -(GNU GPL version 3 or later), but the contributing authors retain copyrights on -their portions of the work.

-

The copyright for ccache as a whole is as follows:

-
-
-
  Copyright (C) 2002-2007 Andrew Tridgell
-  Copyright (C) 2009-2020 Joel Rosdahl
-
-
-
-
-

Files derived from other sources

-
-

The ccache distribution contain some files from other sources and some have -been modified for use in ccache. These files all carry attribution notices, and -may qualify as “separate and independent works in themselves” for purposes of -the GPL: that is, if separated from the ccache sources, they may be usable -under less restrictive terms.

-
-

src/getopt_long.[hc]

-

This implementation of getopt_long() was copied from -PostgreSQL and has the following license text:

-
-
-
  Portions Copyright (c) 1987, 1993, 1994
-  The Regents of the University of California.  All rights reserved.
-
-  Portions Copyright (c) 2003
-  PostgreSQL Global Development Group
-
-  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. 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.
-
-
-
-

src/hashtable*.[hc]

-

This code comes from http://www.cl.cam.ac.uk/~cwc22/hashtable/ with the -following license:

-
-
-
  Copyright (c) 2002, 2004, Christopher Clark
-  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 of the original author; nor the names of any
-      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 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.
-
-
-
-

m4/feature_macros.m4

-

This Autoconf M4 snippet comes from Python 2.6’s -configure.in with the following license:

-
-
-
  A. HISTORY OF THE SOFTWARE
-  ==========================
-
-  Python was created in the early 1990s by Guido van Rossum at Stichting
-  Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
-  as a successor of a language called ABC.  Guido remains Python's
-  principal author, although it includes many contributions from others.
-
-  In 1995, Guido continued his work on Python at the Corporation for
-  National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
-  in Reston, Virginia where he released several versions of the
-  software.
-
-  In May 2000, Guido and the Python core development team moved to
-  BeOpen.com to form the BeOpen PythonLabs team.  In October of the same
-  year, the PythonLabs team moved to Digital Creations (now Zope
-  Corporation, see http://www.zope.com).  In 2001, the Python Software
-  Foundation (PSF, see http://www.python.org/psf/) was formed, a
-  non-profit organization created specifically to own Python-related
-  Intellectual Property.  Zope Corporation is a sponsoring member of
-  the PSF.
-
-  All Python releases are Open Source (see http://www.opensource.org for
-  the Open Source Definition).  Historically, most, but not all, Python
-  releases have also been GPL-compatible; the table below summarizes
-  the various releases.
-
-      Release         Derived     Year        Owner       GPL-
-                      from                                compatible? (1)
-
-      0.9.0 thru 1.2              1991-1995   CWI         yes
-      1.3 thru 1.5.2  1.2         1995-1999   CNRI        yes
-      1.6             1.5.2       2000        CNRI        no
-      2.0             1.6         2000        BeOpen.com  no
-      1.6.1           1.6         2001        CNRI        yes (2)
-      2.1             2.0+1.6.1   2001        PSF         no
-      2.0.1           2.0+1.6.1   2001        PSF         yes
-      2.1.1           2.1+2.0.1   2001        PSF         yes
-      2.2             2.1.1       2001        PSF         yes
-      2.1.2           2.1.1       2002        PSF         yes
-      2.1.3           2.1.2       2002        PSF         yes
-      2.2.1           2.2         2002        PSF         yes
-      2.2.2           2.2.1       2002        PSF         yes
-      2.2.3           2.2.2       2003        PSF         yes
-      2.3             2.2.2       2002-2003   PSF         yes
-      2.3.1           2.3         2002-2003   PSF         yes
-      2.3.2           2.3.1       2002-2003   PSF         yes
-      2.3.3           2.3.2       2002-2003   PSF         yes
-      2.3.4           2.3.3       2004        PSF         yes
-      2.3.5           2.3.4       2005        PSF         yes
-      2.4             2.3         2004        PSF         yes
-      2.4.1           2.4         2005        PSF         yes
-      2.4.2           2.4.1       2005        PSF         yes
-      2.4.3           2.4.2       2006        PSF         yes
-      2.4.4           2.4.3       2006        PSF         yes
-      2.5             2.4         2006        PSF         yes
-      2.5.1           2.5         2007        PSF         yes
-      2.5.2           2.5.1       2008        PSF         yes
-      2.5.3           2.5.2       2008        PSF         yes
-      2.6             2.5         2008        PSF         yes
-      2.6.1           2.6         2008        PSF         yes
-
-  Footnotes:
-
-  (1) GPL-compatible doesn't mean that we're distributing Python under
-      the GPL.  All Python licenses, unlike the GPL, let you distribute
-      a modified version without making your changes open source.  The
-      GPL-compatible licenses make it possible to combine Python with
-      other software that is released under the GPL; the others don't.
-
-  (2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
-      because its license has a choice of law clause.  According to
-      CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
-      is "not incompatible" with the GPL.
-
-  Thanks to the many outside volunteers who have worked under Guido's
-  direction to make these releases possible.
-
-
-  B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
-  ===============================================================
-
-  PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
-  --------------------------------------------
-
-  1. This LICENSE AGREEMENT is between the Python Software Foundation
-  ("PSF"), and the Individual or Organization ("Licensee") accessing and
-  otherwise using this software ("Python") in source or binary form and
-  its associated documentation.
-
-  2. Subject to the terms and conditions of this License Agreement, PSF hereby
-  grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
-  analyze, test, perform and/or display publicly, prepare derivative works,
-  distribute, and otherwise use Python alone or in any derivative version,
-  provided, however, that PSF's License Agreement and PSF's notice of copyright,
-  i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Python
-  Software Foundation; All Rights Reserved" are retained in Python alone or in any
-  derivative version prepared by Licensee.
-
-  3. In the event Licensee prepares a derivative work that is based on
-  or incorporates Python or any part thereof, and wants to make
-  the derivative work available to others as provided herein, then
-  Licensee hereby agrees to include in any such work a brief summary of
-  the changes made to Python.
-
-  4. PSF is making Python available to Licensee on an "AS IS"
-  basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
-  IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
-  DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
-  FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
-  INFRINGE ANY THIRD PARTY RIGHTS.
-
-  5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
-  FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
-  A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
-  OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-  6. This License Agreement will automatically terminate upon a material
-  breach of its terms and conditions.
-
-  7. Nothing in this License Agreement shall be deemed to create any
-  relationship of agency, partnership, or joint venture between PSF and
-  Licensee.  This License Agreement does not grant permission to use PSF
-  trademarks or trade name in a trademark sense to endorse or promote
-  products or services of Licensee, or any third party.
-
-  8. By copying, installing or otherwise using Python, Licensee
-  agrees to be bound by the terms and conditions of this License
-  Agreement.
-
-
-  BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
-  -------------------------------------------
-
-  BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
-
-  1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
-  office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
-  Individual or Organization ("Licensee") accessing and otherwise using
-  this software in source or binary form and its associated
-  documentation ("the Software").
-
-  2. Subject to the terms and conditions of this BeOpen Python License
-  Agreement, BeOpen hereby grants Licensee a non-exclusive,
-  royalty-free, world-wide license to reproduce, analyze, test, perform
-  and/or display publicly, prepare derivative works, distribute, and
-  otherwise use the Software alone or in any derivative version,
-  provided, however, that the BeOpen Python License is retained in the
-  Software, alone or in any derivative version prepared by Licensee.
-
-  3. BeOpen is making the Software available to Licensee on an "AS IS"
-  basis.  BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
-  IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
-  DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
-  FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
-  INFRINGE ANY THIRD PARTY RIGHTS.
-
-  4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
-  SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
-  AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
-  DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-  5. This License Agreement will automatically terminate upon a material
-  breach of its terms and conditions.
-
-  6. This License Agreement shall be governed by and interpreted in all
-  respects by the law of the State of California, excluding conflict of
-  law provisions.  Nothing in this License Agreement shall be deemed to
-  create any relationship of agency, partnership, or joint venture
-  between BeOpen and Licensee.  This License Agreement does not grant
-  permission to use BeOpen trademarks or trade names in a trademark
-  sense to endorse or promote products or services of Licensee, or any
-  third party.  As an exception, the "BeOpen Python" logos available at
-  http://www.pythonlabs.com/logos.html may be used according to the
-  permissions granted on that web page.
-
-  7. By copying, installing or otherwise using the software, Licensee
-  agrees to be bound by the terms and conditions of this License
-  Agreement.
-
-
-  CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
-  ---------------------------------------
-
-  1. This LICENSE AGREEMENT is between the Corporation for National
-  Research Initiatives, having an office at 1895 Preston White Drive,
-  Reston, VA 20191 ("CNRI"), and the Individual or Organization
-  ("Licensee") accessing and otherwise using Python 1.6.1 software in
-  source or binary form and its associated documentation.
-
-  2. Subject to the terms and conditions of this License Agreement, CNRI
-  hereby grants Licensee a nonexclusive, royalty-free, world-wide
-  license to reproduce, analyze, test, perform and/or display publicly,
-  prepare derivative works, distribute, and otherwise use Python 1.6.1
-  alone or in any derivative version, provided, however, that CNRI's
-  License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
-  1995-2001 Corporation for National Research Initiatives; All Rights
-  Reserved" are retained in Python 1.6.1 alone or in any derivative
-  version prepared by Licensee.  Alternately, in lieu of CNRI's License
-  Agreement, Licensee may substitute the following text (omitting the
-  quotes): "Python 1.6.1 is made available subject to the terms and
-  conditions in CNRI's License Agreement.  This Agreement together with
-  Python 1.6.1 may be located on the Internet using the following
-  unique, persistent identifier (known as a handle): 1895.22/1013.  This
-  Agreement may also be obtained from a proxy server on the Internet
-  using the following URL: http://hdl.handle.net/1895.22/1013".
-
-  3. In the event Licensee prepares a derivative work that is based on
-  or incorporates Python 1.6.1 or any part thereof, and wants to make
-  the derivative work available to others as provided herein, then
-  Licensee hereby agrees to include in any such work a brief summary of
-  the changes made to Python 1.6.1.
-
-  4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
-  basis.  CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
-  IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
-  DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
-  FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
-  INFRINGE ANY THIRD PARTY RIGHTS.
-
-  5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
-  1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
-  A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
-  OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-  6. This License Agreement will automatically terminate upon a material
-  breach of its terms and conditions.
-
-  7. This License Agreement shall be governed by the federal
-  intellectual property law of the United States, including without
-  limitation the federal copyright law, and, to the extent such
-  U.S. federal law does not apply, by the law of the Commonwealth of
-  Virginia, excluding Virginia's conflict of law provisions.
-  Notwithstanding the foregoing, with regard to derivative works based
-  on Python 1.6.1 that incorporate non-separable material that was
-  previously distributed under the GNU General Public License (GPL), the
-  law of the Commonwealth of Virginia shall govern this License
-  Agreement only as to issues arising under or with respect to
-  Paragraphs 4, 5, and 7 of this License Agreement.  Nothing in this
-  License Agreement shall be deemed to create any relationship of
-  agency, partnership, or joint venture between CNRI and Licensee.  This
-  License Agreement does not grant permission to use CNRI trademarks or
-  trade name in a trademark sense to endorse or promote products or
-  services of Licensee, or any third party.
-
-  8. By clicking on the "ACCEPT" button where indicated, or by copying,
-  installing or otherwise using Python 1.6.1, Licensee agrees to be
-  bound by the terms and conditions of this License Agreement.
-
-          ACCEPT
-
-
-  CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
-  --------------------------------------------------
-
-  Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
-  The Netherlands.  All rights reserved.
-
-  Permission to use, copy, modify, and distribute this software and its
-  documentation for any purpose and without fee is hereby granted,
-  provided that the above copyright notice appear in all copies and that
-  both that copyright notice and this permission notice appear in
-  supporting documentation, and that the name of Stichting Mathematisch
-  Centrum or CWI not be used in advertising or publicity pertaining to
-  distribution of the software without specific, written prior
-  permission.
-
-  STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
-  THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
-  FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
-  FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
-  OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-
-
-

src/murmurhashneutral2.[hc]

-

This fast hash implementation is released to the public domain by Austin -Appleby. See http://murmurhash.googlepages.com.

-
-
-

src/minitrace.[hc]

-

A library for producing JSON traces suitable for Chrome’s built-in trace viewer -(chrome://tracing). Downloaded from https://github.com/hrydgard/minitrace.

-
-
-
The MIT License (MIT)
-
-Copyright (c) 2014 Henrik Rydgård
-
-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.
-
-
-
-

src/snprintf.c and m4/snprintf.m4

-

This implementation of snprintf() and similar functions was downloaded from -http://www.jhweiss.de/software/snprintf.html and has the following license:

-
-
-
  Copyright (c) 1995 Patrick Powell.
-
-  This code is based on code written by Patrick Powell <papowell@astart.com>.
-  It may be used for any purpose as long as this notice remains intact on all
-  source code distributions.
-
-  Copyright (c) 2008 Holger Weiss.
-
-  This version of the code is maintained by Holger Weiss <holger@jhweiss.de>.
-  My changes to the code may freely be used, modified and/or redistributed for
-  any purpose. It would be nice if additions and fixes to this file (including
-  trivial code cleanups) would be sent back in order to let me include them in
-  the version available at <http://www.jhweiss.de/software/snprintf.html>.
-  However, this is not a requirement for using or redistributing (possibly
-  modified) versions of this file, nor is leaving this notice intact mandatory.
-
-
-
-

src/zlib/*.[hc]

-

This is a bundled subset of zlib 1.2.11 from http://zlib.net with the -following license:

-
-
-
  Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler
-
-  This software is provided 'as-is', without any express or implied
-  warranty.  In no event will the authors be held liable for any damages
-  arising from the use of this software.
-
-  Permission is granted to anyone to use this software for any purpose,
-  including commercial applications, and to alter it and redistribute it
-  freely, subject to the following restrictions:
-
-  1. The origin of this software must not be misrepresented; you must not
-     claim that you wrote the original software. If you use this software
-     in a product, an acknowledgment in the product documentation would be
-     appreciated but is not required.
-  2. Altered source versions must be plainly marked as such, and must not be
-     misrepresented as being the original software.
-  3. This notice may not be removed or altered from any source distribution.
-
-  Jean-loup Gailly        Mark Adler
-  jloup@gzip.org          madler@alumni.caltech.edu
-
-
-
-
-
-

- - - diff --git a/Makefile.in b/Makefile.in deleted file mode 100644 index d10f945..0000000 --- a/Makefile.in +++ /dev/null @@ -1,182 +0,0 @@ -srcdir = @srcdir@ -builddir = @builddir@ -VPATH = @srcdir@ - -prefix = @prefix@ -exec_prefix = @exec_prefix@ -bindir = @bindir@ -mandir = @mandir@ -datarootdir = @datarootdir@ -sysconfdir = @sysconfdir@ -installcmd = @INSTALL@ - -AR = @AR@ -BASH = @BASH@ -CC = @CC@ -CFLAGS = @CFLAGS@ -CPPFLAGS = @CPPFLAGS@ -EXEEXT = @EXEEXT@ -LDFLAGS = @LDFLAGS@ -LIBS = @LIBS@ -RANLIB = @RANLIB@ - -all_cflags = $(CFLAGS) -all_cppflags = @DEFS@ -DSYSCONFDIR=$(sysconfdir) -I. -I$(srcdir)/src -I$(builddir)/unittest $(CPPFLAGS) -extra_libs = @extra_libs@ - -v_at_0 = yes -v_at_ = $(v_at_0) -quiet := $(v_at_$(V)) -Q=$(if $(quiet),@) - -non_3pp_sources = \ - src/args.c \ - src/ccache.c \ - src/cleanup.c \ - src/compopt.c \ - src/conf.c \ - src/confitems.c \ - src/counters.c \ - src/execute.c \ - src/exitfn.c \ - src/hash.c \ - src/hashutil.c \ - src/language.c \ - src/lockfile.c \ - src/manifest.c \ - src/mdfour.c \ - src/stats.c \ - src/util.c -generated_sources = \ - src/confitems_lookup.c \ - src/envtoconfitems_lookup.c \ - src/version.c -3pp_sources = \ - src/getopt_long.c \ - src/hashtable.c \ - src/hashtable_itr.c \ - src/murmurhashneutral2.c \ - src/snprintf.c -extra_sources = @extra_sources@ -base_sources = $(non_3pp_sources) $(generated_sources) $(3pp_sources) $(extra_sources) -base_objs = $(base_sources:.c=.o) - -non_3pp_objs = $(non_3pp_sources:.c=.o) - -ccache_sources = src/main.c $(base_sources) -ccache_objs = $(ccache_sources:.c=.o) - -zlib_sources = \ - src/zlib/adler32.c \ - src/zlib/crc32.c \ - src/zlib/deflate.c \ - src/zlib/gzclose.c \ - src/zlib/gzlib.c \ - src/zlib/gzread.c \ - src/zlib/gzwrite.c \ - src/zlib/inffast.c \ - src/zlib/inflate.c \ - src/zlib/inftrees.c \ - src/zlib/trees.c \ - src/zlib/zutil.c - -zlib_objs = $(zlib_sources:.c=.o) - -test_suites = @test_suites@ -test_sources = unittest/main.c unittest/framework.c unittest/util.c -test_sources += $(test_suites) -test_objs = $(test_sources:.c=.o) - -all_sources = $(ccache_sources) $(test_sources) -all_objs = $(ccache_objs) $(test_objs) $(zlib_objs) - -files_to_clean = \ - $(all_objs) \ - ccache$(EXEEXT) \ - src/*~ \ - src/zlib/libz.a \ - testdir.* \ - unittest/run$(EXEEXT) \ - *~ - -files_to_distclean = Makefile config.h config.log config.status - -.PHONY: all -all: ccache$(EXEEXT) - -ccache$(EXEEXT): $(ccache_objs) $(extra_libs) - $(if $(quiet),@echo " LD $@") - $(Q)$(CC) -o $@ $(ccache_objs) $(LDFLAGS) $(extra_libs) $(LIBS) - -ccache.1: doc/ccache.1 - $(if $(quiet),@echo " CP $@") - $(Q)cp $< $@ - -.PHONY: install -install: ccache$(EXEEXT) @disable_man@ccache.1 - $(if $(quiet),@echo " INSTALL ccache$(EXEEXT)") - $(Q)$(installcmd) -d $(DESTDIR)$(bindir) - $(Q)$(installcmd) -m 755 ccache$(EXEEXT) $(DESTDIR)$(bindir) -@disable_man@ $(if $(quiet),@echo " INSTALL ccache.1") -@disable_man@ $(Q)$(installcmd) -d $(DESTDIR)$(mandir)/man1 -@disable_man@ $(Q)-$(installcmd) -m 644 ccache.1 $(DESTDIR)$(mandir)/man1/ - -.PHONY: clean -clean: - rm -rf $(files_to_clean) - -$(non_3pp_objs) $(test_objs): CFLAGS += @more_warnings@ - -src/snprintf.o: CFLAGS += @no_implicit_fallthrough_warning@ -$(zlib_objs): CPPFLAGS += -include config.h -$(zlib_objs): CFLAGS += @no_implicit_fallthrough_warning@ - -src/zlib/libz.a: $(zlib_objs) - $(if $(quiet),@echo " AR $@") - $(Q)$(AR) cr $@ $(zlib_objs) - $(if $(quiet),@echo " RANLIB $@") - $(Q)$(RANLIB) $@ - -.PHONY: performance -performance: ccache$(EXEEXT) - $(srcdir)/misc/performance --ccache ccache$(EXEEXT) $(CC) $(all_cppflags) $(all_cflags) $(srcdir)/src/ccache.c - -.PHONY: test -test: ccache$(EXEEXT) unittest/run$(EXEEXT) - $(if $(quiet),@echo " TEST unittest/run$(EXEEXT)") - $(Q)unittest/run$(EXEEXT) - $(if $(quiet),@echo " TEST $(srcdir)/test/run") - $(Q)CC='$(CC)' $(BASH) $(srcdir)/test/run - -.PHONY: unittest -unittest: unittest/run$(EXEEXT) - $(if $(quiet),@echo " TEST $@") - $(Q)unittest/run$(EXEEXT) - -unittest/run$(EXEEXT): $(base_objs) $(test_objs) $(extra_libs) - $(if $(quiet),@echo " LD $@") - $(Q)$(CC) -o $@ $(base_objs) $(test_objs) $(LDFLAGS) $(extra_libs) $(LIBS) - -unittest/main.o: unittest/suites.h - -unittest/suites.h: $(test_suites) Makefile - $(if $(quiet),@echo " GEN $@") - $(Q)ls $^ | grep -v Makefile | xargs sed -n 's/TEST_SUITE(\(.*\))/SUITE(\1)/p' >$@ - -.PHONY: check -check: test - -.PHONY: distclean -distclean: clean - rm -rf $(files_to_distclean) - -.PHONY: installcheck -installcheck: ccache$(EXEEXT) unittest/run$(EXEEXT) - unittest/run$(EXEEXT) - CCACHE=$(bindir)/ccache CC='$(CC)' $(BASH) $(srcdir)/test/run - -.c.o: - $(if $(quiet),@echo " CC $@") - $(Q)$(CC) $(all_cppflags) $(all_cflags) -c -o $@ $< - -@include_dev_mk@ diff --git a/README.md b/README.md index c88bb21..aa78593 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,23 @@ -ccache – a fast compiler cache +Ccache – a fast compiler cache ============================== -[![Build Status](https://travis-ci.org/ccache/ccache.svg?branch=master)](https://travis-ci.org/ccache/ccache) +[![Build Status](https://github.com/ccache/ccache/workflows/Build/badge.svg)](https://github.com/ccache/ccache/actions?query=workflow%3A%22Build%22) [![Code Quality: Cpp](https://img.shields.io/lgtm/grade/cpp/g/ccache/ccache.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/ccache/ccache/context:cpp) [![Total Alerts](https://img.shields.io/lgtm/alerts/g/ccache/ccache.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/ccache/ccache/alerts) +[![Gitter](https://img.shields.io/gitter/room/ccache/ccache.svg)](https://gitter.im/ccache/ccache) -ccache is a compiler cache. It speeds up recompilation by caching the result of -previous compilations and detecting when the same compilation is being done -again. Supported languages are C, C++, Objective-C and Objective-C++. +Ccache (or “ccache”) is a compiler cache. It [speeds up +recompilation](https://ccache.dev/performance.html) by caching previous +compilations and detecting when the same compilation is being done again. General information ------------------- * [Main web site](https://ccache.dev) +* [Supported platforms, compilers and languages](https://ccache.dev/platform-compiler-language-support.html) * [Documentation](https://ccache.dev/documentation.html) - * [Latest manual](https://ccache.dev/manual/latest.html) - * [Installation from Git source repository](https://github.com/ccache/ccache/blob/master/doc/INSTALL.md) - * [Installation from release archive](https://github.com/ccache/ccache/blob/master/doc/INSTALL-from-release-archive.md) +* [Installation instructions](https://github.com/ccache/ccache/blob/master/doc/INSTALL.md) * [Release notes](https://ccache.dev/releasenotes.html) * [Credits and history](https://ccache.dev/credits.html) * [License and copyright](https://ccache.dev/license.html) @@ -29,6 +29,7 @@ Contributing to ccache * [Source repository](https://github.com/ccache/ccache) * [Notes on how to contribute](https://github.com/ccache/ccache/blob/master/CONTRIBUTING.md) * [Mailing list](https://lists.samba.org/mailman/listinfo/ccache/) +* [Chat](https://gitter.im/ccache/ccache) * [Bug report info](https://ccache.dev/bugs.html) * [Issue tracker](https://github.com/ccache/ccache/issues) * [Help wanted!](https://github.com/ccache/ccache/labels/help%20wanted) diff --git a/autogen.sh b/autogen.sh deleted file mode 100755 index 709b605..0000000 --- a/autogen.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -set -e - -if [ -f dev_mode_disabled ]; then - cat <&2 -Error: It looks like you are building ccache from a release archive. If so, -there is no need to run autogen.sh. See INSTALL.md for further instructions. - -If you do want to the enable the development mode, delete the file -dev_mode_disabled first, but it's probably a better idea to work with the -proper ccache Git repository directly as described on -. -EOF - exit 1 -fi - -autoheader -autoconf -echo "Now run ./configure and make" diff --git a/ci/build b/ci/build new file mode 100755 index 0000000..ff50bbf --- /dev/null +++ b/ci/build @@ -0,0 +1,32 @@ +#!/bin/sh +# +# This script is used by CI and build-in-docker. + +set -eu + +if [ -n "${VERBOSE:-}" ]; then + set -x +fi + +if [ -n "${SPECIAL:-}" ]; then + exec "ci/$SPECIAL" +else + [ -z ${JOBS:+x} ] && JOBS=$(getconf _NPROCESSORS_ONLN 2>/dev/null) + [ -z ${JOBS:+x} ] && JOBS=2 + + mkdir -p ${BUILDDIR:-build} + cd ${BUILDDIR:-build} + ${CMAKE_PREFIX:-} cmake ${CMAKE_PARAMS:-} ${CCACHE_LOC:-..} + ${CMAKE_PREFIX:-} cmake --build . ${EXTRA_CMAKE_BUILD_FLAGS:-} -- -j$JOBS + case "${RUN_TESTS:-all}" in + all) + CC=${TEST_CC:-${CC}} ctest --output-on-failure -j$JOBS "$@" + ;; + unittest-in-wine) + wine ccache.exe --version + wine unittest/unittest.exe + ;; + none) + ;; + esac +fi diff --git a/ci/build-and-verify-package b/ci/build-and-verify-package new file mode 100755 index 0000000..c9a95d3 --- /dev/null +++ b/ci/build-and-verify-package @@ -0,0 +1,21 @@ +#!/bin/sh + +set -eu + +# Ninja builds with relative paths so that ccache can be used to cache the build +# without resorting to setting base_dir. +export CMAKE_GENERATOR=Ninja + +rm -rf build_package_dir_test +mkdir -p build_package_dir_test +cd build_package_dir_test +cmake .. +ninja -v package + +# Get out of git directory just to be sure. +tmp_dir=$(mktemp -d) +trap "rm -rf $tmp_dir" EXIT + +tar -xf ccache-*.tar.xz -C $tmp_dir + +CCACHE=$(echo $tmp_dir/ccache-*/bin/ccache) ../test/run diff --git a/ci/build-and-verify-source-package b/ci/build-and-verify-source-package new file mode 100755 index 0000000..5a212c2 --- /dev/null +++ b/ci/build-and-verify-source-package @@ -0,0 +1,24 @@ +#!/bin/sh +# +# Test that it works to build from a source archive exported by "git archive" +# outside a Git repository. + +set -eu + +# Ninja builds with relative paths so that ccache can be used to cache the build +# without resorting to setting base_dir. +export CMAKE_GENERATOR=Ninja + +tmp_dir=$(mktemp -d) +trap "rm -rf $tmp_dir" EXIT + +git archive --prefix=ccache/ -o $tmp_dir/ccache.tar.gz HEAD +cd $tmp_dir +tar xf ccache.tar.gz +cd ccache +mkdir build +cd build +cmake .. +ninja -v +jobs=$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1) +ctest --output-on-failure -j $jobs diff --git a/ci/collect-testdir b/ci/collect-testdir new file mode 100755 index 0000000..21a3971 --- /dev/null +++ b/ci/collect-testdir @@ -0,0 +1,12 @@ +#!/bin/sh + +if [ -d testdir ]; then + testdir=testdir +elif [ -d build/testdir ]; then + testdir=build/testdir +else + echo "No testdir found" >&2 + exit 1 +fi + +tar -caf testdir.tar.xz $testdir diff --git a/ci/install-cuda b/ci/install-cuda new file mode 100755 index 0000000..169f2a8 --- /dev/null +++ b/ci/install-cuda @@ -0,0 +1,32 @@ +#!/bin/bash +# +# Version is given in the CUDA variable. + +set -eu + +retry() { + local i=0 + while [ $i -lt 3 ]; do + if "$@"; then + return 0 + fi + i=$((i + 1)) + done + return 1 +} + +echo "Installing CUDA support" + +retry wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-repo-ubuntu1804_${CUDA}_amd64.deb +retry sudo dpkg -i cuda-repo-ubuntu1804_${CUDA}_amd64.deb +retry sudo apt-key adv --fetch-keys http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub +retry sudo apt-get update -qq + +cuda_prefix=${CUDA:0:4} +cuda_prefix=${cuda_prefix/./-} +retry sudo apt-get install --allow-unauthenticated -y cuda-command-line-tools-${cuda_prefix} +retry sudo apt-get clean + +cuda_home=/usr/local/cuda-${CUDA:0:4} +$cuda_home/bin/nvcc --version +echo "${cuda_home}/bin" >>$GITHUB_PATH diff --git a/cmake/.gitattributes b/cmake/.gitattributes new file mode 100644 index 0000000..c217b7d --- /dev/null +++ b/cmake/.gitattributes @@ -0,0 +1 @@ +CcacheVersion.cmake export-subst diff --git a/cmake/CIBuildType.cmake b/cmake/CIBuildType.cmake new file mode 100644 index 0000000..963bc50 --- /dev/null +++ b/cmake/CIBuildType.cmake @@ -0,0 +1,31 @@ +# Add a build type called "CI" which is like RelWithDebInfo but with assertions +# enabled, i.e. without passing -DNDEBUG to the compiler. + +set(CMAKE_CXX_FLAGS_CI ${CMAKE_CXX_FLAGS_RELWITHDEBINFO} CACHE STRING + "Flags used by the C++ compiler during CI builds." + FORCE) +set(CMAKE_C_FLAGS_CI ${CMAKE_C_FLAGS_RELWITHDEBINFO} CACHE STRING + "Flags used by the C compiler during CI builds." + FORCE) +set(CMAKE_EXE_LINKER_FLAGS_CI + ${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} CACHE STRING + "Flags used for linking binaries during CI builds." + FORCE) +set(CMAKE_SHARED_LINKER_FLAGS_CI + ${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} CACHE STRING + "Flags used by the shared libraries linker during CI builds." + FORCE) +mark_as_advanced( + CMAKE_CXX_FLAGS_CI + CMAKE_C_FLAGS_CI + CMAKE_EXE_LINKER_FLAGS_CI + CMAKE_SHARED_LINKER_FLAGS_CI) +# Update the documentation string of CMAKE_BUILD_TYPE for GUIs +set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING + "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel CI." + FORCE) + +string(REPLACE -DNDEBUG "" CMAKE_CXX_FLAGS_CI ${CMAKE_CXX_FLAGS_CI}) +string(REPLACE -DNDEBUG "" CMAKE_C_FLAGS_CI ${CMAKE_C_FLAGS_CI}) +string(STRIP ${CMAKE_CXX_FLAGS_CI} CMAKE_CXX_FLAGS_CI) +string(STRIP ${CMAKE_C_FLAGS_CI} CMAKE_C_FLAGS_CI) diff --git a/cmake/CcachePackConfig.cmake b/cmake/CcachePackConfig.cmake new file mode 100644 index 0000000..daaca30 --- /dev/null +++ b/cmake/CcachePackConfig.cmake @@ -0,0 +1,24 @@ +# Note: This is part of CMakeLists.txt file, not to be confused with +# CPackConfig.cmake. + +if(${CMAKE_VERSION} VERSION_LESS "3.9") + set(CPACK_PACKAGE_DESCRIPTION "${CMAKE_PROJECT_DESCRIPTION}") +endif() + +# From CcacheVersion.cmake. +set(CPACK_PACKAGE_VERSION ${VERSION}) + +set(CPACK_VERBATIM_VARIABLES ON) + +if(WIN32) + set(CPACK_GENERATOR "ZIP") +else() + set(CPACK_GENERATOR "TXZ") +endif() + +set( + CPACK_PACKAGE_FILE_NAME + "ccache-${VERSION}-${CMAKE_HOST_SYSTEM_NAME}-${CMAKE_HOST_SYSTEM_PROCESSOR}" +) + +include(CPack) diff --git a/cmake/CcacheVersion.cmake b/cmake/CcacheVersion.cmake new file mode 100644 index 0000000..fcac1f5 --- /dev/null +++ b/cmake/CcacheVersion.cmake @@ -0,0 +1,64 @@ +# There are three main scenarios: +# +# 1. Building from a source code archive generated by "git archive", e.g. the +# official source code archive or one downloaded directly from GitHub via +# . In this case the +# version_info info string below will be substituted because of export-subst +# in the .gitattributes file. The version will then be correct if VERSION +# refers to a tagged commit. If the commit is not tagged the version will be +# set to the commit hash instead. +# 2. Building from a source code archive not generated by "git archive" (i.e., +# version_info has not been substituted). In this case we fail the +# configuration. +# 3. Building from a Git repository. In this case the version will be a proper +# version if building a tagged commit, otherwise "branch.hash(+dirty)". + +set(version_info "7c44b8c45bd6bc185bf5bd5666f36fcac092d917 HEAD, tag: v4.0, origin/master, origin/HEAD, master") + +if(version_info MATCHES "^([0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f])[0-9a-f]* (.*)") + # Scenario 1. + set(hash "${CMAKE_MATCH_1}") + set(ref_names "${CMAKE_MATCH_2}") + if(ref_names MATCHES "tag: v([^,]+)") + # Tagged commit. + set(VERSION "${CMAKE_MATCH_1}") + else() + # Untagged commit. + set(VERSION "${hash}") + endif() +elseif(EXISTS "${CMAKE_SOURCE_DIR}/.git") + # Scenario 3. + find_package(Git QUIET) + if(NOT GIT_FOUND) + message(SEND_ERROR "Could not find git") + endif() + + macro(git) + execute_process( + COMMAND "${GIT_EXECUTABLE}" ${ARGN} + OUTPUT_VARIABLE git_stdout OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_VARIABLE git_stderr ERROR_STRIP_TRAILING_WHITESPACE) + endmacro() + + git(describe --abbrev=8 --dirty) + if(git_stdout MATCHES "^v([^-]+)(-dirty)?$") + set(VERSION "${CMAKE_MATCH_1}") + if(NOT "${CMAKE_MATCH_2}" STREQUAL "") + set(VERSION "${VERSION}+dirty") + endif() + elseif(git_stdout MATCHES "^v[^-]+-[0-9]+-g([0-9a-f]+)(-dirty)?$") + set(hash "${CMAKE_MATCH_1}") + set(dirty "${CMAKE_MATCH_2}") + string(REPLACE "-" "+" dirty "${dirty}") + + git(rev-parse --abbrev-ref HEAD) + set(branch "${git_stdout}") + + set(VERSION "${branch}.${hash}${dirty}") + endif() # else: fail below +endif() + +if(VERSION STREQUAL "") + # Scenario 2 or unexpected error. + message(SEND_ERROR "Cannot determine Ccache version") +endif() diff --git a/cmake/CheckAsmCompilerFlag.cmake b/cmake/CheckAsmCompilerFlag.cmake new file mode 100644 index 0000000..07f5f8e --- /dev/null +++ b/cmake/CheckAsmCompilerFlag.cmake @@ -0,0 +1,62 @@ +include(CMakeCheckCompilerFlagCommonPatterns) + +function(check_asm_compiler_flag flag var) + if(DEFINED "${var}") + return() + endif() + + set(locale_vars LC_ALL LC_MESSAGES LANG) + foreach(v IN LISTS locale_vars) + set(locale_vars_saved_${v} "$ENV{${v}}") + set(ENV{${v}} C) + endforeach() + + check_compiler_flag_common_patterns(common_patterns) + + set(test_file "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.S") + file(WRITE "${test_file}" ".global main\nmain:\n") + + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${var}") + endif() + try_compile( + ${var} + "${CMAKE_BINARY_DIR}" + "${test_file}" + COMPILE_DEFINITIONS "${flag}" + OUTPUT_VARIABLE output) + + check_compiler_flag_common_patterns(common_fail_patterns) + + foreach(regex ${common_fail_patterns}) + if("${output}" MATCHES "${regex}") + set(${var} 0) + endif() + endforeach() + + if(${${var}}) + set(${var} 1 CACHE INTERNAL "Test ${var}") + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${var} - Success") + endif() + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Performing ASM SOURCE FILE Test ${var} succeeded with the following output:\n" + "${output}\n" + "Source file was:\n${test_file}\n") + else() + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${var} - Failed") + endif() + set(${var} "" CACHE INTERNAL "Test ${var}") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Performing ASM SOURCE FILE Test ${var} failed with the following output:\n" + "${output}\n" + "Source file was:\n${test_file}\n") + endif() + + foreach(v IN LISTS locale_vars) + set(ENV{${v}} ${locale_vars_saved_${v}}) + endforeach() + + set(${var} "${${var}}" PARENT_SCOPE) +endfunction() diff --git a/cmake/CodeAnalysis.cmake b/cmake/CodeAnalysis.cmake new file mode 100644 index 0000000..4e639a7 --- /dev/null +++ b/cmake/CodeAnalysis.cmake @@ -0,0 +1,38 @@ +option(ENABLE_CPPCHECK "Enable static analysis with Cppcheck" OFF) +if(ENABLE_CPPCHECK) + if(${CMAKE_VERSION} VERSION_LESS "3.10") + message(WARNING "Cppcheck requires CMake 3.10") + else() + find_program(CPPCHECK_EXE cppcheck) + mark_as_advanced(CPPCHECK_EXE) # Don't show in CMake UIs + if(CPPCHECK_EXE) + set(CMAKE_CXX_CPPCHECK + ${CPPCHECK_EXE} + --suppressions-list=${CMAKE_SOURCE_DIR}/misc/cppcheck-suppressions.txt + --inline-suppr + -q + --enable=all + --force + --std=c++11 + -I ${CMAKE_SOURCE_DIR} + --template="cppcheck: warning: {id}:{file}:{line}: {message}" + -i src/third_party) + else() + message(WARNING "Cppcheck requested but executable not found") + endif() + endif() +endif() + +option(ENABLE_CLANG_TIDY "Enable static analysis with Clang-Tidy" OFF) +if(ENABLE_CLANG_TIDY) + if(${CMAKE_VERSION} VERSION_LESS "3.6") + message(WARNING "Clang-Tidy requires CMake 3.6") + else() + find_program(CLANGTIDY clang-tidy) + if(CLANGTIDY) + set(CMAKE_CXX_CLANG_TIDY ${CLANGTIDY}) + else() + message(SEND_ERROR "Clang-Tidy requested but executable not found") + endif() + endif() +endif() diff --git a/cmake/DefaultBuildType.cmake b/cmake/DefaultBuildType.cmake new file mode 100644 index 0000000..87b7647 --- /dev/null +++ b/cmake/DefaultBuildType.cmake @@ -0,0 +1,27 @@ +# Set a default build type if none was specified. + +if(CMAKE_BUILD_TYPE OR CMAKE_CONFIGURATION_TYPES) + return() +endif() + +# Default to Release for end user builds (from source archive) and Debug for +# development builds (in a Git repository). +if(EXISTS "${CMAKE_SOURCE_DIR}/.git") + set( + CMAKE_BUILD_TYPE "Debug" + CACHE STRING "Choose the type of build." FORCE) +else() + set( + CMAKE_BUILD_TYPE "Release" + CACHE STRING "Choose the type of build." FORCE) +endif() +message( + STATUS + "Setting CMAKE_BUILD_TYPE to ${CMAKE_BUILD_TYPE} as none was specified." +) + +# Set the possible values of build type for CMake UIs. +set_property( + CACHE CMAKE_BUILD_TYPE + PROPERTY + STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") diff --git a/cmake/Findzstd.cmake b/cmake/Findzstd.cmake new file mode 100644 index 0000000..848348f --- /dev/null +++ b/cmake/Findzstd.cmake @@ -0,0 +1,66 @@ +if(zstd_FOUND) + return() +endif() + +if(ZSTD_FROM_INTERNET) + # Although ${zstd_FIND_VERSION} was requested, let's download a newer version. + # Note: The directory structure has changed in 1.3.0; we only support 1.3.0 + # and newer. + set(zstd_version "1.4.5") + set(zstd_url https://github.com/facebook/zstd/archive/v${zstd_version}.tar.gz) + + set(zstd_dir ${CMAKE_BINARY_DIR}/zstd-${zstd_version}) + set(zstd_build ${CMAKE_BINARY_DIR}/zstd-build) + + if(NOT EXISTS "${zstd_dir}.tar.gz") + file(DOWNLOAD "${zstd_url}" "${zstd_dir}.tar.gz" STATUS download_status) + list(GET download_status 0 error_code) + if(error_code) + file(REMOVE "${zstd_dir}.tar.gz") + list(GET download_status 1 error_message) + message(FATAL "Failed to download zstd: ${error_message}") + endif() + endif() + + execute_process( + COMMAND tar xf "${zstd_dir}.tar.gz" + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" + RESULT_VARIABLE tar_error) + if(NOT tar_error EQUAL 0) + message(FATAL "extracting ${zstd_dir}.tar.gz failed") + endif() + + set(ZSTD_BUILD_SHARED OFF) + add_subdirectory("${zstd_dir}/build/cmake" "${zstd_build}" EXCLUDE_FROM_ALL) + + add_library(ZSTD::ZSTD ALIAS libzstd_static) + set_target_properties( + libzstd_static + PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "$") + + set(zstd_FOUND TRUE) +else() + find_library(ZSTD_LIBRARY zstd) + find_path(ZSTD_INCLUDE_DIR zstd.h) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args( + zstd "please install libzstd or use -DZSTD_FROM_INTERNET=ON" + ZSTD_INCLUDE_DIR ZSTD_LIBRARY) + mark_as_advanced(ZSTD_INCLUDE_DIR ZSTD_LIBRARY) + + add_library(ZSTD::ZSTD UNKNOWN IMPORTED) + set_target_properties( + ZSTD::ZSTD + PROPERTIES + IMPORTED_LOCATION "${ZSTD_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${ZSTD_INCLUDE_DIR}") +endif() + +include(FeatureSummary) +set_package_properties( + zstd + PROPERTIES + URL "https://facebook.github.io/zstd" + DESCRIPTION "Zstandard - Fast real-time compression algorithm") diff --git a/cmake/GenerateConfigurationFile.cmake b/cmake/GenerateConfigurationFile.cmake new file mode 100644 index 0000000..582fedb --- /dev/null +++ b/cmake/GenerateConfigurationFile.cmake @@ -0,0 +1,76 @@ +include(CheckIncludeFile) +set(include_files + linux/fs.h + pwd.h + sys/clonefile.h + sys/ioctl.h + sys/mman.h + sys/time.h + sys/wait.h + sys/file.h + syslog.h + termios.h + dirent.h + strings.h + unistd.h + utime.h + sys/utime.h + varargs.h) +foreach(include_file IN ITEMS ${include_files}) + string(TOUPPER ${include_file} include_var) + string(REGEX REPLACE "[/.]" "_" include_var ${include_var}) + set(include_var HAVE_${include_var}) + check_include_file(${include_file} ${include_var}) +endforeach() + +include(CheckFunctionExists) +set(functions + asctime_r + geteuid + getopt_long + getpwuid + gettimeofday + mkstemp + posix_fallocate + realpath + setenv + strndup + syslog + unsetenv + utimes) +foreach(func IN ITEMS ${functions}) + string(TOUPPER ${func} func_var) + set(func_var HAVE_${func_var}) + check_function_exists(${func} ${func_var}) +endforeach() + +include(CheckStructHasMember) +check_struct_has_member("struct stat" st_ctim sys/stat.h + HAVE_STRUCT_STAT_ST_CTIM) +check_struct_has_member("struct stat" st_mtim sys/stat.h + HAVE_STRUCT_STAT_ST_MTIM) +check_struct_has_member("struct statfs" f_fstypename sys/mount.h + HAVE_STRUCT_STATFS_F_FSTYPENAME) + +include(CheckCXXCompilerFlag) +check_cxx_compiler_flag(-mavx2 HAVE_AVX2) + +list(APPEND CMAKE_REQUIRED_LIBRARIES ws2_32) +list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES ws2_32) + +include(CheckTypeSize) +check_type_size("long long" HAVE_LONG_LONG) + +if(WIN32) + set(_WIN32_WINNT 0x0600) +endif() + +if(CMAKE_SYSTEM MATCHES "Darwin") + set(_DARWIN_C_SOURCE 1) +endif() + +# alias +set(MTR_ENABLED "${ENABLE_TRACING}") + +configure_file(${CMAKE_SOURCE_DIR}/cmake/config.h.in + ${CMAKE_BINARY_DIR}/config.h @ONLY) diff --git a/cmake/GenerateVersionFile.cmake b/cmake/GenerateVersionFile.cmake new file mode 100644 index 0000000..1517d44 --- /dev/null +++ b/cmake/GenerateVersionFile.cmake @@ -0,0 +1,6 @@ +include(CcacheVersion) +configure_file( + ${CMAKE_SOURCE_DIR}/cmake/version.cpp.in + ${CMAKE_BINARY_DIR}/src/version.cpp + @ONLY) +message(STATUS "Ccache version: ${VERSION}") diff --git a/cmake/StandardSettings.cmake b/cmake/StandardSettings.cmake new file mode 100644 index 0000000..755ff9f --- /dev/null +++ b/cmake/StandardSettings.cmake @@ -0,0 +1,52 @@ +# This file provides a special "standard_settings" target which is supposed to +# be linked privately by all other targets. + +add_library(standard_settings INTERFACE) + +# Not supported in CMake 3.4: target_compile_features(project_options INTERFACE +# c_std_11 cxx_std_11) + +if(CMAKE_CXX_COMPILER_ID MATCHES "^GNU|Clang$") + option(ENABLE_COVERAGE "Enable coverage reporting for GCC/Clang" FALSE) + if(ENABLE_COVERAGE) + target_compile_options(standard_settings INTERFACE --coverage -O0 -g) + target_link_libraries(standard_settings INTERFACE --coverage) + endif() + + set(SANITIZERS "") + + option(ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" FALSE) + if(ENABLE_SANITIZER_ADDRESS) + list(APPEND SANITIZERS "address") + endif() + + option(ENABLE_SANITIZER_MEMORY "Enable memory sanitizer" FALSE) + if(ENABLE_SANITIZER_MEMORY) + list(APPEND SANITIZERS "memory") + endif() + + option( + ENABLE_SANITIZER_UNDEFINED_BEHAVIOR + "Enable undefined behavior sanitizer" + FALSE) + if(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR) + list(APPEND SANITIZERS "undefined") + endif() + + option(ENABLE_SANITIZER_THREAD "Enable thread sanitizer" FALSE) + if(ENABLE_SANITIZER_THREAD) + list(APPEND SANITIZERS "thread") + endif() + + foreach(SANITIZER IN LISTS SANITIZERS) + target_compile_options( + standard_settings + INTERFACE -fsanitize=${SANITIZER}) + target_link_libraries( + standard_settings + INTERFACE -fsanitize=${SANITIZER}) + endforeach() + +elseif(MSVC) + target_compile_options(standard_settings INTERFACE /std:c++latest /Zc:preprocessor /Zc:__cplusplus /D_CRT_SECURE_NO_WARNINGS) +endif() diff --git a/cmake/StandardWarnings.cmake b/cmake/StandardWarnings.cmake new file mode 100644 index 0000000..dbb45f1 --- /dev/null +++ b/cmake/StandardWarnings.cmake @@ -0,0 +1,160 @@ +# This file provides a special "standard_warnings" target which is supposed to +# be linked privately by all product and test code, but not by third party code. +add_library(standard_warnings INTERFACE) + +if(IS_DIRECTORY "${CMAKE_SOURCE_DIR}/.git" OR DEFINED ENV{"CI"}) + # Enabled by default for development builds and CI builds. + option(WARNINGS_AS_ERRORS "Treat compiler warnings as errors" TRUE) +else() + # Disabled by default for end user builds so compilation doesn't fail with new + # compilers that may emit new warnings. + option(WARNINGS_AS_ERRORS "Treat compiler warnings as errors" FALSE) +endif() + +include(CheckCXXCompilerFlag) + +# check_cxx_compiler_flag caches the result, so a unique variable name is +# required for every flag to be checked. +# +# Parameters: +# +# * flag [in], e.g. FLAG +# * var_name_of_var_name [in], e.g. "TEMP". This is the variable that "HAS_FLAG" +# will be written to. +function(generate_unique_has_flag_var_name flag var_name_of_var_name) + string(REGEX REPLACE "[=-]" "_" var_name "${flag}") + string(TOUPPER "${var_name}" var_name) + set(${var_name_of_var_name} "HAS_${var_name}" PARENT_SCOPE) +endfunction() + +function(add_target_compile_flag_if_supported_ex target flag alternative_flag) + # has_flag will contain "HAS_$flag" so each flag gets a unique HAS variable. + generate_unique_has_flag_var_name("${flag}" "has_flag") + + # Instead of passing "has_flag" this passes the content of has_flag. + check_cxx_compiler_flag("${flag}" "${has_flag}") + + if(${${has_flag}}) + target_compile_options(${target} INTERFACE "${flag}") + elseif("${alternative_flag}") + add_target_compile_flag_if_supported_ex(${target} ${alternative_flag} "") + endif() +endfunction() + +# TODO: Is there a better way to provide an optional third argument? +macro(add_target_compile_flag_if_supported target flag) + add_target_compile_flag_if_supported_ex("${target}" "${flag}" "") +endmacro() + +set(CLANG_GCC_WARNINGS + -Wall + -Wextra + -Wnon-virtual-dtor + -Wcast-align + -Wunused + -Woverloaded-virtual + -Wpedantic + + # Candidates for enabling in the future: + # -Wshadow + # -Wold-style-cast + # -Wconversion + # -Wsign-conversion + # -Wnull-dereference + # -Wformat=2 +) +# Tested separately as this is not supported by Clang 3.4. +add_target_compile_flag_if_supported(standard_warnings "-Wdouble-promotion") + +if(WARNINGS_AS_ERRORS) + set(CLANG_GCC_WARNINGS ${CLANG_GCC_WARNINGS} -Werror) +endif() + +if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.0) + set( + CLANG_GCC_WARNINGS + ${CLANG_GCC_WARNINGS} + -Qunused-arguments + -Wno-error=unreachable-code) + endif() + + target_compile_options( + standard_warnings + INTERFACE + ${CLANG_GCC_WARNINGS} + -Weverything + -Wno-c++98-compat-pedantic + -Wno-c++98-compat + -Wno-constexpr-not-const + -Wno-conversion + -Wno-disabled-macro-expansion + -Wno-documentation-unknown-command + -Wno-exit-time-destructors + -Wno-format-nonliteral + -Wno-global-constructors + -Wno-implicit-fallthrough + -Wno-padded + -Wno-shorten-64-to-32 + -Wno-sign-conversion + -Wno-weak-vtables + -Wno-old-style-cast) + + # If compiler supports -Wshadow-field-in-constructor, disable only that. + # Otherwise disable shadow. + add_target_compile_flag_if_supported_ex( + standard_warnings "-Wno-shadow-field-in-constructor" "-Wno-shadow") + + # Disable C++20 compatibility for now. + add_target_compile_flag_if_supported(standard_warnings "-Wno-c++2a-compat") + + # If compiler supports these warnings they have to be disabled for now. + add_target_compile_flag_if_supported( + standard_warnings "-Wno-zero-as-null-pointer-constant") + add_target_compile_flag_if_supported( + standard_warnings "-Wno-undefined-func-template") + add_target_compile_flag_if_supported( + standard_warnings "-Wno-return-std-move-in-c++11") +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options( + standard_warnings + INTERFACE ${CLANG_GCC_WARNINGS} + # Warn about logical operations being used where bitwise were probably + # wanted. + -Wlogical-op + + # Candidates for enabling in the future: + # -Wduplicated-cond + # -Wduplicated-branches + # -Wuseless-cast + ) + + # TODO: Exact version or reason unknown, discovered in Ubuntu 14 Docker test + # with GCC 4.8.4 + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8.5) + add_target_compile_flag_if_supported( + standard_warnings "-Wno-missing-field-initializers") + add_target_compile_flag_if_supported( + standard_warnings "-Wno-unused-variable") + endif() +elseif(MSVC) + # Remove any warning level flags added by CMake. + string(REGEX REPLACE "/W[0-4]" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + string(REGEX REPLACE "/W[0-4]" "" CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS}") + string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + + target_compile_options( + standard_warnings + INTERFACE + /W4 + # Ignore bad macro in winbase.h triggered by /Zc:preprocessor + /wd5105 + # Conversion warnings. + /wd4244 + /wd4267 + # Assignment in conditional. + /wd4706 + # Non-underscore-prefixed POSIX functions. + /wd4996 + ) +endif() diff --git a/cmake/config.h.in b/cmake/config.h.in new file mode 100644 index 0000000..11fdf43 --- /dev/null +++ b/cmake/config.h.in @@ -0,0 +1,162 @@ +#pragma once +#ifdef __clang__ +# pragma clang diagnostic push +# if __has_warning("-Wreserved-id-macro") +# pragma clang diagnostic ignored "-Wreserved-id-macro" +# endif +#endif + +// For example for vasprintf under i686-w64-mingw32-g++-posix. The later +// definition of _XOPEN_SOURCE disables certain features on Linux, so we need +// _GNU_SOURCE to re-enable them (makedev, tm_zone). +#define _GNU_SOURCE 1 + +// The later definition of _XOPEN_SOURCE and _POSIX_C_SOURCE disables certain +// features on NetBSD, so we need _NETBSD_SOURCE to re-enable them. +#define _NETBSD_SOURCE 1 + +// The later definition of _XOPEN_SOURCE and _POSIX_C_SOURCE disables certain +// features on FreeBSD, so we need __BSD_VISIBLE to re-enable them. +#define __BSD_VISIBLE 1 + +// The later definition of _XOPEN_SOURCE and _POSIX_C_SOURCE disables u_int on +// Irix 5.3. Defining _BSD_TYPES brings it back. +#define _BSD_TYPES 1 + +// The later definition of _XOPEN_SOURCE and _POSIX_C_SOURCE disables certain +// features on Mac OS X, so we need _DARWIN_C_SOURCE to re-enable them. +#cmakedefine _DARWIN_C_SOURCE + +// Define to activate features from IEEE Stds 1003.1-2001. +#define _POSIX_C_SOURCE 200809L + +#if defined(__SunOS_5_8) || defined(__SunOS_5_9) || defined(__SunOS_5_10) +# define _XOPEN_SOURCE 500 +#elif !defined(__SunOS_5_11) && !defined(__APPLE__) +# define _XOPEN_SOURCE +#endif + +#if defined(__SunOS_5_10) || defined(__SunOS_5_11) +# define __EXTENSIONS__ 1 +#else +# define _XOPEN_SOURCE_EXTENDED +#endif + +// Handle large files when compiled in 32-bit mode. +#ifndef _FILE_OFFSET_BITS +# define _FILE_OFFSET_BITS 64 +#endif + +// clang-format off +#cmakedefine _WIN32_WINNT @_WIN32_WINNT@ +// clang-format on + +#define SYSCONFDIR "@CMAKE_INSTALL_FULL_SYSCONFDIR@" + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + +#cmakedefine MTR_ENABLED + +/* Define to 1 if you have the `asctime_r' function. */ +#cmakedefine HAVE_ASCTIME_R + +/* Define to 1 if your compiler supports AVX2. */ +#cmakedefine HAVE_AVX2 + +/* Define to 1 if you have the `geteuid' function. */ +#cmakedefine HAVE_GETEUID + +/* Define to 1 if you have the `getopt_long' function. */ +#cmakedefine HAVE_GETOPT_LONG + +/* Define to 1 if you have the `getpwuid' function. */ +#cmakedefine HAVE_GETPWUID + +/* Define to 1 if you have the `gettimeofday' function. */ +#cmakedefine HAVE_GETTIMEOFDAY + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_LINUX_FS_H + +/* Define to 1 if the system has the type `long long'. */ +#cmakedefine HAVE_LONG_LONG + +/* Define to 1 if you have the `mkstemp' function. */ +#cmakedefine HAVE_MKSTEMP + +/* Define to 1 if you have the `posix_fallocate. */ +#cmakedefine HAVE_POSIX_FALLOCATE + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_PWD_H + +/* Define to 1 if you have the `realpath' function. */ +#cmakedefine HAVE_REALPATH + +/* Define to 1 if you have the `setenv' function. */ +#cmakedefine HAVE_SETENV + +/* Define to 1 if you have the `strndup' function. */ +#cmakedefine HAVE_STRNDUP + +/* Define to 1 if `f_fstypename' is a member of `struct statfs'. */ +#cmakedefine HAVE_STRUCT_STATFS_F_FSTYPENAME + +/* Define to 1 if `st_ctim' is a member of `struct stat'. */ +#cmakedefine HAVE_STRUCT_STAT_ST_CTIM + +/* Define to 1 if `st_mtim' is a member of `struct stat'. */ +#cmakedefine HAVE_STRUCT_STAT_ST_MTIM + +/* Define to 1 if you have the `syslog' function. */ +#cmakedefine HAVE_SYSLOG + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYSLOG_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_CLONEFILE_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_MMAN_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TIME_H + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#cmakedefine HAVE_SYS_WAIT_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_FILE_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_TERMIOS_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_DIRENT_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UNISTD_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UTIME_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_UTIME_H + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_VARARGS_H + +/* Define to 1 if you have the `unsetenv' function. */ +#cmakedefine HAVE_UNSETENV + +/* Define to 1 if you have the `utimes' function. */ +#cmakedefine HAVE_UTIMES diff --git a/cmake/version.cpp.in b/cmake/version.cpp.in new file mode 100644 index 0000000..291f049 --- /dev/null +++ b/cmake/version.cpp.in @@ -0,0 +1,2 @@ +extern const char CCACHE_VERSION[]; +const char CCACHE_VERSION[] = "@VERSION@"; diff --git a/config.guess b/config.guess deleted file mode 100644 index 9baaa27..0000000 --- a/config.guess +++ /dev/null @@ -1,1476 +0,0 @@ -#! /bin/sh -# Attempt to guess a canonical system name. -# Copyright 1992-2018 Free Software Foundation, Inc. - -timestamp='2018-01-26' - -# This file is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see . -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that -# program. This Exception is an additional permission under section 7 -# of the GNU General Public License, version 3 ("GPLv3"). -# -# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. -# -# You can get the latest version of this script from: -# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess -# -# Please send patches to . - - -me=`echo "$0" | sed -e 's,.*/,,'` - -usage="\ -Usage: $0 [OPTION] - -Output the configuration name of the system \`$me' is run on. - -Options: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.guess ($timestamp) - -Originally written by Per Bothner. -Copyright 1992-2018 Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit ;; - --version | -v ) - echo "$version" ; exit ;; - --help | --h* | -h ) - echo "$usage"; exit ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" >&2 - exit 1 ;; - * ) - break ;; - esac -done - -if test $# != 0; then - echo "$me: too many arguments$help" >&2 - exit 1 -fi - -trap 'exit 1' 1 2 15 - -# CC_FOR_BUILD -- compiler used by this script. Note that the use of a -# compiler to aid in system detection is discouraged as it requires -# temporary files to be created and, as you can see below, it is a -# headache to deal with in a portable fashion. - -# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still -# use `HOST_CC' if defined, but it is deprecated. - -# Portable tmp directory creation inspired by the Autoconf team. - -set_cc_for_build=' -trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; -trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; -: ${TMPDIR=/tmp} ; - { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || - { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || - { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || - { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; -dummy=$tmp/dummy ; -tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; -case $CC_FOR_BUILD,$HOST_CC,$CC in - ,,) echo "int x;" > "$dummy.c" ; - for c in cc gcc c89 c99 ; do - if ($c -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then - CC_FOR_BUILD="$c"; break ; - fi ; - done ; - if test x"$CC_FOR_BUILD" = x ; then - CC_FOR_BUILD=no_compiler_found ; - fi - ;; - ,,*) CC_FOR_BUILD=$CC ;; - ,*,*) CC_FOR_BUILD=$HOST_CC ;; -esac ; set_cc_for_build= ;' - -# This is needed to find uname on a Pyramid OSx when run in the BSD universe. -# (ghazi@noc.rutgers.edu 1994-08-24) -if (test -f /.attbin/uname) >/dev/null 2>&1 ; then - PATH=$PATH:/.attbin ; export PATH -fi - -UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown -UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown -UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown -UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown - -case "$UNAME_SYSTEM" in -Linux|GNU|GNU/*) - # If the system lacks a compiler, then just pick glibc. - # We could probably try harder. - LIBC=gnu - - eval "$set_cc_for_build" - cat <<-EOF > "$dummy.c" - #include - #if defined(__UCLIBC__) - LIBC=uclibc - #elif defined(__dietlibc__) - LIBC=dietlibc - #else - LIBC=gnu - #endif - EOF - eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`" - - # If ldd exists, use it to detect musl libc. - if command -v ldd >/dev/null && \ - ldd --version 2>&1 | grep -q ^musl - then - LIBC=musl - fi - ;; -esac - -# Note: order is significant - the case branches are not exclusive. - -case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in - *:NetBSD:*:*) - # NetBSD (nbsd) targets should (where applicable) match one or - # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, - # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently - # switched to ELF, *-*-netbsd* would select the old - # object file format. This provides both forward - # compatibility and a consistent mechanism for selecting the - # object file format. - # - # Note: NetBSD doesn't particularly care about the vendor - # portion of the name. We always set it to "unknown". - sysctl="sysctl -n hw.machine_arch" - UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ - "/sbin/$sysctl" 2>/dev/null || \ - "/usr/sbin/$sysctl" 2>/dev/null || \ - echo unknown)` - case "$UNAME_MACHINE_ARCH" in - armeb) machine=armeb-unknown ;; - arm*) machine=arm-unknown ;; - sh3el) machine=shl-unknown ;; - sh3eb) machine=sh-unknown ;; - sh5el) machine=sh5le-unknown ;; - earmv*) - arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` - endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` - machine="${arch}${endian}"-unknown - ;; - *) machine="$UNAME_MACHINE_ARCH"-unknown ;; - esac - # The Operating System including object format, if it has switched - # to ELF recently (or will in the future) and ABI. - case "$UNAME_MACHINE_ARCH" in - earm*) - os=netbsdelf - ;; - arm*|i386|m68k|ns32k|sh3*|sparc|vax) - eval "$set_cc_for_build" - if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ELF__ - then - # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). - # Return netbsd for either. FIX? - os=netbsd - else - os=netbsdelf - fi - ;; - *) - os=netbsd - ;; - esac - # Determine ABI tags. - case "$UNAME_MACHINE_ARCH" in - earm*) - expr='s/^earmv[0-9]/-eabi/;s/eb$//' - abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` - ;; - esac - # The OS release - # Debian GNU/NetBSD machines have a different userland, and - # thus, need a distinct triplet. However, they do not need - # kernel version information, so it can be replaced with a - # suitable tag, in the style of linux-gnu. - case "$UNAME_VERSION" in - Debian*) - release='-gnu' - ;; - *) - release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` - ;; - esac - # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: - # contains redundant information, the shorter form: - # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "$machine-${os}${release}${abi}" - exit ;; - *:Bitrig:*:*) - UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` - echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" - exit ;; - *:OpenBSD:*:*) - UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` - echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" - exit ;; - *:LibertyBSD:*:*) - UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` - echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" - exit ;; - *:MidnightBSD:*:*) - echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE" - exit ;; - *:ekkoBSD:*:*) - echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE" - exit ;; - *:SolidBSD:*:*) - echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" - exit ;; - macppc:MirBSD:*:*) - echo powerpc-unknown-mirbsd"$UNAME_RELEASE" - exit ;; - *:MirBSD:*:*) - echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE" - exit ;; - *:Sortix:*:*) - echo "$UNAME_MACHINE"-unknown-sortix - exit ;; - *:Redox:*:*) - echo "$UNAME_MACHINE"-unknown-redox - exit ;; - mips:OSF1:*.*) - echo mips-dec-osf1 - exit ;; - alpha:OSF1:*:*) - case $UNAME_RELEASE in - *4.0) - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` - ;; - *5.*) - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` - ;; - esac - # According to Compaq, /usr/sbin/psrinfo has been available on - # OSF/1 and Tru64 systems produced since 1995. I hope that - # covers most systems running today. This code pipes the CPU - # types through head -n 1, so we only detect the type of CPU 0. - ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` - case "$ALPHA_CPU_TYPE" in - "EV4 (21064)") - UNAME_MACHINE=alpha ;; - "EV4.5 (21064)") - UNAME_MACHINE=alpha ;; - "LCA4 (21066/21068)") - UNAME_MACHINE=alpha ;; - "EV5 (21164)") - UNAME_MACHINE=alphaev5 ;; - "EV5.6 (21164A)") - UNAME_MACHINE=alphaev56 ;; - "EV5.6 (21164PC)") - UNAME_MACHINE=alphapca56 ;; - "EV5.7 (21164PC)") - UNAME_MACHINE=alphapca57 ;; - "EV6 (21264)") - UNAME_MACHINE=alphaev6 ;; - "EV6.7 (21264A)") - UNAME_MACHINE=alphaev67 ;; - "EV6.8CB (21264C)") - UNAME_MACHINE=alphaev68 ;; - "EV6.8AL (21264B)") - UNAME_MACHINE=alphaev68 ;; - "EV6.8CX (21264D)") - UNAME_MACHINE=alphaev68 ;; - "EV6.9A (21264/EV69A)") - UNAME_MACHINE=alphaev69 ;; - "EV7 (21364)") - UNAME_MACHINE=alphaev7 ;; - "EV7.9 (21364A)") - UNAME_MACHINE=alphaev79 ;; - esac - # A Pn.n version is a patched version. - # A Vn.n version is a released version. - # A Tn.n version is a released field test version. - # A Xn.n version is an unreleased experimental baselevel. - # 1.2 uses "1.2" for uname -r. - echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`" - # Reset EXIT trap before exiting to avoid spurious non-zero exit code. - exitcode=$? - trap '' 0 - exit $exitcode ;; - Amiga*:UNIX_System_V:4.0:*) - echo m68k-unknown-sysv4 - exit ;; - *:[Aa]miga[Oo][Ss]:*:*) - echo "$UNAME_MACHINE"-unknown-amigaos - exit ;; - *:[Mm]orph[Oo][Ss]:*:*) - echo "$UNAME_MACHINE"-unknown-morphos - exit ;; - *:OS/390:*:*) - echo i370-ibm-openedition - exit ;; - *:z/VM:*:*) - echo s390-ibm-zvmoe - exit ;; - *:OS400:*:*) - echo powerpc-ibm-os400 - exit ;; - arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) - echo arm-acorn-riscix"$UNAME_RELEASE" - exit ;; - arm*:riscos:*:*|arm*:RISCOS:*:*) - echo arm-unknown-riscos - exit ;; - SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) - echo hppa1.1-hitachi-hiuxmpp - exit ;; - Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) - # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. - if test "`(/bin/universe) 2>/dev/null`" = att ; then - echo pyramid-pyramid-sysv3 - else - echo pyramid-pyramid-bsd - fi - exit ;; - NILE*:*:*:dcosx) - echo pyramid-pyramid-svr4 - exit ;; - DRS?6000:unix:4.0:6*) - echo sparc-icl-nx6 - exit ;; - DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) - case `/usr/bin/uname -p` in - sparc) echo sparc-icl-nx7; exit ;; - esac ;; - s390x:SunOS:*:*) - echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" - exit ;; - sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" - exit ;; - sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" - exit ;; - i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) - echo i386-pc-auroraux"$UNAME_RELEASE" - exit ;; - i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) - eval "$set_cc_for_build" - SUN_ARCH=i386 - # If there is a compiler, see if it is configured for 64-bit objects. - # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. - # This test works for both compilers. - if [ "$CC_FOR_BUILD" != no_compiler_found ]; then - if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - SUN_ARCH=x86_64 - fi - fi - echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" - exit ;; - sun4*:SunOS:6*:*) - # According to config.sub, this is the proper way to canonicalize - # SunOS6. Hard to guess exactly what SunOS6 will be like, but - # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" - exit ;; - sun4*:SunOS:*:*) - case "`/usr/bin/arch -k`" in - Series*|S4*) - UNAME_RELEASE=`uname -v` - ;; - esac - # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`" - exit ;; - sun3*:SunOS:*:*) - echo m68k-sun-sunos"$UNAME_RELEASE" - exit ;; - sun*:*:4.2BSD:*) - UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` - test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 - case "`/bin/arch`" in - sun3) - echo m68k-sun-sunos"$UNAME_RELEASE" - ;; - sun4) - echo sparc-sun-sunos"$UNAME_RELEASE" - ;; - esac - exit ;; - aushp:SunOS:*:*) - echo sparc-auspex-sunos"$UNAME_RELEASE" - exit ;; - # The situation for MiNT is a little confusing. The machine name - # can be virtually everything (everything which is not - # "atarist" or "atariste" at least should have a processor - # > m68000). The system name ranges from "MiNT" over "FreeMiNT" - # to the lowercase version "mint" (or "freemint"). Finally - # the system name "TOS" denotes a system which is actually not - # MiNT. But MiNT is downward compatible to TOS, so this should - # be no problem. - atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint"$UNAME_RELEASE" - exit ;; - atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint"$UNAME_RELEASE" - exit ;; - *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) - echo m68k-atari-mint"$UNAME_RELEASE" - exit ;; - milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) - echo m68k-milan-mint"$UNAME_RELEASE" - exit ;; - hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) - echo m68k-hades-mint"$UNAME_RELEASE" - exit ;; - *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-unknown-mint"$UNAME_RELEASE" - exit ;; - m68k:machten:*:*) - echo m68k-apple-machten"$UNAME_RELEASE" - exit ;; - powerpc:machten:*:*) - echo powerpc-apple-machten"$UNAME_RELEASE" - exit ;; - RISC*:Mach:*:*) - echo mips-dec-mach_bsd4.3 - exit ;; - RISC*:ULTRIX:*:*) - echo mips-dec-ultrix"$UNAME_RELEASE" - exit ;; - VAX*:ULTRIX*:*:*) - echo vax-dec-ultrix"$UNAME_RELEASE" - exit ;; - 2020:CLIX:*:* | 2430:CLIX:*:*) - echo clipper-intergraph-clix"$UNAME_RELEASE" - exit ;; - mips:*:*:UMIPS | mips:*:*:RISCos) - eval "$set_cc_for_build" - sed 's/^ //' << EOF > "$dummy.c" -#ifdef __cplusplus -#include /* for printf() prototype */ - int main (int argc, char *argv[]) { -#else - int main (argc, argv) int argc; char *argv[]; { -#endif - #if defined (host_mips) && defined (MIPSEB) - #if defined (SYSTYPE_SYSV) - printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_SVR4) - printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) - printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); - #endif - #endif - exit (-1); - } -EOF - $CC_FOR_BUILD -o "$dummy" "$dummy.c" && - dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && - SYSTEM_NAME=`"$dummy" "$dummyarg"` && - { echo "$SYSTEM_NAME"; exit; } - echo mips-mips-riscos"$UNAME_RELEASE" - exit ;; - Motorola:PowerMAX_OS:*:*) - echo powerpc-motorola-powermax - exit ;; - Motorola:*:4.3:PL8-*) - echo powerpc-harris-powermax - exit ;; - Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) - echo powerpc-harris-powermax - exit ;; - Night_Hawk:Power_UNIX:*:*) - echo powerpc-harris-powerunix - exit ;; - m88k:CX/UX:7*:*) - echo m88k-harris-cxux7 - exit ;; - m88k:*:4*:R4*) - echo m88k-motorola-sysv4 - exit ;; - m88k:*:3*:R3*) - echo m88k-motorola-sysv3 - exit ;; - AViiON:dgux:*:*) - # DG/UX returns AViiON for all architectures - UNAME_PROCESSOR=`/usr/bin/uname -p` - if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ] - then - if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \ - [ "$TARGET_BINARY_INTERFACE"x = x ] - then - echo m88k-dg-dgux"$UNAME_RELEASE" - else - echo m88k-dg-dguxbcs"$UNAME_RELEASE" - fi - else - echo i586-dg-dgux"$UNAME_RELEASE" - fi - exit ;; - M88*:DolphinOS:*:*) # DolphinOS (SVR3) - echo m88k-dolphin-sysv3 - exit ;; - M88*:*:R3*:*) - # Delta 88k system running SVR3 - echo m88k-motorola-sysv3 - exit ;; - XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) - echo m88k-tektronix-sysv3 - exit ;; - Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) - echo m68k-tektronix-bsd - exit ;; - *:IRIX*:*:*) - echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`" - exit ;; - ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. - echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id - exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' - i*86:AIX:*:*) - echo i386-ibm-aix - exit ;; - ia64:AIX:*:*) - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` - else - IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" - fi - echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV" - exit ;; - *:AIX:2:3) - if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - eval "$set_cc_for_build" - sed 's/^ //' << EOF > "$dummy.c" - #include - - main() - { - if (!__power_pc()) - exit(1); - puts("powerpc-ibm-aix3.2.5"); - exit(0); - } -EOF - if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` - then - echo "$SYSTEM_NAME" - else - echo rs6000-ibm-aix3.2.5 - fi - elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then - echo rs6000-ibm-aix3.2.4 - else - echo rs6000-ibm-aix3.2 - fi - exit ;; - *:AIX:*:[4567]) - IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` - if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then - IBM_ARCH=rs6000 - else - IBM_ARCH=powerpc - fi - if [ -x /usr/bin/lslpp ] ; then - IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | - awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` - else - IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" - fi - echo "$IBM_ARCH"-ibm-aix"$IBM_REV" - exit ;; - *:AIX:*:*) - echo rs6000-ibm-aix - exit ;; - ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) - echo romp-ibm-bsd4.4 - exit ;; - ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and - echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to - exit ;; # report: romp-ibm BSD 4.3 - *:BOSX:*:*) - echo rs6000-bull-bosx - exit ;; - DPX/2?00:B.O.S.:*:*) - echo m68k-bull-sysv3 - exit ;; - 9000/[34]??:4.3bsd:1.*:*) - echo m68k-hp-bsd - exit ;; - hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) - echo m68k-hp-bsd4.4 - exit ;; - 9000/[34678]??:HP-UX:*:*) - HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` - case "$UNAME_MACHINE" in - 9000/31?) HP_ARCH=m68000 ;; - 9000/[34]??) HP_ARCH=m68k ;; - 9000/[678][0-9][0-9]) - if [ -x /usr/bin/getconf ]; then - sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` - sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` - case "$sc_cpu_version" in - 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 - 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 - 532) # CPU_PA_RISC2_0 - case "$sc_kernel_bits" in - 32) HP_ARCH=hppa2.0n ;; - 64) HP_ARCH=hppa2.0w ;; - '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 - esac ;; - esac - fi - if [ "$HP_ARCH" = "" ]; then - eval "$set_cc_for_build" - sed 's/^ //' << EOF > "$dummy.c" - - #define _HPUX_SOURCE - #include - #include - - int main () - { - #if defined(_SC_KERNEL_BITS) - long bits = sysconf(_SC_KERNEL_BITS); - #endif - long cpu = sysconf (_SC_CPU_VERSION); - - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1"); break; - case CPU_PA_RISC2_0: - #if defined(_SC_KERNEL_BITS) - switch (bits) - { - case 64: puts ("hppa2.0w"); break; - case 32: puts ("hppa2.0n"); break; - default: puts ("hppa2.0"); break; - } break; - #else /* !defined(_SC_KERNEL_BITS) */ - puts ("hppa2.0"); break; - #endif - default: puts ("hppa1.0"); break; - } - exit (0); - } -EOF - (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` - test -z "$HP_ARCH" && HP_ARCH=hppa - fi ;; - esac - if [ "$HP_ARCH" = hppa2.0w ] - then - eval "$set_cc_for_build" - - # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating - # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler - # generating 64-bit code. GNU and HP use different nomenclature: - # - # $ CC_FOR_BUILD=cc ./config.guess - # => hppa2.0w-hp-hpux11.23 - # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess - # => hppa64-hp-hpux11.23 - - if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | - grep -q __LP64__ - then - HP_ARCH=hppa2.0w - else - HP_ARCH=hppa64 - fi - fi - echo "$HP_ARCH"-hp-hpux"$HPUX_REV" - exit ;; - ia64:HP-UX:*:*) - HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` - echo ia64-hp-hpux"$HPUX_REV" - exit ;; - 3050*:HI-UX:*:*) - eval "$set_cc_for_build" - sed 's/^ //' << EOF > "$dummy.c" - #include - int - main () - { - long cpu = sysconf (_SC_CPU_VERSION); - /* The order matters, because CPU_IS_HP_MC68K erroneously returns - true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct - results, however. */ - if (CPU_IS_PA_RISC (cpu)) - { - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; - case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; - default: puts ("hppa-hitachi-hiuxwe2"); break; - } - } - else if (CPU_IS_HP_MC68K (cpu)) - puts ("m68k-hitachi-hiuxwe2"); - else puts ("unknown-hitachi-hiuxwe2"); - exit (0); - } -EOF - $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && - { echo "$SYSTEM_NAME"; exit; } - echo unknown-hitachi-hiuxwe2 - exit ;; - 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) - echo hppa1.1-hp-bsd - exit ;; - 9000/8??:4.3bsd:*:*) - echo hppa1.0-hp-bsd - exit ;; - *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) - echo hppa1.0-hp-mpeix - exit ;; - hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) - echo hppa1.1-hp-osf - exit ;; - hp8??:OSF1:*:*) - echo hppa1.0-hp-osf - exit ;; - i*86:OSF1:*:*) - if [ -x /usr/sbin/sysversion ] ; then - echo "$UNAME_MACHINE"-unknown-osf1mk - else - echo "$UNAME_MACHINE"-unknown-osf1 - fi - exit ;; - parisc*:Lites*:*:*) - echo hppa1.1-hp-lites - exit ;; - C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) - echo c1-convex-bsd - exit ;; - C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit ;; - C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) - echo c34-convex-bsd - exit ;; - C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) - echo c38-convex-bsd - exit ;; - C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) - echo c4-convex-bsd - exit ;; - CRAY*Y-MP:*:*:*) - echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*[A-Z]90:*:*:*) - echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ - | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ - -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ - -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*TS:*:*:*) - echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*T3E:*:*:*) - echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*SV1:*:*:*) - echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' - exit ;; - *:UNICOS/mp:*:*) - echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' - exit ;; - F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` - FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` - FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` - echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; - 5000:UNIX_System_V:4.*:*) - FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` - FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` - echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; - i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) - echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE" - exit ;; - sparc*:BSD/OS:*:*) - echo sparc-unknown-bsdi"$UNAME_RELEASE" - exit ;; - *:BSD/OS:*:*) - echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" - exit ;; - *:FreeBSD:*:*) - UNAME_PROCESSOR=`/usr/bin/uname -p` - case "$UNAME_PROCESSOR" in - amd64) - UNAME_PROCESSOR=x86_64 ;; - i386) - UNAME_PROCESSOR=i586 ;; - esac - echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" - exit ;; - i*:CYGWIN*:*) - echo "$UNAME_MACHINE"-pc-cygwin - exit ;; - *:MINGW64*:*) - echo "$UNAME_MACHINE"-pc-mingw64 - exit ;; - *:MINGW*:*) - echo "$UNAME_MACHINE"-pc-mingw32 - exit ;; - *:MSYS*:*) - echo "$UNAME_MACHINE"-pc-msys - exit ;; - i*:PW*:*) - echo "$UNAME_MACHINE"-pc-pw32 - exit ;; - *:Interix*:*) - case "$UNAME_MACHINE" in - x86) - echo i586-pc-interix"$UNAME_RELEASE" - exit ;; - authenticamd | genuineintel | EM64T) - echo x86_64-unknown-interix"$UNAME_RELEASE" - exit ;; - IA64) - echo ia64-unknown-interix"$UNAME_RELEASE" - exit ;; - esac ;; - i*:UWIN*:*) - echo "$UNAME_MACHINE"-pc-uwin - exit ;; - amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) - echo x86_64-unknown-cygwin - exit ;; - prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" - exit ;; - *:GNU:*:*) - # the GNU system - echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`" - exit ;; - *:GNU/*:*:*) - # other systems with GNU libc and userland - echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC" - exit ;; - i*86:Minix:*:*) - echo "$UNAME_MACHINE"-pc-minix - exit ;; - aarch64:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - aarch64_be:Linux:*:*) - UNAME_MACHINE=aarch64_be - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - alpha:Linux:*:*) - case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in - EV5) UNAME_MACHINE=alphaev5 ;; - EV56) UNAME_MACHINE=alphaev56 ;; - PCA56) UNAME_MACHINE=alphapca56 ;; - PCA57) UNAME_MACHINE=alphapca56 ;; - EV6) UNAME_MACHINE=alphaev6 ;; - EV67) UNAME_MACHINE=alphaev67 ;; - EV68*) UNAME_MACHINE=alphaev68 ;; - esac - objdump --private-headers /bin/sh | grep -q ld.so.1 - if test "$?" = 0 ; then LIBC=gnulibc1 ; fi - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - arc:Linux:*:* | arceb:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - arm*:Linux:*:*) - eval "$set_cc_for_build" - if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ARM_EABI__ - then - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - else - if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ARM_PCS_VFP - then - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi - else - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf - fi - fi - exit ;; - avr32*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - cris:Linux:*:*) - echo "$UNAME_MACHINE"-axis-linux-"$LIBC" - exit ;; - crisv32:Linux:*:*) - echo "$UNAME_MACHINE"-axis-linux-"$LIBC" - exit ;; - e2k:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - frv:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - hexagon:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - i*86:Linux:*:*) - echo "$UNAME_MACHINE"-pc-linux-"$LIBC" - exit ;; - ia64:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - k1om:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - m32r*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - m68*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - mips:Linux:*:* | mips64:Linux:*:*) - eval "$set_cc_for_build" - sed 's/^ //' << EOF > "$dummy.c" - #undef CPU - #undef ${UNAME_MACHINE} - #undef ${UNAME_MACHINE}el - #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=${UNAME_MACHINE}el - #else - #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=${UNAME_MACHINE} - #else - CPU= - #endif - #endif -EOF - eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU'`" - test "x$CPU" != x && { echo "$CPU-unknown-linux-$LIBC"; exit; } - ;; - mips64el:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - openrisc*:Linux:*:*) - echo or1k-unknown-linux-"$LIBC" - exit ;; - or32:Linux:*:* | or1k*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - padre:Linux:*:*) - echo sparc-unknown-linux-"$LIBC" - exit ;; - parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-"$LIBC" - exit ;; - parisc:Linux:*:* | hppa:Linux:*:*) - # Look for CPU level - case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; - PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; - *) echo hppa-unknown-linux-"$LIBC" ;; - esac - exit ;; - ppc64:Linux:*:*) - echo powerpc64-unknown-linux-"$LIBC" - exit ;; - ppc:Linux:*:*) - echo powerpc-unknown-linux-"$LIBC" - exit ;; - ppc64le:Linux:*:*) - echo powerpc64le-unknown-linux-"$LIBC" - exit ;; - ppcle:Linux:*:*) - echo powerpcle-unknown-linux-"$LIBC" - exit ;; - riscv32:Linux:*:* | riscv64:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - s390:Linux:*:* | s390x:Linux:*:*) - echo "$UNAME_MACHINE"-ibm-linux-"$LIBC" - exit ;; - sh64*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - sh*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - sparc:Linux:*:* | sparc64:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - tile*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - vax:Linux:*:*) - echo "$UNAME_MACHINE"-dec-linux-"$LIBC" - exit ;; - x86_64:Linux:*:*) - echo "$UNAME_MACHINE"-pc-linux-"$LIBC" - exit ;; - xtensa*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - i*86:DYNIX/ptx:4*:*) - # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. - # earlier versions are messed up and put the nodename in both - # sysname and nodename. - echo i386-sequent-sysv4 - exit ;; - i*86:UNIX_SV:4.2MP:2.*) - # Unixware is an offshoot of SVR4, but it has its own version - # number series starting with 2... - # I am not positive that other SVR4 systems won't match this, - # I just have to hope. -- rms. - # Use sysv4.2uw... so that sysv4* matches it. - echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION" - exit ;; - i*86:OS/2:*:*) - # If we were able to find `uname', then EMX Unix compatibility - # is probably installed. - echo "$UNAME_MACHINE"-pc-os2-emx - exit ;; - i*86:XTS-300:*:STOP) - echo "$UNAME_MACHINE"-unknown-stop - exit ;; - i*86:atheos:*:*) - echo "$UNAME_MACHINE"-unknown-atheos - exit ;; - i*86:syllable:*:*) - echo "$UNAME_MACHINE"-pc-syllable - exit ;; - i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) - echo i386-unknown-lynxos"$UNAME_RELEASE" - exit ;; - i*86:*DOS:*:*) - echo "$UNAME_MACHINE"-pc-msdosdjgpp - exit ;; - i*86:*:4.*:*) - UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` - if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then - echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" - else - echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL" - fi - exit ;; - i*86:*:5:[678]*) - # UnixWare 7.x, OpenUNIX and OpenServer 6. - case `/bin/uname -X | grep "^Machine"` in - *486*) UNAME_MACHINE=i486 ;; - *Pentium) UNAME_MACHINE=i586 ;; - *Pent*|*Celeron) UNAME_MACHINE=i686 ;; - esac - echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}{$UNAME_VERSION}" - exit ;; - i*86:*:3.2:*) - if test -f /usr/options/cb.name; then - UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then - UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` - (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 - (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ - && UNAME_MACHINE=i586 - (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ - && UNAME_MACHINE=i686 - (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ - && UNAME_MACHINE=i686 - echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL" - else - echo "$UNAME_MACHINE"-pc-sysv32 - fi - exit ;; - pc:*:*:*) - # Left here for compatibility: - # uname -m prints for DJGPP always 'pc', but it prints nothing about - # the processor, so we play safe by assuming i586. - # Note: whatever this is, it MUST be the same as what config.sub - # prints for the "djgpp" host, or else GDB configure will decide that - # this is a cross-build. - echo i586-pc-msdosdjgpp - exit ;; - Intel:Mach:3*:*) - echo i386-pc-mach3 - exit ;; - paragon:*:*:*) - echo i860-intel-osf1 - exit ;; - i860:*:4.*:*) # i860-SVR4 - if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then - echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4 - else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4 - fi - exit ;; - mini*:CTIX:SYS*5:*) - # "miniframe" - echo m68010-convergent-sysv - exit ;; - mc68k:UNIX:SYSTEM5:3.51m) - echo m68k-convergent-sysv - exit ;; - M680?0:D-NIX:5.3:*) - echo m68k-diab-dnix - exit ;; - M68*:*:R3V[5678]*:*) - test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; - 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) - OS_REL='' - test -r /etc/.relid \ - && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } - /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; - 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4; exit; } ;; - NCR*:*:4.2:* | MPRAS*:*:4.2:*) - OS_REL='.3' - test -r /etc/.relid \ - && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } - /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } - /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ - && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; - m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) - echo m68k-unknown-lynxos"$UNAME_RELEASE" - exit ;; - mc68030:UNIX_System_V:4.*:*) - echo m68k-atari-sysv4 - exit ;; - TSUNAMI:LynxOS:2.*:*) - echo sparc-unknown-lynxos"$UNAME_RELEASE" - exit ;; - rs6000:LynxOS:2.*:*) - echo rs6000-unknown-lynxos"$UNAME_RELEASE" - exit ;; - PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) - echo powerpc-unknown-lynxos"$UNAME_RELEASE" - exit ;; - SM[BE]S:UNIX_SV:*:*) - echo mips-dde-sysv"$UNAME_RELEASE" - exit ;; - RM*:ReliantUNIX-*:*:*) - echo mips-sni-sysv4 - exit ;; - RM*:SINIX-*:*:*) - echo mips-sni-sysv4 - exit ;; - *:SINIX-*:*:*) - if uname -p 2>/dev/null >/dev/null ; then - UNAME_MACHINE=`(uname -p) 2>/dev/null` - echo "$UNAME_MACHINE"-sni-sysv4 - else - echo ns32k-sni-sysv - fi - exit ;; - PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort - # says - echo i586-unisys-sysv4 - exit ;; - *:UNIX_System_V:4*:FTX*) - # From Gerald Hewes . - # How about differentiating between stratus architectures? -djm - echo hppa1.1-stratus-sysv4 - exit ;; - *:*:*:FTX*) - # From seanf@swdc.stratus.com. - echo i860-stratus-sysv4 - exit ;; - i*86:VOS:*:*) - # From Paul.Green@stratus.com. - echo "$UNAME_MACHINE"-stratus-vos - exit ;; - *:VOS:*:*) - # From Paul.Green@stratus.com. - echo hppa1.1-stratus-vos - exit ;; - mc68*:A/UX:*:*) - echo m68k-apple-aux"$UNAME_RELEASE" - exit ;; - news*:NEWS-OS:6*:*) - echo mips-sony-newsos6 - exit ;; - R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) - if [ -d /usr/nec ]; then - echo mips-nec-sysv"$UNAME_RELEASE" - else - echo mips-unknown-sysv"$UNAME_RELEASE" - fi - exit ;; - BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. - echo powerpc-be-beos - exit ;; - BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. - echo powerpc-apple-beos - exit ;; - BePC:BeOS:*:*) # BeOS running on Intel PC compatible. - echo i586-pc-beos - exit ;; - BePC:Haiku:*:*) # Haiku running on Intel PC compatible. - echo i586-pc-haiku - exit ;; - x86_64:Haiku:*:*) - echo x86_64-unknown-haiku - exit ;; - SX-4:SUPER-UX:*:*) - echo sx4-nec-superux"$UNAME_RELEASE" - exit ;; - SX-5:SUPER-UX:*:*) - echo sx5-nec-superux"$UNAME_RELEASE" - exit ;; - SX-6:SUPER-UX:*:*) - echo sx6-nec-superux"$UNAME_RELEASE" - exit ;; - SX-7:SUPER-UX:*:*) - echo sx7-nec-superux"$UNAME_RELEASE" - exit ;; - SX-8:SUPER-UX:*:*) - echo sx8-nec-superux"$UNAME_RELEASE" - exit ;; - SX-8R:SUPER-UX:*:*) - echo sx8r-nec-superux"$UNAME_RELEASE" - exit ;; - SX-ACE:SUPER-UX:*:*) - echo sxace-nec-superux"$UNAME_RELEASE" - exit ;; - Power*:Rhapsody:*:*) - echo powerpc-apple-rhapsody"$UNAME_RELEASE" - exit ;; - *:Rhapsody:*:*) - echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" - exit ;; - *:Darwin:*:*) - UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown - eval "$set_cc_for_build" - if test "$UNAME_PROCESSOR" = unknown ; then - UNAME_PROCESSOR=powerpc - fi - if test "`echo "$UNAME_RELEASE" | sed -e 's/\..*//'`" -le 10 ; then - if [ "$CC_FOR_BUILD" != no_compiler_found ]; then - if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - case $UNAME_PROCESSOR in - i386) UNAME_PROCESSOR=x86_64 ;; - powerpc) UNAME_PROCESSOR=powerpc64 ;; - esac - fi - # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc - if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_PPC >/dev/null - then - UNAME_PROCESSOR=powerpc - fi - fi - elif test "$UNAME_PROCESSOR" = i386 ; then - # Avoid executing cc on OS X 10.9, as it ships with a stub - # that puts up a graphical alert prompting to install - # developer tools. Any system running Mac OS X 10.7 or - # later (Darwin 11 and later) is required to have a 64-bit - # processor. This is not true of the ARM version of Darwin - # that Apple uses in portable devices. - UNAME_PROCESSOR=x86_64 - fi - echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" - exit ;; - *:procnto*:*:* | *:QNX:[0123456789]*:*) - UNAME_PROCESSOR=`uname -p` - if test "$UNAME_PROCESSOR" = x86; then - UNAME_PROCESSOR=i386 - UNAME_MACHINE=pc - fi - echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE" - exit ;; - *:QNX:*:4*) - echo i386-pc-qnx - exit ;; - NEO-*:NONSTOP_KERNEL:*:*) - echo neo-tandem-nsk"$UNAME_RELEASE" - exit ;; - NSE-*:NONSTOP_KERNEL:*:*) - echo nse-tandem-nsk"$UNAME_RELEASE" - exit ;; - NSR-*:NONSTOP_KERNEL:*:*) - echo nsr-tandem-nsk"$UNAME_RELEASE" - exit ;; - NSV-*:NONSTOP_KERNEL:*:*) - echo nsv-tandem-nsk"$UNAME_RELEASE" - exit ;; - NSX-*:NONSTOP_KERNEL:*:*) - echo nsx-tandem-nsk"$UNAME_RELEASE" - exit ;; - *:NonStop-UX:*:*) - echo mips-compaq-nonstopux - exit ;; - BS2000:POSIX*:*:*) - echo bs2000-siemens-sysv - exit ;; - DS/*:UNIX_System_V:*:*) - echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE" - exit ;; - *:Plan9:*:*) - # "uname -m" is not consistent, so use $cputype instead. 386 - # is converted to i386 for consistency with other x86 - # operating systems. - if test "$cputype" = 386; then - UNAME_MACHINE=i386 - else - UNAME_MACHINE="$cputype" - fi - echo "$UNAME_MACHINE"-unknown-plan9 - exit ;; - *:TOPS-10:*:*) - echo pdp10-unknown-tops10 - exit ;; - *:TENEX:*:*) - echo pdp10-unknown-tenex - exit ;; - KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) - echo pdp10-dec-tops20 - exit ;; - XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) - echo pdp10-xkl-tops20 - exit ;; - *:TOPS-20:*:*) - echo pdp10-unknown-tops20 - exit ;; - *:ITS:*:*) - echo pdp10-unknown-its - exit ;; - SEI:*:*:SEIUX) - echo mips-sei-seiux"$UNAME_RELEASE" - exit ;; - *:DragonFly:*:*) - echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" - exit ;; - *:*VMS:*:*) - UNAME_MACHINE=`(uname -p) 2>/dev/null` - case "$UNAME_MACHINE" in - A*) echo alpha-dec-vms ; exit ;; - I*) echo ia64-dec-vms ; exit ;; - V*) echo vax-dec-vms ; exit ;; - esac ;; - *:XENIX:*:SysV) - echo i386-pc-xenix - exit ;; - i*86:skyos:*:*) - echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`" - exit ;; - i*86:rdos:*:*) - echo "$UNAME_MACHINE"-pc-rdos - exit ;; - i*86:AROS:*:*) - echo "$UNAME_MACHINE"-pc-aros - exit ;; - x86_64:VMkernel:*:*) - echo "$UNAME_MACHINE"-unknown-esx - exit ;; - amd64:Isilon\ OneFS:*:*) - echo x86_64-unknown-onefs - exit ;; -esac - -echo "$0: unable to guess system type" >&2 - -case "$UNAME_MACHINE:$UNAME_SYSTEM" in - mips:Linux | mips64:Linux) - # If we got here on MIPS GNU/Linux, output extra information. - cat >&2 <&2 </dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null` - -hostinfo = `(hostinfo) 2>/dev/null` -/bin/universe = `(/bin/universe) 2>/dev/null` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` -/bin/arch = `(/bin/arch) 2>/dev/null` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` - -UNAME_MACHINE = "$UNAME_MACHINE" -UNAME_RELEASE = "$UNAME_RELEASE" -UNAME_SYSTEM = "$UNAME_SYSTEM" -UNAME_VERSION = "$UNAME_VERSION" -EOF - -exit 1 - -# Local variables: -# eval: (add-hook 'write-file-functions 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/config.h.in b/config.h.in deleted file mode 100644 index 9a07601..0000000 --- a/config.h.in +++ /dev/null @@ -1,259 +0,0 @@ -/* config.h.in. Generated from configure.ac by autoheader. */ - -/* Define if building universal (internal helper macro) */ -#undef AC_APPLE_UNIVERSAL_BUILD - -/* Define to 1 if you have the `asprintf' function. */ -#undef HAVE_ASPRINTF - -/* Define to 1 if you have the `__compar_fn_t' typedef. */ -#undef HAVE_COMPAR_FN_T - -/* Define to 1 if you have the header file. */ -#undef HAVE_CTYPE_H - -/* Define to 1 if you have the header file, and it defines `DIR'. - */ -#undef HAVE_DIRENT_H - -/* Define to 1 if your compiler supports extern inline */ -#undef HAVE_EXTERN_INLINE - -/* Define to 1 if you have the `gethostname' function. */ -#undef HAVE_GETHOSTNAME - -/* Define to 1 if you have the `getopt_long' function. */ -#undef HAVE_GETOPT_LONG - -/* Define to 1 if you have the `getpwuid' function. */ -#undef HAVE_GETPWUID - -/* Define to 1 if you have the `gettimeofday' function. */ -#undef HAVE_GETTIMEOFDAY - -/* Define to 1 if the system has the type `intmax_t'. */ -#undef HAVE_INTMAX_T - -/* Define to 1 if you have the header file. */ -#undef HAVE_INTTYPES_H - -/* Define to 1 if you have the `localeconv' function. */ -#undef HAVE_LOCALECONV - -/* Define to 1 if you have the header file. */ -#undef HAVE_LOCALE_H - -/* Define to 1 if you have the `localtime_r' function. */ -#undef HAVE_LOCALTIME_R - -/* Define to 1 if the system has the type `long long'. */ -#undef HAVE_LONG_LONG - -/* Define to 1 if the system has the type `long long int'. */ -#undef HAVE_LONG_LONG_INT - -/* Define to 1 if you have the header file. */ -#undef HAVE_MEMORY_H - -/* Define to 1 if you have the `mkstemp' function. */ -#undef HAVE_MKSTEMP - -/* Define to 1 if you have the header file, and it defines `DIR'. */ -#undef HAVE_NDIR_H - -/* Define to 1 if the system has the type `ptrdiff_t'. */ -#undef HAVE_PTRDIFF_T - -/* Define to 1 if you have the header file. */ -#undef HAVE_PWD_H - -/* Define to 1 if you have the `realpath' function. */ -#undef HAVE_REALPATH - -/* Define to 1 if you have the `setenv' function. */ -#undef HAVE_SETENV - -/* Define to 1 if you have a C99 compliant `snprintf' function. */ -#undef HAVE_SNPRINTF - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDARG_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDDEF_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDINT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDLIB_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRINGS_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRING_H - -/* Define to 1 if you have the `strndup' function. */ -#undef HAVE_STRNDUP - -/* Define to 1 if you have the `strtok_r' function. */ -#undef HAVE_STRTOK_R - -/* Define to 1 if `decimal_point' is a member of `struct lconv'. */ -#undef HAVE_STRUCT_LCONV_DECIMAL_POINT - -/* Define to 1 if `thousands_sep' is a member of `struct lconv'. */ -#undef HAVE_STRUCT_LCONV_THOUSANDS_SEP - -/* Define to 1 if you have the header file, and it defines `DIR'. - */ -#undef HAVE_SYS_DIR_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_MMAN_H - -/* Define to 1 if you have the header file, and it defines `DIR'. - */ -#undef HAVE_SYS_NDIR_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_STAT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TIME_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TYPES_H - -/* Define to 1 if you have that is POSIX.1 compatible. */ -#undef HAVE_SYS_WAIT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_TERMIOS_H - -/* Define to 1 if the system has the type `uintmax_t'. */ -#undef HAVE_UINTMAX_T - -/* Define to 1 if the system has the type `uintptr_t'. */ -#undef HAVE_UINTPTR_T - -/* Define to 1 if you have the header file. */ -#undef HAVE_UNISTD_H - -/* Define to 1 if you have the `unsetenv' function. */ -#undef HAVE_UNSETENV - -/* Define to 1 if the system has the type `unsigned long long int'. */ -#undef HAVE_UNSIGNED_LONG_LONG_INT - -/* Define to 1 if you have the `utimes' function. */ -#undef HAVE_UTIMES - -/* Define to 1 if you have the header file. */ -#undef HAVE_VARARGS_H - -/* Define to 1 if you have the `vasprintf' function. */ -#undef HAVE_VASPRINTF - -/* Define to 1 if you have the `va_copy' function or macro. */ -#undef HAVE_VA_COPY - -/* Define to 1 if you have a C99 compliant `vsnprintf' function. */ -#undef HAVE_VSNPRINTF - -/* Define to 1 if you have the `__va_copy' function or macro. */ -#undef HAVE___VA_COPY - -/* Define to the address where bug reports for this package should be sent. */ -#undef PACKAGE_BUGREPORT - -/* Define to the full name of this package. */ -#undef PACKAGE_NAME - -/* Define to the full name and version of this package. */ -#undef PACKAGE_STRING - -/* Define to the one symbol short name of this package. */ -#undef PACKAGE_TARNAME - -/* Define to the home page for this package. */ -#undef PACKAGE_URL - -/* Define to the version of this package. */ -#undef PACKAGE_VERSION - -/* Define to 1 if you have the ANSI C header files. */ -#undef STDC_HEADERS - -/* Define to 1 if you can safely include both and . */ -#undef TIME_WITH_SYS_TIME - -/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most - significant byte first (like Motorola and SPARC, unlike Intel). */ -#if defined AC_APPLE_UNIVERSAL_BUILD -# if defined __BIG_ENDIAN__ -# define WORDS_BIGENDIAN 1 -# endif -#else -# ifndef WORDS_BIGENDIAN -# undef WORDS_BIGENDIAN -# endif -#endif - -/* Define on OpenBSD to activate all library features */ -#undef _BSD_SOURCE - -/* Define on Irix to enable u_int */ -#undef _BSD_TYPES - -/* Define on Darwin to activate all library features */ -#undef _DARWIN_C_SOURCE - -/* Define on Linux to activate all library features */ -#undef _GNU_SOURCE - -/* Define on NetBSD to activate all library features */ -#undef _NETBSD_SOURCE - -/* Define to activate features from IEEE Stds 1003.1-2001 */ -#undef _POSIX_C_SOURCE - -/* Windows Vista or newer is required */ -#undef _WIN32_WINNT - -/* Define to the level of X/Open that your system supports */ -#undef _XOPEN_SOURCE - -/* Define to activate Unix95-and-earlier features */ -#undef _XOPEN_SOURCE_EXTENDED - -/* Define on FreeBSD to activate all library features */ -#undef __BSD_VISIBLE - -/* Define to activate Unix95-and-earlier features */ -#undef __EXTENSIONS__ - -/* Define to empty if `const' does not conform to ANSI C. */ -#undef const - -/* Define to `__inline__' or `__inline' if that's what the C compiler - calls it, or to nothing if 'inline' is not supported under any name. */ -#ifndef __cplusplus -#undef inline -#endif - -/* Define to the widest signed integer type if and do - not define. */ -#undef intmax_t - -/* Define to `unsigned int' if does not define. */ -#undef size_t - -/* Define to the widest unsigned integer type if and - do not define. */ -#undef uintmax_t - -/* Define to the type of an unsigned integer type wide enough to hold a - pointer, if such a type exists, and if the system does not define it. */ -#undef uintptr_t diff --git a/config.sub b/config.sub deleted file mode 100644 index 818892c..0000000 --- a/config.sub +++ /dev/null @@ -1,1800 +0,0 @@ -#! /bin/sh -# Configuration validation subroutine script. -# Copyright 1992-2018 Free Software Foundation, Inc. - -timestamp='2018-01-15' - -# This file is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see . -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that -# program. This Exception is an additional permission under section 7 -# of the GNU General Public License, version 3 ("GPLv3"). - - -# Please send patches to . -# -# Configuration subroutine to validate and canonicalize a configuration type. -# Supply the specified configuration type as an argument. -# If it is invalid, we print an error message on stderr and exit with code 1. -# Otherwise, we print the canonical config type on stdout and succeed. - -# You can get the latest version of this script from: -# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub - -# This file is supposed to be the same for all GNU packages -# and recognize all the CPU types, system types and aliases -# that are meaningful with *any* GNU software. -# Each package is responsible for reporting which valid configurations -# it does not support. The user should be able to distinguish -# a failure to support a valid configuration from a meaningless -# configuration. - -# The goal of this file is to map all the various variations of a given -# machine specification into a single specification in the form: -# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM -# or in some cases, the newer four-part form: -# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM -# It is wrong to echo any other type of specification. - -me=`echo "$0" | sed -e 's,.*/,,'` - -usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS - -Canonicalize a configuration name. - -Options: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.sub ($timestamp) - -Copyright 1992-2018 Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit ;; - --version | -v ) - echo "$version" ; exit ;; - --help | --h* | -h ) - echo "$usage"; exit ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" - exit 1 ;; - - *local*) - # First pass through any local machine types. - echo "$1" - exit ;; - - * ) - break ;; - esac -done - -case $# in - 0) echo "$me: missing argument$help" >&2 - exit 1;; - 1) ;; - *) echo "$me: too many arguments$help" >&2 - exit 1;; -esac - -# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). -# Here we must recognize all the valid KERNEL-OS combinations. -maybe_os=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` -case $maybe_os in - nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ - linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ - knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ - kopensolaris*-gnu* | cloudabi*-eabi* | \ - storm-chaos* | os2-emx* | rtmk-nova*) - os=-$maybe_os - basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` - ;; - android-linux) - os=-linux-android - basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown - ;; - *) - basic_machine=`echo "$1" | sed 's/-[^-]*$//'` - if [ "$basic_machine" != "$1" ] - then os=`echo "$1" | sed 's/.*-/-/'` - else os=; fi - ;; -esac - -### Let's recognize common machines as not being operating systems so -### that things like config.sub decstation-3100 work. We also -### recognize some manufacturers as not being operating systems, so we -### can provide default operating systems below. -case $os in - -sun*os*) - # Prevent following clause from handling this invalid input. - ;; - -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ - -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ - -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ - -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ - -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ - -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ - -apple | -axis | -knuth | -cray | -microblaze*) - os= - basic_machine=$1 - ;; - -bluegene*) - os=-cnk - ;; - -sim | -cisco | -oki | -wec | -winbond) - os= - basic_machine=$1 - ;; - -scout) - ;; - -wrs) - os=-vxworks - basic_machine=$1 - ;; - -chorusos*) - os=-chorusos - basic_machine=$1 - ;; - -chorusrdb) - os=-chorusrdb - basic_machine=$1 - ;; - -hiux*) - os=-hiuxwe2 - ;; - -sco6) - os=-sco5v6 - basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` - ;; - -sco5) - os=-sco3.2v5 - basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` - ;; - -sco4) - os=-sco3.2v4 - basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2.[4-9]*) - os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` - basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2v[4-9]*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` - ;; - -sco5v6*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` - ;; - -sco*) - os=-sco3.2v2 - basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` - ;; - -udk*) - basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` - ;; - -isc) - os=-isc2.2 - basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` - ;; - -clix*) - basic_machine=clipper-intergraph - ;; - -isc*) - basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` - ;; - -lynx*178) - os=-lynxos178 - ;; - -lynx*5) - os=-lynxos5 - ;; - -lynx*) - os=-lynxos - ;; - -ptx*) - basic_machine=`echo "$1" | sed -e 's/86-.*/86-sequent/'` - ;; - -psos*) - os=-psos - ;; - -mint | -mint[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; -esac - -# Decode aliases for certain CPU-COMPANY combinations. -case $basic_machine in - # Recognize the basic CPU types without company name. - # Some are omitted here because they have special meanings below. - 1750a | 580 \ - | a29k \ - | aarch64 | aarch64_be \ - | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ - | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ - | am33_2.0 \ - | arc | arceb \ - | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ - | avr | avr32 \ - | ba \ - | be32 | be64 \ - | bfin \ - | c4x | c8051 | clipper \ - | d10v | d30v | dlx | dsp16xx \ - | e2k | epiphany \ - | fido | fr30 | frv | ft32 \ - | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ - | hexagon \ - | i370 | i860 | i960 | ia16 | ia64 \ - | ip2k | iq2000 \ - | k1om \ - | le32 | le64 \ - | lm32 \ - | m32c | m32r | m32rle | m68000 | m68k | m88k \ - | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ - | mips | mipsbe | mipseb | mipsel | mipsle \ - | mips16 \ - | mips64 | mips64el \ - | mips64octeon | mips64octeonel \ - | mips64orion | mips64orionel \ - | mips64r5900 | mips64r5900el \ - | mips64vr | mips64vrel \ - | mips64vr4100 | mips64vr4100el \ - | mips64vr4300 | mips64vr4300el \ - | mips64vr5000 | mips64vr5000el \ - | mips64vr5900 | mips64vr5900el \ - | mipsisa32 | mipsisa32el \ - | mipsisa32r2 | mipsisa32r2el \ - | mipsisa32r6 | mipsisa32r6el \ - | mipsisa64 | mipsisa64el \ - | mipsisa64r2 | mipsisa64r2el \ - | mipsisa64r6 | mipsisa64r6el \ - | mipsisa64sb1 | mipsisa64sb1el \ - | mipsisa64sr71k | mipsisa64sr71kel \ - | mipsr5900 | mipsr5900el \ - | mipstx39 | mipstx39el \ - | mn10200 | mn10300 \ - | moxie \ - | mt \ - | msp430 \ - | nds32 | nds32le | nds32be \ - | nios | nios2 | nios2eb | nios2el \ - | ns16k | ns32k \ - | open8 | or1k | or1knd | or32 \ - | pdp10 | pj | pjl \ - | powerpc | powerpc64 | powerpc64le | powerpcle \ - | pru \ - | pyramid \ - | riscv32 | riscv64 \ - | rl78 | rx \ - | score \ - | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ - | sh64 | sh64le \ - | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ - | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ - | spu \ - | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ - | ubicom32 \ - | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ - | visium \ - | wasm32 \ - | x86 | xc16x | xstormy16 | xtensa \ - | z8k | z80) - basic_machine=$basic_machine-unknown - ;; - c54x) - basic_machine=tic54x-unknown - ;; - c55x) - basic_machine=tic55x-unknown - ;; - c6x) - basic_machine=tic6x-unknown - ;; - leon|leon[3-9]) - basic_machine=sparc-$basic_machine - ;; - m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) - basic_machine=$basic_machine-unknown - os=-none - ;; - m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65) - ;; - ms1) - basic_machine=mt-unknown - ;; - - strongarm | thumb | xscale) - basic_machine=arm-unknown - ;; - xgate) - basic_machine=$basic_machine-unknown - os=-none - ;; - xscaleeb) - basic_machine=armeb-unknown - ;; - - xscaleel) - basic_machine=armel-unknown - ;; - - # We use `pc' rather than `unknown' - # because (1) that's what they normally are, and - # (2) the word "unknown" tends to confuse beginning users. - i*86 | x86_64) - basic_machine=$basic_machine-pc - ;; - # Object if more than one company name word. - *-*-*) - echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 - exit 1 - ;; - # Recognize the basic CPU types with company name. - 580-* \ - | a29k-* \ - | aarch64-* | aarch64_be-* \ - | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ - | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ - | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ - | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ - | avr-* | avr32-* \ - | ba-* \ - | be32-* | be64-* \ - | bfin-* | bs2000-* \ - | c[123]* | c30-* | [cjt]90-* | c4x-* \ - | c8051-* | clipper-* | craynv-* | cydra-* \ - | d10v-* | d30v-* | dlx-* \ - | e2k-* | elxsi-* \ - | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ - | h8300-* | h8500-* \ - | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ - | hexagon-* \ - | i*86-* | i860-* | i960-* | ia16-* | ia64-* \ - | ip2k-* | iq2000-* \ - | k1om-* \ - | le32-* | le64-* \ - | lm32-* \ - | m32c-* | m32r-* | m32rle-* \ - | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ - | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ - | microblaze-* | microblazeel-* \ - | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ - | mips16-* \ - | mips64-* | mips64el-* \ - | mips64octeon-* | mips64octeonel-* \ - | mips64orion-* | mips64orionel-* \ - | mips64r5900-* | mips64r5900el-* \ - | mips64vr-* | mips64vrel-* \ - | mips64vr4100-* | mips64vr4100el-* \ - | mips64vr4300-* | mips64vr4300el-* \ - | mips64vr5000-* | mips64vr5000el-* \ - | mips64vr5900-* | mips64vr5900el-* \ - | mipsisa32-* | mipsisa32el-* \ - | mipsisa32r2-* | mipsisa32r2el-* \ - | mipsisa32r6-* | mipsisa32r6el-* \ - | mipsisa64-* | mipsisa64el-* \ - | mipsisa64r2-* | mipsisa64r2el-* \ - | mipsisa64r6-* | mipsisa64r6el-* \ - | mipsisa64sb1-* | mipsisa64sb1el-* \ - | mipsisa64sr71k-* | mipsisa64sr71kel-* \ - | mipsr5900-* | mipsr5900el-* \ - | mipstx39-* | mipstx39el-* \ - | mmix-* \ - | mt-* \ - | msp430-* \ - | nds32-* | nds32le-* | nds32be-* \ - | nios-* | nios2-* | nios2eb-* | nios2el-* \ - | none-* | np1-* | ns16k-* | ns32k-* \ - | open8-* \ - | or1k*-* \ - | orion-* \ - | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ - | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ - | pru-* \ - | pyramid-* \ - | riscv32-* | riscv64-* \ - | rl78-* | romp-* | rs6000-* | rx-* \ - | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ - | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ - | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ - | sparclite-* \ - | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ - | tahoe-* \ - | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ - | tile*-* \ - | tron-* \ - | ubicom32-* \ - | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ - | vax-* \ - | visium-* \ - | wasm32-* \ - | we32k-* \ - | x86-* | x86_64-* | xc16x-* | xps100-* \ - | xstormy16-* | xtensa*-* \ - | ymp-* \ - | z8k-* | z80-*) - ;; - # Recognize the basic CPU types without company name, with glob match. - xtensa*) - basic_machine=$basic_machine-unknown - ;; - # Recognize the various machine names and aliases which stand - # for a CPU type and a company and sometimes even an OS. - 386bsd) - basic_machine=i386-pc - os=-bsd - ;; - 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) - basic_machine=m68000-att - ;; - 3b*) - basic_machine=we32k-att - ;; - a29khif) - basic_machine=a29k-amd - os=-udi - ;; - abacus) - basic_machine=abacus-unknown - ;; - adobe68k) - basic_machine=m68010-adobe - os=-scout - ;; - alliant | fx80) - basic_machine=fx80-alliant - ;; - altos | altos3068) - basic_machine=m68k-altos - ;; - am29k) - basic_machine=a29k-none - os=-bsd - ;; - amd64) - basic_machine=x86_64-pc - ;; - amd64-*) - basic_machine=x86_64-`echo "$basic_machine" | sed 's/^[^-]*-//'` - ;; - amdahl) - basic_machine=580-amdahl - os=-sysv - ;; - amiga | amiga-*) - basic_machine=m68k-unknown - ;; - amigaos | amigados) - basic_machine=m68k-unknown - os=-amigaos - ;; - amigaunix | amix) - basic_machine=m68k-unknown - os=-sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - os=-sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - os=-bsd - ;; - aros) - basic_machine=i386-pc - os=-aros - ;; - asmjs) - basic_machine=asmjs-unknown - ;; - aux) - basic_machine=m68k-apple - os=-aux - ;; - balance) - basic_machine=ns32k-sequent - os=-dynix - ;; - blackfin) - basic_machine=bfin-unknown - os=-linux - ;; - blackfin-*) - basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'` - os=-linux - ;; - bluegene*) - basic_machine=powerpc-ibm - os=-cnk - ;; - c54x-*) - basic_machine=tic54x-`echo "$basic_machine" | sed 's/^[^-]*-//'` - ;; - c55x-*) - basic_machine=tic55x-`echo "$basic_machine" | sed 's/^[^-]*-//'` - ;; - c6x-*) - basic_machine=tic6x-`echo "$basic_machine" | sed 's/^[^-]*-//'` - ;; - c90) - basic_machine=c90-cray - os=-unicos - ;; - cegcc) - basic_machine=arm-unknown - os=-cegcc - ;; - convex-c1) - basic_machine=c1-convex - os=-bsd - ;; - convex-c2) - basic_machine=c2-convex - os=-bsd - ;; - convex-c32) - basic_machine=c32-convex - os=-bsd - ;; - convex-c34) - basic_machine=c34-convex - os=-bsd - ;; - convex-c38) - basic_machine=c38-convex - os=-bsd - ;; - cray | j90) - basic_machine=j90-cray - os=-unicos - ;; - craynv) - basic_machine=craynv-cray - os=-unicosmp - ;; - cr16 | cr16-*) - basic_machine=cr16-unknown - os=-elf - ;; - crds | unos) - basic_machine=m68k-crds - ;; - crisv32 | crisv32-* | etraxfs*) - basic_machine=crisv32-axis - ;; - cris | cris-* | etrax*) - basic_machine=cris-axis - ;; - crx) - basic_machine=crx-unknown - os=-elf - ;; - da30 | da30-*) - basic_machine=m68k-da30 - ;; - decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) - basic_machine=mips-dec - ;; - decsystem10* | dec10*) - basic_machine=pdp10-dec - os=-tops10 - ;; - decsystem20* | dec20*) - basic_machine=pdp10-dec - os=-tops20 - ;; - delta | 3300 | motorola-3300 | motorola-delta \ - | 3300-motorola | delta-motorola) - basic_machine=m68k-motorola - ;; - delta88) - basic_machine=m88k-motorola - os=-sysv3 - ;; - dicos) - basic_machine=i686-pc - os=-dicos - ;; - djgpp) - basic_machine=i586-pc - os=-msdosdjgpp - ;; - dpx20 | dpx20-*) - basic_machine=rs6000-bull - os=-bosx - ;; - dpx2*) - basic_machine=m68k-bull - os=-sysv3 - ;; - e500v[12]) - basic_machine=powerpc-unknown - os=$os"spe" - ;; - e500v[12]-*) - basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` - os=$os"spe" - ;; - ebmon29k) - basic_machine=a29k-amd - os=-ebmon - ;; - elxsi) - basic_machine=elxsi-elxsi - os=-bsd - ;; - encore | umax | mmax) - basic_machine=ns32k-encore - ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - os=-ose - ;; - fx2800) - basic_machine=i860-alliant - ;; - genix) - basic_machine=ns32k-ns - ;; - gmicro) - basic_machine=tron-gmicro - os=-sysv - ;; - go32) - basic_machine=i386-pc - os=-go32 - ;; - h3050r* | hiux*) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - h8300hms) - basic_machine=h8300-hitachi - os=-hms - ;; - h8300xray) - basic_machine=h8300-hitachi - os=-xray - ;; - h8500hms) - basic_machine=h8500-hitachi - os=-hms - ;; - harris) - basic_machine=m88k-harris - os=-sysv3 - ;; - hp300-*) - basic_machine=m68k-hp - ;; - hp300bsd) - basic_machine=m68k-hp - os=-bsd - ;; - hp300hpux) - basic_machine=m68k-hp - os=-hpux - ;; - hp3k9[0-9][0-9] | hp9[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k2[0-9][0-9] | hp9k31[0-9]) - basic_machine=m68000-hp - ;; - hp9k3[2-9][0-9]) - basic_machine=m68k-hp - ;; - hp9k6[0-9][0-9] | hp6[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k7[0-79][0-9] | hp7[0-79][0-9]) - basic_machine=hppa1.1-hp - ;; - hp9k78[0-9] | hp78[0-9]) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][13679] | hp8[0-9][13679]) - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][0-9] | hp8[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hppaosf) - basic_machine=hppa1.1-hp - os=-osf - ;; - hppro) - basic_machine=hppa1.1-hp - os=-proelf - ;; - i370-ibm* | ibm*) - basic_machine=i370-ibm - ;; - i*86v32) - basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` - os=-sysv32 - ;; - i*86v4*) - basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` - os=-sysv4 - ;; - i*86v) - basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` - os=-sysv - ;; - i*86sol2) - basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` - os=-solaris2 - ;; - i386mach) - basic_machine=i386-mach - os=-mach - ;; - vsta) - basic_machine=i386-unknown - os=-vsta - ;; - iris | iris4d) - basic_machine=mips-sgi - case $os in - -irix*) - ;; - *) - os=-irix4 - ;; - esac - ;; - isi68 | isi) - basic_machine=m68k-isi - os=-sysv - ;; - leon-*|leon[3-9]-*) - basic_machine=sparc-`echo "$basic_machine" | sed 's/-.*//'` - ;; - m68knommu) - basic_machine=m68k-unknown - os=-linux - ;; - m68knommu-*) - basic_machine=m68k-`echo "$basic_machine" | sed 's/^[^-]*-//'` - os=-linux - ;; - magnum | m3230) - basic_machine=mips-mips - os=-sysv - ;; - merlin) - basic_machine=ns32k-utek - os=-sysv - ;; - microblaze*) - basic_machine=microblaze-xilinx - ;; - mingw64) - basic_machine=x86_64-pc - os=-mingw64 - ;; - mingw32) - basic_machine=i686-pc - os=-mingw32 - ;; - mingw32ce) - basic_machine=arm-unknown - os=-mingw32ce - ;; - miniframe) - basic_machine=m68000-convergent - ;; - *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; - mips3*-*) - basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'` - ;; - mips3*) - basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`-unknown - ;; - monitor) - basic_machine=m68k-rom68k - os=-coff - ;; - morphos) - basic_machine=powerpc-unknown - os=-morphos - ;; - moxiebox) - basic_machine=moxie-unknown - os=-moxiebox - ;; - msdos) - basic_machine=i386-pc - os=-msdos - ;; - ms1-*) - basic_machine=`echo "$basic_machine" | sed -e 's/ms1-/mt-/'` - ;; - msys) - basic_machine=i686-pc - os=-msys - ;; - mvs) - basic_machine=i370-ibm - os=-mvs - ;; - nacl) - basic_machine=le32-unknown - os=-nacl - ;; - ncr3000) - basic_machine=i486-ncr - os=-sysv4 - ;; - netbsd386) - basic_machine=i386-unknown - os=-netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - os=-linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - os=-newsos - ;; - news1000) - basic_machine=m68030-sony - os=-newsos - ;; - news-3600 | risc-news) - basic_machine=mips-sony - os=-newsos - ;; - necv70) - basic_machine=v70-nec - os=-sysv - ;; - next | m*-next) - basic_machine=m68k-next - case $os in - -nextstep* ) - ;; - -ns2*) - os=-nextstep2 - ;; - *) - os=-nextstep3 - ;; - esac - ;; - nh3000) - basic_machine=m68k-harris - os=-cxux - ;; - nh[45]000) - basic_machine=m88k-harris - os=-cxux - ;; - nindy960) - basic_machine=i960-intel - os=-nindy - ;; - mon960) - basic_machine=i960-intel - os=-mon960 - ;; - nonstopux) - basic_machine=mips-compaq - os=-nonstopux - ;; - np1) - basic_machine=np1-gould - ;; - neo-tandem) - basic_machine=neo-tandem - ;; - nse-tandem) - basic_machine=nse-tandem - ;; - nsr-tandem) - basic_machine=nsr-tandem - ;; - nsv-tandem) - basic_machine=nsv-tandem - ;; - nsx-tandem) - basic_machine=nsx-tandem - ;; - op50n-* | op60c-*) - basic_machine=hppa1.1-oki - os=-proelf - ;; - openrisc | openrisc-*) - basic_machine=or32-unknown - ;; - os400) - basic_machine=powerpc-ibm - os=-os400 - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - os=-ose - ;; - os68k) - basic_machine=m68k-none - os=-os68k - ;; - pa-hitachi) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - paragon) - basic_machine=i860-intel - os=-osf - ;; - parisc) - basic_machine=hppa-unknown - os=-linux - ;; - parisc-*) - basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'` - os=-linux - ;; - pbd) - basic_machine=sparc-tti - ;; - pbb) - basic_machine=m68k-tti - ;; - pc532 | pc532-*) - basic_machine=ns32k-pc532 - ;; - pc98) - basic_machine=i386-pc - ;; - pc98-*) - basic_machine=i386-`echo "$basic_machine" | sed 's/^[^-]*-//'` - ;; - pentium | p5 | k5 | k6 | nexgen | viac3) - basic_machine=i586-pc - ;; - pentiumpro | p6 | 6x86 | athlon | athlon_*) - basic_machine=i686-pc - ;; - pentiumii | pentium2 | pentiumiii | pentium3) - basic_machine=i686-pc - ;; - pentium4) - basic_machine=i786-pc - ;; - pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) - basic_machine=i586-`echo "$basic_machine" | sed 's/^[^-]*-//'` - ;; - pentiumpro-* | p6-* | 6x86-* | athlon-*) - basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` - ;; - pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) - basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` - ;; - pentium4-*) - basic_machine=i786-`echo "$basic_machine" | sed 's/^[^-]*-//'` - ;; - pn) - basic_machine=pn-gould - ;; - power) basic_machine=power-ibm - ;; - ppc | ppcbe) basic_machine=powerpc-unknown - ;; - ppc-* | ppcbe-*) - basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` - ;; - ppcle | powerpclittle) - basic_machine=powerpcle-unknown - ;; - ppcle-* | powerpclittle-*) - basic_machine=powerpcle-`echo "$basic_machine" | sed 's/^[^-]*-//'` - ;; - ppc64) basic_machine=powerpc64-unknown - ;; - ppc64-*) basic_machine=powerpc64-`echo "$basic_machine" | sed 's/^[^-]*-//'` - ;; - ppc64le | powerpc64little) - basic_machine=powerpc64le-unknown - ;; - ppc64le-* | powerpc64little-*) - basic_machine=powerpc64le-`echo "$basic_machine" | sed 's/^[^-]*-//'` - ;; - ps2) - basic_machine=i386-ibm - ;; - pw32) - basic_machine=i586-unknown - os=-pw32 - ;; - rdos | rdos64) - basic_machine=x86_64-pc - os=-rdos - ;; - rdos32) - basic_machine=i386-pc - os=-rdos - ;; - rom68k) - basic_machine=m68k-rom68k - os=-coff - ;; - rm[46]00) - basic_machine=mips-siemens - ;; - rtpc | rtpc-*) - basic_machine=romp-ibm - ;; - s390 | s390-*) - basic_machine=s390-ibm - ;; - s390x | s390x-*) - basic_machine=s390x-ibm - ;; - sa29200) - basic_machine=a29k-amd - os=-udi - ;; - sb1) - basic_machine=mipsisa64sb1-unknown - ;; - sb1el) - basic_machine=mipsisa64sb1el-unknown - ;; - sde) - basic_machine=mipsisa32-sde - os=-elf - ;; - sei) - basic_machine=mips-sei - os=-seiux - ;; - sequent) - basic_machine=i386-sequent - ;; - sh5el) - basic_machine=sh5le-unknown - ;; - simso-wrs) - basic_machine=sparclite-wrs - os=-vxworks - ;; - sps7) - basic_machine=m68k-bull - os=-sysv2 - ;; - spur) - basic_machine=spur-unknown - ;; - st2000) - basic_machine=m68k-tandem - ;; - stratus) - basic_machine=i860-stratus - os=-sysv4 - ;; - strongarm-* | thumb-*) - basic_machine=arm-`echo "$basic_machine" | sed 's/^[^-]*-//'` - ;; - sun2) - basic_machine=m68000-sun - ;; - sun2os3) - basic_machine=m68000-sun - os=-sunos3 - ;; - sun2os4) - basic_machine=m68000-sun - os=-sunos4 - ;; - sun3os3) - basic_machine=m68k-sun - os=-sunos3 - ;; - sun3os4) - basic_machine=m68k-sun - os=-sunos4 - ;; - sun4os3) - basic_machine=sparc-sun - os=-sunos3 - ;; - sun4os4) - basic_machine=sparc-sun - os=-sunos4 - ;; - sun4sol2) - basic_machine=sparc-sun - os=-solaris2 - ;; - sun3 | sun3-*) - basic_machine=m68k-sun - ;; - sun4) - basic_machine=sparc-sun - ;; - sun386 | sun386i | roadrunner) - basic_machine=i386-sun - ;; - sv1) - basic_machine=sv1-cray - os=-unicos - ;; - symmetry) - basic_machine=i386-sequent - os=-dynix - ;; - t3e) - basic_machine=alphaev5-cray - os=-unicos - ;; - t90) - basic_machine=t90-cray - os=-unicos - ;; - tile*) - basic_machine=$basic_machine-unknown - os=-linux-gnu - ;; - tx39) - basic_machine=mipstx39-unknown - ;; - tx39el) - basic_machine=mipstx39el-unknown - ;; - toad1) - basic_machine=pdp10-xkl - os=-tops20 - ;; - tower | tower-32) - basic_machine=m68k-ncr - ;; - tpf) - basic_machine=s390x-ibm - os=-tpf - ;; - udi29k) - basic_machine=a29k-amd - os=-udi - ;; - ultra3) - basic_machine=a29k-nyu - os=-sym1 - ;; - v810 | necv810) - basic_machine=v810-nec - os=-none - ;; - vaxv) - basic_machine=vax-dec - os=-sysv - ;; - vms) - basic_machine=vax-dec - os=-vms - ;; - vpp*|vx|vx-*) - basic_machine=f301-fujitsu - ;; - vxworks960) - basic_machine=i960-wrs - os=-vxworks - ;; - vxworks68) - basic_machine=m68k-wrs - os=-vxworks - ;; - vxworks29k) - basic_machine=a29k-wrs - os=-vxworks - ;; - w65*) - basic_machine=w65-wdc - os=-none - ;; - w89k-*) - basic_machine=hppa1.1-winbond - os=-proelf - ;; - x64) - basic_machine=x86_64-pc - ;; - xbox) - basic_machine=i686-pc - os=-mingw32 - ;; - xps | xps100) - basic_machine=xps100-honeywell - ;; - xscale-* | xscalee[bl]-*) - basic_machine=`echo "$basic_machine" | sed 's/^xscale/arm/'` - ;; - ymp) - basic_machine=ymp-cray - os=-unicos - ;; - none) - basic_machine=none-none - os=-none - ;; - -# Here we handle the default manufacturer of certain CPU types. It is in -# some cases the only manufacturer, in others, it is the most popular. - w89k) - basic_machine=hppa1.1-winbond - ;; - op50n) - basic_machine=hppa1.1-oki - ;; - op60c) - basic_machine=hppa1.1-oki - ;; - romp) - basic_machine=romp-ibm - ;; - mmix) - basic_machine=mmix-knuth - ;; - rs6000) - basic_machine=rs6000-ibm - ;; - vax) - basic_machine=vax-dec - ;; - pdp11) - basic_machine=pdp11-dec - ;; - we32k) - basic_machine=we32k-att - ;; - sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) - basic_machine=sh-unknown - ;; - cydra) - basic_machine=cydra-cydrome - ;; - orion) - basic_machine=orion-highlevel - ;; - orion105) - basic_machine=clipper-highlevel - ;; - mac | mpw | mac-mpw) - basic_machine=m68k-apple - ;; - pmac | pmac-mpw) - basic_machine=powerpc-apple - ;; - *-unknown) - # Make sure to match an already-canonicalized machine name. - ;; - *) - echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 - exit 1 - ;; -esac - -# Here we canonicalize certain aliases for manufacturers. -case $basic_machine in - *-digital*) - basic_machine=`echo "$basic_machine" | sed 's/digital.*/dec/'` - ;; - *-commodore*) - basic_machine=`echo "$basic_machine" | sed 's/commodore.*/cbm/'` - ;; - *) - ;; -esac - -# Decode manufacturer-specific aliases for certain operating systems. - -if [ x"$os" != x"" ] -then -case $os in - # First match some system type aliases that might get confused - # with valid system types. - # -solaris* is a basic system type, with this one exception. - -auroraux) - os=-auroraux - ;; - -solaris1 | -solaris1.*) - os=`echo $os | sed -e 's|solaris1|sunos4|'` - ;; - -solaris) - os=-solaris2 - ;; - -unixware*) - os=-sysv4.2uw - ;; - -gnu/linux*) - os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` - ;; - # es1800 is here to avoid being matched by es* (a different OS) - -es1800*) - os=-ose - ;; - # Now accept the basic system types. - # The portable systems comes first. - # Each alternative MUST end in a * to match a version number. - # -sysv* is not here because it comes later, after sysvr4. - -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ - | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ - | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ - | -sym* | -kopensolaris* | -plan9* \ - | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ - | -aos* | -aros* | -cloudabi* | -sortix* \ - | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ - | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ - | -hiux* | -knetbsd* | -mirbsd* | -netbsd* \ - | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ - | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ - | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ - | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ - | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ - | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \ - | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ - | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ - | -linux-newlib* | -linux-musl* | -linux-uclibc* \ - | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ - | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* \ - | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ - | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ - | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ - | -morphos* | -superux* | -rtmk* | -windiss* \ - | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ - | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ - | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox* | -bme*) - # Remember, each alternative MUST END IN *, to match a version number. - ;; - -qnx*) - case $basic_machine in - x86-* | i*86-*) - ;; - *) - os=-nto$os - ;; - esac - ;; - -nto-qnx*) - ;; - -nto*) - os=`echo $os | sed -e 's|nto|nto-qnx|'` - ;; - -sim | -xray | -os68k* | -v88r* \ - | -windows* | -osx | -abug | -netware* | -os9* \ - | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) - ;; - -mac*) - os=`echo "$os" | sed -e 's|mac|macos|'` - ;; - -linux-dietlibc) - os=-linux-dietlibc - ;; - -linux*) - os=`echo $os | sed -e 's|linux|linux-gnu|'` - ;; - -sunos5*) - os=`echo "$os" | sed -e 's|sunos5|solaris2|'` - ;; - -sunos6*) - os=`echo "$os" | sed -e 's|sunos6|solaris3|'` - ;; - -opened*) - os=-openedition - ;; - -os400*) - os=-os400 - ;; - -wince*) - os=-wince - ;; - -utek*) - os=-bsd - ;; - -dynix*) - os=-bsd - ;; - -acis*) - os=-aos - ;; - -atheos*) - os=-atheos - ;; - -syllable*) - os=-syllable - ;; - -386bsd) - os=-bsd - ;; - -ctix* | -uts*) - os=-sysv - ;; - -nova*) - os=-rtmk-nova - ;; - -ns2) - os=-nextstep2 - ;; - -nsk*) - os=-nsk - ;; - # Preserve the version number of sinix5. - -sinix5.*) - os=`echo $os | sed -e 's|sinix|sysv|'` - ;; - -sinix*) - os=-sysv4 - ;; - -tpf*) - os=-tpf - ;; - -triton*) - os=-sysv3 - ;; - -oss*) - os=-sysv3 - ;; - -svr4*) - os=-sysv4 - ;; - -svr3) - os=-sysv3 - ;; - -sysvr4) - os=-sysv4 - ;; - # This must come after -sysvr4. - -sysv*) - ;; - -ose*) - os=-ose - ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - os=-mint - ;; - -zvmoe) - os=-zvmoe - ;; - -dicos*) - os=-dicos - ;; - -pikeos*) - # Until real need of OS specific support for - # particular features comes up, bare metal - # configurations are quite functional. - case $basic_machine in - arm*) - os=-eabi - ;; - *) - os=-elf - ;; - esac - ;; - -nacl*) - ;; - -ios) - ;; - -none) - ;; - *) - # Get rid of the `-' at the beginning of $os. - os=`echo $os | sed 's/[^-]*-//'` - echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2 - exit 1 - ;; -esac -else - -# Here we handle the default operating systems that come with various machines. -# The value should be what the vendor currently ships out the door with their -# machine or put another way, the most popular os provided with the machine. - -# Note that if you're going to try to match "-MANUFACTURER" here (say, -# "-sun"), then you have to tell the case statement up towards the top -# that MANUFACTURER isn't an operating system. Otherwise, code above -# will signal an error saying that MANUFACTURER isn't an operating -# system, and we'll never get to this point. - -case $basic_machine in - score-*) - os=-elf - ;; - spu-*) - os=-elf - ;; - *-acorn) - os=-riscix1.2 - ;; - arm*-rebel) - os=-linux - ;; - arm*-semi) - os=-aout - ;; - c4x-* | tic4x-*) - os=-coff - ;; - c8051-*) - os=-elf - ;; - hexagon-*) - os=-elf - ;; - tic54x-*) - os=-coff - ;; - tic55x-*) - os=-coff - ;; - tic6x-*) - os=-coff - ;; - # This must come before the *-dec entry. - pdp10-*) - os=-tops20 - ;; - pdp11-*) - os=-none - ;; - *-dec | vax-*) - os=-ultrix4.2 - ;; - m68*-apollo) - os=-domain - ;; - i386-sun) - os=-sunos4.0.2 - ;; - m68000-sun) - os=-sunos3 - ;; - m68*-cisco) - os=-aout - ;; - mep-*) - os=-elf - ;; - mips*-cisco) - os=-elf - ;; - mips*-*) - os=-elf - ;; - or32-*) - os=-coff - ;; - *-tti) # must be before sparc entry or we get the wrong os. - os=-sysv3 - ;; - sparc-* | *-sun) - os=-sunos4.1.1 - ;; - pru-*) - os=-elf - ;; - *-be) - os=-beos - ;; - *-ibm) - os=-aix - ;; - *-knuth) - os=-mmixware - ;; - *-wec) - os=-proelf - ;; - *-winbond) - os=-proelf - ;; - *-oki) - os=-proelf - ;; - *-hp) - os=-hpux - ;; - *-hitachi) - os=-hiux - ;; - i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) - os=-sysv - ;; - *-cbm) - os=-amigaos - ;; - *-dg) - os=-dgux - ;; - *-dolphin) - os=-sysv3 - ;; - m68k-ccur) - os=-rtu - ;; - m88k-omron*) - os=-luna - ;; - *-next) - os=-nextstep - ;; - *-sequent) - os=-ptx - ;; - *-crds) - os=-unos - ;; - *-ns) - os=-genix - ;; - i370-*) - os=-mvs - ;; - *-gould) - os=-sysv - ;; - *-highlevel) - os=-bsd - ;; - *-encore) - os=-bsd - ;; - *-sgi) - os=-irix - ;; - *-siemens) - os=-sysv4 - ;; - *-masscomp) - os=-rtu - ;; - f30[01]-fujitsu | f700-fujitsu) - os=-uxpv - ;; - *-rom68k) - os=-coff - ;; - *-*bug) - os=-coff - ;; - *-apple) - os=-macos - ;; - *-atari*) - os=-mint - ;; - *) - os=-none - ;; -esac -fi - -# Here we handle the case where we know the os, and the CPU type, but not the -# manufacturer. We pick the logical manufacturer. -vendor=unknown -case $basic_machine in - *-unknown) - case $os in - -riscix*) - vendor=acorn - ;; - -sunos*) - vendor=sun - ;; - -cnk*|-aix*) - vendor=ibm - ;; - -beos*) - vendor=be - ;; - -hpux*) - vendor=hp - ;; - -mpeix*) - vendor=hp - ;; - -hiux*) - vendor=hitachi - ;; - -unos*) - vendor=crds - ;; - -dgux*) - vendor=dg - ;; - -luna*) - vendor=omron - ;; - -genix*) - vendor=ns - ;; - -mvs* | -opened*) - vendor=ibm - ;; - -os400*) - vendor=ibm - ;; - -ptx*) - vendor=sequent - ;; - -tpf*) - vendor=ibm - ;; - -vxsim* | -vxworks* | -windiss*) - vendor=wrs - ;; - -aux*) - vendor=apple - ;; - -hms*) - vendor=hitachi - ;; - -mpw* | -macos*) - vendor=apple - ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - vendor=atari - ;; - -vos*) - vendor=stratus - ;; - esac - basic_machine=`echo "$basic_machine" | sed "s/unknown/$vendor/"` - ;; -esac - -echo "$basic_machine$os" -exit - -# Local variables: -# eval: (add-hook 'write-file-functions 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/configure b/configure deleted file mode 100755 index 63b22ee..0000000 --- a/configure +++ /dev/null @@ -1,7773 +0,0 @@ -#! /bin/sh -# Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69. -# -# -# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. -# -# -# This configure script is free software; the Free Software Foundation -# gives unlimited permission to copy, distribute and modify it. -## -------------------- ## -## M4sh Initialization. ## -## -------------------- ## - -# Be more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi - - -as_nl=' -' -export as_nl -# Printing a long string crashes Solaris 7 /usr/bin/printf. -as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo -# Prefer a ksh shell builtin over an external printf program on Solaris, -# but without wasting forks for bash or zsh. -if test -z "$BASH_VERSION$ZSH_VERSION" \ - && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='print -r --' - as_echo_n='print -rn --' -elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='printf %s\n' - as_echo_n='printf %s' -else - if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then - as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' - as_echo_n='/usr/ucb/echo -n' - else - as_echo_body='eval expr "X$1" : "X\\(.*\\)"' - as_echo_n_body='eval - arg=$1; - case $arg in #( - *"$as_nl"*) - expr "X$arg" : "X\\(.*\\)$as_nl"; - arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; - esac; - expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" - ' - export as_echo_n_body - as_echo_n='sh -c $as_echo_n_body as_echo' - fi - export as_echo_body - as_echo='sh -c $as_echo_body as_echo' -fi - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - PATH_SEPARATOR=: - (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { - (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || - PATH_SEPARATOR=';' - } -fi - - -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -IFS=" "" $as_nl" - -# Find who we are. Look in the path if we contain no directory separator. -as_myself= -case $0 in #(( - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break - done -IFS=$as_save_IFS - - ;; -esac -# We did not find ourselves, most probably we were run as `sh COMMAND' -# in which case we are not to be found in the path. -if test "x$as_myself" = x; then - as_myself=$0 -fi -if test ! -f "$as_myself"; then - $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - exit 1 -fi - -# Unset variables that we do not need and which cause bugs (e.g. in -# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" -# suppresses any "Segmentation fault" message there. '((' could -# trigger a bug in pdksh 5.2.14. -for as_var in BASH_ENV ENV MAIL MAILPATH -do eval test x\${$as_var+set} = xset \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -# Use a proper internal environment variable to ensure we don't fall - # into an infinite loop, continuously re-executing ourselves. - if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then - _as_can_reexec=no; export _as_can_reexec; - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -as_fn_exit 255 - fi - # We don't want this to propagate to other subprocesses. - { _as_can_reexec=; unset _as_can_reexec;} -if test "x$CONFIG_SHELL" = x; then - as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which - # is contrary to our usage. Disable this feature. - alias -g '\${1+\"\$@\"}'='\"\$@\"' - setopt NO_GLOB_SUBST -else - case \`(set -o) 2>/dev/null\` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi -" - as_required="as_fn_return () { (exit \$1); } -as_fn_success () { as_fn_return 0; } -as_fn_failure () { as_fn_return 1; } -as_fn_ret_success () { return 0; } -as_fn_ret_failure () { return 1; } - -exitcode=0 -as_fn_success || { exitcode=1; echo as_fn_success failed.; } -as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } -as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } -as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } -if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : - -else - exitcode=1; echo positional parameters were not saved. -fi -test x\$exitcode = x0 || exit 1 -test -x / || exit 1" - as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO - as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO - eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && - test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 -test \$(( 1 + 1 )) = 2 || exit 1" - if (eval "$as_required") 2>/dev/null; then : - as_have_required=yes -else - as_have_required=no -fi - if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : - -else - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -as_found=false -for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - as_found=: - case $as_dir in #( - /*) - for as_base in sh bash ksh sh5; do - # Try only shells that exist, to save several forks. - as_shell=$as_dir/$as_base - if { test -f "$as_shell" || test -f "$as_shell.exe"; } && - { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : - CONFIG_SHELL=$as_shell as_have_required=yes - if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : - break 2 -fi -fi - done;; - esac - as_found=false -done -$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && - { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : - CONFIG_SHELL=$SHELL as_have_required=yes -fi; } -IFS=$as_save_IFS - - - if test "x$CONFIG_SHELL" != x; then : - export CONFIG_SHELL - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -exit 255 -fi - - if test x$as_have_required = xno; then : - $as_echo "$0: This script requires a shell more modern than all" - $as_echo "$0: the shells that I found on your system." - if test x${ZSH_VERSION+set} = xset ; then - $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" - $as_echo "$0: be upgraded to zsh 4.3.4 or later." - else - $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, -$0: including any error possibly output before this -$0: message. Then install a modern shell, or manually run -$0: the script under such a shell if you do have one." - fi - exit 1 -fi -fi -fi -SHELL=${CONFIG_SHELL-/bin/sh} -export SHELL -# Unset more variables known to interfere with behavior of common tools. -CLICOLOR_FORCE= GREP_OPTIONS= -unset CLICOLOR_FORCE GREP_OPTIONS - -## --------------------- ## -## M4sh Shell Functions. ## -## --------------------- ## -# as_fn_unset VAR -# --------------- -# Portably unset VAR. -as_fn_unset () -{ - { eval $1=; unset $1;} -} -as_unset=as_fn_unset - -# as_fn_set_status STATUS -# ----------------------- -# Set $? to STATUS, without forking. -as_fn_set_status () -{ - return $1 -} # as_fn_set_status - -# as_fn_exit STATUS -# ----------------- -# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. -as_fn_exit () -{ - set +e - as_fn_set_status $1 - exit $1 -} # as_fn_exit - -# as_fn_mkdir_p -# ------------- -# Create "$as_dir" as a directory, including parents if necessary. -as_fn_mkdir_p () -{ - - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || eval $as_mkdir_p || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" - - -} # as_fn_mkdir_p - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -# as_fn_append VAR VALUE -# ---------------------- -# Append the text in VALUE to the end of the definition contained in VAR. Take -# advantage of any shell optimizations that allow amortized linear growth over -# repeated appends, instead of the typical quadratic growth present in naive -# implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : - eval 'as_fn_append () - { - eval $1+=\$2 - }' -else - as_fn_append () - { - eval $1=\$$1\$2 - } -fi # as_fn_append - -# as_fn_arith ARG... -# ------------------ -# Perform arithmetic evaluation on the ARGs, and store the result in the -# global $as_val. Take advantage of shells that can avoid forks. The arguments -# must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : - eval 'as_fn_arith () - { - as_val=$(( $* )) - }' -else - as_fn_arith () - { - as_val=`expr "$@" || test $? -eq 1` - } -fi # as_fn_arith - - -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- -# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are -# provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. -as_fn_error () -{ - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 - fi - $as_echo "$as_me: error: $2" >&2 - as_fn_exit $as_status -} # as_fn_error - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi - -if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - - - as_lineno_1=$LINENO as_lineno_1a=$LINENO - as_lineno_2=$LINENO as_lineno_2a=$LINENO - eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && - test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { - # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) - sed -n ' - p - /[$]LINENO/= - ' <$as_myself | - sed ' - s/[$]LINENO.*/&-/ - t lineno - b - :lineno - N - :loop - s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ - t loop - s/-\n.*// - ' >$as_me.lineno && - chmod +x "$as_me.lineno" || - { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } - - # If we had to re-execute with $CONFIG_SHELL, we're ensured to have - # already done that, so ensure we don't try to do so again and fall - # in an infinite loop. This has already happened in practice. - _as_can_reexec=no; export _as_can_reexec - # Don't try to exec as it changes $[0], causing all sort of problems - # (the dirname of $[0] is not the place where we might find the - # original and so on. Autoconf is especially sensitive to this). - . "./$as_me.lineno" - # Exit status is that of the last command. - exit -} - -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in #((((( --n*) - case `echo 'xy\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - xy) ECHO_C='\c';; - *) echo `echo ksh88 bug on AIX 6.1` > /dev/null - ECHO_T=' ';; - esac;; -*) - ECHO_N='-n';; -esac - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir 2>/dev/null -fi -if (echo >conf$$.file) 2>/dev/null; then - if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' - elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln - else - as_ln_s='cp -pR' - fi -else - as_ln_s='cp -pR' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - -if mkdir -p . 2>/dev/null; then - as_mkdir_p='mkdir -p "$as_dir"' -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - -as_test_x='test -x' -as_executable_p=as_fn_executable_p - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -test -n "$DJDIR" || exec 7<&0 &1 - -# Name of the host. -# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, -# so uname gets run too. -ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` - -# -# Initializations. -# -ac_default_prefix=/usr/local -ac_clean_files= -ac_config_libobj_dir=. -LIBOBJS= -cross_compiling=no -subdirs= -MFLAGS= -MAKEFLAGS= - -# Identity of this package. -PACKAGE_NAME= -PACKAGE_TARNAME= -PACKAGE_VERSION= -PACKAGE_STRING= -PACKAGE_BUGREPORT= -PACKAGE_URL= - -# Factoring default headers for most tests. -ac_includes_default="\ -#include -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_SYS_STAT_H -# include -#endif -#ifdef STDC_HEADERS -# include -# include -#else -# ifdef HAVE_STDLIB_H -# include -# endif -#endif -#ifdef HAVE_STRING_H -# if !defined STDC_HEADERS && defined HAVE_MEMORY_H -# include -# endif -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif -#ifdef HAVE_INTTYPES_H -# include -#endif -#ifdef HAVE_STDINT_H -# include -#endif -#ifdef HAVE_UNISTD_H -# include -#endif" - -ac_header_list= -ac_subst_vars='LTLIBOBJS -GPERF -LIBOBJS -EGREP -GREP -BASH -AR -RANLIB -INSTALL_DATA -INSTALL_SCRIPT -INSTALL_PROGRAM -CPP -OBJEXT -EXEEXT -ac_ct_CC -CPPFLAGS -LDFLAGS -CFLAGS -CC -test_suites -no_implicit_fallthrough_warning -more_warnings -include_dev_mk -extra_sources -extra_libs -disable_man -host_os -host_vendor -host_cpu -host -build_os -build_vendor -build_cpu -build -target_alias -host_alias -build_alias -LIBS -ECHO_T -ECHO_N -ECHO_C -DEFS -mandir -localedir -libdir -psdir -pdfdir -dvidir -htmldir -infodir -docdir -oldincludedir -includedir -runstatedir -localstatedir -sharedstatedir -sysconfdir -datadir -datarootdir -libexecdir -sbindir -bindir -program_transform_name -prefix -exec_prefix -PACKAGE_URL -PACKAGE_BUGREPORT -PACKAGE_STRING -PACKAGE_VERSION -PACKAGE_TARNAME -PACKAGE_NAME -PATH_SEPARATOR -SHELL' -ac_subst_files='' -ac_user_opts=' -enable_option_checking -enable_more_warnings -with_bundled_zlib -enable_man -enable_tracing -' - ac_precious_vars='build_alias -host_alias -target_alias -CC -CFLAGS -LDFLAGS -LIBS -CPPFLAGS -CPP' - - -# Initialize some variables set by options. -ac_init_help= -ac_init_version=false -ac_unrecognized_opts= -ac_unrecognized_sep= -# The variables have the same names as the options, with -# dashes changed to underlines. -cache_file=/dev/null -exec_prefix=NONE -no_create= -no_recursion= -prefix=NONE -program_prefix=NONE -program_suffix=NONE -program_transform_name=s,x,x, -silent= -site= -srcdir= -verbose= -x_includes=NONE -x_libraries=NONE - -# Installation directory options. -# These are left unexpanded so users can "make install exec_prefix=/foo" -# and all the variables that are supposed to be based on exec_prefix -# by default will actually change. -# Use braces instead of parens because sh, perl, etc. also accept them. -# (The list follows the same order as the GNU Coding Standards.) -bindir='${exec_prefix}/bin' -sbindir='${exec_prefix}/sbin' -libexecdir='${exec_prefix}/libexec' -datarootdir='${prefix}/share' -datadir='${datarootdir}' -sysconfdir='${prefix}/etc' -sharedstatedir='${prefix}/com' -localstatedir='${prefix}/var' -runstatedir='${localstatedir}/run' -includedir='${prefix}/include' -oldincludedir='/usr/include' -docdir='${datarootdir}/doc/${PACKAGE}' -infodir='${datarootdir}/info' -htmldir='${docdir}' -dvidir='${docdir}' -pdfdir='${docdir}' -psdir='${docdir}' -libdir='${exec_prefix}/lib' -localedir='${datarootdir}/locale' -mandir='${datarootdir}/man' - -ac_prev= -ac_dashdash= -for ac_option -do - # If the previous option needs an argument, assign it. - if test -n "$ac_prev"; then - eval $ac_prev=\$ac_option - ac_prev= - continue - fi - - case $ac_option in - *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; - *=) ac_optarg= ;; - *) ac_optarg=yes ;; - esac - - # Accept the important Cygnus configure options, so we can diagnose typos. - - case $ac_dashdash$ac_option in - --) - ac_dashdash=yes ;; - - -bindir | --bindir | --bindi | --bind | --bin | --bi) - ac_prev=bindir ;; - -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) - bindir=$ac_optarg ;; - - -build | --build | --buil | --bui | --bu) - ac_prev=build_alias ;; - -build=* | --build=* | --buil=* | --bui=* | --bu=*) - build_alias=$ac_optarg ;; - - -cache-file | --cache-file | --cache-fil | --cache-fi \ - | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) - ac_prev=cache_file ;; - -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ - | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) - cache_file=$ac_optarg ;; - - --config-cache | -C) - cache_file=config.cache ;; - - -datadir | --datadir | --datadi | --datad) - ac_prev=datadir ;; - -datadir=* | --datadir=* | --datadi=* | --datad=*) - datadir=$ac_optarg ;; - - -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ - | --dataroo | --dataro | --datar) - ac_prev=datarootdir ;; - -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ - | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) - datarootdir=$ac_optarg ;; - - -disable-* | --disable-*) - ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"enable_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval enable_$ac_useropt=no ;; - - -docdir | --docdir | --docdi | --doc | --do) - ac_prev=docdir ;; - -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) - docdir=$ac_optarg ;; - - -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) - ac_prev=dvidir ;; - -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) - dvidir=$ac_optarg ;; - - -enable-* | --enable-*) - ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"enable_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval enable_$ac_useropt=\$ac_optarg ;; - - -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ - | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ - | --exec | --exe | --ex) - ac_prev=exec_prefix ;; - -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ - | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ - | --exec=* | --exe=* | --ex=*) - exec_prefix=$ac_optarg ;; - - -gas | --gas | --ga | --g) - # Obsolete; use --with-gas. - with_gas=yes ;; - - -help | --help | --hel | --he | -h) - ac_init_help=long ;; - -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) - ac_init_help=recursive ;; - -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) - ac_init_help=short ;; - - -host | --host | --hos | --ho) - ac_prev=host_alias ;; - -host=* | --host=* | --hos=* | --ho=*) - host_alias=$ac_optarg ;; - - -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) - ac_prev=htmldir ;; - -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ - | --ht=*) - htmldir=$ac_optarg ;; - - -includedir | --includedir | --includedi | --included | --include \ - | --includ | --inclu | --incl | --inc) - ac_prev=includedir ;; - -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ - | --includ=* | --inclu=* | --incl=* | --inc=*) - includedir=$ac_optarg ;; - - -infodir | --infodir | --infodi | --infod | --info | --inf) - ac_prev=infodir ;; - -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) - infodir=$ac_optarg ;; - - -libdir | --libdir | --libdi | --libd) - ac_prev=libdir ;; - -libdir=* | --libdir=* | --libdi=* | --libd=*) - libdir=$ac_optarg ;; - - -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ - | --libexe | --libex | --libe) - ac_prev=libexecdir ;; - -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ - | --libexe=* | --libex=* | --libe=*) - libexecdir=$ac_optarg ;; - - -localedir | --localedir | --localedi | --localed | --locale) - ac_prev=localedir ;; - -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) - localedir=$ac_optarg ;; - - -localstatedir | --localstatedir | --localstatedi | --localstated \ - | --localstate | --localstat | --localsta | --localst | --locals) - ac_prev=localstatedir ;; - -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ - | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) - localstatedir=$ac_optarg ;; - - -mandir | --mandir | --mandi | --mand | --man | --ma | --m) - ac_prev=mandir ;; - -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) - mandir=$ac_optarg ;; - - -nfp | --nfp | --nf) - # Obsolete; use --without-fp. - with_fp=no ;; - - -no-create | --no-create | --no-creat | --no-crea | --no-cre \ - | --no-cr | --no-c | -n) - no_create=yes ;; - - -no-recursion | --no-recursion | --no-recursio | --no-recursi \ - | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) - no_recursion=yes ;; - - -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ - | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ - | --oldin | --oldi | --old | --ol | --o) - ac_prev=oldincludedir ;; - -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ - | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ - | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) - oldincludedir=$ac_optarg ;; - - -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) - ac_prev=prefix ;; - -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) - prefix=$ac_optarg ;; - - -program-prefix | --program-prefix | --program-prefi | --program-pref \ - | --program-pre | --program-pr | --program-p) - ac_prev=program_prefix ;; - -program-prefix=* | --program-prefix=* | --program-prefi=* \ - | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) - program_prefix=$ac_optarg ;; - - -program-suffix | --program-suffix | --program-suffi | --program-suff \ - | --program-suf | --program-su | --program-s) - ac_prev=program_suffix ;; - -program-suffix=* | --program-suffix=* | --program-suffi=* \ - | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) - program_suffix=$ac_optarg ;; - - -program-transform-name | --program-transform-name \ - | --program-transform-nam | --program-transform-na \ - | --program-transform-n | --program-transform- \ - | --program-transform | --program-transfor \ - | --program-transfo | --program-transf \ - | --program-trans | --program-tran \ - | --progr-tra | --program-tr | --program-t) - ac_prev=program_transform_name ;; - -program-transform-name=* | --program-transform-name=* \ - | --program-transform-nam=* | --program-transform-na=* \ - | --program-transform-n=* | --program-transform-=* \ - | --program-transform=* | --program-transfor=* \ - | --program-transfo=* | --program-transf=* \ - | --program-trans=* | --program-tran=* \ - | --progr-tra=* | --program-tr=* | --program-t=*) - program_transform_name=$ac_optarg ;; - - -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) - ac_prev=pdfdir ;; - -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) - pdfdir=$ac_optarg ;; - - -psdir | --psdir | --psdi | --psd | --ps) - ac_prev=psdir ;; - -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) - psdir=$ac_optarg ;; - - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - silent=yes ;; - - -runstatedir | --runstatedir | --runstatedi | --runstated \ - | --runstate | --runstat | --runsta | --runst | --runs \ - | --run | --ru | --r) - ac_prev=runstatedir ;; - -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ - | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ - | --run=* | --ru=* | --r=*) - runstatedir=$ac_optarg ;; - - -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) - ac_prev=sbindir ;; - -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ - | --sbi=* | --sb=*) - sbindir=$ac_optarg ;; - - -sharedstatedir | --sharedstatedir | --sharedstatedi \ - | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ - | --sharedst | --shareds | --shared | --share | --shar \ - | --sha | --sh) - ac_prev=sharedstatedir ;; - -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ - | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ - | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ - | --sha=* | --sh=*) - sharedstatedir=$ac_optarg ;; - - -site | --site | --sit) - ac_prev=site ;; - -site=* | --site=* | --sit=*) - site=$ac_optarg ;; - - -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) - ac_prev=srcdir ;; - -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) - srcdir=$ac_optarg ;; - - -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ - | --syscon | --sysco | --sysc | --sys | --sy) - ac_prev=sysconfdir ;; - -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ - | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) - sysconfdir=$ac_optarg ;; - - -target | --target | --targe | --targ | --tar | --ta | --t) - ac_prev=target_alias ;; - -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) - target_alias=$ac_optarg ;; - - -v | -verbose | --verbose | --verbos | --verbo | --verb) - verbose=yes ;; - - -version | --version | --versio | --versi | --vers | -V) - ac_init_version=: ;; - - -with-* | --with-*) - ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"with_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval with_$ac_useropt=\$ac_optarg ;; - - -without-* | --without-*) - ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"with_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval with_$ac_useropt=no ;; - - --x) - # Obsolete; use --with-x. - with_x=yes ;; - - -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ - | --x-incl | --x-inc | --x-in | --x-i) - ac_prev=x_includes ;; - -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ - | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) - x_includes=$ac_optarg ;; - - -x-libraries | --x-libraries | --x-librarie | --x-librari \ - | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) - ac_prev=x_libraries ;; - -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ - | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) - x_libraries=$ac_optarg ;; - - -*) as_fn_error $? "unrecognized option: \`$ac_option' -Try \`$0 --help' for more information" - ;; - - *=*) - ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` - # Reject names that are not valid shell variable names. - case $ac_envvar in #( - '' | [0-9]* | *[!_$as_cr_alnum]* ) - as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; - esac - eval $ac_envvar=\$ac_optarg - export $ac_envvar ;; - - *) - # FIXME: should be removed in autoconf 3.0. - $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 - expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && - $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 - : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" - ;; - - esac -done - -if test -n "$ac_prev"; then - ac_option=--`echo $ac_prev | sed 's/_/-/g'` - as_fn_error $? "missing argument to $ac_option" -fi - -if test -n "$ac_unrecognized_opts"; then - case $enable_option_checking in - no) ;; - fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; - *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; - esac -fi - -# Check all directory arguments for consistency. -for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ - datadir sysconfdir sharedstatedir localstatedir includedir \ - oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir runstatedir -do - eval ac_val=\$$ac_var - # Remove trailing slashes. - case $ac_val in - */ ) - ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` - eval $ac_var=\$ac_val;; - esac - # Be sure to have absolute directory names. - case $ac_val in - [\\/$]* | ?:[\\/]* ) continue;; - NONE | '' ) case $ac_var in *prefix ) continue;; esac;; - esac - as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" -done - -# There might be people who depend on the old broken behavior: `$host' -# used to hold the argument of --host etc. -# FIXME: To remove some day. -build=$build_alias -host=$host_alias -target=$target_alias - -# FIXME: To remove some day. -if test "x$host_alias" != x; then - if test "x$build_alias" = x; then - cross_compiling=maybe - elif test "x$build_alias" != "x$host_alias"; then - cross_compiling=yes - fi -fi - -ac_tool_prefix= -test -n "$host_alias" && ac_tool_prefix=$host_alias- - -test "$silent" = yes && exec 6>/dev/null - - -ac_pwd=`pwd` && test -n "$ac_pwd" && -ac_ls_di=`ls -di .` && -ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || - as_fn_error $? "working directory cannot be determined" -test "X$ac_ls_di" = "X$ac_pwd_ls_di" || - as_fn_error $? "pwd does not report name of working directory" - - -# Find the source files, if location was not specified. -if test -z "$srcdir"; then - ac_srcdir_defaulted=yes - # Try the directory containing this script, then the parent directory. - ac_confdir=`$as_dirname -- "$as_myself" || -$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_myself" : 'X\(//\)[^/]' \| \ - X"$as_myself" : 'X\(//\)$' \| \ - X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_myself" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - srcdir=$ac_confdir - if test ! -r "$srcdir/$ac_unique_file"; then - srcdir=.. - fi -else - ac_srcdir_defaulted=no -fi -if test ! -r "$srcdir/$ac_unique_file"; then - test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." - as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" -fi -ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" -ac_abs_confdir=`( - cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" - pwd)` -# When building in place, set srcdir=. -if test "$ac_abs_confdir" = "$ac_pwd"; then - srcdir=. -fi -# Remove unnecessary trailing slashes from srcdir. -# Double slashes in file names in object file debugging info -# mess up M-x gdb in Emacs. -case $srcdir in -*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; -esac -for ac_var in $ac_precious_vars; do - eval ac_env_${ac_var}_set=\${${ac_var}+set} - eval ac_env_${ac_var}_value=\$${ac_var} - eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} - eval ac_cv_env_${ac_var}_value=\$${ac_var} -done - -# -# Report the --help message. -# -if test "$ac_init_help" = "long"; then - # Omit some internal or obsolete options to make the list less imposing. - # This message is too long to be a string in the A/UX 3.1 sh. - cat <<_ACEOF -\`configure' configures this package to adapt to many kinds of systems. - -Usage: $0 [OPTION]... [VAR=VALUE]... - -To assign environment variables (e.g., CC, CFLAGS...), specify them as -VAR=VALUE. See below for descriptions of some of the useful variables. - -Defaults for the options are specified in brackets. - -Configuration: - -h, --help display this help and exit - --help=short display options specific to this package - --help=recursive display the short help of all the included packages - -V, --version display version information and exit - -q, --quiet, --silent do not print \`checking ...' messages - --cache-file=FILE cache test results in FILE [disabled] - -C, --config-cache alias for \`--cache-file=config.cache' - -n, --no-create do not create output files - --srcdir=DIR find the sources in DIR [configure dir or \`..'] - -Installation directories: - --prefix=PREFIX install architecture-independent files in PREFIX - [$ac_default_prefix] - --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX - [PREFIX] - -By default, \`make install' will install all the files in -\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify -an installation prefix other than \`$ac_default_prefix' using \`--prefix', -for instance \`--prefix=\$HOME'. - -For better control, use the options below. - -Fine tuning of the installation directories: - --bindir=DIR user executables [EPREFIX/bin] - --sbindir=DIR system admin executables [EPREFIX/sbin] - --libexecdir=DIR program executables [EPREFIX/libexec] - --sysconfdir=DIR read-only single-machine data [PREFIX/etc] - --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] - --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] - --libdir=DIR object code libraries [EPREFIX/lib] - --includedir=DIR C header files [PREFIX/include] - --oldincludedir=DIR C header files for non-gcc [/usr/include] - --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] - --datadir=DIR read-only architecture-independent data [DATAROOTDIR] - --infodir=DIR info documentation [DATAROOTDIR/info] - --localedir=DIR locale-dependent data [DATAROOTDIR/locale] - --mandir=DIR man documentation [DATAROOTDIR/man] - --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] - --htmldir=DIR html documentation [DOCDIR] - --dvidir=DIR dvi documentation [DOCDIR] - --pdfdir=DIR pdf documentation [DOCDIR] - --psdir=DIR ps documentation [DOCDIR] -_ACEOF - - cat <<\_ACEOF - -System types: - --build=BUILD configure for building on BUILD [guessed] - --host=HOST cross-compile to build programs to run on HOST [BUILD] -_ACEOF -fi - -if test -n "$ac_init_help"; then - - cat <<\_ACEOF - -Optional Features: - --disable-option-checking ignore unrecognized --enable/--with options - --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) - --enable-FEATURE[=ARG] include FEATURE [ARG=yes] - --enable-more-warnings enable more compiler warnings - --disable-man disable installing man pages - --enable-tracing enable possibility to use internal ccache tracing - -Optional Packages: - --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] - --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) - --with-bundled-zlib use bundled zlib instead of the system's default - zlib - -Some influential environment variables: - CC C compiler command - CFLAGS C compiler flags - LDFLAGS linker flags, e.g. -L if you have libraries in a - nonstandard directory - LIBS libraries to pass to the linker, e.g. -l - CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if - you have headers in a nonstandard directory - CPP C preprocessor - -Use these variables to override the choices made by `configure' or to help -it to find libraries and programs with nonstandard names/locations. - -Report bugs to the package provider. -_ACEOF -ac_status=$? -fi - -if test "$ac_init_help" = "recursive"; then - # If there are subdirs, report their specific --help. - for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue - test -d "$ac_dir" || - { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || - continue - ac_builddir=. - -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix - -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix - - cd "$ac_dir" || { ac_status=$?; continue; } - # Check for guested configure. - if test -f "$ac_srcdir/configure.gnu"; then - echo && - $SHELL "$ac_srcdir/configure.gnu" --help=recursive - elif test -f "$ac_srcdir/configure"; then - echo && - $SHELL "$ac_srcdir/configure" --help=recursive - else - $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 - fi || ac_status=$? - cd "$ac_pwd" || { ac_status=$?; break; } - done -fi - -test -n "$ac_init_help" && exit $ac_status -if $ac_init_version; then - cat <<\_ACEOF -configure -generated by GNU Autoconf 2.69 - -Copyright (C) 2012 Free Software Foundation, Inc. -This configure script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it. -_ACEOF - exit -fi - -## ------------------------ ## -## Autoconf initialization. ## -## ------------------------ ## - -# ac_fn_c_try_compile LINENO -# -------------------------- -# Try to compile conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext - if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_compile - -# ac_fn_c_try_cpp LINENO -# ---------------------- -# Try to preprocess conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_cpp () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } > conftest.i && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_cpp - -# ac_fn_c_try_link LINENO -# ----------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_link () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext conftest$ac_exeext - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && { - test "$cross_compiling" = yes || - test -x conftest$ac_exeext - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information - # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would - # interfere with the next link command; also delete a directory that is - # left behind by Apple's compiler. We do this before executing the actions. - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_link - -# ac_fn_c_check_type LINENO TYPE VAR INCLUDES -# ------------------------------------------- -# Tests whether TYPE exists after having included INCLUDES, setting cache -# variable VAR accordingly. -ac_fn_c_check_type () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - eval "$3=no" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -if (sizeof ($2)) - return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -if (sizeof (($2))) - return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - -else - eval "$3=yes" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_type - -# ac_fn_c_try_run LINENO -# ---------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes -# that executables *can* be run. -ac_fn_c_try_run () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then : - ac_retval=0 -else - $as_echo "$as_me: program exited with status $ac_status" >&5 - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=$ac_status -fi - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_run - -# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES -# ------------------------------------------------------- -# Tests whether HEADER exists and can be compiled using the include files in -# INCLUDES, setting the cache variable VAR accordingly. -ac_fn_c_check_header_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -#include <$2> -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - eval "$3=yes" -else - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_header_compile - -# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES -# ------------------------------------------------------- -# Tests whether HEADER exists, giving a warning if it cannot be compiled using -# the include files in INCLUDES and setting the cache variable VAR -# accordingly. -ac_fn_c_check_header_mongrel () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if eval \${$3+:} false; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -else - # Is the header compilable? -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 -$as_echo_n "checking $2 usability... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -#include <$2> -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_header_compiler=yes -else - ac_header_compiler=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 -$as_echo "$ac_header_compiler" >&6; } - -# Is the header present? -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 -$as_echo_n "checking $2 presence... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include <$2> -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - ac_header_preproc=yes -else - ac_header_preproc=no -fi -rm -f conftest.err conftest.i conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 -$as_echo "$ac_header_preproc" >&6; } - -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( - yes:no: ) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 -$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 -$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} - ;; - no:yes:* ) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 -$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 -$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 -$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 -$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 -$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} - ;; -esac - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - eval "$3=\$ac_header_compiler" -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_header_mongrel - -# ac_fn_c_check_func LINENO FUNC VAR -# ---------------------------------- -# Tests whether FUNC exists, setting the cache variable VAR accordingly -ac_fn_c_check_func () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -/* Define $2 to an innocuous variant, in case declares $2. - For example, HP-UX 11i declares gettimeofday. */ -#define $2 innocuous_$2 - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $2 (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif - -#undef $2 - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $2 (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined __stub_$2 || defined __stub___$2 -choke me -#endif - -int -main () -{ -return $2 (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - eval "$3=yes" -else - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_func - -# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES -# ---------------------------------------------------- -# Tries to find if the field MEMBER exists in type AGGR, after including -# INCLUDES, setting cache variable VAR accordingly. -ac_fn_c_check_member () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 -$as_echo_n "checking for $2.$3... " >&6; } -if eval \${$4+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$5 -int -main () -{ -static $2 ac_aggr; -if (ac_aggr.$3) -return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - eval "$4=yes" -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$5 -int -main () -{ -static $2 ac_aggr; -if (sizeof ac_aggr.$3) -return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - eval "$4=yes" -else - eval "$4=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$4 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_member -cat >config.log <<_ACEOF -This file contains any messages produced by compilers while -running configure, to aid debugging if configure makes a mistake. - -It was created by $as_me, which was -generated by GNU Autoconf 2.69. Invocation command line was - - $ $0 $@ - -_ACEOF -exec 5>>config.log -{ -cat <<_ASUNAME -## --------- ## -## Platform. ## -## --------- ## - -hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` - -/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` -/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` -/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` -/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` - -_ASUNAME - -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - $as_echo "PATH: $as_dir" - done -IFS=$as_save_IFS - -} >&5 - -cat >&5 <<_ACEOF - - -## ----------- ## -## Core tests. ## -## ----------- ## - -_ACEOF - - -# Keep a trace of the command line. -# Strip out --no-create and --no-recursion so they do not pile up. -# Strip out --silent because we don't want to record it for future runs. -# Also quote any args containing shell meta-characters. -# Make two passes to allow for proper duplicate-argument suppression. -ac_configure_args= -ac_configure_args0= -ac_configure_args1= -ac_must_keep_next=false -for ac_pass in 1 2 -do - for ac_arg - do - case $ac_arg in - -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - continue ;; - *\'*) - ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - case $ac_pass in - 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; - 2) - as_fn_append ac_configure_args1 " '$ac_arg'" - if test $ac_must_keep_next = true; then - ac_must_keep_next=false # Got value, back to normal. - else - case $ac_arg in - *=* | --config-cache | -C | -disable-* | --disable-* \ - | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ - | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ - | -with-* | --with-* | -without-* | --without-* | --x) - case "$ac_configure_args0 " in - "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; - esac - ;; - -* ) ac_must_keep_next=true ;; - esac - fi - as_fn_append ac_configure_args " '$ac_arg'" - ;; - esac - done -done -{ ac_configure_args0=; unset ac_configure_args0;} -{ ac_configure_args1=; unset ac_configure_args1;} - -# When interrupted or exit'd, cleanup temporary files, and complete -# config.log. We remove comments because anyway the quotes in there -# would cause problems or look ugly. -# WARNING: Use '\'' to represent an apostrophe within the trap. -# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. -trap 'exit_status=$? - # Save into config.log some information that might help in debugging. - { - echo - - $as_echo "## ---------------- ## -## Cache variables. ## -## ---------------- ##" - echo - # The following way of writing the cache mishandles newlines in values, -( - for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( - *) { eval $ac_var=; unset $ac_var;} ;; - esac ;; - esac - done - (set) 2>&1 | - case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - sed -n \ - "s/'\''/'\''\\\\'\'''\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" - ;; #( - *) - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) - echo - - $as_echo "## ----------------- ## -## Output variables. ## -## ----------------- ##" - echo - for ac_var in $ac_subst_vars - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - $as_echo "$ac_var='\''$ac_val'\''" - done | sort - echo - - if test -n "$ac_subst_files"; then - $as_echo "## ------------------- ## -## File substitutions. ## -## ------------------- ##" - echo - for ac_var in $ac_subst_files - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - $as_echo "$ac_var='\''$ac_val'\''" - done | sort - echo - fi - - if test -s confdefs.h; then - $as_echo "## ----------- ## -## confdefs.h. ## -## ----------- ##" - echo - cat confdefs.h - echo - fi - test "$ac_signal" != 0 && - $as_echo "$as_me: caught signal $ac_signal" - $as_echo "$as_me: exit $exit_status" - } >&5 - rm -f core *.core core.conftest.* && - rm -f -r conftest* confdefs* conf$$* $ac_clean_files && - exit $exit_status -' 0 -for ac_signal in 1 2 13 15; do - trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal -done -ac_signal=0 - -# confdefs.h avoids OS command line length limits that DEFS can exceed. -rm -f -r conftest* confdefs.h - -$as_echo "/* confdefs.h */" > confdefs.h - -# Predefined preprocessor variables. - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_NAME "$PACKAGE_NAME" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_TARNAME "$PACKAGE_TARNAME" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_VERSION "$PACKAGE_VERSION" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_STRING "$PACKAGE_STRING" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_URL "$PACKAGE_URL" -_ACEOF - - -# Let the site file select an alternate cache file if it wants to. -# Prefer an explicitly selected file to automatically selected ones. -ac_site_file1=NONE -ac_site_file2=NONE -if test -n "$CONFIG_SITE"; then - # We do not want a PATH search for config.site. - case $CONFIG_SITE in #(( - -*) ac_site_file1=./$CONFIG_SITE;; - */*) ac_site_file1=$CONFIG_SITE;; - *) ac_site_file1=./$CONFIG_SITE;; - esac -elif test "x$prefix" != xNONE; then - ac_site_file1=$prefix/share/config.site - ac_site_file2=$prefix/etc/config.site -else - ac_site_file1=$ac_default_prefix/share/config.site - ac_site_file2=$ac_default_prefix/etc/config.site -fi -for ac_site_file in "$ac_site_file1" "$ac_site_file2" -do - test "x$ac_site_file" = xNONE && continue - if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 -$as_echo "$as_me: loading site script $ac_site_file" >&6;} - sed 's/^/| /' "$ac_site_file" >&5 - . "$ac_site_file" \ - || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "failed to load site script $ac_site_file -See \`config.log' for more details" "$LINENO" 5; } - fi -done - -if test -r "$cache_file"; then - # Some versions of bash will fail to source /dev/null (special files - # actually), so we avoid doing that. DJGPP emulates it as a regular file. - if test /dev/null != "$cache_file" && test -f "$cache_file"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 -$as_echo "$as_me: loading cache $cache_file" >&6;} - case $cache_file in - [\\/]* | ?:[\\/]* ) . "$cache_file";; - *) . "./$cache_file";; - esac - fi -else - { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 -$as_echo "$as_me: creating cache $cache_file" >&6;} - >$cache_file -fi - -as_fn_append ac_header_list " stdarg.h" -as_fn_append ac_header_list " varargs.h" -# Check that the precious variables saved in the cache have kept the same -# value. -ac_cache_corrupted=false -for ac_var in $ac_precious_vars; do - eval ac_old_set=\$ac_cv_env_${ac_var}_set - eval ac_new_set=\$ac_env_${ac_var}_set - eval ac_old_val=\$ac_cv_env_${ac_var}_value - eval ac_new_val=\$ac_env_${ac_var}_value - case $ac_old_set,$ac_new_set in - set,) - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 -$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,set) - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 -$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,);; - *) - if test "x$ac_old_val" != "x$ac_new_val"; then - # differences in whitespace do not lead to failure. - ac_old_val_w=`echo x $ac_old_val` - ac_new_val_w=`echo x $ac_new_val` - if test "$ac_old_val_w" != "$ac_new_val_w"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 -$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} - ac_cache_corrupted=: - else - { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 -$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} - eval $ac_var=\$ac_old_val - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 -$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 -$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} - fi;; - esac - # Pass precious variables to config.status. - if test "$ac_new_set" = set; then - case $ac_new_val in - *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; - *) ac_arg=$ac_var=$ac_new_val ;; - esac - case " $ac_configure_args " in - *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. - *) as_fn_append ac_configure_args " '$ac_arg'" ;; - esac - fi -done -if $ac_cache_corrupted; then - { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 -$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} - as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 -fi -## -------------------- ## -## Main body of script. ## -## -------------------- ## - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: configuring ccache" >&5 -$as_echo "$as_me: configuring ccache" >&6;} - -ac_config_headers="$ac_config_headers config.h" - - -ac_aux_dir= -for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do - if test -f "$ac_dir/install-sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install-sh -c" - break - elif test -f "$ac_dir/install.sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install.sh -c" - break - elif test -f "$ac_dir/shtool"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/shtool install -c" - break - fi -done -if test -z "$ac_aux_dir"; then - as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 -fi - -# These three variables are undocumented and unsupported, -# and are intended to be withdrawn in a future Autoconf release. -# They can cause serious problems if a builder's source tree is in a directory -# whose full name contains unusual characters. -ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. -ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. -ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. - - -# Make sure we can run config.sub. -$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || - as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 -$as_echo_n "checking build system type... " >&6; } -if ${ac_cv_build+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_build_alias=$build_alias -test "x$ac_build_alias" = x && - ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` -test "x$ac_build_alias" = x && - as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 -ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 -$as_echo "$ac_cv_build" >&6; } -case $ac_cv_build in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; -esac -build=$ac_cv_build -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_build -shift -build_cpu=$1 -build_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -build_os=$* -IFS=$ac_save_IFS -case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 -$as_echo_n "checking host system type... " >&6; } -if ${ac_cv_host+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "x$host_alias" = x; then - ac_cv_host=$ac_cv_build -else - ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 -$as_echo "$ac_cv_host" >&6; } -case $ac_cv_host in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; -esac -host=$ac_cv_host -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_host -shift -host_cpu=$1 -host_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -host_os=$* -IFS=$ac_save_IFS -case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac - - - -case $host in - *mingw32* | *mingw64* | *cygwin* | *wince* | *mingwce*) - windows_os=yes - -$as_echo "#define _WIN32_WINNT 0x0600" >>confdefs.h - - ;; - *os400* | *aix*) - AR="ar -X64" - ;; -esac - - - - - - - - - - -# The later defininition of _XOPEN_SOURCE disables certain features -# on Linux, so we need _GNU_SOURCE to re-enable them (makedev, tm_zone). - -$as_echo "#define _GNU_SOURCE 1" >>confdefs.h - - -# The later defininition of _XOPEN_SOURCE and _POSIX_C_SOURCE disables -# certain features on NetBSD, so we need _NETBSD_SOURCE to re-enable -# them. - -$as_echo "#define _NETBSD_SOURCE 1" >>confdefs.h - - -# The later defininition of _XOPEN_SOURCE and _POSIX_C_SOURCE disables -# certain features on FreeBSD, so we need __BSD_VISIBLE to re-enable -# them. - -$as_echo "#define __BSD_VISIBLE 1" >>confdefs.h - - -# The later defininition of _XOPEN_SOURCE and _POSIX_C_SOURCE disables -# u_int on Irix 5.3. Defining _BSD_TYPES brings it back. - -$as_echo "#define _BSD_TYPES 1" >>confdefs.h - - -# The later defininition of _XOPEN_SOURCE and _POSIX_C_SOURCE disables -# certain features on Mac OS X, so we need _DARWIN_C_SOURCE to re-enable -# them. - -$as_echo "#define _DARWIN_C_SOURCE 1" >>confdefs.h - - -define_xopen_source=yes - -ac_sys_system=`uname -s` -if test "$ac_sys_system" = "AIX" -o "$ac_sys_system" = "Monterey64" \ - -o "$ac_sys_system" = "UnixWare" -o "$ac_sys_system" = "OpenUNIX"; then - ac_sys_release=`uname -v` -else - ac_sys_release=`uname -r` -fi - -# Some systems cannot stand _XOPEN_SOURCE being defined at all; they -# disable features if it is defined, without any means to access these -# features as extensions. For these systems, we skip the definition of -# _XOPEN_SOURCE. Before adding a system to the list to gain access to -# some feature, make sure there is no alternative way to access this -# feature. Also, when using wildcards, make sure you have verified the -# need for not defining _XOPEN_SOURCE on all systems matching the -# wildcard, and that the wildcard does not include future systems -# (which may remove their limitations). -case $ac_sys_system/$ac_sys_release in - # On OpenBSD, select(2) is not available if _XOPEN_SOURCE is defined, - # even though select is a POSIX function. Reported by J. Ribbens. - # Reconfirmed for OpenBSD 3.3 by Zachary Hamm, for 3.4 by Jason Ish. - OpenBSD/2.* | OpenBSD/3.[0123456789] | OpenBSD/4.[0123]) - define_xopen_source=no - # OpenBSD undoes our definition of __BSD_VISIBLE if _XOPEN_SOURCE is - # also defined. This can be overridden by defining _BSD_SOURCE - # As this has a different meaning on Linux, only define it on OpenBSD - -$as_echo "#define _BSD_SOURCE 1" >>confdefs.h - - ;; - # Defining _XOPEN_SOURCE on NetBSD version prior to the introduction of - # _NETBSD_SOURCE disables certain features (eg. setgroups). Reported by - # Marc Recht - NetBSD/1.5 | NetBSD/1.5.* | NetBSD/1.6 | NetBSD/1.6.* | NetBSD/1.6[A-S]) - define_xopen_source=no;; - # On Solaris 2.6, sys/wait.h is inconsistent in the usage - # of union __?sigval. Reported by Stuart Bishop. - SunOS/5.6) - define_xopen_source=no;; - # On UnixWare 7, u_long is never defined with _XOPEN_SOURCE, - # but used in /usr/include/netinet/tcp.h. Reported by Tim Rice. - # Reconfirmed for 7.1.4 by Martin v. Loewis. - OpenUNIX/8.0.0| UnixWare/7.1.[0-4]) - define_xopen_source=no;; - # On OpenServer 5, u_short is never defined with _XOPEN_SOURCE, - # but used in struct sockaddr.sa_family. Reported by Tim Rice. - SCO_SV/3.2) - define_xopen_source=no;; - # On FreeBSD 4, the math functions C89 does not cover are never defined - # with _XOPEN_SOURCE and __BSD_VISIBLE does not re-enable them. - FreeBSD/4.*) - define_xopen_source=no;; - # On MacOS X 10.2, a bug in ncurses.h means that it craps out if - # _XOPEN_EXTENDED_SOURCE is defined. Apparently, this is fixed in 10.3, which - # identifies itself as Darwin/7.* - # On Mac OS X 10.4, defining _POSIX_C_SOURCE or _XOPEN_SOURCE - # disables platform specific features beyond repair. - # On Mac OS X 10.3, defining _POSIX_C_SOURCE or _XOPEN_SOURCE - # has no effect, don't bother defining them - Darwin/[6789].*) - define_xopen_source=no;; - # On AIX 4 and 5.1, mbstate_t is defined only when _XOPEN_SOURCE == 500 but - # used in wcsnrtombs() and mbsnrtowcs() even if _XOPEN_SOURCE is not defined - # or has another value. By not (re)defining it, the defaults come in place. - AIX/4) - define_xopen_source=no;; - AIX/5|AIX/7) - if test `uname -r` -eq 1; then - define_xopen_source=no - fi - ;; - # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from - # defining NI_NUMERICHOST. - QNX/6.3.2) - define_xopen_source=no - ;; - -esac - -if test $define_xopen_source = yes -then - # On Solaris w/ g++ it appears that _XOPEN_SOURCE has to be - # defined precisely as g++ defines it - # Furthermore, on Solaris 10, XPG6 requires the use of a C99 - # compiler - case $ac_sys_system/$ac_sys_release in - SunOS/5.8|SunOS/5.9|SunOS/5.10) - -$as_echo "#define _XOPEN_SOURCE 500" >>confdefs.h - - ;; - *) - -$as_echo "#define _XOPEN_SOURCE 700" >>confdefs.h - - ;; - esac - - # On Tru64 Unix 4.0F, defining _XOPEN_SOURCE also requires - # definition of _XOPEN_SOURCE_EXTENDED and _POSIX_C_SOURCE, or else - # several APIs are not declared. Since this is also needed in some - # cases for HP-UX, we define it globally. - # except for Solaris 10, where it must not be defined, - # as it implies XPG4.2 - case $ac_sys_system/$ac_sys_release in - SunOS/5.10|SunOS/5.11) - -$as_echo "#define __EXTENSIONS__ 1" >>confdefs.h - - ;; - *) - -$as_echo "#define _XOPEN_SOURCE_EXTENDED 1" >>confdefs.h - - ;; - esac - - -$as_echo "#define _POSIX_C_SOURCE 200809L" >>confdefs.h - - -fi - -# _AC_LANG_COMPILER_CLANG -# --------------------- -# Check whether the compiler for the current language is clang. -# Adapted from standard autoconf function: _AC_LANG_COMPILER_GNU -# -# Note: clang also identifies itself as a GNU compiler (gcc 4.2.1) -# for compatibility reasons, so that cannot be used to determine -# _AC_LANG_COMPILER_CLANG - - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -else - CC="$ac_cv_prog_CC" -fi - -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - fi -fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" - fi -fi -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl.exe - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl.exe -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_CC" && break -done - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -fi - -fi - - -test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } - -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done - -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" -# Try to create an executable without -o first, disregard a.out. -# It will help us diagnose broken compilers, and finding out an intuition -# of exeext. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 -$as_echo_n "checking whether the C compiler works... " >&6; } -ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` - -# The possible output files: -ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" - -ac_rmfiles= -for ac_file in $ac_files -do - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - * ) ac_rmfiles="$ac_rmfiles $ac_file";; - esac -done -rm -f $ac_rmfiles - -if { { ac_try="$ac_link_default" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link_default") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. -# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' -# in a Makefile. We should not override ac_cv_exeext if it was cached, -# so that the user can short-circuit this test for compilers unknown to -# Autoconf. -for ac_file in $ac_files '' -do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) - ;; - [ab].out ) - # We found the default executable, but exeext='' is most - # certainly right. - break;; - *.* ) - if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; - then :; else - ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - fi - # We set ac_cv_exeext here because the later test for it is not - # safe: cross compilers may not add the suffix if given an `-o' - # argument, so we may need to know it at that point already. - # Even if this section looks crufty: it has the advantage of - # actually working. - break;; - * ) - break;; - esac -done -test "$ac_cv_exeext" = no && ac_cv_exeext= - -else - ac_file='' -fi -if test -z "$ac_file"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -$as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "C compiler cannot create executables -See \`config.log' for more details" "$LINENO" 5; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 -$as_echo_n "checking for C compiler default output file name... " >&6; } -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 -$as_echo "$ac_file" >&6; } -ac_exeext=$ac_cv_exeext - -rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out -ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 -$as_echo_n "checking for suffix of executables... " >&6; } -if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - # If both `conftest.exe' and `conftest' are `present' (well, observable) -# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will -# work properly (i.e., refer to `conftest.exe'), while it won't with -# `rm'. -for ac_file in conftest.exe conftest conftest.*; do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - break;; - * ) break;; - esac -done -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest conftest$ac_cv_exeext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 -$as_echo "$ac_cv_exeext" >&6; } - -rm -f conftest.$ac_ext -EXEEXT=$ac_cv_exeext -ac_exeext=$EXEEXT -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ -FILE *f = fopen ("conftest.out", "w"); - return ferror (f) || fclose (f) != 0; - - ; - return 0; -} -_ACEOF -ac_clean_files="$ac_clean_files conftest.out" -# Check that the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 -$as_echo_n "checking whether we are cross compiling... " >&6; } -if test "$cross_compiling" != yes; then - { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - if { ac_try='./conftest$ac_cv_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then - cross_compiling=no - else - if test "$cross_compiling" = maybe; then - cross_compiling=yes - else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot run C compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details" "$LINENO" 5; } - fi - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 -$as_echo "$cross_compiling" >&6; } - -rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out -ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 -$as_echo_n "checking for suffix of object files... " >&6; } -if ${ac_cv_objext+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.o conftest.obj -if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - for ac_file in conftest.o conftest.obj conftest.*; do - test -f "$ac_file" || continue; - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; - *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` - break;; - esac -done -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of object files: cannot compile -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest.$ac_cv_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 -$as_echo "$ac_cv_objext" >&6; } -OBJEXT=$ac_cv_objext -ac_objext=$OBJEXT -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 -$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } -if ${ac_cv_c_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_compiler_gnu=yes -else - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 -$as_echo "$ac_cv_c_compiler_gnu" >&6; } -if test $ac_compiler_gnu = yes; then - GCC=yes -else - GCC= -fi -ac_test_CFLAGS=${CFLAGS+set} -ac_save_CFLAGS=$CFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 -$as_echo_n "checking whether $CC accepts -g... " >&6; } -if ${ac_cv_prog_cc_g+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_c_werror_flag=$ac_c_werror_flag - ac_c_werror_flag=yes - ac_cv_prog_cc_g=no - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -else - CFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - -else - ac_c_werror_flag=$ac_save_c_werror_flag - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 -$as_echo "$ac_cv_prog_cc_g" >&6; } -if test "$ac_test_CFLAGS" = set; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi -else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 -$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } -if ${ac_cv_prog_cc_c89+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_prog_cc_c89=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -struct stat; -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; - -/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters - inside strings and character constants. */ -#define FOO(x) 'x' -int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; - -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; -int -main () -{ -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; - ; - return 0; -} -_ACEOF -for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ - -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_c89=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext - test "x$ac_cv_prog_cc_c89" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC - -fi -# AC_CACHE_VAL -case "x$ac_cv_prog_cc_c89" in - x) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -$as_echo "none needed" >&6; } ;; - xno) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -$as_echo "unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c89" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 -$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; -esac -if test "x$ac_cv_prog_cc_c89" != xno; then : - -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the clang C compiler" >&5 -$as_echo_n "checking whether we are using the clang C compiler... " >&6; } -if ${ac_cv_c_compiler_clang+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -#ifndef __clang__ - choke me -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_compiler_clang=yes -else - ac_compiler_clang=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_clang=$ac_compiler_clang - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_clang" >&5 -$as_echo "$ac_cv_c_compiler_clang" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5 -$as_echo_n "checking for $CC option to accept ISO C99... " >&6; } -if ${ac_cv_prog_cc_c99+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_prog_cc_c99=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#include -#include -#include - -// Check varargs macros. These examples are taken from C99 6.10.3.5. -#define debug(...) fprintf (stderr, __VA_ARGS__) -#define showlist(...) puts (#__VA_ARGS__) -#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) -static void -test_varargs_macros (void) -{ - int x = 1234; - int y = 5678; - debug ("Flag"); - debug ("X = %d\n", x); - showlist (The first, second, and third items.); - report (x>y, "x is %d but y is %d", x, y); -} - -// Check long long types. -#define BIG64 18446744073709551615ull -#define BIG32 4294967295ul -#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) -#if !BIG_OK - your preprocessor is broken; -#endif -#if BIG_OK -#else - your preprocessor is broken; -#endif -static long long int bignum = -9223372036854775807LL; -static unsigned long long int ubignum = BIG64; - -struct incomplete_array -{ - int datasize; - double data[]; -}; - -struct named_init { - int number; - const wchar_t *name; - double average; -}; - -typedef const char *ccp; - -static inline int -test_restrict (ccp restrict text) -{ - // See if C++-style comments work. - // Iterate through items via the restricted pointer. - // Also check for declarations in for loops. - for (unsigned int i = 0; *(text+i) != '\0'; ++i) - continue; - return 0; -} - -// Check varargs and va_copy. -static void -test_varargs (const char *format, ...) -{ - va_list args; - va_start (args, format); - va_list args_copy; - va_copy (args_copy, args); - - const char *str; - int number; - float fnumber; - - while (*format) - { - switch (*format++) - { - case 's': // string - str = va_arg (args_copy, const char *); - break; - case 'd': // int - number = va_arg (args_copy, int); - break; - case 'f': // float - fnumber = va_arg (args_copy, double); - break; - default: - break; - } - } - va_end (args_copy); - va_end (args); -} - -int -main () -{ - - // Check bool. - _Bool success = false; - - // Check restrict. - if (test_restrict ("String literal") == 0) - success = true; - char *restrict newvar = "Another string"; - - // Check varargs. - test_varargs ("s, d' f .", "string", 65, 34.234); - test_varargs_macros (); - - // Check flexible array members. - struct incomplete_array *ia = - malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); - ia->datasize = 10; - for (int i = 0; i < ia->datasize; ++i) - ia->data[i] = i * 1.234; - - // Check named initializers. - struct named_init ni = { - .number = 34, - .name = L"Test wide string", - .average = 543.34343, - }; - - ni.number = 58; - - int dynamic_array[ni.number]; - dynamic_array[ni.number - 1] = 543; - - // work around unused variable warnings - return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x' - || dynamic_array[ni.number - 1] != 543); - - ; - return 0; -} -_ACEOF -for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99 -do - CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_c99=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext - test "x$ac_cv_prog_cc_c99" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC - -fi -# AC_CACHE_VAL -case "x$ac_cv_prog_cc_c99" in - x) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -$as_echo "none needed" >&6; } ;; - xno) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -$as_echo "unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c99" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 -$as_echo "$ac_cv_prog_cc_c99" >&6; } ;; -esac -if test "x$ac_cv_prog_cc_c99" != xno; then : - -fi - - -if test "$ac_cv_prog_cc_c99" = no; then - as_fn_error $? "cannot find a C99-compatible compiler" "$LINENO" 5 -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 -$as_echo_n "checking how to run the C preprocessor... " >&6; } -# On Suns, sometimes $CPP names a directory. -if test -n "$CPP" && test -d "$CPP"; then - CPP= -fi -if test -z "$CPP"; then - if ${ac_cv_prog_CPP+:} false; then : - $as_echo_n "(cached) " >&6 -else - # Double quotes because CPP needs to be expanded - for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" - do - ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - break -fi - - done - ac_cv_prog_CPP=$CPP - -fi - CPP=$ac_cv_prog_CPP -else - ac_cv_prog_CPP=$CPP -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 -$as_echo "$CPP" >&6; } -ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -# Find a good install program. We prefer a C program (faster), -# so one script is as good as another. But avoid the broken or -# incompatible versions: -# SysV /etc/install, /usr/sbin/install -# SunOS /usr/etc/install -# IRIX /sbin/install -# AIX /bin/install -# AmigaOS /C/install, which installs bootblocks on floppy discs -# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag -# AFS /usr/afsws/bin/install, which mishandles nonexistent args -# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" -# OS/2's system install, which has a completely different semantic -# ./install, which can be erroneously created by make from ./install.sh. -# Reject install programs that cannot install multiple files. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 -$as_echo_n "checking for a BSD-compatible install... " >&6; } -if test -z "$INSTALL"; then -if ${ac_cv_path_install+:} false; then : - $as_echo_n "(cached) " >&6 -else - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - # Account for people who put trailing slashes in PATH elements. -case $as_dir/ in #(( - ./ | .// | /[cC]/* | \ - /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ - ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ - /usr/ucb/* ) ;; - *) - # OSF1 and SCO ODT 3.0 have their own names for install. - # Don't use installbsd from OSF since it installs stuff as root - # by default. - for ac_prog in ginstall scoinst install; do - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then - if test $ac_prog = install && - grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then - # AIX install. It has an incompatible calling convention. - : - elif test $ac_prog = install && - grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then - # program-specific install script used by HP pwplus--don't use. - : - else - rm -rf conftest.one conftest.two conftest.dir - echo one > conftest.one - echo two > conftest.two - mkdir conftest.dir - if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && - test -s conftest.one && test -s conftest.two && - test -s conftest.dir/conftest.one && - test -s conftest.dir/conftest.two - then - ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" - break 3 - fi - fi - fi - done - done - ;; -esac - - done -IFS=$as_save_IFS - -rm -rf conftest.one conftest.two conftest.dir - -fi - if test "${ac_cv_path_install+set}" = set; then - INSTALL=$ac_cv_path_install - else - # As a last resort, use the slow shell script. Don't cache a - # value for INSTALL within a source directory, because that will - # break other packages using the cache if that directory is - # removed, or if the value is a relative name. - INSTALL=$ac_install_sh - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 -$as_echo "$INSTALL" >&6; } - -# Use test -z because SunOS4 sh mishandles braces in ${var-val}. -# It thinks the first close brace ends the variable substitution. -test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' - -test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' - -test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. -set dummy ${ac_tool_prefix}ranlib; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_RANLIB+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$RANLIB"; then - ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -RANLIB=$ac_cv_prog_RANLIB -if test -n "$RANLIB"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 -$as_echo "$RANLIB" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_RANLIB"; then - ac_ct_RANLIB=$RANLIB - # Extract the first word of "ranlib", so it can be a program name with args. -set dummy ranlib; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_RANLIB"; then - ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_RANLIB="ranlib" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB -if test -n "$ac_ct_RANLIB"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 -$as_echo "$ac_ct_RANLIB" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_RANLIB" = x; then - RANLIB=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - RANLIB=$ac_ct_RANLIB - fi -else - RANLIB="$ac_cv_prog_RANLIB" -fi - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. -set dummy ${ac_tool_prefix}ar; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_AR+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$AR"; then - ac_cv_prog_AR="$AR" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_AR="${ac_tool_prefix}ar" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -AR=$ac_cv_prog_AR -if test -n "$AR"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 -$as_echo "$AR" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_AR"; then - ac_ct_AR=$AR - # Extract the first word of "ar", so it can be a program name with args. -set dummy ar; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_AR+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_AR"; then - ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_AR="ar" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_AR=$ac_cv_prog_ac_ct_AR -if test -n "$ac_ct_AR"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 -$as_echo "$ac_ct_AR" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_AR" = x; then - AR="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - AR=$ac_ct_AR - fi -else - AR="$ac_cv_prog_AR" -fi - -if test -z "$AR"; then - as_fn_error $? "cannot find ar" "$LINENO" 5 -fi - -# Prefer bash, needed for test.sh -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}bash", so it can be a program name with args. -set dummy ${ac_tool_prefix}bash; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_BASH+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $BASH in - [\\/]* | ?:[\\/]*) - ac_cv_path_BASH="$BASH" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_BASH="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - - ;; -esac -fi -BASH=$ac_cv_path_BASH -if test -n "$BASH"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $BASH" >&5 -$as_echo "$BASH" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_path_BASH"; then - ac_pt_BASH=$BASH - # Extract the first word of "bash", so it can be a program name with args. -set dummy bash; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_ac_pt_BASH+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $ac_pt_BASH in - [\\/]* | ?:[\\/]*) - ac_cv_path_ac_pt_BASH="$ac_pt_BASH" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_ac_pt_BASH="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - - ;; -esac -fi -ac_pt_BASH=$ac_cv_path_ac_pt_BASH -if test -n "$ac_pt_BASH"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_BASH" >&5 -$as_echo "$ac_pt_BASH" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_pt_BASH" = x; then - BASH=""/bin/bash"" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - BASH=$ac_pt_BASH - fi -else - BASH="$ac_cv_path_BASH" -fi - - -# If GCC (or clang), turn on warnings. -if test "$ac_compiler_gnu" = yes; then - CFLAGS="$CFLAGS -Wall -W" -else - CFLAGS="$CFLAGS -O" -fi - -# Check whether --enable-more_warnings was given. -if test "${enable_more_warnings+set}" = set; then : - enableval=$enable_more_warnings; -fi - -if test x${enable_more_warnings} = xyes; then - more_warnings="-Wextra -Wpedantic" - if test "$ac_compiler_clang" = yes; then - more_warnings="$more_warnings -Weverything" - more_warnings="$more_warnings -Wno-conversion" - more_warnings="$more_warnings -Wno-disabled-macro-expansion" - more_warnings="$more_warnings -Wno-format-nonliteral" - more_warnings="$more_warnings -Wno-padded" - more_warnings="$more_warnings -Wno-shorten-64-to-32" - more_warnings="$more_warnings -Wno-sign-conversion" - fi -fi - - -ac_header_dirent=no -for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do - as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5 -$as_echo_n "checking for $ac_hdr that defines DIR... " >&6; } -if eval \${$as_ac_Header+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include <$ac_hdr> - -int -main () -{ -if ((DIR *) 0) -return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - eval "$as_ac_Header=yes" -else - eval "$as_ac_Header=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$as_ac_Header - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_hdr" | $as_tr_cpp` 1 -_ACEOF - -ac_header_dirent=$ac_hdr; break -fi - -done -# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. -if test $ac_header_dirent = dirent.h; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5 -$as_echo_n "checking for library containing opendir... " >&6; } -if ${ac_cv_search_opendir+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_func_search_save_LIBS=$LIBS -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char opendir (); -int -main () -{ -return opendir (); - ; - return 0; -} -_ACEOF -for ac_lib in '' dir; do - if test -z "$ac_lib"; then - ac_res="none required" - else - ac_res=-l$ac_lib - LIBS="-l$ac_lib $ac_func_search_save_LIBS" - fi - if ac_fn_c_try_link "$LINENO"; then : - ac_cv_search_opendir=$ac_res -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext - if ${ac_cv_search_opendir+:} false; then : - break -fi -done -if ${ac_cv_search_opendir+:} false; then : - -else - ac_cv_search_opendir=no -fi -rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5 -$as_echo "$ac_cv_search_opendir" >&6; } -ac_res=$ac_cv_search_opendir -if test "$ac_res" != no; then : - test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" - -fi - -else - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5 -$as_echo_n "checking for library containing opendir... " >&6; } -if ${ac_cv_search_opendir+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_func_search_save_LIBS=$LIBS -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char opendir (); -int -main () -{ -return opendir (); - ; - return 0; -} -_ACEOF -for ac_lib in '' x; do - if test -z "$ac_lib"; then - ac_res="none required" - else - ac_res=-l$ac_lib - LIBS="-l$ac_lib $ac_func_search_save_LIBS" - fi - if ac_fn_c_try_link "$LINENO"; then : - ac_cv_search_opendir=$ac_res -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext - if ${ac_cv_search_opendir+:} false; then : - break -fi -done -if ${ac_cv_search_opendir+:} false; then : - -else - ac_cv_search_opendir=no -fi -rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5 -$as_echo "$ac_cv_search_opendir" >&6; } -ac_res=$ac_cv_search_opendir -if test "$ac_res" != no; then : - test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" - -fi - -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5 -$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; } -if ${ac_cv_header_time+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#include - -int -main () -{ -if ((struct tm *) 0) -return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_header_time=yes -else - ac_cv_header_time=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5 -$as_echo "$ac_cv_header_time" >&6; } -if test $ac_cv_header_time = yes; then - -$as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h - -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5 -$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; } -if ${ac_cv_header_sys_wait_h+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#ifndef WEXITSTATUS -# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8) -#endif -#ifndef WIFEXITED -# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) -#endif - -int -main () -{ - int s; - wait (&s); - s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_header_sys_wait_h=yes -else - ac_cv_header_sys_wait_h=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5 -$as_echo "$ac_cv_header_sys_wait_h" >&6; } -if test $ac_cv_header_sys_wait_h = yes; then - -$as_echo "#define HAVE_SYS_WAIT_H 1" >>confdefs.h - -fi - - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 -$as_echo_n "checking for grep that handles long lines and -e... " >&6; } -if ${ac_cv_path_GREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -z "$GREP"; then - ac_path_GREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in grep ggrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_GREP" || continue -# Check for GNU ac_path_GREP and select it if it is found. - # Check for GNU $ac_path_GREP -case `"$ac_path_GREP" --version 2>&1` in -*GNU*) - ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'GREP' >> "conftest.nl" - "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_GREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_GREP="$ac_path_GREP" - ac_path_GREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_GREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_GREP"; then - as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_GREP=$GREP -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 -$as_echo "$ac_cv_path_GREP" >&6; } - GREP="$ac_cv_path_GREP" - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 -$as_echo_n "checking for egrep... " >&6; } -if ${ac_cv_path_EGREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 - then ac_cv_path_EGREP="$GREP -E" - else - if test -z "$EGREP"; then - ac_path_EGREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in egrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_EGREP" || continue -# Check for GNU ac_path_EGREP and select it if it is found. - # Check for GNU $ac_path_EGREP -case `"$ac_path_EGREP" --version 2>&1` in -*GNU*) - ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'EGREP' >> "conftest.nl" - "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_EGREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_EGREP="$ac_path_EGREP" - ac_path_EGREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_EGREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_EGREP"; then - as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_EGREP=$EGREP -fi - - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 -$as_echo "$ac_cv_path_EGREP" >&6; } - EGREP="$ac_cv_path_EGREP" - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 -$as_echo_n "checking for ANSI C header files... " >&6; } -if ${ac_cv_header_stdc+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#include -#include - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_header_stdc=yes -else - ac_cv_header_stdc=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - -if test $ac_cv_header_stdc = yes; then - # SunOS 4.x string.h does not declare mem*, contrary to ANSI. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "memchr" >/dev/null 2>&1; then : - -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "free" >/dev/null 2>&1; then : - -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. - if test "$cross_compiling" = yes; then : - : -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#if ((' ' & 0x0FF) == 0x020) -# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') -# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) -#else -# define ISLOWER(c) \ - (('a' <= (c) && (c) <= 'i') \ - || ('j' <= (c) && (c) <= 'r') \ - || ('s' <= (c) && (c) <= 'z')) -# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) -#endif - -#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) -int -main () -{ - int i; - for (i = 0; i < 256; i++) - if (XOR (islower (i), ISLOWER (i)) - || toupper (i) != TOUPPER (i)) - return 2; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - -else - ac_cv_header_stdc=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 -$as_echo "$ac_cv_header_stdc" >&6; } -if test $ac_cv_header_stdc = yes; then - -$as_echo "#define STDC_HEADERS 1" >>confdefs.h - -fi - -# On IRIX 5.3, sys/types and inttypes.h are conflicting. -for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ - inttypes.h stdint.h unistd.h -do : - as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default -" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - -ac_fn_c_check_type "$LINENO" "long long" "ac_cv_type_long_long" "$ac_includes_default" -if test "x$ac_cv_type_long_long" = xyes; then : - -cat >>confdefs.h <<_ACEOF -#define HAVE_LONG_LONG 1 -_ACEOF - - -fi - - -for ac_header in ctype.h pwd.h stdlib.h string.h strings.h sys/time.h sys/mman.h -do : - as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - -for ac_header in termios.h -do : - ac_fn_c_check_header_mongrel "$LINENO" "termios.h" "ac_cv_header_termios_h" "$ac_includes_default" -if test "x$ac_cv_header_termios_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_TERMIOS_H 1 -_ACEOF - -fi - -done - - -for ac_func in gethostname -do : - ac_fn_c_check_func "$LINENO" "gethostname" "ac_cv_func_gethostname" -if test "x$ac_cv_func_gethostname" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_GETHOSTNAME 1 -_ACEOF - -fi -done - -for ac_func in getopt_long -do : - ac_fn_c_check_func "$LINENO" "getopt_long" "ac_cv_func_getopt_long" -if test "x$ac_cv_func_getopt_long" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_GETOPT_LONG 1 -_ACEOF - -fi -done - -for ac_func in getpwuid -do : - ac_fn_c_check_func "$LINENO" "getpwuid" "ac_cv_func_getpwuid" -if test "x$ac_cv_func_getpwuid" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_GETPWUID 1 -_ACEOF - -fi -done - -for ac_func in gettimeofday -do : - ac_fn_c_check_func "$LINENO" "gettimeofday" "ac_cv_func_gettimeofday" -if test "x$ac_cv_func_gettimeofday" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_GETTIMEOFDAY 1 -_ACEOF - -fi -done - -for ac_func in localtime_r -do : - ac_fn_c_check_func "$LINENO" "localtime_r" "ac_cv_func_localtime_r" -if test "x$ac_cv_func_localtime_r" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LOCALTIME_R 1 -_ACEOF - -fi -done - -for ac_func in mkstemp -do : - ac_fn_c_check_func "$LINENO" "mkstemp" "ac_cv_func_mkstemp" -if test "x$ac_cv_func_mkstemp" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_MKSTEMP 1 -_ACEOF - -fi -done - -for ac_func in realpath -do : - ac_fn_c_check_func "$LINENO" "realpath" "ac_cv_func_realpath" -if test "x$ac_cv_func_realpath" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_REALPATH 1 -_ACEOF - -fi -done - -for ac_func in setenv -do : - ac_fn_c_check_func "$LINENO" "setenv" "ac_cv_func_setenv" -if test "x$ac_cv_func_setenv" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_SETENV 1 -_ACEOF - -fi -done - -for ac_func in strndup -do : - ac_fn_c_check_func "$LINENO" "strndup" "ac_cv_func_strndup" -if test "x$ac_cv_func_strndup" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_STRNDUP 1 -_ACEOF - -fi -done - -for ac_func in strtok_r -do : - ac_fn_c_check_func "$LINENO" "strtok_r" "ac_cv_func_strtok_r" -if test "x$ac_cv_func_strtok_r" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_STRTOK_R 1 -_ACEOF - -fi -done - -for ac_func in unsetenv -do : - ac_fn_c_check_func "$LINENO" "unsetenv" "ac_cv_func_unsetenv" -if test "x$ac_cv_func_unsetenv" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_UNSETENV 1 -_ACEOF - -fi -done - -for ac_func in utimes -do : - ac_fn_c_check_func "$LINENO" "utimes" "ac_cv_func_utimes" -if test "x$ac_cv_func_utimes" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_UTIMES 1 -_ACEOF - -fi -done - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compar_fn_t in stdlib.h" >&5 -$as_echo_n "checking for compar_fn_t in stdlib.h... " >&6; } -if ${ccache_cv_COMPAR_FN_T+:} false; then : - $as_echo_n "(cached) " >&6 -else - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ -void test_fn(void) { qsort(NULL, 0, 0, (__compar_fn_t)NULL); } - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ccache_cv_COMPAR_FN_T=yes -else - ccache_cv_COMPAR_FN_T=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ccache_cv_COMPAR_FN_T" >&5 -$as_echo "$ccache_cv_COMPAR_FN_T" >&6; } -if test x"$ccache_cv_COMPAR_FN_T" = x"yes"; then - -$as_echo "#define HAVE_COMPAR_FN_T 1" >>confdefs.h - -fi - -# $Id: snprintf.m4,v 1.1.1.1 2008/01/06 03:24:00 holger Exp $ - -# Copyright (c) 2008 Holger Weiss . -# -# This code may freely be used, modified and/or redistributed for any purpose. -# It would be nice if additions and fixes to this file (including trivial code -# cleanups) would be sent back in order to let me include them in the version -# available at . However, this is -# not a requirement for using or redistributing (possibly modified) versions of -# this file, nor is leaving this notice intact mandatory. - -# HW_HEADER_STDARG_H -# ------------------ -# Define HAVE_STDARG_H to 1 if is available. -# HW_HEADER_STDARG_H - -# HW_HEADER_VARARGS_H -# ------------------- -# Define HAVE_VARARGS_H to 1 if is available. -# HW_HEADER_VARARGS_H - -# HW_FUNC_VA_COPY -# --------------- -# Set $hw_cv_func_va_copy to "yes" or "no". Define HAVE_VA_COPY to 1 if -# $hw_cv_func_va_copy is set to "yes". Note that it's "unspecified whether -# va_copy and va_end are macros or identifiers declared with external linkage." -# (C99: 7.15.1, 1) Therefore, the presence of va_copy(3) cannot simply "be -# tested with #ifdef", as suggested by the Autoconf manual (5.5.1). -# HW_FUNC_VA_COPY - -# HW_FUNC___VA_COPY -# ----------------- -# Set $hw_cv_func___va_copy to "yes" or "no". Define HAVE___VA_COPY to 1 if -# $hw_cv_func___va_copy is set to "yes". -# HW_FUNC___VA_COPY - -# HW_FUNC_VSNPRINTF -# ----------------- -# Set $hw_cv_func_vsnprintf and $hw_cv_func_vsnprintf_c99 to "yes" or "no", -# respectively. Define HAVE_VSNPRINTF to 1 only if $hw_cv_func_vsnprintf_c99 -# is set to "yes". Otherwise, define vsnprintf to rpl_vsnprintf and make sure -# the replacement function will be built. -# HW_FUNC_VSNPRINTF - -# HW_FUNC_SNPRINTF -# ---------------- -# Set $hw_cv_func_snprintf and $hw_cv_func_snprintf_c99 to "yes" or "no", -# respectively. Define HAVE_SNPRINTF to 1 only if $hw_cv_func_snprintf_c99 -# is set to "yes". Otherwise, define snprintf to rpl_snprintf and make sure -# the replacement function will be built. -# HW_FUNC_SNPRINTF - -# HW_FUNC_VASPRINTF -# ----------------- -# Set $hw_cv_func_vasprintf to "yes" or "no". Define HAVE_VASPRINTF to 1 if -# $hw_cv_func_vasprintf is set to "yes". Otherwise, define vasprintf to -# rpl_vasprintf and make sure the replacement function will be built. -# HW_FUNC_VASPRINTF - -# HW_FUNC_ASPRINTF -# ---------------- -# Set $hw_cv_func_asprintf to "yes" or "no". Define HAVE_ASPRINTF to 1 if -# $hw_cv_func_asprintf is set to "yes". Otherwise, define asprintf to -# rpl_asprintf and make sure the replacement function will be built. -# HW_FUNC_ASPRINTF - -# _HW_FUNC_XPRINTF_REPLACE -# ------------------------ -# Arrange for building snprintf.c. Must be called if one or more of the -# functions provided by snprintf.c are needed. -# _HW_FUNC_XPRINTF_REPLACE - - - - - - for ac_header in $ac_header_list -do : - as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default -" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - - - - - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for unsigned long long int" >&5 -$as_echo_n "checking for unsigned long long int... " >&6; } -if ${ac_cv_type_unsigned_long_long_int+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_type_unsigned_long_long_int=yes - if test "x${ac_cv_prog_cc_c99-no}" = xno; then - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - /* For now, do not test the preprocessor; as of 2007 there are too many - implementations with broken preprocessors. Perhaps this can - be revisited in 2012. In the meantime, code should not expect - #if to work with literals wider than 32 bits. */ - /* Test literals. */ - long long int ll = 9223372036854775807ll; - long long int nll = -9223372036854775807LL; - unsigned long long int ull = 18446744073709551615ULL; - /* Test constant expressions. */ - typedef int a[((-9223372036854775807LL < 0 && 0 < 9223372036854775807ll) - ? 1 : -1)]; - typedef int b[(18446744073709551615ULL <= (unsigned long long int) -1 - ? 1 : -1)]; - int i = 63; -int -main () -{ -/* Test availability of runtime routines for shift and division. */ - long long int llmax = 9223372036854775807ll; - unsigned long long int ullmax = 18446744073709551615ull; - return ((ll << 63) | (ll >> 63) | (ll < i) | (ll > i) - | (llmax / ll) | (llmax % ll) - | (ull << 63) | (ull >> 63) | (ull << i) | (ull >> i) - | (ullmax / ull) | (ullmax % ull)); - ; - return 0; -} - -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - -else - ac_cv_type_unsigned_long_long_int=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_unsigned_long_long_int" >&5 -$as_echo "$ac_cv_type_unsigned_long_long_int" >&6; } - if test $ac_cv_type_unsigned_long_long_int = yes; then - -$as_echo "#define HAVE_UNSIGNED_LONG_LONG_INT 1" >>confdefs.h - - fi - - - ac_fn_c_check_func "$LINENO" "vsnprintf" "ac_cv_func_vsnprintf" -if test "x$ac_cv_func_vsnprintf" = xyes; then : - hw_cv_func_vsnprintf=yes -else - hw_cv_func_vsnprintf=no -fi - - if test "$hw_cv_func_vsnprintf" = yes; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether vsnprintf is C99 compliant" >&5 -$as_echo_n "checking whether vsnprintf is C99 compliant... " >&6; } -if ${hw_cv_func_vsnprintf_c99+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "$cross_compiling" = yes; then : - hw_cv_func_vsnprintf_c99=no -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#if HAVE_STDARG_H - #include - #endif - #include - static int testprintf(char *buf, size_t size, const char *format, ...) - { - int result; - va_list ap; - va_start(ap, format); - result = vsnprintf(buf, size, format, ap); - va_end(ap); - return result; - } -int -main () -{ -char buf[43]; - if (testprintf(buf, 4, "The answer is %27.2g.", 42.0) != 42 || - testprintf(buf, 0, "No, it's %32zu.", (size_t)42) != 42 || - buf[0] != 'T' || buf[3] != '\0') - return 1; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - hw_cv_func_vsnprintf_c99=yes -else - hw_cv_func_vsnprintf_c99=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hw_cv_func_vsnprintf_c99" >&5 -$as_echo "$hw_cv_func_vsnprintf_c99" >&6; } -else - hw_cv_func_snprintf_c99=no -fi - if test "$hw_cv_func_vsnprintf_c99" = yes; then : - -$as_echo "#define HAVE_VSNPRINTF 1" >>confdefs.h - -else - for ac_header in inttypes.h locale.h stddef.h stdint.h -do : - as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - ac_fn_c_check_member "$LINENO" "struct lconv" "decimal_point" "ac_cv_member_struct_lconv_decimal_point" "#include -" -if test "x$ac_cv_member_struct_lconv_decimal_point" = xyes; then : - -cat >>confdefs.h <<_ACEOF -#define HAVE_STRUCT_LCONV_DECIMAL_POINT 1 -_ACEOF - - -fi -ac_fn_c_check_member "$LINENO" "struct lconv" "thousands_sep" "ac_cv_member_struct_lconv_thousands_sep" "#include -" -if test "x$ac_cv_member_struct_lconv_thousands_sep" = xyes; then : - -cat >>confdefs.h <<_ACEOF -#define HAVE_STRUCT_LCONV_THOUSANDS_SEP 1 -_ACEOF - - -fi - - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for long long int" >&5 -$as_echo_n "checking for long long int... " >&6; } -if ${ac_cv_type_long_long_int+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_type_long_long_int=yes - if test "x${ac_cv_prog_cc_c99-no}" = xno; then - ac_cv_type_long_long_int=$ac_cv_type_unsigned_long_long_int - if test $ac_cv_type_long_long_int = yes; then - if test "$cross_compiling" = yes; then : - : -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - #ifndef LLONG_MAX - # define HALF \ - (1LL << (sizeof (long long int) * CHAR_BIT - 2)) - # define LLONG_MAX (HALF - 1 + HALF) - #endif -int -main () -{ -long long int n = 1; - int i; - for (i = 0; ; i++) - { - long long int m = n << i; - if (m >> i != n) - return 1; - if (LLONG_MAX / 2 < m) - break; - } - return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - -else - ac_cv_type_long_long_int=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - - fi - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_long_long_int" >&5 -$as_echo "$ac_cv_type_long_long_int" >&6; } - if test $ac_cv_type_long_long_int = yes; then - -$as_echo "#define HAVE_LONG_LONG_INT 1" >>confdefs.h - - fi - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for unsigned long long int" >&5 -$as_echo_n "checking for unsigned long long int... " >&6; } -if ${ac_cv_type_unsigned_long_long_int+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_type_unsigned_long_long_int=yes - if test "x${ac_cv_prog_cc_c99-no}" = xno; then - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - /* For now, do not test the preprocessor; as of 2007 there are too many - implementations with broken preprocessors. Perhaps this can - be revisited in 2012. In the meantime, code should not expect - #if to work with literals wider than 32 bits. */ - /* Test literals. */ - long long int ll = 9223372036854775807ll; - long long int nll = -9223372036854775807LL; - unsigned long long int ull = 18446744073709551615ULL; - /* Test constant expressions. */ - typedef int a[((-9223372036854775807LL < 0 && 0 < 9223372036854775807ll) - ? 1 : -1)]; - typedef int b[(18446744073709551615ULL <= (unsigned long long int) -1 - ? 1 : -1)]; - int i = 63; -int -main () -{ -/* Test availability of runtime routines for shift and division. */ - long long int llmax = 9223372036854775807ll; - unsigned long long int ullmax = 18446744073709551615ull; - return ((ll << 63) | (ll >> 63) | (ll < i) | (ll > i) - | (llmax / ll) | (llmax % ll) - | (ull << 63) | (ull >> 63) | (ull << i) | (ull >> i) - | (ullmax / ull) | (ullmax % ull)); - ; - return 0; -} - -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - -else - ac_cv_type_unsigned_long_long_int=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_unsigned_long_long_int" >&5 -$as_echo "$ac_cv_type_unsigned_long_long_int" >&6; } - if test $ac_cv_type_unsigned_long_long_int = yes; then - -$as_echo "#define HAVE_UNSIGNED_LONG_LONG_INT 1" >>confdefs.h - - fi - - ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" -if test "x$ac_cv_type_size_t" = xyes; then : - -else - -cat >>confdefs.h <<_ACEOF -#define size_t unsigned int -_ACEOF - -fi - - - - ac_fn_c_check_type "$LINENO" "intmax_t" "ac_cv_type_intmax_t" "$ac_includes_default" -if test "x$ac_cv_type_intmax_t" = xyes; then : - -$as_echo "#define HAVE_INTMAX_T 1" >>confdefs.h - -else - test $ac_cv_type_long_long_int = yes \ - && ac_type='long long int' \ - || ac_type='long int' - -cat >>confdefs.h <<_ACEOF -#define intmax_t $ac_type -_ACEOF - -fi - - - - - ac_fn_c_check_type "$LINENO" "uintmax_t" "ac_cv_type_uintmax_t" "$ac_includes_default" -if test "x$ac_cv_type_uintmax_t" = xyes; then : - -$as_echo "#define HAVE_UINTMAX_T 1" >>confdefs.h - -else - test $ac_cv_type_unsigned_long_long_int = yes \ - && ac_type='unsigned long long int' \ - || ac_type='unsigned long int' - -cat >>confdefs.h <<_ACEOF -#define uintmax_t $ac_type -_ACEOF - -fi - - - - ac_fn_c_check_type "$LINENO" "uintptr_t" "ac_cv_type_uintptr_t" "$ac_includes_default" -if test "x$ac_cv_type_uintptr_t" = xyes; then : - -$as_echo "#define HAVE_UINTPTR_T 1" >>confdefs.h - -else - for ac_type in 'unsigned int' 'unsigned long int' \ - 'unsigned long long int'; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ -static int test_array [1 - 2 * !(sizeof (void *) <= sizeof ($ac_type))]; -test_array [0] = 0; -return test_array [0]; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - -cat >>confdefs.h <<_ACEOF -#define uintptr_t $ac_type -_ACEOF - - ac_type= -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - test -z "$ac_type" && break - done -fi - - - ac_fn_c_check_type "$LINENO" "ptrdiff_t" "ac_cv_type_ptrdiff_t" "$ac_includes_default" -if test "x$ac_cv_type_ptrdiff_t" = xyes; then : - -cat >>confdefs.h <<_ACEOF -#define HAVE_PTRDIFF_T 1 -_ACEOF - - -fi - - for ac_func in localeconv -do : - ac_fn_c_check_func "$LINENO" "localeconv" "ac_cv_func_localeconv" -if test "x$ac_cv_func_localeconv" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LOCALECONV 1 -_ACEOF - -fi -done - - - if test "x$_hw_cv_func_xprintf_replace_done" != xyes; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 -$as_echo_n "checking for an ANSI C-conforming const... " >&6; } -if ${ac_cv_c_const+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - -#ifndef __cplusplus - /* Ultrix mips cc rejects this sort of thing. */ - typedef int charset[2]; - const charset cs = { 0, 0 }; - /* SunOS 4.1.1 cc rejects this. */ - char const *const *pcpcc; - char **ppc; - /* NEC SVR4.0.2 mips cc rejects this. */ - struct point {int x, y;}; - static struct point const zero = {0,0}; - /* AIX XL C 1.02.0.0 rejects this. - It does not let you subtract one const X* pointer from another in - an arm of an if-expression whose if-part is not a constant - expression */ - const char *g = "string"; - pcpcc = &g + (g ? g-g : 0); - /* HPUX 7.0 cc rejects these. */ - ++pcpcc; - ppc = (char**) pcpcc; - pcpcc = (char const *const *) ppc; - { /* SCO 3.2v4 cc rejects this sort of thing. */ - char tx; - char *t = &tx; - char const *s = 0 ? (char *) 0 : (char const *) 0; - - *t++ = 0; - if (s) return 0; - } - { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ - int x[] = {25, 17}; - const int *foo = &x[0]; - ++foo; - } - { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ - typedef const int *iptr; - iptr p = 0; - ++p; - } - { /* AIX XL C 1.02.0.0 rejects this sort of thing, saying - "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ - struct s { int j; const int *ap[3]; } bx; - struct s *b = &bx; b->j = 5; - } - { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ - const int foo = 10; - if (!foo) return 0; - } - return !cs[0] && !zero.x; -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_c_const=yes -else - ac_cv_c_const=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 -$as_echo "$ac_cv_c_const" >&6; } -if test $ac_cv_c_const = no; then - -$as_echo "#define const /**/" >>confdefs.h - -fi - - - - - case " $LIBOBJS " in - *" snprintf.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS snprintf.$ac_objext" - ;; -esac - - _hw_cv_func_xprintf_replace_done=yes -fi - -fi - - - ac_fn_c_check_func "$LINENO" "snprintf" "ac_cv_func_snprintf" -if test "x$ac_cv_func_snprintf" = xyes; then : - hw_cv_func_snprintf=yes -else - hw_cv_func_snprintf=no -fi - - if test "$hw_cv_func_snprintf" = yes; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether snprintf is C99 compliant" >&5 -$as_echo_n "checking whether snprintf is C99 compliant... " >&6; } -if ${hw_cv_func_snprintf_c99+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "$cross_compiling" = yes; then : - hw_cv_func_snprintf_c99=no -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ -char buf[43]; - if (snprintf(buf, 4, "The answer is %27.2g.", 42.0) != 42 || - snprintf(buf, 0, "No, it's %32zu.", (size_t)42) != 42 || - buf[0] != 'T' || buf[3] != '\0') - return 1; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - hw_cv_func_snprintf_c99=yes -else - hw_cv_func_snprintf_c99=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hw_cv_func_snprintf_c99" >&5 -$as_echo "$hw_cv_func_snprintf_c99" >&6; } -else - hw_cv_func_snprintf_c99=no -fi - if test "$hw_cv_func_snprintf_c99" = yes; then : - -$as_echo "#define HAVE_SNPRINTF 1" >>confdefs.h - -else - - if test "x$_hw_cv_func_xprintf_replace_done" != xyes; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 -$as_echo_n "checking for an ANSI C-conforming const... " >&6; } -if ${ac_cv_c_const+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - -#ifndef __cplusplus - /* Ultrix mips cc rejects this sort of thing. */ - typedef int charset[2]; - const charset cs = { 0, 0 }; - /* SunOS 4.1.1 cc rejects this. */ - char const *const *pcpcc; - char **ppc; - /* NEC SVR4.0.2 mips cc rejects this. */ - struct point {int x, y;}; - static struct point const zero = {0,0}; - /* AIX XL C 1.02.0.0 rejects this. - It does not let you subtract one const X* pointer from another in - an arm of an if-expression whose if-part is not a constant - expression */ - const char *g = "string"; - pcpcc = &g + (g ? g-g : 0); - /* HPUX 7.0 cc rejects these. */ - ++pcpcc; - ppc = (char**) pcpcc; - pcpcc = (char const *const *) ppc; - { /* SCO 3.2v4 cc rejects this sort of thing. */ - char tx; - char *t = &tx; - char const *s = 0 ? (char *) 0 : (char const *) 0; - - *t++ = 0; - if (s) return 0; - } - { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ - int x[] = {25, 17}; - const int *foo = &x[0]; - ++foo; - } - { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ - typedef const int *iptr; - iptr p = 0; - ++p; - } - { /* AIX XL C 1.02.0.0 rejects this sort of thing, saying - "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ - struct s { int j; const int *ap[3]; } bx; - struct s *b = &bx; b->j = 5; - } - { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ - const int foo = 10; - if (!foo) return 0; - } - return !cs[0] && !zero.x; -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_c_const=yes -else - ac_cv_c_const=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 -$as_echo "$ac_cv_c_const" >&6; } -if test $ac_cv_c_const = no; then - -$as_echo "#define const /**/" >>confdefs.h - -fi - - - - - case " $LIBOBJS " in - *" snprintf.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS snprintf.$ac_objext" - ;; -esac - - _hw_cv_func_xprintf_replace_done=yes -fi - -fi - - - - - - - - for ac_func in vasprintf -do : - ac_fn_c_check_func "$LINENO" "vasprintf" "ac_cv_func_vasprintf" -if test "x$ac_cv_func_vasprintf" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_VASPRINTF 1 -_ACEOF - hw_cv_func_vasprintf=yes -else - hw_cv_func_vasprintf=no -fi -done - - if test "$hw_cv_func_vasprintf" = no; then : - for ac_header in stdlib.h -do : - ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default" -if test "x$ac_cv_header_stdlib_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_STDLIB_H 1 -_ACEOF - -fi - -done - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for va_copy" >&5 -$as_echo_n "checking for va_copy... " >&6; } -if ${hw_cv_func_va_copy+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "$cross_compiling" = yes; then : - hw_cv_func_va_copy=no -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#if HAVE_STDARG_H - #include - #elif HAVE_VARARGS_H - #include - #endif -int -main () -{ -va_list ap, aq; va_copy(aq, ap); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - hw_cv_func_va_copy=yes -else - hw_cv_func_va_copy=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hw_cv_func_va_copy" >&5 -$as_echo "$hw_cv_func_va_copy" >&6; } - if test "$hw_cv_func_va_copy" = yes; then : - -$as_echo "#define HAVE_VA_COPY 1" >>confdefs.h - -fi - - if test "$hw_cv_func_va_copy" = no; then : - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __va_copy" >&5 -$as_echo_n "checking for __va_copy... " >&6; } -if ${hw_cv_func___va_copy+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "$cross_compiling" = yes; then : - hw_cv_func___va_copy=no -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#if HAVE_STDARG_H - #include - #elif HAVE_VARARGS_H - #include - #endif -int -main () -{ -va_list ap, aq; __va_copy(aq, ap); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - hw_cv_func___va_copy=yes -else - hw_cv_func___va_copy=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hw_cv_func___va_copy" >&5 -$as_echo "$hw_cv_func___va_copy" >&6; } - if test "$hw_cv_func___va_copy" = yes; then : - -$as_echo "#define HAVE___VA_COPY 1" >>confdefs.h - -fi - -fi - - if test "x$_hw_cv_func_xprintf_replace_done" != xyes; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 -$as_echo_n "checking for an ANSI C-conforming const... " >&6; } -if ${ac_cv_c_const+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - -#ifndef __cplusplus - /* Ultrix mips cc rejects this sort of thing. */ - typedef int charset[2]; - const charset cs = { 0, 0 }; - /* SunOS 4.1.1 cc rejects this. */ - char const *const *pcpcc; - char **ppc; - /* NEC SVR4.0.2 mips cc rejects this. */ - struct point {int x, y;}; - static struct point const zero = {0,0}; - /* AIX XL C 1.02.0.0 rejects this. - It does not let you subtract one const X* pointer from another in - an arm of an if-expression whose if-part is not a constant - expression */ - const char *g = "string"; - pcpcc = &g + (g ? g-g : 0); - /* HPUX 7.0 cc rejects these. */ - ++pcpcc; - ppc = (char**) pcpcc; - pcpcc = (char const *const *) ppc; - { /* SCO 3.2v4 cc rejects this sort of thing. */ - char tx; - char *t = &tx; - char const *s = 0 ? (char *) 0 : (char const *) 0; - - *t++ = 0; - if (s) return 0; - } - { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ - int x[] = {25, 17}; - const int *foo = &x[0]; - ++foo; - } - { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ - typedef const int *iptr; - iptr p = 0; - ++p; - } - { /* AIX XL C 1.02.0.0 rejects this sort of thing, saying - "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ - struct s { int j; const int *ap[3]; } bx; - struct s *b = &bx; b->j = 5; - } - { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ - const int foo = 10; - if (!foo) return 0; - } - return !cs[0] && !zero.x; -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_c_const=yes -else - ac_cv_c_const=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 -$as_echo "$ac_cv_c_const" >&6; } -if test $ac_cv_c_const = no; then - -$as_echo "#define const /**/" >>confdefs.h - -fi - - - - - case " $LIBOBJS " in - *" snprintf.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS snprintf.$ac_objext" - ;; -esac - - _hw_cv_func_xprintf_replace_done=yes -fi - -fi - - - for ac_func in asprintf -do : - ac_fn_c_check_func "$LINENO" "asprintf" "ac_cv_func_asprintf" -if test "x$ac_cv_func_asprintf" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_ASPRINTF 1 -_ACEOF - hw_cv_func_asprintf=yes -else - hw_cv_func_asprintf=no -fi -done - - if test "$hw_cv_func_asprintf" = no; then : - - if test "x$_hw_cv_func_xprintf_replace_done" != xyes; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 -$as_echo_n "checking for an ANSI C-conforming const... " >&6; } -if ${ac_cv_c_const+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - -#ifndef __cplusplus - /* Ultrix mips cc rejects this sort of thing. */ - typedef int charset[2]; - const charset cs = { 0, 0 }; - /* SunOS 4.1.1 cc rejects this. */ - char const *const *pcpcc; - char **ppc; - /* NEC SVR4.0.2 mips cc rejects this. */ - struct point {int x, y;}; - static struct point const zero = {0,0}; - /* AIX XL C 1.02.0.0 rejects this. - It does not let you subtract one const X* pointer from another in - an arm of an if-expression whose if-part is not a constant - expression */ - const char *g = "string"; - pcpcc = &g + (g ? g-g : 0); - /* HPUX 7.0 cc rejects these. */ - ++pcpcc; - ppc = (char**) pcpcc; - pcpcc = (char const *const *) ppc; - { /* SCO 3.2v4 cc rejects this sort of thing. */ - char tx; - char *t = &tx; - char const *s = 0 ? (char *) 0 : (char const *) 0; - - *t++ = 0; - if (s) return 0; - } - { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ - int x[] = {25, 17}; - const int *foo = &x[0]; - ++foo; - } - { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ - typedef const int *iptr; - iptr p = 0; - ++p; - } - { /* AIX XL C 1.02.0.0 rejects this sort of thing, saying - "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ - struct s { int j; const int *ap[3]; } bx; - struct s *b = &bx; b->j = 5; - } - { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ - const int foo = 10; - if (!foo) return 0; - } - return !cs[0] && !zero.x; -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_c_const=yes -else - ac_cv_c_const=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 -$as_echo "$ac_cv_c_const" >&6; } -if test $ac_cv_c_const = no; then - -$as_echo "#define const /**/" >>confdefs.h - -fi - - - - - case " $LIBOBJS " in - *" snprintf.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS snprintf.$ac_objext" - ;; -esac - - _hw_cv_func_xprintf_replace_done=yes -fi - -fi - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing cos" >&5 -$as_echo_n "checking for library containing cos... " >&6; } -if ${ac_cv_search_cos+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_func_search_save_LIBS=$LIBS -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char cos (); -int -main () -{ -return cos (); - ; - return 0; -} -_ACEOF -for ac_lib in '' m; do - if test -z "$ac_lib"; then - ac_res="none required" - else - ac_res=-l$ac_lib - LIBS="-l$ac_lib $ac_func_search_save_LIBS" - fi - if ac_fn_c_try_link "$LINENO"; then : - ac_cv_search_cos=$ac_res -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext - if ${ac_cv_search_cos+:} false; then : - break -fi -done -if ${ac_cv_search_cos+:} false; then : - -else - ac_cv_search_cos=no -fi -rm conftest.$ac_ext -LIBS=$ac_func_search_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_cos" >&5 -$as_echo "$ac_cv_search_cos" >&6; } -ac_res=$ac_cv_search_cos -if test "$ac_res" != no; then : - test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" - -fi - - - - -# Check whether --with-bundled-zlib was given. -if test "${with_bundled_zlib+set}" = set; then : - withval=$with_bundled_zlib; -fi - -if test x${with_bundled_zlib} = x; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for zlib >= 1.2.3" >&5 -$as_echo_n "checking for zlib >= 1.2.3... " >&6; } -if ${ccache_cv_zlib_1_2_3+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ - - #if (ZLIB_VERNUM >= 0x1230) - #else - #error "ZLIB_VERNUM < 0x1230" - #endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ccache_cv_zlib_1_2_3=yes -else - ccache_cv_zlib_1_2_3=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ccache_cv_zlib_1_2_3" >&5 -$as_echo "$ccache_cv_zlib_1_2_3" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gzdopen in -lz" >&5 -$as_echo_n "checking for gzdopen in -lz... " >&6; } -if ${ac_cv_lib_z_gzdopen+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lz $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char gzdopen (); -int -main () -{ -return gzdopen (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_z_gzdopen=yes -else - ac_cv_lib_z_gzdopen=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_gzdopen" >&5 -$as_echo "$ac_cv_lib_z_gzdopen" >&6; } -if test "x$ac_cv_lib_z_gzdopen" = xyes; then : - true -fi - - if test $ccache_cv_zlib_1_2_3 = yes && test $ac_cv_lib_z_gzdopen = yes; then - use_bundled_zlib=no - else - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using bundled zlib" >&5 -$as_echo "$as_me: WARNING: using bundled zlib" >&2;} - use_bundled_zlib=yes - fi -else - if test x${with_bundled_zlib} = xno; then - { $as_echo "$as_me:${as_lineno-$LINENO}: using system zlib as requested" >&5 -$as_echo "$as_me: using system zlib as requested" >&6;} - use_bundled_zlib=no - else - { $as_echo "$as_me:${as_lineno-$LINENO}: using bundled zlib as requested" >&5 -$as_echo "$as_me: using bundled zlib as requested" >&6;} - use_bundled_zlib=yes - fi -fi - -if test x${use_bundled_zlib} = xyes; then - CPPFLAGS="$CPPFLAGS -I\$(srcdir)/src/zlib" - extra_libs="src/zlib/libz.a" - mkdir -p src/zlib -else - LIBS="$LIBS -lz" -fi - -# Check whether --enable-man was given. -if test "${enable_man+set}" = set; then : - enableval=$enable_man; -fi - -if test x${enable_man} = xno; then - disable_man='#' -fi - -# Check whether --enable-tracing was given. -if test "${enable_tracing+set}" = set; then : - enableval=$enable_tracing; -fi - -if test x${enable_tracing} = xyes; then - CPPFLAGS="$CPPFLAGS -DMTR_ENABLED" - extra_sources="src/minitrace.c" -fi - -if test x${windows_os} = xyes; then - LIBS="$LIBS -lws2_32" -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 -$as_echo_n "checking whether byte ordering is bigendian... " >&6; } -if ${ac_cv_c_bigendian+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_c_bigendian=unknown - # See if we're dealing with a universal compiler. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifndef __APPLE_CC__ - not a universal capable compiler - #endif - typedef int dummy; - -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - - # Check for potential -arch flags. It is not universal unless - # there are at least two -arch flags with different values. - ac_arch= - ac_prev= - for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do - if test -n "$ac_prev"; then - case $ac_word in - i?86 | x86_64 | ppc | ppc64) - if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then - ac_arch=$ac_word - else - ac_cv_c_bigendian=universal - break - fi - ;; - esac - ac_prev= - elif test "x$ac_word" = "x-arch"; then - ac_prev=arch - fi - done -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - if test $ac_cv_c_bigendian = unknown; then - # See if sys/param.h defines the BYTE_ORDER macro. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - #include - -int -main () -{ -#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ - && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ - && LITTLE_ENDIAN) - bogus endian macros - #endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - # It does; now see whether it defined to BIG_ENDIAN or not. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - #include - -int -main () -{ -#if BYTE_ORDER != BIG_ENDIAN - not big endian - #endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_c_bigendian=yes -else - ac_cv_c_bigendian=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - fi - if test $ac_cv_c_bigendian = unknown; then - # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -int -main () -{ -#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) - bogus endian macros - #endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - # It does; now see whether it defined to _BIG_ENDIAN or not. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -int -main () -{ -#ifndef _BIG_ENDIAN - not big endian - #endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_c_bigendian=yes -else - ac_cv_c_bigendian=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - fi - if test $ac_cv_c_bigendian = unknown; then - # Compile a test program. - if test "$cross_compiling" = yes; then : - # Try to guess by grepping values from an object file. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -short int ascii_mm[] = - { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; - short int ascii_ii[] = - { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; - int use_ascii (int i) { - return ascii_mm[i] + ascii_ii[i]; - } - short int ebcdic_ii[] = - { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; - short int ebcdic_mm[] = - { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; - int use_ebcdic (int i) { - return ebcdic_mm[i] + ebcdic_ii[i]; - } - extern int foo; - -int -main () -{ -return use_ascii (foo) == use_ebcdic (foo); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then - ac_cv_c_bigendian=yes - fi - if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then - if test "$ac_cv_c_bigendian" = unknown; then - ac_cv_c_bigendian=no - else - # finding both strings is unlikely to happen, but who knows? - ac_cv_c_bigendian=unknown - fi - fi -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ - - /* Are we little or big endian? From Harbison&Steele. */ - union - { - long int l; - char c[sizeof (long int)]; - } u; - u.l = 1; - return u.c[sizeof (long int) - 1] == 1; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - ac_cv_c_bigendian=no -else - ac_cv_c_bigendian=yes -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 -$as_echo "$ac_cv_c_bigendian" >&6; } - case $ac_cv_c_bigendian in #( - yes) - $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h -;; #( - no) - ;; #( - universal) - -$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h - - ;; #( - *) - as_fn_error $? "unknown endianness - presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; - esac - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 -$as_echo_n "checking for inline... " >&6; } -if ${ac_cv_c_inline+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_c_inline=no -for ac_kw in inline __inline__ __inline; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifndef __cplusplus -typedef int foo_t; -static $ac_kw foo_t static_foo () {return 0; } -$ac_kw foo_t foo () {return 0; } -#endif - -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_c_inline=$ac_kw -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - test "$ac_cv_c_inline" != no && break -done - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 -$as_echo "$ac_cv_c_inline" >&6; } - -case $ac_cv_c_inline in - inline | yes) ;; - *) - case $ac_cv_c_inline in - no) ac_val=;; - *) ac_val=$ac_cv_c_inline;; - esac - cat >>confdefs.h <<_ACEOF -#ifndef __cplusplus -#define inline $ac_val -#endif -_ACEOF - ;; -esac - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for extern inline" >&5 -$as_echo_n "checking for extern inline... " >&6; } -if ${ac_cv_c_extern_inline+:} false; then : - $as_echo_n "(cached) " >&6 -else - - ac_cv_c_extern_inline=no - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - extern $ac_cv_c_inline double foo(double x); - extern $ac_cv_c_inline double foo(double x) { return x+1.0; }; - double foo (double x) { return x + 1.0; }; - -int -main () -{ -foo(1.0) - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_c_extern_inline="yes" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_extern_inline" >&5 -$as_echo "$ac_cv_c_extern_inline" >&6; } -if test "$ac_cv_c_extern_inline" != no ; then - -$as_echo "#define HAVE_EXTERN_INLINE 1" >>confdefs.h - -fi - -mkdir -p .deps src unittest - -if test ! -f $srcdir/dev_mode_disabled; then - { $as_echo "$as_me:${as_lineno-$LINENO}: developer mode enabled" >&5 -$as_echo "$as_me: developer mode enabled" >&6;} - ac_config_files="$ac_config_files dev.mk" - - include_dev_mk='include dev.mk' - version=`(git --git-dir=$srcdir/.git describe --dirty 2>/dev/null || echo vunknown) | sed -e 's/v//' -e 's/-/+/' -e 's/-/_/g'` - mkdir -p src - echo "extern const char CCACHE_VERSION[]; const char CCACHE_VERSION[] = \"$version\";" >src/version.c - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gperf", so it can be a program name with args. -set dummy ${ac_tool_prefix}gperf; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_GPERF+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$GPERF"; then - ac_cv_prog_GPERF="$GPERF" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_GPERF="${ac_tool_prefix}gperf" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -GPERF=$ac_cv_prog_GPERF -if test -n "$GPERF"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GPERF" >&5 -$as_echo "$GPERF" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_GPERF"; then - ac_ct_GPERF=$GPERF - # Extract the first word of "gperf", so it can be a program name with args. -set dummy gperf; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_GPERF+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_GPERF"; then - ac_cv_prog_ac_ct_GPERF="$ac_ct_GPERF" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_GPERF="gperf" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_GPERF=$ac_cv_prog_ac_ct_GPERF -if test -n "$ac_ct_GPERF"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_GPERF" >&5 -$as_echo "$ac_ct_GPERF" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_GPERF" = x; then - GPERF="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - GPERF=$ac_ct_GPERF - fi -else - GPERF="$ac_cv_prog_GPERF" -fi - - if test -z "$GPERF"; then - as_fn_error $? "please install gperf" "$LINENO" 5 - fi -else - { $as_echo "$as_me:${as_lineno-$LINENO}: developer mode disabled" >&5 -$as_echo "$as_me: developer mode disabled" >&6;} -fi - -if test ! -f $srcdir/src/version.c -a ! -f src/version.c ; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unable to determine ccache version" >&5 -$as_echo "$as_me: WARNING: unable to determine ccache version" >&2;} - echo "extern const char CCACHE_VERSION[]; const char CCACHE_VERSION[] = \"unknown\";" >src/version.c -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler supports -Wno-implicit-fallthrough" >&5 -$as_echo_n "checking whether C compiler supports -Wno-implicit-fallthrough... " >&6; } -saved_cflags=$CFLAGS -CFLAGS=-Wno-implicit-fallthrough -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - no_implicit_fallthrough_warning="-Wno-implicit-fallthrough" -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -CFLAGS=$saved_cflags - -test_suites=`cd $srcdir && ls unittest/test_*.c | egrep -v 'BASE|BACKUP|LOCAL|REMOTE' | xargs echo` - -ac_config_files="$ac_config_files Makefile" - -cat >confcache <<\_ACEOF -# This file is a shell script that caches the results of configure -# tests run on this system so they can be shared between configure -# scripts and configure runs, see configure's option --config-cache. -# It is not useful on other systems. If it contains results you don't -# want to keep, you may remove or edit it. -# -# config.status only pays attention to the cache file if you give it -# the --recheck option to rerun configure. -# -# `ac_cv_env_foo' variables (set or unset) will be overridden when -# loading this file, other *unset* `ac_cv_foo' will be assigned the -# following values. - -_ACEOF - -# The following way of writing the cache mishandles newlines in values, -# but we know of no workaround that is simple, portable, and efficient. -# So, we kill variables containing newlines. -# Ultrix sh set writes to stderr and can't be redirected directly, -# and sets the high bit in the cache file unless we assign to the vars. -( - for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( - *) { eval $ac_var=; unset $ac_var;} ;; - esac ;; - esac - done - - (set) 2>&1 | - case $as_nl`(ac_space=' '; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - # `set' does not quote correctly, so add quotes: double-quote - # substitution turns \\\\ into \\, and sed turns \\ into \. - sed -n \ - "s/'/'\\\\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" - ;; #( - *) - # `set' quotes correctly as required by POSIX, so do not add quotes. - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) | - sed ' - /^ac_cv_env_/b end - t clear - :clear - s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ - t end - s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ - :end' >>confcache -if diff "$cache_file" confcache >/dev/null 2>&1; then :; else - if test -w "$cache_file"; then - if test "x$cache_file" != "x/dev/null"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 -$as_echo "$as_me: updating cache $cache_file" >&6;} - if test ! -f "$cache_file" || test -h "$cache_file"; then - cat confcache >"$cache_file" - else - case $cache_file in #( - */* | ?:*) - mv -f confcache "$cache_file"$$ && - mv -f "$cache_file"$$ "$cache_file" ;; #( - *) - mv -f confcache "$cache_file" ;; - esac - fi - fi - else - { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 -$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} - fi -fi -rm -f confcache - -test "x$prefix" = xNONE && prefix=$ac_default_prefix -# Let make expand exec_prefix. -test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' - -DEFS=-DHAVE_CONFIG_H - -ac_libobjs= -ac_ltlibobjs= -U= -for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue - # 1. Remove the extension, and $U if already installed. - ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' - ac_i=`$as_echo "$ac_i" | sed "$ac_script"` - # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR - # will be set to the directory where LIBOBJS objects are built. - as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" - as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' -done -LIBOBJS=$ac_libobjs - -LTLIBOBJS=$ac_ltlibobjs - - - - -: "${CONFIG_STATUS=./config.status}" -ac_write_fail=0 -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files $CONFIG_STATUS" -{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 -$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} -as_write_fail=0 -cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 -#! $SHELL -# Generated by $as_me. -# Run this file to recreate the current configuration. -# Compiler output produced by configure, useful for debugging -# configure, is in config.log if it exists. - -debug=false -ac_cs_recheck=false -ac_cs_silent=false - -SHELL=\${CONFIG_SHELL-$SHELL} -export SHELL -_ASEOF -cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 -## -------------------- ## -## M4sh Initialization. ## -## -------------------- ## - -# Be more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi - - -as_nl=' -' -export as_nl -# Printing a long string crashes Solaris 7 /usr/bin/printf. -as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo -# Prefer a ksh shell builtin over an external printf program on Solaris, -# but without wasting forks for bash or zsh. -if test -z "$BASH_VERSION$ZSH_VERSION" \ - && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='print -r --' - as_echo_n='print -rn --' -elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='printf %s\n' - as_echo_n='printf %s' -else - if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then - as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' - as_echo_n='/usr/ucb/echo -n' - else - as_echo_body='eval expr "X$1" : "X\\(.*\\)"' - as_echo_n_body='eval - arg=$1; - case $arg in #( - *"$as_nl"*) - expr "X$arg" : "X\\(.*\\)$as_nl"; - arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; - esac; - expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" - ' - export as_echo_n_body - as_echo_n='sh -c $as_echo_n_body as_echo' - fi - export as_echo_body - as_echo='sh -c $as_echo_body as_echo' -fi - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - PATH_SEPARATOR=: - (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { - (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || - PATH_SEPARATOR=';' - } -fi - - -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -IFS=" "" $as_nl" - -# Find who we are. Look in the path if we contain no directory separator. -as_myself= -case $0 in #(( - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break - done -IFS=$as_save_IFS - - ;; -esac -# We did not find ourselves, most probably we were run as `sh COMMAND' -# in which case we are not to be found in the path. -if test "x$as_myself" = x; then - as_myself=$0 -fi -if test ! -f "$as_myself"; then - $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - exit 1 -fi - -# Unset variables that we do not need and which cause bugs (e.g. in -# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" -# suppresses any "Segmentation fault" message there. '((' could -# trigger a bug in pdksh 5.2.14. -for as_var in BASH_ENV ENV MAIL MAILPATH -do eval test x\${$as_var+set} = xset \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - - -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- -# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are -# provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. -as_fn_error () -{ - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 - fi - $as_echo "$as_me: error: $2" >&2 - as_fn_exit $as_status -} # as_fn_error - - -# as_fn_set_status STATUS -# ----------------------- -# Set $? to STATUS, without forking. -as_fn_set_status () -{ - return $1 -} # as_fn_set_status - -# as_fn_exit STATUS -# ----------------- -# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. -as_fn_exit () -{ - set +e - as_fn_set_status $1 - exit $1 -} # as_fn_exit - -# as_fn_unset VAR -# --------------- -# Portably unset VAR. -as_fn_unset () -{ - { eval $1=; unset $1;} -} -as_unset=as_fn_unset -# as_fn_append VAR VALUE -# ---------------------- -# Append the text in VALUE to the end of the definition contained in VAR. Take -# advantage of any shell optimizations that allow amortized linear growth over -# repeated appends, instead of the typical quadratic growth present in naive -# implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : - eval 'as_fn_append () - { - eval $1+=\$2 - }' -else - as_fn_append () - { - eval $1=\$$1\$2 - } -fi # as_fn_append - -# as_fn_arith ARG... -# ------------------ -# Perform arithmetic evaluation on the ARGs, and store the result in the -# global $as_val. Take advantage of shells that can avoid forks. The arguments -# must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : - eval 'as_fn_arith () - { - as_val=$(( $* )) - }' -else - as_fn_arith () - { - as_val=`expr "$@" || test $? -eq 1` - } -fi # as_fn_arith - - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi - -if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in #((((( --n*) - case `echo 'xy\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - xy) ECHO_C='\c';; - *) echo `echo ksh88 bug on AIX 6.1` > /dev/null - ECHO_T=' ';; - esac;; -*) - ECHO_N='-n';; -esac - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir 2>/dev/null -fi -if (echo >conf$$.file) 2>/dev/null; then - if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' - elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln - else - as_ln_s='cp -pR' - fi -else - as_ln_s='cp -pR' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - - -# as_fn_mkdir_p -# ------------- -# Create "$as_dir" as a directory, including parents if necessary. -as_fn_mkdir_p () -{ - - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || eval $as_mkdir_p || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" - - -} # as_fn_mkdir_p -if mkdir -p . 2>/dev/null; then - as_mkdir_p='mkdir -p "$as_dir"' -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -as_test_x='test -x' -as_executable_p=as_fn_executable_p - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -exec 6>&1 -## ----------------------------------- ## -## Main body of $CONFIG_STATUS script. ## -## ----------------------------------- ## -_ASEOF -test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# Save the log message, to keep $0 and so on meaningful, and to -# report actual input values of CONFIG_FILES etc. instead of their -# values after options handling. -ac_log=" -This file was extended by $as_me, which was -generated by GNU Autoconf 2.69. Invocation command line was - - CONFIG_FILES = $CONFIG_FILES - CONFIG_HEADERS = $CONFIG_HEADERS - CONFIG_LINKS = $CONFIG_LINKS - CONFIG_COMMANDS = $CONFIG_COMMANDS - $ $0 $@ - -on `(hostname || uname -n) 2>/dev/null | sed 1q` -" - -_ACEOF - -case $ac_config_files in *" -"*) set x $ac_config_files; shift; ac_config_files=$*;; -esac - -case $ac_config_headers in *" -"*) set x $ac_config_headers; shift; ac_config_headers=$*;; -esac - - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -# Files that config.status was made for. -config_files="$ac_config_files" -config_headers="$ac_config_headers" - -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -ac_cs_usage="\ -\`$as_me' instantiates files and other configuration actions -from templates according to the current configuration. Unless the files -and actions are specified as TAGs, all are instantiated by default. - -Usage: $0 [OPTION]... [TAG]... - - -h, --help print this help, then exit - -V, --version print version number and configuration settings, then exit - --config print configuration, then exit - -q, --quiet, --silent - do not print progress messages - -d, --debug don't remove temporary files - --recheck update $as_me by reconfiguring in the same conditions - --file=FILE[:TEMPLATE] - instantiate the configuration file FILE - --header=FILE[:TEMPLATE] - instantiate the configuration header FILE - -Configuration files: -$config_files - -Configuration headers: -$config_headers - -Report bugs to the package provider." - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" -ac_cs_version="\\ -config.status -configured by $0, generated by GNU Autoconf 2.69, - with options \\"\$ac_cs_config\\" - -Copyright (C) 2012 Free Software Foundation, Inc. -This config.status script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it." - -ac_pwd='$ac_pwd' -srcdir='$srcdir' -INSTALL='$INSTALL' -test -n "\$AWK" || AWK=awk -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# The default lists apply if the user does not specify any file. -ac_need_defaults=: -while test $# != 0 -do - case $1 in - --*=?*) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` - ac_shift=: - ;; - --*=) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg= - ac_shift=: - ;; - *) - ac_option=$1 - ac_optarg=$2 - ac_shift=shift - ;; - esac - - case $ac_option in - # Handling of the options. - -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) - ac_cs_recheck=: ;; - --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) - $as_echo "$ac_cs_version"; exit ;; - --config | --confi | --conf | --con | --co | --c ) - $as_echo "$ac_cs_config"; exit ;; - --debug | --debu | --deb | --de | --d | -d ) - debug=: ;; - --file | --fil | --fi | --f ) - $ac_shift - case $ac_optarg in - *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; - '') as_fn_error $? "missing file argument" ;; - esac - as_fn_append CONFIG_FILES " '$ac_optarg'" - ac_need_defaults=false;; - --header | --heade | --head | --hea ) - $ac_shift - case $ac_optarg in - *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - as_fn_append CONFIG_HEADERS " '$ac_optarg'" - ac_need_defaults=false;; - --he | --h) - # Conflict between --help and --header - as_fn_error $? "ambiguous option: \`$1' -Try \`$0 --help' for more information.";; - --help | --hel | -h ) - $as_echo "$ac_cs_usage"; exit ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil | --si | --s) - ac_cs_silent=: ;; - - # This is an error. - -*) as_fn_error $? "unrecognized option: \`$1' -Try \`$0 --help' for more information." ;; - - *) as_fn_append ac_config_targets " $1" - ac_need_defaults=false ;; - - esac - shift -done - -ac_configure_extra_args= - -if $ac_cs_silent; then - exec 6>/dev/null - ac_configure_extra_args="$ac_configure_extra_args --silent" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -if \$ac_cs_recheck; then - set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion - shift - \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 - CONFIG_SHELL='$SHELL' - export CONFIG_SHELL - exec "\$@" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -exec 5>>config.log -{ - echo - sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX -## Running $as_me. ## -_ASBOX - $as_echo "$ac_log" -} >&5 - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - -# Handling of arguments. -for ac_config_target in $ac_config_targets -do - case $ac_config_target in - "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; - "dev.mk") CONFIG_FILES="$CONFIG_FILES dev.mk" ;; - "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; - - *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; - esac -done - - -# If the user did not use the arguments to specify the items to instantiate, -# then the envvar interface is used. Set only those that are not. -# We use the long form for the default assignment because of an extremely -# bizarre bug on SunOS 4.1.3. -if $ac_need_defaults; then - test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files - test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers -fi - -# Have a temporary directory for convenience. Make it in the build tree -# simply because there is no reason against having it here, and in addition, -# creating and moving files from /tmp can sometimes cause problems. -# Hook for its removal unless debugging. -# Note that there is a small window in which the directory will not be cleaned: -# after its creation but before its name has been assigned to `$tmp'. -$debug || -{ - tmp= ac_tmp= - trap 'exit_status=$? - : "${ac_tmp:=$tmp}" - { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status -' 0 - trap 'as_fn_exit 1' 1 2 13 15 -} -# Create a (secure) tmp directory for tmp files. - -{ - tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && - test -d "$tmp" -} || -{ - tmp=./conf$$-$RANDOM - (umask 077 && mkdir "$tmp") -} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 -ac_tmp=$tmp - -# Set up the scripts for CONFIG_FILES section. -# No need to generate them if there are no CONFIG_FILES. -# This happens for instance with `./config.status config.h'. -if test -n "$CONFIG_FILES"; then - - -ac_cr=`echo X | tr X '\015'` -# On cygwin, bash can eat \r inside `` if the user requested igncr. -# But we know of no other shell where ac_cr would be empty at this -# point, so we can use a bashism as a fallback. -if test "x$ac_cr" = x; then - eval ac_cr=\$\'\\r\' -fi -ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` -if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then - ac_cs_awk_cr='\\r' -else - ac_cs_awk_cr=$ac_cr -fi - -echo 'BEGIN {' >"$ac_tmp/subs1.awk" && -_ACEOF - - -{ - echo "cat >conf$$subs.awk <<_ACEOF" && - echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && - echo "_ACEOF" -} >conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 -ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` -ac_delim='%!_!# ' -for ac_last_try in false false false false false :; do - . ./conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - - ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` - if test $ac_delim_n = $ac_delim_num; then - break - elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - else - ac_delim="$ac_delim!$ac_delim _$ac_delim!! " - fi -done -rm -f conf$$subs.sh - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && -_ACEOF -sed -n ' -h -s/^/S["/; s/!.*/"]=/ -p -g -s/^[^!]*!// -:repl -t repl -s/'"$ac_delim"'$// -t delim -:nl -h -s/\(.\{148\}\)..*/\1/ -t more1 -s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ -p -n -b repl -:more1 -s/["\\]/\\&/g; s/^/"/; s/$/"\\/ -p -g -s/.\{148\}// -t nl -:delim -h -s/\(.\{148\}\)..*/\1/ -t more2 -s/["\\]/\\&/g; s/^/"/; s/$/"/ -p -b -:more2 -s/["\\]/\\&/g; s/^/"/; s/$/"\\/ -p -g -s/.\{148\}// -t delim -' >$CONFIG_STATUS || ac_write_fail=1 -rm -f conf$$subs.awk -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -_ACAWK -cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && - for (key in S) S_is_set[key] = 1 - FS = "" - -} -{ - line = $ 0 - nfields = split(line, field, "@") - substed = 0 - len = length(field[1]) - for (i = 2; i < nfields; i++) { - key = field[i] - keylen = length(key) - if (S_is_set[key]) { - value = S[key] - line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) - len += length(value) + length(field[++i]) - substed = 1 - } else - len += 1 + keylen - } - - print line -} - -_ACAWK -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then - sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" -else - cat -fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ - || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 -_ACEOF - -# VPATH may cause trouble with some makes, so we remove sole $(srcdir), -# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and -# trailing colons and then remove the whole line if VPATH becomes empty -# (actually we leave an empty line to preserve line numbers). -if test "x$srcdir" = x.; then - ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ -h -s/// -s/^/:/ -s/[ ]*$/:/ -s/:\$(srcdir):/:/g -s/:\${srcdir}:/:/g -s/:@srcdir@:/:/g -s/^:*// -s/:*$// -x -s/\(=[ ]*\).*/\1/ -G -s/\n// -s/^[^=]*=[ ]*$// -}' -fi - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -fi # test -n "$CONFIG_FILES" - -# Set up the scripts for CONFIG_HEADERS section. -# No need to generate them if there are no CONFIG_HEADERS. -# This happens for instance with `./config.status Makefile'. -if test -n "$CONFIG_HEADERS"; then -cat >"$ac_tmp/defines.awk" <<\_ACAWK || -BEGIN { -_ACEOF - -# Transform confdefs.h into an awk script `defines.awk', embedded as -# here-document in config.status, that substitutes the proper values into -# config.h.in to produce config.h. - -# Create a delimiter string that does not exist in confdefs.h, to ease -# handling of long lines. -ac_delim='%!_!# ' -for ac_last_try in false false :; do - ac_tt=`sed -n "/$ac_delim/p" confdefs.h` - if test -z "$ac_tt"; then - break - elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 - else - ac_delim="$ac_delim!$ac_delim _$ac_delim!! " - fi -done - -# For the awk script, D is an array of macro values keyed by name, -# likewise P contains macro parameters if any. Preserve backslash -# newline sequences. - -ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* -sed -n ' -s/.\{148\}/&'"$ac_delim"'/g -t rset -:rset -s/^[ ]*#[ ]*define[ ][ ]*/ / -t def -d -:def -s/\\$// -t bsnl -s/["\\]/\\&/g -s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ -D["\1"]=" \3"/p -s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p -d -:bsnl -s/["\\]/\\&/g -s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ -D["\1"]=" \3\\\\\\n"\\/p -t cont -s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p -t cont -d -:cont -n -s/.\{148\}/&'"$ac_delim"'/g -t clear -:clear -s/\\$// -t bsnlc -s/["\\]/\\&/g; s/^/"/; s/$/"/p -d -:bsnlc -s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p -b cont -' >$CONFIG_STATUS || ac_write_fail=1 - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 - for (key in D) D_is_set[key] = 1 - FS = "" -} -/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { - line = \$ 0 - split(line, arg, " ") - if (arg[1] == "#") { - defundef = arg[2] - mac1 = arg[3] - } else { - defundef = substr(arg[1], 2) - mac1 = arg[2] - } - split(mac1, mac2, "(") #) - macro = mac2[1] - prefix = substr(line, 1, index(line, defundef) - 1) - if (D_is_set[macro]) { - # Preserve the white space surrounding the "#". - print prefix "define", macro P[macro] D[macro] - next - } else { - # Replace #undef with comments. This is necessary, for example, - # in the case of _POSIX_SOURCE, which is predefined and required - # on some systems where configure will not decide to define it. - if (defundef == "undef") { - print "/*", prefix defundef, macro, "*/" - next - } - } -} -{ print } -_ACAWK -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 -fi # test -n "$CONFIG_HEADERS" - - -eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " -shift -for ac_tag -do - case $ac_tag in - :[FHLC]) ac_mode=$ac_tag; continue;; - esac - case $ac_mode$ac_tag in - :[FHL]*:*);; - :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; - :[FH]-) ac_tag=-:-;; - :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; - esac - ac_save_IFS=$IFS - IFS=: - set x $ac_tag - IFS=$ac_save_IFS - shift - ac_file=$1 - shift - - case $ac_mode in - :L) ac_source=$1;; - :[FH]) - ac_file_inputs= - for ac_f - do - case $ac_f in - -) ac_f="$ac_tmp/stdin";; - *) # Look for the file first in the build tree, then in the source tree - # (if the path is not absolute). The absolute path cannot be DOS-style, - # because $ac_f cannot contain `:'. - test -f "$ac_f" || - case $ac_f in - [\\/$]*) false;; - *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; - esac || - as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; - esac - case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac - as_fn_append ac_file_inputs " '$ac_f'" - done - - # Let's still pretend it is `configure' which instantiates (i.e., don't - # use $as_me), people would be surprised to read: - # /* config.h. Generated by config.status. */ - configure_input='Generated from '` - $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' - `' by configure.' - if test x"$ac_file" != x-; then - configure_input="$ac_file. $configure_input" - { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 -$as_echo "$as_me: creating $ac_file" >&6;} - fi - # Neutralize special characters interpreted by sed in replacement strings. - case $configure_input in #( - *\&* | *\|* | *\\* ) - ac_sed_conf_input=`$as_echo "$configure_input" | - sed 's/[\\\\&|]/\\\\&/g'`;; #( - *) ac_sed_conf_input=$configure_input;; - esac - - case $ac_tag in - *:-:* | *:-) cat >"$ac_tmp/stdin" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; - esac - ;; - esac - - ac_dir=`$as_dirname -- "$ac_file" || -$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$ac_file" : 'X\(//\)[^/]' \| \ - X"$ac_file" : 'X\(//\)$' \| \ - X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$ac_file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - as_dir="$ac_dir"; as_fn_mkdir_p - ac_builddir=. - -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix - -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix - - - case $ac_mode in - :F) - # - # CONFIG_FILE - # - - case $INSTALL in - [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; - *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; - esac -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# If the template does not know about datarootdir, expand it. -# FIXME: This hack should be removed a few years after 2.60. -ac_datarootdir_hack=; ac_datarootdir_seen= -ac_sed_dataroot=' -/datarootdir/ { - p - q -} -/@datadir@/p -/@docdir@/p -/@infodir@/p -/@localedir@/p -/@mandir@/p' -case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in -*datarootdir*) ac_datarootdir_seen=yes;; -*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 -$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 - ac_datarootdir_hack=' - s&@datadir@&$datadir&g - s&@docdir@&$docdir&g - s&@infodir@&$infodir&g - s&@localedir@&$localedir&g - s&@mandir@&$mandir&g - s&\\\${datarootdir}&$datarootdir&g' ;; -esac -_ACEOF - -# Neutralize VPATH when `$srcdir' = `.'. -# Shell code in configure.ac might set extrasub. -# FIXME: do we really want to maintain this feature? -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_sed_extra="$ac_vpsub -$extrasub -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -:t -/@[a-zA-Z_][a-zA-Z_0-9]*@/!b -s|@configure_input@|$ac_sed_conf_input|;t t -s&@top_builddir@&$ac_top_builddir_sub&;t t -s&@top_build_prefix@&$ac_top_build_prefix&;t t -s&@srcdir@&$ac_srcdir&;t t -s&@abs_srcdir@&$ac_abs_srcdir&;t t -s&@top_srcdir@&$ac_top_srcdir&;t t -s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t -s&@builddir@&$ac_builddir&;t t -s&@abs_builddir@&$ac_abs_builddir&;t t -s&@abs_top_builddir@&$ac_abs_top_builddir&;t t -s&@INSTALL@&$ac_INSTALL&;t t -$ac_datarootdir_hack -" -eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ - >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - -test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && - { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && - { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ - "$ac_tmp/out"`; test -z "$ac_out"; } && - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&5 -$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&2;} - - rm -f "$ac_tmp/stdin" - case $ac_file in - -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; - *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; - esac \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - ;; - :H) - # - # CONFIG_HEADER - # - if test x"$ac_file" != x-; then - { - $as_echo "/* $configure_input */" \ - && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" - } >"$ac_tmp/config.h" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then - { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 -$as_echo "$as_me: $ac_file is unchanged" >&6;} - else - rm -f "$ac_file" - mv "$ac_tmp/config.h" "$ac_file" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - fi - else - $as_echo "/* $configure_input */" \ - && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ - || as_fn_error $? "could not create -" "$LINENO" 5 - fi - ;; - - - esac - -done # for ac_tag - - -as_fn_exit 0 -_ACEOF -ac_clean_files=$ac_clean_files_save - -test $ac_write_fail = 0 || - as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 - - -# configure is writing to config.log, and then calls config.status. -# config.status does its own redirection, appending to config.log. -# Unfortunately, on DOS this fails, as config.log is still kept open -# by configure, so config.status won't be able to write to it; its -# output is simply discarded. So we exec the FD to /dev/null, -# effectively closing config.log, so it can be properly (re)opened and -# appended to by config.status. When coming back to configure, we -# need to make the FD available again. -if test "$no_create" != yes; then - ac_cs_success=: - ac_config_status_args= - test "$silent" = yes && - ac_config_status_args="$ac_config_status_args --quiet" - exec 5>/dev/null - $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false - exec 5>>config.log - # Use ||, not &&, to avoid exiting from the if with $? = 1, which - # would make configure fail if this is the last instruction. - $ac_cs_success || as_fn_exit 1 -fi -if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 -$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} -fi - - -cat <config.h.tmp -#ifndef CCACHE_CONFIG_H -#define CCACHE_CONFIG_H -#ifdef __clang__ -#pragma clang diagnostic push -#if __has_warning("-Wreserved-id-macro") -#pragma clang diagnostic ignored "-Wreserved-id-macro" -#endif -#endif - -EOF -cat config.h >>config.h.tmp -cat <>config.h.tmp - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif -#endif // ifndef CCACHE_CONFIG_H -EOF -mv config.h.tmp config.h - -{ $as_echo "$as_me:${as_lineno-$LINENO}: now build ccache by running make" >&5 -$as_echo "$as_me: now build ccache by running make" >&6;} diff --git a/configure.ac b/configure.ac deleted file mode 100644 index 8278592..0000000 --- a/configure.ac +++ /dev/null @@ -1,266 +0,0 @@ -dnl Process this file with autoconf to produce a configure script. - -AC_INIT() -AC_PREREQ(2.52) - -AC_MSG_NOTICE([configuring ccache]) - -AC_CONFIG_HEADER(config.h) - -AC_CANONICAL_HOST - -case $host in - *mingw32* | *mingw64* | *cygwin* | *wince* | *mingwce*) - windows_os=yes - AC_DEFINE(_WIN32_WINNT,0x0600, Windows Vista or newer is required) - ;; - *os400* | *aix*) - AR="ar -X64" - ;; -esac - -AC_SUBST(disable_man) -AC_SUBST(extra_libs) -AC_SUBST(extra_sources) -AC_SUBST(include_dev_mk) -AC_SUBST(more_warnings) -AC_SUBST(no_implicit_fallthrough_warning) -AC_SUBST(test_suites) - -m4_include(m4/feature_macros.m4) -m4_include(m4/clang.m4) - -dnl Checks for programs. -AC_PROG_CC -_AC_LANG_COMPILER_CLANG -AC_PROG_CC_C99 -if test "$ac_cv_prog_cc_c99" = no; then - AC_MSG_ERROR(cannot find a C99-compatible compiler) -fi - -AC_PROG_CPP -AC_PROG_INSTALL -AC_PROG_RANLIB -AC_CHECK_TOOL(AR, ar) -if test -z "$AR"; then - AC_MSG_ERROR(cannot find ar) -fi - -# Prefer bash, needed for test.sh -AC_PATH_TOOL(BASH, bash, "/bin/bash") - -# If GCC (or clang), turn on warnings. -if test "$ac_compiler_gnu" = yes; then - CFLAGS="$CFLAGS -Wall -W" -else - CFLAGS="$CFLAGS -O" -fi - -AC_ARG_ENABLE(more_warnings, - [AS_HELP_STRING([--enable-more-warnings], - [enable more compiler warnings])]) -if test x${enable_more_warnings} = xyes; then - more_warnings="-Wextra -Wpedantic" - if test "$ac_compiler_clang" = yes; then - more_warnings="$more_warnings -Weverything" - more_warnings="$more_warnings -Wno-conversion" - more_warnings="$more_warnings -Wno-disabled-macro-expansion" - more_warnings="$more_warnings -Wno-format-nonliteral" - more_warnings="$more_warnings -Wno-padded" - more_warnings="$more_warnings -Wno-shorten-64-to-32" - more_warnings="$more_warnings -Wno-sign-conversion" - fi -fi - -AC_HEADER_DIRENT -AC_HEADER_TIME -AC_HEADER_SYS_WAIT - -AC_CHECK_TYPES(long long) - -AC_CHECK_HEADERS(ctype.h pwd.h stdlib.h string.h strings.h sys/time.h sys/mman.h) -AC_CHECK_HEADERS(termios.h) - -AC_CHECK_FUNCS(gethostname) -AC_CHECK_FUNCS(getopt_long) -AC_CHECK_FUNCS(getpwuid) -AC_CHECK_FUNCS(gettimeofday) -AC_CHECK_FUNCS(localtime_r) -AC_CHECK_FUNCS(mkstemp) -AC_CHECK_FUNCS(realpath) -AC_CHECK_FUNCS(setenv) -AC_CHECK_FUNCS(strndup) -AC_CHECK_FUNCS(strtok_r) -AC_CHECK_FUNCS(unsetenv) -AC_CHECK_FUNCS(utimes) - -AC_CACHE_CHECK([for compar_fn_t in stdlib.h],ccache_cv_COMPAR_FN_T, [ - AC_TRY_COMPILE( - [#include ], - [void test_fn(void) { qsort(NULL, 0, 0, (__compar_fn_t)NULL); }], - ccache_cv_COMPAR_FN_T=yes, - ccache_cv_COMPAR_FN_T=no)]) -if test x"$ccache_cv_COMPAR_FN_T" = x"yes"; then - AC_DEFINE(HAVE_COMPAR_FN_T, 1, - Define to 1 if you have the `__compar_fn_t' typedef.) -fi - -dnl Replacements of snprintf and friends. -m4_include(m4/snprintf.m4) -HW_FUNC_VSNPRINTF -HW_FUNC_SNPRINTF -HW_FUNC_VASPRINTF -HW_FUNC_ASPRINTF - -dnl Check if -lm is needed. -AC_SEARCH_LIBS(cos, m) - - -dnl Check for zlib -AC_ARG_WITH(bundled-zlib, - [AS_HELP_STRING([--with-bundled-zlib], - [use bundled zlib instead of the system's default zlib])]) -if test x${with_bundled_zlib} = x; then - AC_CACHE_CHECK( - [for zlib >= 1.2.3], - [ccache_cv_zlib_1_2_3], - AC_TRY_COMPILE( - [#include ], - [ - #if (ZLIB_VERNUM >= 0x1230) - #else - #error "ZLIB_VERNUM < 0x1230" - #endif - ], - [ccache_cv_zlib_1_2_3=yes], - [ccache_cv_zlib_1_2_3=no])) - AC_CHECK_LIB(z, gzdopen, true) - if test $ccache_cv_zlib_1_2_3 = yes && test $ac_cv_lib_z_gzdopen = yes; then - use_bundled_zlib=no - else - AC_MSG_WARN(using bundled zlib) - use_bundled_zlib=yes - fi -else - if test x${with_bundled_zlib} = xno; then - AC_MSG_NOTICE(using system zlib as requested) - use_bundled_zlib=no - else - AC_MSG_NOTICE(using bundled zlib as requested) - use_bundled_zlib=yes - fi -fi - -if test x${use_bundled_zlib} = xyes; then - CPPFLAGS="$CPPFLAGS -I\$(srcdir)/src/zlib" - extra_libs="src/zlib/libz.a" - mkdir -p src/zlib -else - LIBS="$LIBS -lz" -fi - -AC_ARG_ENABLE(man, - [AS_HELP_STRING([--disable-man], - [disable installing man pages])]) -if test x${enable_man} = xno; then - disable_man='#' -fi - -AC_ARG_ENABLE(tracing, - [AS_HELP_STRING([--enable-tracing], - [enable possibility to use internal ccache tracing])]) -if test x${enable_tracing} = xyes; then - CPPFLAGS="$CPPFLAGS -DMTR_ENABLED" - extra_sources="src/minitrace.c" -fi - -dnl Linking on Windows needs ws2_32 -if test x${windows_os} = xyes; then - LIBS="$LIBS -lws2_32" -fi - -AC_C_BIGENDIAN - -AC_C_INLINE - -dnl Check for "extern inline". -AC_CACHE_CHECK( - for extern inline, - ac_cv_c_extern_inline, - [ - ac_cv_c_extern_inline=no - AC_TRY_COMPILE( - [ - extern $ac_cv_c_inline double foo(double x); - extern $ac_cv_c_inline double foo(double x) { return x+1.0; }; - double foo (double x) { return x + 1.0; }; - ], - [foo(1.0)], - [ac_cv_c_extern_inline="yes"])]) -if test "$ac_cv_c_extern_inline" != no ; then - AC_DEFINE(HAVE_EXTERN_INLINE, 1, - Define to 1 if your compiler supports extern inline) -fi - -mkdir -p .deps src unittest - -dnl Enable developer mode if dev.mk.in exists. -if test ! -f $srcdir/dev_mode_disabled; then - AC_MSG_NOTICE(developer mode enabled) - AC_CONFIG_FILES([dev.mk]) - include_dev_mk='include dev.mk' - version=`(git --git-dir=$srcdir/.git describe --dirty 2>/dev/null || echo vunknown) | sed -e 's/v//' -e 's/-/+/' -e 's/-/_/g'` - mkdir -p src - echo "extern const char CCACHE_VERSION@<:@@:>@; const char CCACHE_VERSION@<:@@:>@ = \"$version\";" >src/version.c - AC_CHECK_TOOL(GPERF, gperf) - if test -z "$GPERF"; then - AC_MSG_ERROR(please install gperf) - fi -else - AC_MSG_NOTICE(developer mode disabled) -fi - -if test ! -f $srcdir/src/version.c -a ! -f src/version.c ; then - AC_MSG_WARN(unable to determine ccache version) - echo "extern const char CCACHE_VERSION@<:@@:>@; const char CCACHE_VERSION@<:@@:>@ = \"unknown\";" >src/version.c -fi - -dnl Check for -Wno-implicit-fallthrough -AC_MSG_CHECKING([whether C compiler supports -Wno-implicit-fallthrough]) -saved_cflags=$CFLAGS -CFLAGS=-Wno-implicit-fallthrough -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], - [AC_MSG_RESULT([yes])] - [no_implicit_fallthrough_warning="-Wno-implicit-fallthrough"], - [AC_MSG_RESULT([no])] -) -CFLAGS=$saved_cflags - -dnl Find test suite files. -test_suites=`cd $srcdir && ls unittest/test_*.c | egrep -v 'BASE|BACKUP|LOCAL|REMOTE' | xargs echo` - -AC_CONFIG_FILES([Makefile]) -AC_OUTPUT - -cat <config.h.tmp -#ifndef CCACHE_CONFIG_H -#define CCACHE_CONFIG_H -#ifdef __clang__ -#pragma clang diagnostic push -#if __has_warning("-Wreserved-id-macro") -#pragma clang diagnostic ignored "-Wreserved-id-macro" -#endif -#endif - -EOF -cat config.h >>config.h.tmp -cat <>config.h.tmp - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif -#endif // ifndef CCACHE_CONFIG_H -EOF -mv config.h.tmp config.h - -AC_MSG_NOTICE(now build ccache by running make) diff --git a/dev.mk.in b/dev.mk.in deleted file mode 100644 index fe1bcd3..0000000 --- a/dev.mk.in +++ /dev/null @@ -1,246 +0,0 @@ -# GNU make syntax reigns in this file. - -all_cflags += -Werror -all_cppflags += -MD -MP -MF .deps/$(subst .._,,$(subst /,_,$(subst $(srcdir)/,,$<))).d -MQ $@ - -A2X = a2x -ASCIIDOC = asciidoc -CPPCHECK = cppcheck -CPPCHECK_SUPPRESSIONS = misc/cppcheck-suppressions.txt -SHELLCHECK = shellcheck -SHELLCHECK_EXCLUDES = misc/shellcheck-excludes.txt -COMPILEDB = compiledb -CLANG_TIDY = clang-tidy -SCAN_BUILD = scan-build -DOCKER = docker -GPERF = @GPERF@ -TEST = test - -version := \ - $(shell (git --git-dir=$(srcdir)/.git describe --dirty || git --git-dir=$(srcdir)/.git describe || echo vunknown) \ - 2>/dev/null | sed -e 's/v//' -e 's/-/+/' -e 's/-/_/g') - -dist_dir = ccache-$(version) -dist_archives = \ - ccache-$(version).tar.gz \ - ccache-$(version).tar.xz - -generated_docs = \ - LICENSE.html \ - doc/AUTHORS.html \ - doc/MANUAL.html \ - doc/NEWS.html \ - doc/ccache.1 -built_dist_files = $(generated_sources) $(generated_docs) - -headers = \ - src/ccache.h \ - src/compopt.h \ - src/conf.h \ - src/confitems.h \ - src/counters.h \ - src/envtoconfitems.h \ - src/getopt_long.h \ - src/hash.h \ - src/hashtable.h \ - src/hashtable_itr.h \ - src/hashtable_private.h \ - src/hashutil.h \ - src/language.h \ - src/macroskip.h \ - src/manifest.h \ - src/mdfour.h \ - src/minitrace.h \ - src/murmurhashneutral2.h \ - src/system.h \ - unittest/framework.h \ - unittest/util.h -generated_headers = \ - unittest/suites.h - -files_to_clean += *.tar.gz *.tar.xz *.xml doc/*.xml .deps/* perfdir.* -files_to_clean += compile_commands.json -files_to_clean += src/confitems_lookup.c -files_to_clean += src/envtoconfitems_lookup.c -files_to_distclean += $(built_dist_files) src/version.c unittest/suites.h -files_to_distclean += .deps dev.mk - -source_dist_files = \ - $(non_3pp_sources) \ - $(3pp_sources) \ - $(headers) \ - $(test_sources) \ - CONTRIBUTING.md \ - GPL-3.0.txt \ - LICENSE.adoc \ - Makefile.in \ - README.md \ - autogen.sh \ - config.guess \ - config.h.in \ - config.sub \ - configure \ - configure.ac \ - dev.mk.in \ - doc/AUTHORS.adoc \ - doc/MANUAL.adoc \ - doc/NEWS.adoc \ - install-sh \ - m4 \ - src/confitems.gperf \ - src/confitems_lookup.c \ - src/envtoconfitems.gperf \ - src/envtoconfitems_lookup.c \ - src/main.c \ - src/minitrace.c \ - src/zlib/*.c \ - src/zlib/*.h \ - test/run \ - test/suites/*.bash - -dist_files = \ - $(addprefix $(srcdir)/, $(source_dist_files)) \ - $(built_dist_files) - -ifneq ($(shell sed 's/.*"\(.*\)".*/\1/' src/version.c 2>/dev/null),$(version)) - $(shell echo 'extern const char CCACHE_VERSION[]; const char CCACHE_VERSION[] = "$(version)";' >src/version.c) -endif -src/version.o: src/version.c - -# $(1): Name. -# $(2): Command for fixing up source file before the gperf call. -define generate_gperf_lookup -src/$(1)_lookup.c: src/$(1).gperf - $$(if $$(quiet),@echo " GEN $$@") - $$(Q)$(2) $$< | $$(GPERF) | sed 's/#error/#warning/' >$$@.tmp -# Fix for gperf < 3.1 (fix parameter type and remove inlining of the get function): - $$(Q)perl -00 -pi -e 's/unsigned int len/size_t len/; s/#ifdef __GNUC__.*?gnu_inline.*?#endif\n#endif\n//sg' $$@.tmp - $$(Q)echo "size_t $(1)_count(void) { return $$$$(perl -ne '/TOTAL_KEYWORDS = (.+?),/ && print $$$$1' $$@.tmp); }" >>$$@.tmp - $$(Q)mv $$@.tmp $$@ -endef - -add_confitems_numbers = \ - perl -pae '$$$$s = 1 if /^%%/; s/ITEM/$$$$n++ . ", ITEM"/e if $$$$s == 1' - -$(eval $(call generate_gperf_lookup,confitems,$(add_confitems_numbers))) -$(eval $(call generate_gperf_lookup,envtoconfitems,cat)) - -.PHONY: dist -dist: $(dist_archives) - -$(dist_archives): $(dist_files) - tmpdir=$$(mktemp -d /tmp/tmp-ccache-dist.XXXXXX) && \ - dir=$$tmpdir/$(dist_dir) && \ - mkdir $$dir && \ - (cd $(srcdir) && \ - rsync -r --relative $(source_dist_files) $$dir) && \ - cp $(srcdir)/doc/INSTALL-from-release-archive.md $$dir/INSTALL.md && \ - (cd $(builddir) && \ - rsync -r --relative $(built_dist_files) $$dir) && \ - echo "Remove this file to enable developer mode." >$$dir/dev_mode_disabled && \ - (cd $$tmpdir && \ - tarcompression= && \ - case $@ in \ - *.gz) tarcompression=-z ;; \ - *.xz) tarcompression=-J ;; \ - esac && \ - tar -c $$tarcompression -f $(CURDIR)/$@ $(dist_dir)) && \ - rm -rf $$tmpdir - -# $(1): extra configure options -define do_distcheck - tmpdir=$$(mktemp -d /tmp/tmp-ccache-distcheck.XXXXXX) && \ - (cd $$tmpdir && \ - tar xf $(CURDIR)/$< && \ - mkdir -p $(dist_dir)/build && \ - chmod -R a-w $(dist_dir) && \ - chmod u+w $(dist_dir)/build && \ - cd $(dist_dir)/build && \ - ../configure --enable-more-warnings --prefix=$$tmpdir/root $(1) && \ - $(MAKE) install CFLAGS=-Werror V=1 && \ - $(MAKE) installcheck) && \ - chmod -R u+w $$tmpdir/$(dist_dir) && \ - rm -rf $$tmpdir -endef - -.PHONY: distcheck -distcheck: $(firstword $(dist_archives)) - $(call do_distcheck, --without-bundled-zlib) - $(call do_distcheck, --with-bundled-zlib) - $(call do_distcheck, CC=clang) - -.PHONY: docs -docs: $(generated_docs) - -%.html: %.adoc - @mkdir -p $(@D) - $(if $(quiet),@echo " ASCIIDOC $@") - $(Q)$(ASCIIDOC) -o $@ -a revnumber=$(version) -a toc -b xhtml11 $< - -%.xml: %.adoc - @mkdir -p $(@D) -# Make literals stand out as bold in the man page: - $(if $(quiet),@echo " ASCIIDOC $@") - $(Q)$(ASCIIDOC) -a revnumber=$(version) -d manpage -b docbook -o - $< | \ - perl -pe 's!(.*?)!\1!g' >$@ - -doc/ccache.1: doc/MANUAL.xml - $(if $(quiet),@echo " A2X $@") - $(Q)$(A2X) --doctype manpage --format manpage $< - -.PHONY: update-authors -update-authors: - git log --pretty=format:"%H %aN%n%(trailers:only)" \ - | grep -Ev 'd7c5056beda5483fcd5c098165fffd9be86fe98d|http|Conflicts:' \ - | grep '^[^ ]' \ - | sed -r -e 's/[^ ]+/*/' -e 's/<.*//' -e 's/ *$$//' \ - | sort -u \ - | perl -00 -p -i -e 's/^\*.*/ . "\n"/es' $(srcdir)/doc/AUTHORS.adoc - -.PHONY: check-syntax -check-syntax: - $(CC) $(all_cppflags) -I. $(all_cflags) -S -o /dev/null $(CHK_SOURCES) - -.PHONY: cppcheck -cppcheck: - $(CPPCHECK) --suppressions-list=$(CPPCHECK_SUPPRESSIONS) \ - --inline-suppr -q --enable=all --force -I . \ - --template='cppcheck: warning: {id}:{file}:{line}: {message}' \ - $(non_3pp_sources) src/confitems_lookup.c src/main.c $(test_sources) - -.PHONY: shellcheck -shellcheck: test/suites/*.bash - $(SHELLCHECK) --shell=bash --exclude=$(shell sed -e 's/:.*//' <$(SHELLCHECK_EXCLUDES) | grep -v '#' | tr '\n' ',' | sed -e 's/,$$//') $^ - -.PHONY: uncrustify -uncrustify: - uncrustify -c misc/uncrustify.cfg --no-backup --replace $(non_3pp_sources) $(test_sources) - -# pip install compiledb -compile_commands.json: - $(COMPILEDB) -n $(MAKE) all unittest - -.PHONY: tidy -tidy: compile_commands.json - $(CLANG_TIDY) $(all_sources) - -.PHONY: analyze -analyze: - $(SCAN_BUILD) --use-cc=$(CC) $(srcdir)/configure - $(SCAN_BUILD) --use-cc=$(CC) --status-bugs $(MAKE) -B - -BUILDENV = ubuntu -DOCKER_IMAGE_TAG = ccache/build:$(BUILDENV) - -.PHONY: docker -docker: buildenv/$(BUILDENV)/Dockerfile - $(DOCKER) inspect $(DOCKER_IMAGE_TAG) >/dev/null || $(DOCKER) build -t $(DOCKER_IMAGE_TAG) buildenv/$(BUILDENV) - $(DOCKER) run --rm -v $(PWD):/build -w /build $(DOCKER_IMAGE_TAG) misc/build.sh $(TEST) - -.PHONY: travis -travis: .travis/Dockerfile - $(DOCKER) inspect travis-build >/dev/null || $(DOCKER) build -t travis-build .travis - $(DOCKER) run --rm --volume $(PWD):/src --tmpfs /dst:rw,exec --env ASAN_OPTIONS='$(ASAN_OPTIONS)' travis-build \ - sh -c "cd /src && ./autogen.sh && cd /dst && CC=$(CC) CFLAGS='$(CFLAGS)' LDFLAGS='$(LDFLAGS)' /src/configure $(HOST) && make V=$(V) && make V=$(V) $(TEST)" - --include .deps/*.d diff --git a/dev_mode_disabled b/dev_mode_disabled deleted file mode 100644 index ba72f4f..0000000 --- a/dev_mode_disabled +++ /dev/null @@ -1 +0,0 @@ -Remove this file to enable developer mode. diff --git a/doc/AUTHORS.adoc b/doc/AUTHORS.adoc index 9f88a4a..cc0df94 100644 --- a/doc/AUTHORS.adoc +++ b/doc/AUTHORS.adoc @@ -1,10 +1,10 @@ -ccache authors +Ccache authors ============== -ccache was originally written by Andrew Tridgell and is currently developed and +Ccache was originally written by Andrew Tridgell and is currently developed and maintained by Joel Rosdahl. -ccache is a collective work with contributions from many people, including: +Ccache is a collective work with contributions from many people, including: * Alexander Korsunsky * Alexander Lanin @@ -17,24 +17,31 @@ ccache is a collective work with contributions from many people, including: * Andrew Boie * Andrew Stubbs * Andrew Tridgell +* Arne Hasselbring * Bernhard Bauer * Björn Jacke +* Breno Guimaraes * Chiaki Ishikawa * Chris AtLee +* Chris Burr * Clemens Rabe * Cristian Adam * David Givone * Deepak Yadav * Doug Anderson * Edward Z. Yang -* Erik Johansson +* Enrico Sorichetti +* Erik Flodin * Francois Marier * Gabriel Scherer * Geert Bosch * Geert Kloosterman +* Gregor Jasny * Grigory Entin +* Harsh Shandilya * Havard Graff * Hongli Lai +* Igor Pylypiv * Ivan Vaigult * Ivan Volnov * Jiang Jiang @@ -62,6 +69,7 @@ ccache is a collective work with contributions from many people, including: * Martin Pool * Mathias De Maré * Matthias Kretz +* Matt Whitlock * Melven Roehrig-Zoellner * Michael Marineau * Michael Meeks @@ -82,22 +90,26 @@ ccache is a collective work with contributions from many people, including: * Orion Poplawski * Owen Mann * Patrick von Reth +* Paul Bunch +* Paul Fultz II * Paul Griffith * Pavel Boldin * Pavol Sakac * Per Nordlöw * Peter Budai * Philippe Proulx +* Philipp Storz * Rafael Kitover * Ramiro Polla * Robert Yang * Robin H. Johnson * Rolf Bjarne Kvinge -* Russell King * RW * Ryan Brown +* Ryan Egesdahl * Sam Gross * Steffen Dettmer +* Sumit Jamgade * Thomas Otto * Thomas Röfer * Timofei Kushnir diff --git a/doc/AUTHORS.html b/doc/AUTHORS.html deleted file mode 100644 index 1f397ff..0000000 --- a/doc/AUTHORS.html +++ /dev/null @@ -1,1295 +0,0 @@ - - - - - - -ccache authors - - - - - -
-
-
-

ccache was originally written by Andrew Tridgell and is currently developed and -maintained by Joel Rosdahl.

-

ccache is a collective work with contributions from many people, including:

-
    -
  • -

    -Alexander Korsunsky -

    -
  • -
  • -

    -Alexander Lanin -

    -
  • -
  • -

    -Alexey Tourbin -

    -
  • -
  • -

    -Alfred Landrum -

    -
  • -
  • -

    -Anders F Björklund -

    -
  • -
  • -

    -Andrea Bittau -

    -
  • -
  • -

    -Andreas Huber -

    -
  • -
  • -

    -André Klitzing -

    -
  • -
  • -

    -Andrew Boie -

    -
  • -
  • -

    -Andrew Stubbs -

    -
  • -
  • -

    -Andrew Tridgell -

    -
  • -
  • -

    -Bernhard Bauer -

    -
  • -
  • -

    -Björn Jacke -

    -
  • -
  • -

    -Chiaki Ishikawa -

    -
  • -
  • -

    -Chris AtLee -

    -
  • -
  • -

    -Clemens Rabe -

    -
  • -
  • -

    -Cristian Adam -

    -
  • -
  • -

    -David Givone -

    -
  • -
  • -

    -Deepak Yadav -

    -
  • -
  • -

    -Doug Anderson -

    -
  • -
  • -

    -Edward Z. Yang -

    -
  • -
  • -

    -Erik Johansson -

    -
  • -
  • -

    -Francois Marier -

    -
  • -
  • -

    -Gabriel Scherer -

    -
  • -
  • -

    -Geert Bosch -

    -
  • -
  • -

    -Geert Kloosterman -

    -
  • -
  • -

    -Grigory Entin -

    -
  • -
  • -

    -Havard Graff -

    -
  • -
  • -

    -Hongli Lai -

    -
  • -
  • -

    -Ivan Vaigult -

    -
  • -
  • -

    -Ivan Volnov -

    -
  • -
  • -

    -Jiang Jiang -

    -
  • -
  • -

    -Joel Galenson -

    -
  • -
  • -

    -Joel Rosdahl -

    -
  • -
  • -

    -John Basila -

    -
  • -
  • -

    -John Coiner -

    -
  • -
  • -

    -Jon Bernard -

    -
  • -
  • -

    -Jonny Yu -

    -
  • -
  • -

    -Jørgen P. Tjernø -

    -
  • -
  • -

    -Josh Soref -

    -
  • -
  • -

    -Justin Lebar -

    -
  • -
  • -

    -Karl Chen -

    -
  • -
  • -

    -Kona Blend -

    -
  • -
  • -

    -Kovarththanan Rajaratnam -

    -
  • -
  • -

    -Lalit Chhabra -

    -
  • -
  • -

    -Lars Gustäbel -

    -
  • -
  • -

    -Leanid Chaika -

    -
  • -
  • -

    -Loïc Yhuel -

    -
  • -
  • -

    -Luboš Luňák -

    -
  • -
  • -

    -luzpaz -

    -
  • -
  • -

    -Maarten Maathuis -

    -
  • -
  • -

    -Mark Starovoytov -

    -
  • -
  • -

    -Martin Ettl -

    -
  • -
  • -

    -Martin Pool -

    -
  • -
  • -

    -Mathias De Maré -

    -
  • -
  • -

    -Matthias Kretz -

    -
  • -
  • -

    -Melven Roehrig-Zoellner -

    -
  • -
  • -

    -Michael Marineau -

    -
  • -
  • -

    -Michael Meeks -

    -
  • -
  • -

    -Michał Mirosław -

    -
  • -
  • -

    -Mihai Serban -

    -
  • -
  • -

    -Mike Blumenkrantz -

    -
  • -
  • -

    -Mike Frysinger -

    -
  • -
  • -

    -Mike Gulick -

    -
  • -
  • -

    -Mikhail Kolomeytsev -

    -
  • -
  • -

    -Mizuha Himuraki -

    -
  • -
  • -

    -Mostyn Bramley-Moore -

    -
  • -
  • -

    -Neil Mushell -

    -
  • -
  • -

    -Nick Schultz -

    -
  • -
  • -

    -Norbert Lange -

    -
  • -
  • -

    -Oded Shimon -

    -
  • -
  • -

    -Olle Liljenzin -

    -
  • -
  • -

    -Orgad Shaneh -

    -
  • -
  • -

    -Orion Poplawski -

    -
  • -
  • -

    -Owen Mann -

    -
  • -
  • -

    -Patrick von Reth -

    -
  • -
  • -

    -Paul Griffith -

    -
  • -
  • -

    -Pavel Boldin -

    -
  • -
  • -

    -Pavol Sakac -

    -
  • -
  • -

    -Per Nordlöw -

    -
  • -
  • -

    -Peter Budai -

    -
  • -
  • -

    -Philippe Proulx -

    -
  • -
  • -

    -Rafael Kitover -

    -
  • -
  • -

    -Ramiro Polla -

    -
  • -
  • -

    -Robert Yang -

    -
  • -
  • -

    -Robin H. Johnson -

    -
  • -
  • -

    -Rolf Bjarne Kvinge -

    -
  • -
  • -

    -Russell King -

    -
  • -
  • -

    -RW -

    -
  • -
  • -

    -Ryan Brown -

    -
  • -
  • -

    -Sam Gross -

    -
  • -
  • -

    -Steffen Dettmer -

    -
  • -
  • -

    -Thomas Otto -

    -
  • -
  • -

    -Thomas Röfer -

    -
  • -
  • -

    -Timofei Kushnir -

    -
  • -
  • -

    -Tim Potter -

    -
  • -
  • -

    -Tomasz Miąsko -

    -
  • -
  • -

    -Tom Hughes -

    -
  • -
  • -

    -Tor Arne Vestbø -

    -
  • -
  • -

    -Vadim Petrochenkov -

    -
  • -
  • -

    -Ville Skyttä -

    -
  • -
  • -

    -William S Fulton -

    -
  • -
  • -

    -Wilson Snyder -

    -
  • -
  • -

    -Xavier René-Corail -

    -
  • -
  • -

    -Yiding Jia -

    -
  • -
  • -

    -Yvan Janssens -

    -
  • -
-

Thanks!

-
-
-
-

- - - diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt new file mode 100644 index 0000000..53f6351 --- /dev/null +++ b/doc/CMakeLists.txt @@ -0,0 +1,66 @@ +find_program(ASCIIDOC_EXE asciidoc) +mark_as_advanced(ASCIIDOC_EXE) # Don't show in CMake UIs + +if(NOT ASCIIDOC_EXE) + message(WARNING "Could not find asciidoc; documentation will not be generated") +else() + # + # HTML documentation + # + function(generate_html adoc_file) + get_filename_component(base_name "${adoc_file}" NAME_WE) + set(html_file "${base_name}.html") + add_custom_command( + OUTPUT "${html_file}" + COMMAND + ${ASCIIDOC_EXE} + -o "${html_file}" + -a revnumber="${VERSION}" + -a toc + -b xhtml11 + "${CMAKE_SOURCE_DIR}/${adoc_file}" + MAIN_DEPENDENCY "${CMAKE_SOURCE_DIR}/${adoc_file}" + ) + set(html_files "${html_files}" "${html_file}" PARENT_SCOPE) + endfunction() + + generate_html(LICENSE.adoc) + generate_html(doc/AUTHORS.adoc) + generate_html(doc/MANUAL.adoc) + generate_html(doc/NEWS.adoc) + + add_custom_target(doc-html DEPENDS "${html_files}") + set(doc_files "${html_files}") + + # + # Man page + # + find_program(A2X_EXE a2x) + mark_as_advanced(A2X_EXE) # Don't show in CMake UIs + if(NOT A2X_EXE) + message(WARNING "Could not find a2x; man page will not be generated") + else() + # MANUAL.adoc -> MANUAL.xml -> man page + add_custom_command( + OUTPUT MANUAL.xml + COMMAND + ${ASCIIDOC_EXE} + -o - + -a revnumber=${VERSION} + -d manpage + -b docbook "${CMAKE_SOURCE_DIR}/doc/MANUAL.adoc" + | perl -pe 's!\(.*?\)!\\1!g' + >MANUAL.xml + MAIN_DEPENDENCY "${CMAKE_SOURCE_DIR}/doc/MANUAL.adoc" + ) + add_custom_command( + OUTPUT ccache.1 + COMMAND a2x --doctype manpage --format manpage MANUAL.xml + MAIN_DEPENDENCY MANUAL.xml + ) + add_custom_target(doc-man-page DEPENDS ccache.1) + set(doc_files "${doc_files}" ccache.1) + endif() + + add_custom_target(doc DEPENDS "${doc_files}") +endif() diff --git a/doc/DEVELOPER.md b/doc/DEVELOPER.md new file mode 100644 index 0000000..e211c9f --- /dev/null +++ b/doc/DEVELOPER.md @@ -0,0 +1,33 @@ +Developer manual +================ + +Tracing +------- + +In order to see what ccache is doing, it is possible to enable internal +tracing: + +* Build ccache with the `-DENABLE_TRACING=1` cmake option. +* Set the environment variable `CCACHE_INTERNAL_TRACE` to instruct ccache to + create trace files at runtime. + +There will be one trace file per ccache invocation, named as the object file +with a `.ccache-trace` suffix, e.g. `file.o.ccache-trace`. The trace file can +then be loaded into the `chrome://tracing` page of Chromium/Chrome. + +You can combine several trace files into by using the `misc/combine-trace-files` +script: + + misc/combine-trace-files *.o.ccache-trace | gzip > ccache.trace.gz + +(The gzip step is optional; Chrome supports both plain trace files and gzipped +trace files.) The script will offset each individual trace by its start time in +the combined file. + +There is also a script called `summarize-trace-files` that generates a summary +(per job slot) of all the ccache runs: + + misc/combine-trace-files *.o.ccache-trace | misc/summarize-trace-files 4 > ccache.trace + +The script takes the number of job slots you used when building (e.g. `4` for +`make -j4`) as the first argument. diff --git a/doc/INSTALL.md b/doc/INSTALL.md new file mode 100644 index 0000000..f27053f --- /dev/null +++ b/doc/INSTALL.md @@ -0,0 +1,68 @@ +Ccache installation +=================== + +Prerequisites +------------- + +To build ccache you need: + +- CMake 3.4.3 or newer. +- A C++11 compiler. +- A C99 compiler. +- [libzstd](https://www.zstd.net). If you don't have libzstd installed and + can't or don't want to install it in a standard system location, there are + two options: + 1. Install zstd in a custom path and set `CMAKE_PREFIX_PATH` to it, e.g. + by passing `-DCMAKE_PREFIX_PATH=/some/custom/path` to `cmake`, or + 2. Pass `-DZSTD_FROM_INTERNET=ON` to `cmake` to make it download libzstd + from the Internet and unpack it in the local binary tree. Ccache will + then be linked statically to the locally built libzstd. + +Optional: + +- GNU Bourne Again SHell (bash) for tests. +- [AsciiDoc](https://www.methods.co.nz/asciidoc/) to build the HTML + documentation. +- [xsltproc](http://xmlsoft.org/XSLT/xsltproc2.html) to build the man page. +- [Python](https://www.python.org) to debug and run the performance test suite. + + +Installation +------------ + +Here is the typical way to build and install ccache: + + mkdir build + cd build + cmake -DCMAKE_BUILD_TYPE=Release .. + make + make install + +You can set the installation directory to e.g. `/usr` by adding +`-DCMAKE_INSTALL_PREFIX=/usr` to the `cmake` command. You can set the directory +where the secondary configuration file should be located to e.g. `/etc` by +adding `-DCMAKE_INSTALL_SYSCONFDIR=/etc`. + +There are two ways to use ccache. You can either prefix your compilation +commands with `ccache` or you can create a symbolic link (named as your +compiler) to ccache. The first method is most convenient if you just want to +try out ccache or wish to use it for some specific projects. The second method +is most useful for when you wish to use ccache for all your compilations. + +To install for usage by the first method just copy ccache to somewhere in your +path. + +To install for the second method, do something like this: + + cp ccache /usr/local/bin/ + ln -s ccache /usr/local/bin/gcc + ln -s ccache /usr/local/bin/g++ + ln -s ccache /usr/local/bin/cc + ln -s ccache /usr/local/bin/c++ + +And so forth. This will work as long as `/usr/local/bin` comes before the path +to the compiler (which is usually in `/usr/bin`). After installing you may wish +to run `which gcc` to make sure that the correct link is being used. + +NOTE: Do not use a hard link, use a symbolic link. A hard link will cause +"interesting" problems. diff --git a/doc/MANUAL.adoc b/doc/MANUAL.adoc index 0b3cd1c..25f345a 100644 --- a/doc/MANUAL.adoc +++ b/doc/MANUAL.adoc @@ -8,7 +8,7 @@ CCACHE(1) Name ---- -ccache - a fast C/C++ compiler cache +Ccache - a fast C/C++ compiler cache Synopsis @@ -23,39 +23,15 @@ _compiler_ [_compiler options_] (via symbolic link) Description ----------- -ccache is a compiler cache. It speeds up recompilation by caching the result of +Ccache is a compiler cache. It speeds up recompilation by caching the result of previous compilations and detecting when the same compilation is being done -again. Supported languages are C, C\+\+, Objective-C and Objective-C++. +again. -ccache has been carefully written to always produce exactly the same compiler +Ccache has been carefully written to always produce exactly the same compiler output that you would get without the cache. The only way you should be able to tell that you are using ccache is the speed. Currently known exceptions to this -goal are listed under <<_caveats,CAVEATS>>. If you ever discover an -undocumented case where ccache changes the output of your compiler, please let -us know. - - -Features -~~~~~~~~ - -* Keeps statistics on hits/misses. -* Automatic cache size management. -* Can cache compilations that generate warnings. -* Easy installation. -* Low overhead. -* Optionally compresses files in the cache to reduce disk space. - - -Limitations -~~~~~~~~~~~ - -* Only knows how to cache the compilation of a single - C/C\+\+/Objective-C/Objective-C++ file. Other types of compilations - (multi-file compilation, linking, etc) will silently fall back to running the - real compiler. -* Only works with GCC and compilers that behave similar enough. -* Some compiler flags are not supported. If such a flag is detected, ccache - will silently fall back to running the real compiler. +goal are listed under <<_caveats,CAVEATS>>. If you discover an undocumented case +where ccache changes the output of your compiler, please let us know. Run modes @@ -90,17 +66,21 @@ but currently doesn't interact well with other tools that do the same thing. See <<_using_ccache_with_other_compiler_wrappers,USING CCACHE WITH OTHER COMPILER WRAPPERS>>. -WARNING: Do not use a hard link, use a symbolic link. A hard link will cause -``interesting'' problems. +WARNING: Use a symbolic links for masquerading, not hard links. -Options -------- +Command line options +-------------------- -These options only apply when you invoke ccache as ``ccache''. When invoked as -a compiler (via a symlink as described in the previous section), the normal -compiler options apply and you should refer to the compiler's documentation. +These command line options only apply when you invoke ccache as ``ccache''. +When invoked as a compiler (via a symlink as described in the previous +section), the normal compiler options apply and you should refer to the +compiler's documentation. -*`-c, --cleanup`*:: + +Common options +~~~~~~~~~~~~~~ + +*`-c`*, *`--cleanup`*:: Clean up the cache by removing old cached files until the specified file number and cache size limits are not exceeded. This also recalculates the @@ -110,37 +90,33 @@ compiler options apply and you should refer to the compiler's documentation. cleanup is mostly useful if you manually modify the cache contents or believe that the cache size statistics may be inaccurate. -*`-C, --clear`*:: +*`-C`*, *`--clear`*:: Clear the entire cache, removing all cached files, but keeping the configuration file. -*`--dump-manifest`*=_PATH_:: - - Dump manifest file at PATH in text format. This is only useful when - debugging ccache and its behavior. - -*`-k, --get-config`*=_KEY_:: +*`-d`*, *`--directory`* _PATH_ - Print the value of configuration option _KEY_. See - <<_configuration,CONFIGURATION>> for more information. + Let the subsequent command line options operate on cache directory PATH + instead of the default for. For example, to show statistics for a cache + directory at `/shared/ccache` you can run `ccache -d /shared/ccache -s`. -*`--hash-file`*=_PATH_:: +*`--evict-older-than`* _AGE_:: - Print the hash (in format `-`) of the file at PATH. This is only - useful when debugging ccache and its behavior. + Remove files older than _AGE_ from the cache. _AGE_ should be an unsigned + integer with a `d` (days) or `s` (seconds) suffix. -*`-h, --help`*:: +*`-h`*, *`--help`*:: - Print an options summary page. + Print a summary of command line options. -*`-F, --max-files`*=_N_:: +*`-F`* _NUM_, *`--max-files`* _NUM_:: - Set the maximum number of files allowed in the cache. Use 0 for no limit. - The value is stored in a configuration file in the cache directory and - applies to all future compilations. + Set the maximum number of files allowed in the cache to _NUM_. Use 0 for no + limit. The value is stored in a configuration file in the cache directory + and applies to all future compilations. -*`-M, --max-size`*=_SIZE_:: +*`-M`* _SIZE_, *`--max-size`* _SIZE_:: Set the maximum size of the files stored in the cache. _SIZE_ should be a number followed by an optional suffix: k, M, G, T (decimal), Ki, Mi, Gi or @@ -148,38 +124,90 @@ compiler options apply and you should refer to the compiler's documentation. stored in a configuration file in the cache directory and applies to all future compilations. -*`--print-stats`*:: +*`-X`* _LEVEL_, *`--recompress`* _LEVEL_:: - Print statistics counter IDs and corresponding values machine-parsable - (tab-separated) format. + Recompress the cache using compression level _LEVEL_. The level can be an + integer, with the same semantics as the + <> configuration setting), or + the special value *uncompressed* for no compression. See + <<_cache_compression,CACHE COMPRESSION>> for more information. This can + potentionally take a long time since all files in the cache need to be + visited. Only files that are currently compressed with a different level + than _LEVEL_ will be recompressed. -*`-o, --set-config`*=_KEY=VALUE_:: +*`-o`* _KEY=VALUE_, *`--set-config`* _KEY_=_VALUE_:: Set configuration option _KEY_ to _VALUE_. See <<_configuration,CONFIGURATION>> for more information. -*`-p, --show-config`*:: +*`-x`*, *`--show-compression`*:: + + Print cache compression statistics. See <<_cache_compression,CACHE + COMPRESSION>> for more information. This can potentionally take a long time + since all files in the cache need to be visited. + +*`-p`*, *`--show-config`*:: Print current configuration options and from where they originate (environment variable, configuration file or compile-time default) in human-readable format. -*`-s, --show-stats`*:: +*`-s`*, *`--show-stats`*:: Print a summary of configuration and statistics counters in human-readable format. -*`-V, --version`*:: +*`-V`*, *`--version`*:: Print version and copyright information. -*`-z, --zero-stats`*:: +*`-z`*, *`--zero-stats`*:: Zero the cache statistics (but not the configuration options). +Options for scripting or debugging +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +*`--checksum-file`* _PATH_:: + + Print the checksum (64 bit XXH3) of the file at _PATH_. + +*`--dump-manifest`* _PATH_:: + + Dump manifest file at _PATH_ in text format to standard output. This is + only useful when debugging ccache and its behavior. + +*`--dump-result`* _PATH_:: + + Dump result file at _PATH_ in text format to standard output. This is only + useful when debugging ccache and its behavior. + +*`--extract-result`* _PATH_:: + + Extract data stored in the result file at _PATH_. The data will be written + to *ccache-result.** files in to the current working directory. This is + only useful when debugging ccache and its behavior. + +*`-k`* _KEY_, *`--get-config`* _KEY_:: + + Print the value of configuration option _KEY_. See + <<_configuration,CONFIGURATION>> for more information. + +*`--hash-file`* _PATH_:: + + Print the hash (160 bit BLAKE3) of the file at _PATH_. This is only useful + when debugging ccache and its behavior. + +*`--print-stats`*:: + + Print statistics counter IDs and corresponding values machine-parsable + (tab-separated) format. + + + Extra options -------------- +~~~~~~~~~~~~~ When run as a compiler, ccache usually just takes the same command line options as the compiler you are using. The only exception to this is the option @@ -206,23 +234,41 @@ compiler than what ccache thinks. Configuration ------------- -ccache's default behavior can be overridden by configuration file settings, +ccache's default behavior can be overridden by settings in configuration files, which in turn can be overridden by environment variables with names starting -with *CCACHE_*. ccache normally reads configuration from two files: first a +with *CCACHE_*. Ccache normally reads configuration from two files: first a system-level configuration file and secondly a cache-specific configuration file. The priority of configuration settings is as follows (where 1 is highest): 1. Environment variables. -2. The cache-specific configuration file *__/ccache.conf* (typically - *$HOME/.ccache/ccache.conf*). -3. The system-wide configuration file *__/ccache.conf* (typically - */etc/ccache.conf* or */usr/local/etc/ccache.conf*). +2. The primary (cache-specific) configuration file (see below). +3. The secondary (system-wide read-only) configuration file + *__/ccache.conf* (typically */etc/ccache.conf* or + */usr/local/etc/ccache.conf*). 4. Compile-time defaults. -As a special case, if the environment variable *CCACHE_CONFIGPATH* is set, -ccache reads configuration from the specified path instead of the default -paths. +As a special case, if the the environment variable *CCACHE_CONFIGPATH* is set +it specifies the primary configuration file and the secondary (system-wide) +configuration file won't be read. + + +Location of the primary configuration file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The location of the primary (cache-specific) configuration is determined like +this: + +1. If *CCACHE_CONFIGPATH* is set, use that path. +2. Otherwise, if <> (*CCACHE_DIR*) is set then + use */ccache.conf*. +3. Otherwise, if there is a legacy *$HOME/.ccache* directory then use + *$HOME/.ccache/ccache.conf. +4. Otherwise, if *XDG_CONFIG_HOME* is set then use + *$XDG_CONFIG_HOME/ccache/ccache.conf*. +5. Otherwise, use *%APPDATA%/ccache/ccache.conf* (Windows), + *$HOME/Library/Preferences/ccache/ccache.conf (macOS) or + *$HOME/.config/ccache/ccache.conf* (other systems). Configuration file syntax @@ -240,68 +286,121 @@ max_size = 10G Boolean values ~~~~~~~~~~~~~~ -Some settings are boolean values (i.e. truth values). In a configuration file, -such values must be set to the string *true* or *false*. For the corresponding -environment variables, the semantics are a bit different: a set environment -variable means ``true'' (even if set to the empty string), the following -case-insensitive negative values are considered an error (rather than -surprising the user): *0*, *false*, *disable* and *no*, and an unset -environment variable means ``false''. Each boolean environment variable also -has a negated form starting with *CCACHE_NO*. For example, *CCACHE_COMPRESS* -can be set to force compression and *CCACHE_NOCOMPRESS* can be set to force no -compression. +Some configuration options are boolean values (i.e. truth values). In a +configuration file, such values must be set to the string *true* or *false*. +For the corresponding environment variables, the semantics are a bit different: +* A set environment variable means ``true'' (even if set to the empty string). +* The following case-insensitive negative values are considered an error + (instead of surprising the user): *0*, *false*, *disable* and *no*. +* An unset environment variable means ``false''. -Configuration settings +Each boolean environment variable also has a negated form starting with +*CCACHE_NO*. For example, *CCACHE_COMPRESS* can be set to force compression and +*CCACHE_NOCOMPRESS* can be set to force no compression. + + +Configuration options ~~~~~~~~~~~~~~~~~~~~~~ -Below is a list of available configuration settings. The corresponding +Below is a list of available configuration options. The corresponding environment variable name is indicated in parentheses after each configuration -setting key. - -*base_dir* (*CCACHE_BASEDIR*):: - - This setting should be an absolute path to a directory. ccache then - rewrites absolute paths into relative paths before computing the hash that - identifies the compilation, but only for paths under the specified - directory. If set to the empty string (which is the default), no rewriting - is done. A typical path to use as the base directory is your home directory - or another directory that is a parent of your build directories. Don't use - `/` as the base directory since that will make ccache also rewrite paths to - system header files, which doesn't gain anything. +option key. + +[[config_absolute_paths_in_stderr]] *absolute_paths_in_stderr* (*CCACHE_ABSSTDERR*):: + + This option specifies whether ccache should rewrite relative paths in the + compiler's standard error output to absolute paths. This can be useful if + you use <> with a build system (e.g. CMake with + the "Unix Makefiles" generator) that executes the compiler in a different + working directory, which makes relative paths in compiler errors or + warnings incorrect. The default is false. + +[[config_base_dir]] *base_dir* (*CCACHE_BASEDIR*):: + + This option should be an absolute path to a directory. If set, ccache will + rewrite absolute paths into paths relative to the current working + directory, but only absolute paths that begin with *base_dir*. Cache + results can then be shared for compilations in different directories even + if the project uses absolute paths in the compiler command line. See also + the discussion under <<_compiling_in_different_directories,COMPILING IN + DIFFERENT DIRECTORIES>>. If set to the empty string (which is the default), + no rewriting is done. ++ +A typical path to use as *base_dir* is your home directory or another directory +that is a parent of your project directories. Don't use `/` as the base +directory since that will make ccache also rewrite paths to system header +files, which typically is contraproductive. ++ +For example, say that Alice's current working directory is +`/home/alice/project1/build` and that she compiles like this: ++ +------------------------------------------------------------------------------- +ccache gcc -I/usr/include/example -I/home/alice/project2/include -c /home/alice/project1/src/example.c +------------------------------------------------------------------------------- ++ +Here is what ccache will actually execute for different *base_dir* values: + -See also the discussion under <<_compiling_in_different_directories,COMPILING -IN DIFFERENT DIRECTORIES>>. +------------------------------------------------------------------------------- +# Current working directory: /home/alice/project1/build -*cache_dir* (*CCACHE_DIR*):: +# With base_dir = /: +gcc -I../../../../usr/include/example -I../../project2/include -c ../src/example.c - This setting specifies where ccache will keep its cached compiler outputs. - It will only take effect if set in the system-wide configuration file or as - an environment variable. The default is *$HOME/.ccache*. +# With base_dir = /home or /home/alice: +gcc -I/usr/include/example -I../../project2/include -c ../src/example.c -*cache_dir_levels* (*CCACHE_NLEVELS*):: +# With base_dir = /home/alice/project1 or /home/alice/project1/src: +gcc -I/usr/include/example -I/home/alice/project2/include -c ../src/example.c +------------------------------------------------------------------------------- ++ +If Bob has put `project1` and `project2` in `/home/bob/stuff` and both users +have set *base_dir* to `/home` or `/home/$USER`, then Bob will get a cache hit +(if they share ccache directory) since the actual command line will be +identical to that of Alice: ++ +------------------------------------------------------------------------------- +# Current working directory: /home/bob/stuff/project1/build - This setting allows you to choose the number of directory levels in the - cache directory. The default is 2. The minimum is 1 and the maximum is 8. +# With base_dir = /home or /home/bob: +gcc -I/usr/include/example -I../../project2/include -c ../src/example.c +------------------------------------------------------------------------------- ++ +Without *base_dir* there will be a cache miss since the absolute paths will +differ. With *base_dir* set to `/` there will be a cache miss since the +relative path to `/usr/include/example` will be different. With *base_dir* set +to `/home/bob/stuff/project1` there will a cache miss since the path to +project2 will be a different absolute path. -*compiler* (*CCACHE_COMPILER* or (deprecated) *CCACHE_CC*):: +[[config_cache_dir]] *cache_dir* (*CCACHE_DIR*):: - This setting can be used to force the name of the compiler to use. If set - to the empty string (which is the default), ccache works it out from the + This option specifies where ccache will keep its cached compiler outputs. + It will only take effect if set in the system-wide configuration file or as + an environment variable. The default is *$XDG_CACHE_HOME/ccache* if + *XDG_CACHE_HOME* is set, otherwise *$HOME/.cache/ccache*. Exception: If the + legacy directory *$HOME/.ccache* exists then that directory is the default. + + See also <<_location_of_the_primary_configuration_file,LOCATION OF THE + PRIMARY CONFIGURATION FILE>>. + +[[config_compiler]] *compiler* (*CCACHE_COMPILER* or (deprecated) *CCACHE_CC*):: + + This option can be used to force the name of the compiler to use. If set to + the empty string (which is the default), ccache works it out from the command line. -*compiler_check* (*CCACHE_COMPILERCHECK*):: +[[config_compiler_check]] *compiler_check* (*CCACHE_COMPILERCHECK*):: By default, ccache includes the modification time (``mtime'') and size of the compiler in the hash to ensure that results retrieved from the cache - are accurate. This setting can be used to select another strategy. Possible + are accurate. This option can be used to select another strategy. Possible values are: + -- *content*:: Hash the content of the compiler binary. This makes ccache very slightly - slower compared to the *mtime* setting, but makes it cope better with - compiler upgrades during a build bootstrapping process. + slower compared to *mtime*, but makes it cope better with compiler upgrades + during a build bootstrapping process. *mtime*:: Hash the compiler's mtime and size, which is fast. This is the default. *none*:: @@ -309,8 +408,8 @@ IN DIFFERENT DIRECTORIES>>. use the cached results even though the compiler's mtime or size has changed (e.g. if the compiler is built as part of your build system and the compiler's source has not changed, or if the compiler only has changes that - don't affect code generation). You should only use the *none* setting if - you know what you are doing. + don't affect code generation). You should only use *none* if you know what + you are doing. *string:value*:: Use *value* as the string to calculate hash from. This can be the compiler revision number you retrieved earlier and set here via environment variable. @@ -344,91 +443,145 @@ method will hash the mtime and size of the other compiler wrapper, which means that ccache won't be able to detect a compiler upgrade. Using a suitable command to identify the compiler is thus safer, but it's also slower, so you should consider continue using the *mtime* method in combination with -the *prefix_command* setting if possible. See +the *prefix_command* option if possible. See <<_using_ccache_with_other_compiler_wrappers,USING CCACHE WITH OTHER COMPILER WRAPPERS>>. -- -- -*compression* (*CCACHE_COMPRESS* or *CCACHE_NOCOMPRESS*, see <<_boolean_values,Boolean values>> above):: +[[config_compression]] *compression* (*CCACHE_COMPRESS* or *CCACHE_NOCOMPRESS*, see <<_boolean_values,Boolean values>> above):: - If true, ccache will compress object files and other compiler output it - puts in the cache. However, this setting has no effect on how files are - retrieved from the cache; compressed and uncompressed results will still be - usable regardless of this setting. The default is false. + If true, ccache will compress data it puts in the cache. However, this + option has no effect on how files are retrieved from the cache; compressed + and uncompressed results will still be usable regardless of this option. + The default is true. ++ +Compression is done using the Zstandard algorithm. The algorithm is fast enough +that there should be little reason to turn off compression to gain performance. +One exception is if the cache is located on a compressed file system, in which +case the compression performed by ccache of course is redundant. ++ +Compression will be disabled if file cloning (the +<> option) or hard linking (the +<> option) is enabled. -*compression_level* (*CCACHE_COMPRESSLEVEL*):: +[[config_compression_level]] *compression_level* (*CCACHE_COMPRESSLEVEL*):: - This setting determines the level at which ccache will compress object - files. It only has effect if *compression* is enabled. The value defaults - to 6, and must be no lower than 1 (fastest, worst compression) and no - higher than 9 (slowest, best compression). + This option determines the level at which ccache will compress object files + using the real-time compression algorithm Zstandard. It only has effect if + <> is enabled (which it is by default). + Zstandard is extremely fast for decompression and very fast for compression + for lower compression levels. The default is 0. ++ +Semantics of *compression_level*: ++ +-- +*> 0*:: + A positive value corresponds to normal Zstandard compression levels. Lower + levels (e.g. *1*) mean faster compression but worse compression ratio. + Higher levels (e.g. *19*) mean slower compression but better compression + ratio. The maximum possible value depends on the libzstd version. + Decompression speed is essentially the same for all levels. +*< 0*:: + A negative value corresponds to Zstandard's “ultra-fast” compression + levels, which are even faster than level 1 but with less good compression + ratios. For instance, level *-3* corresponds to “--fast=3” for the *zstd* + command line tool. +*0* (default):: + The value *0* means that ccache will choose a suitable level, currently + *1*. +-- -*cpp_extension* (*CCACHE_EXTENSION*):: +[[config_cpp_extension]] *cpp_extension* (*CCACHE_EXTENSION*):: - This setting can be used to force a certain extension for the intermediate + This option can be used to force a certain extension for the intermediate preprocessed file. The default is to automatically determine the extension to use for intermediate preprocessor files based on the type of file being compiled, but that sometimes doesn't work. For example, when using the ``aCC'' compiler on HP-UX, set the cpp extension to *i*. -*debug* (*CCACHE_DEBUG* or *CCACHE_NODEBUG*, see <<_boolean_values,Boolean values>> above):: +[[config_debug]] *debug* (*CCACHE_DEBUG* or *CCACHE_NODEBUG*, see <<_boolean_values,Boolean values>> above):: If true, enable the debug mode. The debug mode creates per-object debug files that are helpful when debugging unexpected cache misses. Note however that ccache performance will be reduced slightly. See <<_cache_debugging,debugging>> for more information. The default is false. -*depend_mode* (*CCACHE_DEPEND* or *CCACHE_NODEPEND*, see <<_boolean_values,Boolean values>> above):: +[[config_depend_mode]] *depend_mode* (*CCACHE_DEPEND* or *CCACHE_NODEPEND*, see <<_boolean_values,Boolean values>> above):: If true, the depend mode will be used. The default is false. See <<_the_depend_mode,THE DEPEND MODE>>. -*direct_mode* (*CCACHE_DIRECT* or *CCACHE_NODIRECT*, see <<_boolean_values,Boolean values>> above):: +[[config_direct_mode]] *direct_mode* (*CCACHE_DIRECT* or *CCACHE_NODIRECT*, see <<_boolean_values,Boolean values>> above):: If true, the direct mode will be used. The default is true. See <<_the_direct_mode,THE DIRECT MODE>>. -*disable* (*CCACHE_DISABLE* or *CCACHE_NODISABLE*, see <<_boolean_values,Boolean values>> above):: +[[config_disable]] *disable* (*CCACHE_DISABLE* or *CCACHE_NODISABLE*, see <<_boolean_values,Boolean values>> above):: When true, ccache will just call the real compiler, bypassing the cache completely. The default is false. -*extra_files_to_hash* (*CCACHE_EXTRAFILES*):: +[[config_extra_files_to_hash]] *extra_files_to_hash* (*CCACHE_EXTRAFILES*):: - This setting is a list of paths to files that ccache will include in the - the hash sum that identifies the build. The list separator is semicolon on + This option is a list of paths to files that ccache will include in the the + hash sum that identifies the build. The list separator is semicolon on Windows systems and colon on other systems. -*hard_link* (*CCACHE_HARDLINK* or *CCACHE_NOHARDLINK*, see <<_boolean_values,Boolean values>> above):: +[[config_file_clone]] *file_clone* (*CCACHE_FILECLONE* or *CCACHE_NOFILECLONE*, see <<_boolean_values,Boolean values>> above):: - If true, ccache will attempt to use hard links from the cache directory - when creating the compiler output rather than using a file copy. Hard links - are never made for compressed cache files. This means that you should not - enable compression if you want to use hard links. The default is false. + If true, ccache will attempt to use file cloning (also known as “copy on + write”, “CoW” or “reflinks”) to store and fetch cached compiler results. + *file_clone* has priority over <>. The + default is false. + -WARNING: Do not enable this option unless you are aware of the consequences. -Using hard links may be slightly faster in some situations, but there are -several pitfalls since the resulting object file will share i-node with the -cached object file: +Files stored by cloning cannot be compressed, so the cache size will likely be +significantly larger if this option is enabled. However, performance may be +improved depending on the use case. + -1. If the resulting object file is modified in any way, the cached object file - will be modified as well. For instance, if you run `strip object.o` or `echo - >object.o`, you will corrupt the cache. -2. Programs that rely on modification times (like ``make'') can be confused - since ccache updates the cached files' modification times as part of the - automatic cache size management. This will affect object files in the build - tree as well, which can retrigger the linking step even though nothing - really has changed. +Unlike the <> option, *file_clone* is completely +safe to use, but not all file systems support the feature. For such file +systems, ccache will fall back to use plain copying (or hard links if +<> is enabled). + +[[config_hard_link]] *hard_link* (*CCACHE_HARDLINK* or *CCACHE_NOHARDLINK*, see <<_boolean_values,Boolean values>> above):: -*hash_dir* (*CCACHE_HASHDIR* or *CCACHE_NOHASHDIR*, see <<_boolean_values,Boolean values>> above):: + If true, ccache will attempt to use hard links to store and fetch cached + object files. The default is false. ++ +Files stored via hard links cannot be compressed, so the cache size will likely +be significantly larger if this option is enabled. However, performance may be +improved depending on the use case. ++ +WARNING: Do not enable this option unless you are aware of these caveats: ++ +* If the resulting file is modified, the file in the cache will also be + modified since they share content, which corrupts the cache entry. As of + version 4.0, ccache makes stored and fetched object files read-only as a + safety measure guard. Furthermore, a simple integrity check is made for + cached object files by verifying that their sizes are correct. This means + that mistakes like `strip file.o` or `echo >file.o` will be detected even if + the object file is made writeable, but a modification that doesn't change the + file size will not. +* Programs that don't expect that files from two different identical + compilations are hard links to each other can fail. +* Programs that rely on modification times (like ``make'') can be confused if + several users (or one user with several build trees) use the same cache + directory. The reason for this is that the object files share i-nodes and + therefore modification times. If *file.o* is in build tree A (hard-linked + from the cache) and *file.o* then is produced by ccache in build tree B by + hard-linking from the cache, the modification timestamp will be updated for + *file.o* in build tree A as well. This can retrigger relinking in build tree + A even though nothing really has changed. + +[[config_hash_dir]] *hash_dir* (*CCACHE_HASHDIR* or *CCACHE_NOHASHDIR*, see <<_boolean_values,Boolean values>> above):: If true (which is the default), ccache will include the current working directory (CWD) in the hash that is used to distinguish two compilations when generating debug info (compiler option *-g* with variations). - Exception: The CWD will not be included in the hash if *base_dir* is set - (and matches the CWD) and the compiler option *-fdebug-prefix-map* is used. - See also the discussion under + Exception: The CWD will not be included in the hash if + <> is set (and matches the CWD) and the + compiler option *-fdebug-prefix-map* is used. See also the discussion under <<_compiling_in_different_directories,COMPILING IN DIFFERENT DIRECTORIES>>. + The reason for including the CWD in the hash by default is to prevent a problem @@ -436,48 +589,80 @@ with the storage of the current working directory in the debug info of an object file, which can lead ccache to return a cached object file that has the working directory in the debug info set incorrectly. + -You can disable this setting to get cache hits when compiling the same source +You can disable this option to get cache hits when compiling the same source code in different directories if you don't mind that CWD in the debug info might be incorrect. -*ignore_headers_in_manifest* (*CCACHE_IGNOREHEADERS*):: +[[config_ignore_headers_in_manifest]] *ignore_headers_in_manifest* (*CCACHE_IGNOREHEADERS*):: - This setting is a list of paths to files (or directories with headers) that + This option is a list of paths to files (or directories with headers) that ccache will *not* include in the manifest list that makes up the direct mode. Note that this can cause stale cache hits if those headers do indeed change. The list separator is semicolon on Windows systems and colon on other systems. -*keep_comments_cpp* (*CCACHE_COMMENTS* or *CCACHE_NOCOMMENTS*, see <<_boolean_values,Boolean values>> above):: +[[config_ignore_options]] *ignore_options* (*CCACHE_IGNOREOPTIONS*):: + + This option is a space-delimited list of compiler options that ccache will + exclude from the hash. Excluding a compiler option from the hash can be + useful when you know it doesn't affect the result (but ccache doesn't know + that), or when it does and you don't care. If a compiler option in the list + is suffixed with an asterisk (`*`) it will be matched as a prefix. For + example, `-fmessage-length=*` will match both `-fmessage-length=20` and + `-fmessage-length=70`. + +[[config_inode_cache]] *inode_cache* (*CCACHE_INODECACHE* or *CCACHE_NOINODECACHE*, see <<_boolean_values,Boolean values>> above):: + + If true, enables caching of source file hashes based on device, inode and + timestamps. This will reduce the time spent on hashing included files as + the result can be resused between compilations. ++ +The feature is still experimental and thus off by default. It is currently not +available on Windows. ++ +The feature requires *temporary_dir* to be located on a local filesystem. + +[[config_keep_comments_cpp]] *keep_comments_cpp* (*CCACHE_COMMENTS* or *CCACHE_NOCOMMENTS*, see <<_boolean_values,Boolean values>> above):: If true, ccache will not discard the comments before hashing preprocessor output. This can be used to check documentation with *-Wdocumentation*. -*limit_multiple* (*CCACHE_LIMIT_MULTIPLE*):: +[[config_limit_multiple]] *limit_multiple* (*CCACHE_LIMIT_MULTIPLE*):: Sets the limit when cleaning up. Files are deleted (in LRU order) until the levels are below the limit. The default is 0.8 (= 80%). See <<_automatic_cleanup,AUTOMATIC CLEANUP>> for more information. -*log_file* (*CCACHE_LOGFILE*):: +[[config_log_file]] *log_file* (*CCACHE_LOGFILE*):: If set to a file path, ccache will write information on what it is doing to the specified file. This is useful for tracking down problems. ++ +If set to *syslog*, ccache will log using `syslog()` instead of to a file. If +you use rsyslogd, you can add something like this to `/etc/rsyslog.conf` or a +file in `/etc/rsyslog.d`: ++ +------------------------------------------------------------------------------- +# log ccache to file +:programname, isequal, "ccache" /var/log/ccache +# remove from syslog +& ~ +------------------------------------------------------------------------------- -*max_files* (*CCACHE_MAXFILES*):: +[[config_max_files]] *max_files* (*CCACHE_MAXFILES*):: This option specifies the maximum number of files to keep in the cache. Use 0 for no limit (which is the default). See also <<_cache_size_management,CACHE SIZE MANAGEMENT>>. -*max_size* (*CCACHE_MAXSIZE*):: +[[config_max_size]] *max_size* (*CCACHE_MAXSIZE*):: This option specifies the maximum size of the cache. Use 0 for no limit. The default value is 5G. Available suffixes: k, M, G, T (decimal) and Ki, Mi, Gi, Ti (binary). The default suffix is G. See also <<_cache_size_management,CACHE SIZE MANAGEMENT>>. -*path* (*CCACHE_PATH*):: +[[config_path]] *path* (*CCACHE_PATH*):: If set, ccache will search directories in this list when looking for the real compiler. The list separator is semicolon on Windows systems and colon @@ -485,7 +670,7 @@ might be incorrect. matching the compiler name in the normal *PATH* that isn't a symbolic link to ccache itself. -*pch_external_checksum* (*CCACHE_PCH_EXTSUM* or *CCACHE_NOPCH_EXTSUM*, see <<_boolean_values,Boolean values>> above):: +[[config_pch_external_checksum]] *pch_external_checksum* (*CCACHE_PCH_EXTSUM* or *CCACHE_NOPCH_EXTSUM*, see <<_boolean_values,Boolean values>> above):: When this option is set, and ccache finds a precompiled header file, ccache will look for a file with the extension ``.sum'' added @@ -493,41 +678,42 @@ might be incorrect. of the precompiled header itself to work around the performance penalty of hashing very large files. -*prefix_command* (*CCACHE_PREFIX*):: +[[config_prefix_command]] *prefix_command* (*CCACHE_PREFIX*):: This option adds a list of prefixes (separated by space) to the command line that ccache uses when invoking the compiler. See also <<_using_ccache_with_other_compiler_wrappers,USING CCACHE WITH OTHER COMPILER WRAPPERS>>. -*prefix_command_cpp* (*CCACHE_PREFIX_CPP*):: +[[config_prefix_command_cpp]] *prefix_command_cpp* (*CCACHE_PREFIX_CPP*):: This option adds a list of prefixes (separated by space) to the command line that ccache uses when invoking the preprocessor. -*read_only* (*CCACHE_READONLY* or *CCACHE_NOREADONLY*, see <<_boolean_values,Boolean values>> above):: +[[config_read_only]] *read_only* (*CCACHE_READONLY* or *CCACHE_NOREADONLY*, see <<_boolean_values,Boolean values>> above):: - If true, ccache will attempt to use existing cached object files, but it - will not add new results to the cache. Statistics counters will still be - updated, though, unless the *stats* option is set to *false*. + If true, ccache will attempt to use existing cached results, but it will not + add new results to the cache. Statistics counters will still be updated, + though, unless the <> option is set to *false*. + If you are using this because your ccache directory is read-only, you need to -set *temporary_dir* since ccache will fail to create temporary files otherwise. -You may also want to set *stats = false* to make ccache not even try to update -stats files. +set <> since ccache will fail to create +temporary files otherwise. You may also want to set <> to +*false* make ccache not even try to update stats files. -*read_only_direct* (*CCACHE_READONLY_DIRECT* or *CCACHE_NOREADONLY_DIRECT*, see <<_boolean_values,Boolean values>> above):: +[[config_read_only_direct]] *read_only_direct* (*CCACHE_READONLY_DIRECT* or *CCACHE_NOREADONLY_DIRECT*, see <<_boolean_values,Boolean values>> above):: - Just like *read_only* except that ccache will only try to retrieve results - from the cache using the direct mode, not the preprocessor mode. See - documentation for *read_only* regarding using a read-only ccache directory. + Just like <> except that ccache will only try + to retrieve results from the cache using the direct mode, not the + preprocessor mode. See documentation for <> + regarding using a read-only ccache directory. -*recache* (*CCACHE_RECACHE* or *CCACHE_NORECACHE*, see <<_boolean_values,Boolean values>> above):: +[[config_recache]] *recache* (*CCACHE_RECACHE* or *CCACHE_NORECACHE*, see <<_boolean_values,Boolean values>> above):: If true, ccache will not use any previously stored result. New results will still be cached, possibly overwriting any pre-existing results. -*run_second_cpp* (*CCACHE_CPP2* or *CCACHE_NOCPP2*, see <<_boolean_values,Boolean values>> above):: +[[config_run_second_cpp]] *run_second_cpp* (*CCACHE_CPP2* or *CCACHE_NOCPP2*, see <<_boolean_values,Boolean values>> above):: If true, ccache will first run the preprocessor to preprocess the source code (see <<_the_preprocessor_mode,THE PREPROCESSOR MODE>>) and then on a @@ -548,81 +734,89 @@ preprocessor information, and only process the *#include* directives. When run in this way, the preprocessor arguments will be passed to the compiler since it still has to do _some_ preprocessing (like macros). -*sloppiness* (*CCACHE_SLOPPINESS*):: +[[config_sloppiness]] *sloppiness* (*CCACHE_SLOPPINESS*):: By default, ccache tries to give as few false cache hits as possible. However, in certain situations it's possible that you know things that - ccache can't take for granted. This setting makes it possible to tell + ccache can't take for granted. This option makes it possible to tell ccache to relax some checks in order to increase the hit rate. The value - should be a comma-separated string with options. Available options are: + should be a comma-separated string with one or several of the following + values: + -- *clang_index_store*:: Ignore the Clang compiler option *-index-store-path* and its argument when computing the manifest hash. This is useful if you use Xcode, which uses an index store path derived from the local project path. Note that the index - store won't be updated correctly on cache hits if you enable this option. + store won't be updated correctly on cache hits if you enable this + sloppiness. *file_stat_matches*:: - ccache normally examines a file's contents to determine whether it matches - the cached version. With this option set, ccache will consider a file as - matching its cached version if the mtimes and ctimes match. + Ccache normally examines a file's contents to determine whether it matches + the cached version. With this sloppiness set, ccache will consider a file + as matching its cached version if the mtimes and ctimes match. *file_stat_matches_ctime*:: Ignore ctimes when *file_stat_matches* is enabled. This can be useful when backdating files' mtimes in a controlled way. *include_file_ctime*:: By default, ccache will not cache a file if it includes a header whose - ctime is too new. This option disables that check. + ctime is too new. This sloppiness disables that check. *include_file_mtime*:: By default, ccache will not cache a file if it includes a header whose - mtime is too new. This option disables that check. + mtime is too new. This sloppiness disables that check. *locale*:: - ccache includes the environment variables *LANG*, *LC_ALL*, *LC_CTYPE* and + Ccache includes the environment variables *LANG*, *LC_ALL*, *LC_CTYPE* and *LC_MESSAGES* in the hash by default since they may affect localization of - compiler warning messages. Set this option to tell ccache not to do that. + compiler warning messages. Set this sloppiness to tell ccache not to do + that. *pch_defines*:: Be sloppy about **#define**s when precompiling a header file. See <<_precompiled_headers,PRECOMPILED HEADERS>> for more information. +*modules*:: + By default, ccache will not cache compilations if *-fmodules* is used since + it cannot hash the state of compiler's internal representation of relevant + modules. This sloppiness allows caching in such a case. See + <<_c_modules,C++ MODULES>> for more information. *system_headers*:: By default, ccache will also include all system headers in the manifest. - With this option set, ccache will only include system headers in the hash - but not add the system header files to the list of include files. + With this sloppiness set, ccache will only include system headers in the + hash but not add the system header files to the list of include files. *time_macros*:: - Ignore `__DATE__` and `__TIME__` being present in the source code. + Ignore `__DATE__`, `__TIME__` and `__TIMESTAMP__` being present in the + source code. -- + See the discussion under <<_troubleshooting,TROUBLESHOOTING>> for more information. -*stats* (*CCACHE_STATS* or *CCACHE_NOSTATS*, see <<_boolean_values,Boolean values>> above):: +[[config_stats]] *stats* (*CCACHE_STATS* or *CCACHE_NOSTATS*, see <<_boolean_values,Boolean values>> above):: If true, ccache will update the statistics counters on each compilation. The default is true. -*temporary_dir* (*CCACHE_TEMPDIR*):: +[[config_temporary_dir]] *temporary_dir* (*CCACHE_TEMPDIR*):: - This setting specifies where ccache will put temporary files. The default - is */tmp*. + This option specifies where ccache will put temporary files. The default is + */run/user//ccache-tmp* if */run/user/* exists, otherwise + */tmp*. + NOTE: In previous versions of ccache, *CCACHE_TEMPDIR* had to be on the same - filesystem as the *CCACHE_DIR* path, but this requirement has been - relaxed.) +filesystem as the *CCACHE_DIR* path, but this requirement has been relaxed.) -*umask* (*CCACHE_UMASK*):: +[[config_umask]] *umask* (*CCACHE_UMASK*):: - This setting specifies the umask for ccache and all child processes (such - as the compiler). This is mostly useful when you wish to share your cache - with other users. Note that this also affects the file permissions set on - the object files created from your compilations. + This option specifies the umask for files and directories in the cache + directory. This is mostly useful when you wish to share your cache with + other users. Cache size management --------------------- By default, ccache has a 5 GB limit on the total size of files in the cache and -no limit on the number of files. You can set different limits using the -*-M*/*--max-size* and *-F*/*--max-files* options. Use *ccache -s/--show-stats* -to see the cache size and the currently configured limits (in addition to other -various statistics). +no limit on the number of files. You can set different limits using the command +line options *-M*/*--max-size* and *-F*/*--max-files*. Use *ccache +-s/--show-stats* to see the cache size and the currently configured limits (in +addition to other various statistics). Cleanup can be triggered in two different ways: automatic and manual. @@ -630,7 +824,7 @@ Cleanup can be triggered in two different ways: automatic and manual. Automatic cleanup ~~~~~~~~~~~~~~~~~ -ccache maintains counters for various statistics about the cache, including the +Ccache maintains counters for various statistics about the cache, including the size and number of all cached files. In order to improve performance and reduce issues with concurrent ccache invocations, there is one statistics file for each of the sixteen subdirectories in the cache. @@ -647,8 +841,9 @@ will: 1. Count all files in the subdirectory and compute their aggregated size. 2. Remove files in LRU (least recently used) order until the size is at most *limit_multiple * max_size / 16* and the number of files is at most - *limit_multiple * max_files / 16*, where *limit_multiple*, *max_size* and - *max_files* are configuration settings. + *limit_multiple * max_files / 16*, where + <>, <> + and <> are configuration options. 3. Set the size and file number counters to match the files that were kept. The reason for removing more files than just those needed to not exceed the max @@ -661,18 +856,56 @@ Manual cleanup You can run *ccache -c/--cleanup* to force cleanup of the whole cache, i.e. all of the sixteen subdirectories. This will recalculate the statistics counters -and make sure that the *max_size* and *max_files* settings are not exceeded. -Note that *limit_multiple* is not taken into account for manual cleanup. +and make sure that the configuration options *max_size* and +<> are not exceeded. Note that +<> is not taken into account for manual +cleanup. Cache compression ----------------- -ccache can optionally compress all files it puts into the cache using the -compression library zlib. While this may involve a tiny performance slowdown, -it increases the number of files that fit in the cache. You can turn on -compression with the *compression* configuration setting and you can also tweak -the compression level with *compression_level*. +Ccache will by default compress all data it puts into the cache using the +compression algorithm Zstandard (zstd) using compression level 1. The algorithm +is fast enough that there should be little reason to turn off compression to +gain performance. One exception is if the cache is located on a compressed file +system, in which case the compression performed by ccache of course is +redundant. See the documentation for the configuration options +<> and +<> for more information. + +You can use the command line option *-x/--show-compression* to print +information related to compression. Example: + +------------------------------------------------------------------------------- +Total data: 14.8 GB (16.0 GB disk blocks) +Compressed data: 11.3 GB (30.6% of original size) + - Original data: 36.9 GB + - Compression ratio: 3.267 x (69.4% space savings) +Incompressible data: 3.5 GB +------------------------------------------------------------------------------- + +Notes: + +* The “disk blocks” size is the cache size when taking disk block size into + account. This value should match the “cache size” value from “ccache + --show-stats”. The other size numbers refer to actual content sizes. +* “Compressed data” refers to result and manifest files stored in the cache. +* “Incompressible data” refers to files that are always stored uncompressed + (triggered by enabling <> or + <>) or unknown files (for instance files + created by older ccache versions). +* The compression ratio is affected by + <>. + +The cache data can also be recompressed to another compression level (or made +uncompressed) with the command line option *-X/--recompress*. If you choose to +disable compression by default or to use a low compression level, you can +(re)compress newly cached data with a higher compression level after the build +or at another time when there are more CPU cycles available, for instance every +night. Full recompression potentially takes a lot of time, but only files that +are currently compressed with a different level than the target level will be +recompressed. Cache statistics @@ -687,8 +920,9 @@ Cache statistics Uncachable compilation or linking by an autoconf test. | bad compiler arguments | -Malformed compiler argument, e.g. missing a value for an option that requires -an argument or failure to read a file specified by an option argument. +Malformed compiler argument, e.g. missing a value for a compiler option that +requires an argument or failure to read a file specified by a compiler option +argument. | cache file missing | A file was unexpectedly missing from the cache. This only happens in rare @@ -718,6 +952,9 @@ The compiler was called for preprocessing, not compiling. Preconditions for using <<_precompiled_headers,precompiled headers>> were not fulfilled. +| can't use modules | +Preconditions for using <<_c_modules,C++ modules>> were not fulfilled. + | ccache internal error | Unexpected failure, e.g. due to problems reading/writing the cache. @@ -729,8 +966,8 @@ being reached or due to explicit *ccache -c/--cleanup* calls. The compilation failed. No result stored in the cache. | compiler check failed | -A compiler check program specified by *compiler_check* (*CCACHE_COMPILERCHECK*) -failed. +A compiler check program specified by +<> (*CCACHE_COMPILERCHECK*) failed. | compiler produced empty output | The compiler's output file (typically an object file) was empty after @@ -748,8 +985,8 @@ normally never do, so ccache is not designed to store such output in the cache. The compiler to execute could not be found. | error hashing extra file | -Failure reading a file specified by *extra_files_to_hash* -(*CCACHE_EXTRAFILES*). +Failure reading a file specified by +<> (*CCACHE_EXTRAFILES*). | files in cache | Current number of files in the cache. @@ -797,20 +1034,20 @@ How ccache works The basic idea is to detect when you are compiling exactly the same code a second time and reuse the previously produced output. The detection is done by hashing different kinds of information that should be unique for the -compilation and then using the hash sum to identify the cached output. ccache -uses MD4, a very fast cryptographic hash algorithm, for the hashing. (MD4 is -nowadays too weak to be useful in cryptographic contexts, but it should be safe -enough to be used to identify recompilations.) On a cache hit, ccache is able -to supply all of the correct compiler outputs (including all warnings, -dependency file, etc) from the cache. - -ccache has two ways of gathering information used to look up results in the +compilation and then using the hash sum to identify the cached output. Ccache +uses BLAKE3, a very fast cryptographic hash algorithm, for the hashing. On a +cache hit, ccache is able to supply all of the correct compiler outputs +(including all warnings, dependency file, etc) from the cache. Data stored in +the cache is checksummed with XXH3, an extremely fast non-cryptographic +algorithm, to detect corruption. + +Ccache has two ways of gathering information used to look up results in the cache: -* the *direct mode*, where ccache hashes the source code and include files - directly * the *preprocessor mode*, where ccache runs the preprocessor on the source code and hashes the result +* the *direct mode*, where ccache hashes the source code and include files + directly The direct mode is generally faster since running the preprocessor has some overhead. @@ -830,10 +1067,26 @@ The following information is always included in the hash: * the extension used by the compiler for a file with preprocessor output (normally *.i* for C code and *.ii* for C++ code) * the compiler's size and modification time (or other compiler-specific - information specified by the *compiler_check* setting) + information specified by <>) * the name of the compiler -* the current directory (if the *hash_dir* setting is enabled) -* contents of files specified by the *extra_files_to_hash* setting (if any) +* the current directory (if <> is enabled) +* contents of files specified by + <> (if any) + + +The preprocessor mode +~~~~~~~~~~~~~~~~~~~~~ + +In the preprocessor mode, the hash is formed of the common information and: + +* the preprocessor output from running the compiler with *-E* +* the command line options except those that affect include files (*-I*, + *-include*, *-D*, etc; the theory is that these command line options will + change the preprocessor output if they have any effect at all) +* any standard error output generated by the preprocessor + +Based on the hash, the cached compilation result can be looked up directly in +the cache. The direct mode @@ -842,7 +1095,7 @@ The direct mode In the direct mode, the hash is formed of the common information and: * the input source file -* the command line options +* the compiler options Based on the hash, a data structure called ``manifest'' is looked up in the cache. The manifest contains: @@ -871,7 +1124,7 @@ in the absolute majority of cases. The direct mode will be disabled if any of the following holds: -* the configuration setting *direct_mode* is false +* <> is false * a modification time of one of the include files is too new (needed to avoid a race condition) * a compiler option not supported by the direct mode is used: @@ -881,21 +1134,6 @@ The direct mode will be disabled if any of the following holds: * the string `__TIME__` is present in the source code -The preprocessor mode -~~~~~~~~~~~~~~~~~~~~~ - -In the preprocessor mode, the hash is formed of the common information and: - -* the preprocessor output from running the compiler with *-E* -* the command line options except options that affect include files (*-I*, - *-include*, *-D*, etc; the theory is that these options will change the - preprocessor output if they have any effect at all) -* any standard error output generated by the preprocessor - -Based on the hash, the cached compilation result can be looked up directly in -the cache. - - The depend mode ~~~~~~~~~~~~~~~ @@ -925,18 +1163,18 @@ Disadvantages: The depend mode will be disabled if any of the following holds: -* the configuration setting *depend_mode* is false -* the configuration setting *run_second_cpp* is false -* the compiler is not generating dependencies using *-MD* or *-MMD* +* <> is false. +* <> is false. +* The compiler is not generating dependencies using *-MD* or *-MMD*. Cache debugging --------------- To find out what information ccache actually is hashing, you can enable the -debug mode via the configuration setting *debug* or by setting *CCACHE_DEBUG* -in the environment. This can be useful if you are investigating why you don't -get cache hits. Note that performance will be reduced slightly. +debug mode via the configuration option <> or by setting +*CCACHE_DEBUG* in the environment. This can be useful if you are investigating +why you don't get cache hits. Note that performance will be reduced slightly. When the debug mode is enabled, ccache will create up to five additional files next to the object file: @@ -961,9 +1199,9 @@ Log for this object file. |============================================================================== -In the direct mode, ccache uses the MD4 hash of the *ccache-input-c* -+ *ccache-input-d* data (where *+* means concatenation), while the -*ccache-input-c* + *ccache-input-p* data is used in the preprocessor mode. +In the direct mode, ccache uses the 160 bit BLAKE3 hash of the +*ccache-input-c* + *ccache-input-d* data (where *+* means concatenation), while +the *ccache-input-c* + *ccache-input-p* data is used in the preprocessor mode. The *ccache-input-text* file is a combined text version of the three binary input files. It has three sections (“COMMON”, “DIRECT MODE” and @@ -1008,38 +1246,38 @@ directories: file, you must either: + -- -** use the *-fdebug-prefix-map=_old_=_new_* option for relocating debug info to - a common prefix (e.g. *-fdebug-prefix-map=$PWD=.*); or +** use the compiler option *-fdebug-prefix-map=_old_=_new_* for relocating + debug info to a common prefix (e.g. *-fdebug-prefix-map=$PWD=.*); or ** set *hash_dir = false*. -- * If you use absolute paths anywhere on the command line (e.g. the source code file path or an argument to compiler options like *-I* and *-MF*), you must - to set *base_dir* to an absolute path to a ``base directory''. ccache will - then rewrite absolute paths under that directory to relative before computing - the hash. + set <> to an absolute path to a ``base + directory''. Ccache will then rewrite absolute paths under that directory to + relative before computing the hash. Precompiled headers ------------------- -ccache has support for GCC's precompiled headers. However, you have to do some +Ccache has support for GCC's precompiled headers. However, you have to do some things to make it work properly: -* You must set *sloppiness* to *pch_defines,time_macros*. The reason is that - ccache can't tell whether `__TIME__` or `__DATE__` is used when using a - precompiled header. Further, it can't detect changes in **#define**s in the - source code because of how preprocessing works in combination with - precompiled headers. +* You must set <> to *pch_defines,time_macros*. + The reason is that ccache can't tell whether `__TIME__`, `__DATE__` or + `__TIMESTAMP__` is used when using a precompiled header. Further, it can't + detect changes in **#define**s in the source code because of how + preprocessing works in combination with precompiled headers. * You must either: + -- -** use the *-include* compiler option to include the precompiled header (i.e., +** use the compiler option *-include* to include the precompiled header (i.e., don't use *#include* in the source code to include the header; the filename itself must be sufficient to find the header, i.e. *-I* paths are not searched); or -** (for the Clang compiler) use the *-include-pch* compiler option to include +** (for the Clang compiler) use the compiler option *-include-pch* to include the PCH file generated from the precompiled header; or -** (for the GCC compiler) add the *-fpch-preprocess* compiler option when +** (for the GCC compiler) add the compiler option *-fpch-preprocess* when compiling. If you don't do this, either the non-precompiled version of the header file @@ -1049,6 +1287,22 @@ non-precompiled header file is not available). -- +C++ modules +----------- + +Ccache has support for Clang's *-fmodules* option. In practice ccache only +additionally hashes *module.modulemap* files; it does not know how Clang +handles its cached binary form of modules so those are ignored. This should not +matter in practice: as long as everything else (including *module.modulemap* +files) is the same the cached result should work. Still, you must set +<> to *modules* to allow caching. + +You must use both <<_the_direct_mode,*direct mode*>> and +<<_the_depend_mode,*depend mode*>>. When using <<_the_preprocessor_mode,the +preprocessor mode>> Clang does not provide enough information to allow hashing +of *module.modulemap* files. + + Sharing a cache --------------- @@ -1057,11 +1311,11 @@ directory. To share a cache without unpleasant side effects, the following conditions should to be met: * Use the same cache directory. -* Make sure that the configuration setting *hard_link* is false (which is the - default). +* Make sure that the configuration option <> is + false (which is the default). * Make sure that all users are in the same group. -* Set the configuration setting *umask* to 002. This ensures that cached files - are accessible to everyone in the group. +* Set the configuration option <> to 002. This ensures + that cached files are accessible to everyone in the group. * Make sure that all users have write permission in the entire cache directory (and that you trust all users of the shared cache). * Make sure that the setgid bit is set on all directories in the cache. This @@ -1093,10 +1347,10 @@ filesystems), but keep in mind that: * Having the cache on NFS may slow down compilation. Make sure to do some benchmarking to see if it's worth it. -* ccache hasn't been tested very thoroughly on NFS. +* Ccache hasn't been tested very thoroughly on NFS. -A tip is to set *temporary_dir* to a directory on the local host to avoid NFS -traffic for temporary files. +A tip is to set <> to a directory on the +local host to avoid NFS traffic for temporary files. It is recommended to use the same operating system version when using a shared cache. If operating system versions are different then system include files @@ -1111,27 +1365,30 @@ Using ccache with other compiler wrappers The recommended way of combining ccache with another compiler wrapper (such as ``distcc'') is by letting ccache execute the compiler wrapper. This is -accomplished by defining the configuration setting *prefix_command*, for +accomplished by defining <>, for example by setting the environment variable *CCACHE_PREFIX* to the name of the -wrapper (e.g. *distcc*). ccache will then prefix the command line with the +wrapper (e.g. *distcc*). Ccache will then prefix the command line with the specified command when running the compiler. To specify several prefix -commands, set *prefix_command* to a colon-separated list of commands. +commands, set <> to a colon-separated +list of commands. -Unless you set *compiler_check* to a suitable command (see the description of -that configuration option), it is not recommended to use the form *ccache -anotherwrapper compiler args* as the compilation command. It's also not -recommended to use the masquerading technique for the other compiler wrapper. -The reason is that by default, ccache will in both cases hash the mtime and -size of the other wrapper instead of the real compiler, which means that: +Unless you set <> to a suitable command +(see the description of that configuration option), it is not recommended to +use the form *ccache anotherwrapper compiler args* as the compilation command. +It's also not recommended to use the masquerading technique for the other +compiler wrapper. The reason is that by default, ccache will in both cases hash +the mtime and size of the other wrapper instead of the real compiler, which +means that: * Compiler upgrades will not be detected properly. * The cached results will not be shared between compilations with and without the other wrapper. -Another minor thing is that if *prefix_command* is used, ccache will not invoke -the other wrapper when running the preprocessor, which increases performance. -You can use the *prefix_command_cpp* configuration setting if you also want to -invoke the other wrapper when doing preprocessing (normally by adding *-E*). +Another minor thing is that if <> is +used, ccache will not invoke the other wrapper when running the preprocessor, +which increases performance. You can use +<> if you also want to invoke +the other wrapper when doing preprocessing (normally by adding *-E*). Caveats @@ -1139,19 +1396,6 @@ Caveats * The direct mode fails to pick up new header files in some rare scenarios. See <<_the_direct_mode,THE DIRECT MODE>> above. -* When run via ccache, warning messages produced by GCC 4.9 and newer will only - be colored when the environment variable *GCC_COLORS* is set. An alternative - to setting *GCC_COLORS* is to pass `-fdiagnostics-color` explicitly when - compiling (but then color codes will also be present when redirecting stderr - to a file). -* If ccache guesses that the compiler may emit colored warnings, then a - compilation with stderr referring to a TTY will be considered different from - a compilation with a redirected stderr, thus not sharing cache entries. This - happens for clang by default and for GCC when *GCC_COLORS* is set as - mentioned above. If you want to share cache hits, you can pass - `-f[no-]diagnostics-color` (GCC) or `-f[no-]color-diagnostics` (clang) - explicitly when compiling (but then color codes will be either on or off for - both the TTY and the redirected case). Troubleshooting @@ -1161,16 +1405,16 @@ General ~~~~~~~ A general tip for getting information about what ccache is doing is to enable -debug logging by setting the configuration option *debug* (or the environment -variable *CCACHE_DEBUG*); see <<_cache_debugging,debugging>> for more -information. Another way of keeping track of what is happening is to check the -output of *ccache -s*. +debug logging by setting the configuration option <> (or +the environment variable *CCACHE_DEBUG*); see <<_cache_debugging,debugging>> +for more information. Another way of keeping track of what is happening is to +check the output of *ccache -s*. Performance ~~~~~~~~~~~ -ccache has been written to perform well out of the box, but sometimes you may +Ccache has been written to perform well out of the box, but sometimes you may have to do some adjustments of how you use the compiler and ccache in order to improve performance. @@ -1192,33 +1436,42 @@ problems and what may be done to increase the hit rate: affect the preprocessor output. ** The compiler option *-Xpreprocessor* or *-Wp,_X_* (except *-Wp,-MD,_path_*, *-Wp,-MMD,_path_*, and *-Wp,-D_define_*) is used. -** This was the first compilation with a new value of the base directory - setting. +** This was the first compilation with a new value of the + <>. ** A modification time of one of the include files is too new (created the same second as the compilation is being done). This check is made to avoid a race - condition. To fix this, create the include file earlier in the build process, - if possible, or set *sloppiness* to *include_file_ctime, include_file_mtime* - if you are willing to take the risk. (The race condition consists of these - events: the preprocessor is run; an include file is modified by someone; the - new include file is hashed by ccache; the real compiler is run on the - preprocessor's output, which contains data from the old header file; the - wrong object file is stored in the cache.) -** The `__TIME__` preprocessor macro is (potentially) being used. ccache turns + condition. To fix this, create the include file earlier in the build + process, if possible, or set <> to + *include_file_ctime, include_file_mtime* if you are willing to take the risk. + (The race condition consists of these events: the preprocessor is run; an + include file is modified by someone; the new include file is hashed by + ccache; the real compiler is run on the preprocessor's output, which contains + data from the old header file; the wrong object file is stored in the cache.) +** The `__TIME__` preprocessor macro is (potentially) being used. Ccache turns off direct mode if `__TIME__` is present in the source code. This is done as a safety measure since the string indicates that a `__TIME__` macro _may_ affect the output. (To be sure, ccache would have to run the preprocessor, but the sole point of the direct mode is to avoid that.) If you know that `__TIME__` isn't used in practise, or don't care if ccache produces objects where `__TIME__` is expanded to something in the past, you can set - *sloppiness* to *time_macros*. + <> to *time_macros*. ** The `__DATE__` preprocessor macro is (potentially) being used and the date has changed. This is similar to how `__TIME__` is handled. If `__DATE__` is present in the source code, ccache hashes the current date in order to be able to produce the correct object file if the `__DATE__` macro affects the output. If you know that `__DATE__` isn't used in practise, or don't care if ccache produces objects where `__DATE__` is expanded to something in the - past, you can set *sloppiness* to *time_macros*. -** The input file path has changed. ccache includes the input file path in the + past, you can set <> to *time_macros*. +** The `__TIMESTAMP__` preprocessor macro is (potentially) being used and the + source file's modification time has changed. This is similar to how + `__TIME__` is handled. If `__TIMESTAMP__` is present in the source code, + ccache hashes the string representation of the source file's modification + time in order to be able to produce the correct object file if the + `__TIMESTAMP__` macro affects the output. If you know that `__TIMESTAMP__` + isn't used in practise, or don't care if ccache produces objects where + `__TIMESTAMP__` is expanded to something in the past, you can set + <> to *time_macros*. +** The input file path has changed. Ccache includes the input file path in the direct mode hash to be able to take relative include files into account and to produce a correct object file if the source code includes a `__FILE__` macro. @@ -1226,19 +1479,21 @@ problems and what may be done to increase the hit rate: compiled and cached before, ccache has either detected that something has changed anyway or a cleanup has been performed (either explicitly or implicitly when a cache limit has been reached). Some perhaps unobvious - things that may result in a cache miss are usage of `__TIME__` or - `__DATE__` macros, or use of automatically generated code that contains a - timestamp, build counter or other volatile information. + things that may result in a cache miss are usage of `__TIME__`, `__DATE__` or + `__TIMESTAMP__` macros, or use of automatically generated code that contains + a timestamp, build counter or other volatile information. * If ``multiple source files'' has been incremented, it's an indication that - the compiler has been invoked on several source code files at once. ccache + the compiler has been invoked on several source code files at once. Ccache doesn't support that. Compile the source code files separately if possible. * If ``unsupported compiler option'' has been incremented, enable debug logging - and check which option was rejected. + and check which compiler option was rejected. * If ``preprocessor error'' has been incremented, one possible reason is that precompiled headers are being used. See <<_precompiled_headers,PRECOMPILED HEADERS>> for how to remedy this. * If ``can't use precompiled header'' has been incremented, see <<_precompiled_headers,PRECOMPILED HEADERS>>. +* If ``can't use modules'' has been incremented, see + <<_c_modules,C++ MODULES>>. Corrupt object files @@ -1248,8 +1503,8 @@ It should be noted that ccache is susceptible to general storage problems. If a bad object file sneaks into the cache for some reason, it will of course stay bad. Some possible reasons for erroneous object files are bad hardware (disk drive, disk controller, memory, etc), buggy drivers or file systems, a bad -*prefix_command* or compiler wrapper. If this happens, the easiest way of -fixing it is this: +<> or compiler wrapper. If this +happens, the easiest way of fixing it is this: 1. Build so that the bad object file ends up in the build tree. 2. Remove the bad object file from the build tree. @@ -1273,6 +1528,6 @@ etc, can be found on ccache's web site: . Author ------ -ccache was originally written by Andrew Tridgell and is currently developed and +Ccache was originally written by Andrew Tridgell and is currently developed and maintained by Joel Rosdahl. See AUTHORS.txt or AUTHORS.html and for a list of contributors. diff --git a/doc/MANUAL.html b/doc/MANUAL.html deleted file mode 100644 index da39f21..0000000 --- a/doc/MANUAL.html +++ /dev/null @@ -1,2728 +0,0 @@ - - - - - - -CCACHE(1) - - - - - -
-
-

Name

-
-

ccache - a fast C/C++ compiler cache

-
-
-
-

Synopsis

-
-
-
ccache [options]
-ccache compiler [compiler options]
-compiler [compiler options]                   (via symbolic link)
-
-
-
-
-
-

Description

-
-

ccache is a compiler cache. It speeds up recompilation by caching the result of -previous compilations and detecting when the same compilation is being done -again. Supported languages are C, C++, Objective-C and Objective-C++.

-

ccache has been carefully written to always produce exactly the same compiler -output that you would get without the cache. The only way you should be able to -tell that you are using ccache is the speed. Currently known exceptions to this -goal are listed under CAVEATS. If you ever discover an -undocumented case where ccache changes the output of your compiler, please let -us know.

-
-

Features

-
    -
  • -

    -Keeps statistics on hits/misses. -

    -
  • -
  • -

    -Automatic cache size management. -

    -
  • -
  • -

    -Can cache compilations that generate warnings. -

    -
  • -
  • -

    -Easy installation. -

    -
  • -
  • -

    -Low overhead. -

    -
  • -
  • -

    -Optionally compresses files in the cache to reduce disk space. -

    -
  • -
-
-
-

Limitations

-
    -
  • -

    -Only knows how to cache the compilation of a single - C/C++/Objective-C/Objective-C++ file. Other types of compilations - (multi-file compilation, linking, etc) will silently fall back to running the - real compiler. -

    -
  • -
  • -

    -Only works with GCC and compilers that behave similar enough. -

    -
  • -
  • -

    -Some compiler flags are not supported. If such a flag is detected, ccache - will silently fall back to running the real compiler. -

    -
  • -
-
-
-
-
-

Run modes

-
-

There are two ways to use ccache. You can either prefix your compilation -commands with ccache or you can let ccache masquerade as the compiler by -creating a symbolic link (named as the compiler) to ccache. The first method is -most convenient if you just want to try out ccache or wish to use it for some -specific projects. The second method is most useful for when you wish to use -ccache for all your compilations.

-

To use the first method, just make sure that ccache is in your PATH.

-

To use the symlinks method, do something like this:

-
-
-
cp ccache /usr/local/bin/
-ln -s ccache /usr/local/bin/gcc
-ln -s ccache /usr/local/bin/g++
-ln -s ccache /usr/local/bin/cc
-ln -s ccache /usr/local/bin/c++
-
-

And so forth. This will work as long as the directory with symlinks comes -before the path to the compiler (which is usually in /usr/bin). After -installing you may wish to run “which gcc” to make sure that the correct link -is being used.

-
- - - -
-
Warning
-
The technique of letting ccache masquerade as the compiler works well, -but currently doesn’t interact well with other tools that do the same thing. -See USING CCACHE WITH OTHER COMPILER WRAPPERS.
-
-
- - - -
-
Warning
-
Do not use a hard link, use a symbolic link. A hard link will cause -“interesting” problems.
-
-
-
-
-

Options

-
-

These options only apply when you invoke ccache as “ccache”. When invoked as -a compiler (via a symlink as described in the previous section), the normal -compiler options apply and you should refer to the compiler’s documentation.

-
-
--c, --cleanup -
-
-

- Clean up the cache by removing old cached files until the specified file - number and cache size limits are not exceeded. This also recalculates the - cache file count and size totals. Normally, there is no need to initiate - cleanup manually as ccache keeps the cache below the specified limits at - runtime and keeps statistics up to date on each compilation. Forcing a - cleanup is mostly useful if you manually modify the cache contents or - believe that the cache size statistics may be inaccurate. -

-
-
--C, --clear -
-
-

- Clear the entire cache, removing all cached files, but keeping the - configuration file. -

-
-
---dump-manifest=PATH -
-
-

- Dump manifest file at PATH in text format. This is only useful when - debugging ccache and its behavior. -

-
-
--k, --get-config=KEY -
-
-

- Print the value of configuration option KEY. See - CONFIGURATION for more information. -

-
-
---hash-file=PATH -
-
-

- Print the hash (in format <MD4>-<size>) of the file at PATH. This is only - useful when debugging ccache and its behavior. -

-
-
--h, --help -
-
-

- Print an options summary page. -

-
-
--F, --max-files=N -
-
-

- Set the maximum number of files allowed in the cache. Use 0 for no limit. - The value is stored in a configuration file in the cache directory and - applies to all future compilations. -

-
-
--M, --max-size=SIZE -
-
-

- Set the maximum size of the files stored in the cache. SIZE should be a - number followed by an optional suffix: k, M, G, T (decimal), Ki, Mi, Gi or - Ti (binary). The default suffix is G. Use 0 for no limit. The value is - stored in a configuration file in the cache directory and applies to all - future compilations. -

-
-
---print-stats -
-
-

- Print statistics counter IDs and corresponding values machine-parsable - (tab-separated) format. -

-
-
--o, --set-config=KEY=VALUE -
-
-

- Set configuration option KEY to VALUE. See - CONFIGURATION for more information. -

-
-
--p, --show-config -
-
-

- Print current configuration options and from where they originate - (environment variable, configuration file or compile-time default) in - human-readable format. -

-
-
--s, --show-stats -
-
-

- Print a summary of configuration and statistics counters in human-readable - format. -

-
-
--V, --version -
-
-

- Print version and copyright information. -

-
-
--z, --zero-stats -
-
-

- Zero the cache statistics (but not the configuration options). -

-
-
-
-
-
-

Extra options

-
-

When run as a compiler, ccache usually just takes the same command line options -as the compiler you are using. The only exception to this is the option ---ccache-skip. That option can be used to tell ccache to avoid interpreting -the next option in any way and to pass it along to the compiler as-is.

-
- - - -
-
Note
-
--ccache-skip currently only tells ccache not to interpret the next -option as a special compiler option — the option will still be included in the -direct mode hash.
-
-

The reason this can be important is that ccache does need to parse the command -line and determine what is an input filename and what is a compiler option, as -it needs the input filename to determine the name of the resulting object file -(among other things). The heuristic ccache uses when parsing the command line -is that any argument that exists as a file is treated as an input file name. By -using --ccache-skip you can force an option to not be treated as an input -file name and instead be passed along to the compiler as a command line option.

-

Another case where --ccache-skip can be useful is if ccache interprets an -option specially but shouldn’t, since the option has another meaning for your -compiler than what ccache thinks.

-
-
-
-

Configuration

-
-

ccache’s default behavior can be overridden by configuration file settings, -which in turn can be overridden by environment variables with names starting -with CCACHE_. ccache normally reads configuration from two files: first a -system-level configuration file and secondly a cache-specific configuration -file. The priority of configuration settings is as follows (where 1 is -highest):

-
    -
  1. -

    -Environment variables. -

    -
  2. -
  3. -

    -The cache-specific configuration file <ccachedir>/ccache.conf (typically - $HOME/.ccache/ccache.conf). -

    -
  4. -
  5. -

    -The system-wide configuration file <sysconfdir>/ccache.conf (typically - /etc/ccache.conf or /usr/local/etc/ccache.conf). -

    -
  6. -
  7. -

    -Compile-time defaults. -

    -
  8. -
-

As a special case, if the environment variable CCACHE_CONFIGPATH is set, -ccache reads configuration from the specified path instead of the default -paths.

-
-

Configuration file syntax

-

Configuration files are in a simple “key = value” format, one setting per -line. Lines starting with a hash sign are comments. Blank lines are ignored, as -is whitespace surrounding keys and values. Example:

-
-
-
# Set maximum cache size to 10 GB:
-max_size = 10G
-
-
-
-

Boolean values

-

Some settings are boolean values (i.e. truth values). In a configuration file, -such values must be set to the string true or false. For the corresponding -environment variables, the semantics are a bit different: a set environment -variable means “true” (even if set to the empty string), the following -case-insensitive negative values are considered an error (rather than -surprising the user): 0, false, disable and no, and an unset -environment variable means “false”. Each boolean environment variable also -has a negated form starting with CCACHE_NO. For example, CCACHE_COMPRESS -can be set to force compression and CCACHE_NOCOMPRESS can be set to force no -compression.

-
-
-

Configuration settings

-

Below is a list of available configuration settings. The corresponding -environment variable name is indicated in parentheses after each configuration -setting key.

-
-
-base_dir (CCACHE_BASEDIR) -
-
-

- This setting should be an absolute path to a directory. ccache then - rewrites absolute paths into relative paths before computing the hash that - identifies the compilation, but only for paths under the specified - directory. If set to the empty string (which is the default), no rewriting - is done. A typical path to use as the base directory is your home directory - or another directory that is a parent of your build directories. Don’t use - / as the base directory since that will make ccache also rewrite paths to - system header files, which doesn’t gain anything. -

-

See also the discussion under COMPILING IN DIFFERENT DIRECTORIES.

-
-
-cache_dir (CCACHE_DIR) -
-
-

- This setting specifies where ccache will keep its cached compiler outputs. - It will only take effect if set in the system-wide configuration file or as - an environment variable. The default is $HOME/.ccache. -

-
-
-cache_dir_levels (CCACHE_NLEVELS) -
-
-

- This setting allows you to choose the number of directory levels in the - cache directory. The default is 2. The minimum is 1 and the maximum is 8. -

-
-
-compiler (CCACHE_COMPILER or (deprecated) CCACHE_CC) -
-
-

- This setting can be used to force the name of the compiler to use. If set - to the empty string (which is the default), ccache works it out from the - command line. -

-
-
-compiler_check (CCACHE_COMPILERCHECK) -
-
-

- By default, ccache includes the modification time (“mtime”) and size of - the compiler in the hash to ensure that results retrieved from the cache - are accurate. This setting can be used to select another strategy. Possible - values are: -

-
-
-
-
-content -
-
-

- Hash the content of the compiler binary. This makes ccache very slightly - slower compared to the mtime setting, but makes it cope better with - compiler upgrades during a build bootstrapping process. -

-
-
-mtime -
-
-

- Hash the compiler’s mtime and size, which is fast. This is the default. -

-
-
-none -
-
-

- Don’t hash anything. This may be good for situations where you can safely - use the cached results even though the compiler’s mtime or size has changed - (e.g. if the compiler is built as part of your build system and the - compiler’s source has not changed, or if the compiler only has changes that - don’t affect code generation). You should only use the none setting if - you know what you are doing. -

-
-
-string:value -
-
-

- Use value as the string to calculate hash from. This can be the compiler - revision number you retrieved earlier and set here via environment variable. -

-
-
-a command string -
-
-

- Hash the standard output and standard error output of the specified - command. The string will be split on whitespace to find out the command and - arguments to run. No other interpretation of the command string will be - done, except that the special word %compiler% will be replaced with the - path to the compiler. Several commands can be specified with semicolon as - separator. Examples: -

-
-
-
-
-
%compiler% -v
-
-
-
-
%compiler% -dumpmachine; %compiler% -dumpversion
-
-

You should make sure that the specified command is as fast as possible since it -will be run once for each ccache invocation.

-

Identifying the compiler using a command is useful if you want to avoid cache -misses when the compiler has been rebuilt but not changed.

-

Another case is when the compiler (as seen by ccache) actually isn’t the real -compiler but another compiler wrapper — in that case, the default mtime -method will hash the mtime and size of the other compiler wrapper, which means -that ccache won’t be able to detect a compiler upgrade. Using a suitable -command to identify the compiler is thus safer, but it’s also slower, so you -should consider continue using the mtime method in combination with -the prefix_command setting if possible. See -USING CCACHE WITH OTHER COMPILER WRAPPERS.

-
-
-
-
-
-
-compression (CCACHE_COMPRESS or CCACHE_NOCOMPRESS, see Boolean values above) -
-
-

- If true, ccache will compress object files and other compiler output it - puts in the cache. However, this setting has no effect on how files are - retrieved from the cache; compressed and uncompressed results will still be - usable regardless of this setting. The default is false. -

-
-
-compression_level (CCACHE_COMPRESSLEVEL) -
-
-

- This setting determines the level at which ccache will compress object - files. It only has effect if compression is enabled. The value defaults - to 6, and must be no lower than 1 (fastest, worst compression) and no - higher than 9 (slowest, best compression). -

-
-
-cpp_extension (CCACHE_EXTENSION) -
-
-

- This setting can be used to force a certain extension for the intermediate - preprocessed file. The default is to automatically determine the extension - to use for intermediate preprocessor files based on the type of file being - compiled, but that sometimes doesn’t work. For example, when using the - “aCC” compiler on HP-UX, set the cpp extension to i. -

-
-
-debug (CCACHE_DEBUG or CCACHE_NODEBUG, see Boolean values above) -
-
-

- If true, enable the debug mode. The debug mode creates per-object debug - files that are helpful when debugging unexpected cache misses. Note however - that ccache performance will be reduced slightly. See - debugging for more information. The default is false. -

-
-
-depend_mode (CCACHE_DEPEND or CCACHE_NODEPEND, see Boolean values above) -
-
-

- If true, the depend mode will be used. The default is false. See - THE DEPEND MODE. -

-
-
-direct_mode (CCACHE_DIRECT or CCACHE_NODIRECT, see Boolean values above) -
-
-

- If true, the direct mode will be used. The default is true. See - THE DIRECT MODE. -

-
-
-disable (CCACHE_DISABLE or CCACHE_NODISABLE, see Boolean values above) -
-
-

- When true, ccache will just call the real compiler, bypassing the cache - completely. The default is false. -

-
-
-extra_files_to_hash (CCACHE_EXTRAFILES) -
-
-

- This setting is a list of paths to files that ccache will include in the - the hash sum that identifies the build. The list separator is semicolon on - Windows systems and colon on other systems. -

-
-
-hard_link (CCACHE_HARDLINK or CCACHE_NOHARDLINK, see Boolean values above) -
-
-

- If true, ccache will attempt to use hard links from the cache directory - when creating the compiler output rather than using a file copy. Hard links - are never made for compressed cache files. This means that you should not - enable compression if you want to use hard links. The default is false. -

-
- - - -
-
Warning
-
Do not enable this option unless you are aware of the consequences. -Using hard links may be slightly faster in some situations, but there are -several pitfalls since the resulting object file will share i-node with the -cached object file:
-
-
    -
  1. -

    -If the resulting object file is modified in any way, the cached object file - will be modified as well. For instance, if you run strip object.o or echo - >object.o, you will corrupt the cache. -

    -
  2. -
  3. -

    -Programs that rely on modification times (like “make”) can be confused - since ccache updates the cached files' modification times as part of the - automatic cache size management. This will affect object files in the build - tree as well, which can retrigger the linking step even though nothing - really has changed. -

    -
  4. -
-
-
-hash_dir (CCACHE_HASHDIR or CCACHE_NOHASHDIR, see Boolean values above) -
-
-

- If true (which is the default), ccache will include the current working - directory (CWD) in the hash that is used to distinguish two compilations - when generating debug info (compiler option -g with variations). - Exception: The CWD will not be included in the hash if base_dir is set - (and matches the CWD) and the compiler option -fdebug-prefix-map is used. - See also the discussion under - COMPILING IN DIFFERENT DIRECTORIES. -

-

The reason for including the CWD in the hash by default is to prevent a problem -with the storage of the current working directory in the debug info of an -object file, which can lead ccache to return a cached object file that has the -working directory in the debug info set incorrectly.

-

You can disable this setting to get cache hits when compiling the same source -code in different directories if you don’t mind that CWD in the debug info -might be incorrect.

-
-
-ignore_headers_in_manifest (CCACHE_IGNOREHEADERS) -
-
-

- This setting is a list of paths to files (or directories with headers) that - ccache will not include in the manifest list that makes up the direct - mode. Note that this can cause stale cache hits if those headers do indeed - change. The list separator is semicolon on Windows systems and colon on - other systems. -

-
-
-keep_comments_cpp (CCACHE_COMMENTS or CCACHE_NOCOMMENTS, see Boolean values above) -
-
-

- If true, ccache will not discard the comments before hashing preprocessor - output. This can be used to check documentation with -Wdocumentation. -

-
-
-limit_multiple (CCACHE_LIMIT_MULTIPLE) -
-
-

- Sets the limit when cleaning up. Files are deleted (in LRU order) until the - levels are below the limit. The default is 0.8 (= 80%). See - AUTOMATIC CLEANUP for more information. -

-
-
-log_file (CCACHE_LOGFILE) -
-
-

- If set to a file path, ccache will write information on what it is doing to - the specified file. This is useful for tracking down problems. -

-
-
-max_files (CCACHE_MAXFILES) -
-
-

- This option specifies the maximum number of files to keep in the cache. Use - 0 for no limit (which is the default). See also - CACHE SIZE MANAGEMENT. -

-
-
-max_size (CCACHE_MAXSIZE) -
-
-

- This option specifies the maximum size of the cache. Use 0 for no limit. - The default value is 5G. Available suffixes: k, M, G, T (decimal) and Ki, - Mi, Gi, Ti (binary). The default suffix is G. See also - CACHE SIZE MANAGEMENT. -

-
-
-path (CCACHE_PATH) -
-
-

- If set, ccache will search directories in this list when looking for the - real compiler. The list separator is semicolon on Windows systems and colon - on other systems. If not set, ccache will look for the first executable - matching the compiler name in the normal PATH that isn’t a symbolic link - to ccache itself. -

-
-
-pch_external_checksum (CCACHE_PCH_EXTSUM or CCACHE_NOPCH_EXTSUM, see Boolean values above) -
-
-

- When this option is set, and ccache finds a precompiled header file, - ccache will look for a file with the extension “.sum” added - (e.g. “pre.h.gch.sum”), and if found, it will hash this file instead - of the precompiled header itself to work around the performance - penalty of hashing very large files. -

-
-
-prefix_command (CCACHE_PREFIX) -
-
-

- This option adds a list of prefixes (separated by space) to the command - line that ccache uses when invoking the compiler. See also - USING CCACHE WITH OTHER COMPILER WRAPPERS. -

-
-
-prefix_command_cpp (CCACHE_PREFIX_CPP) -
-
-

- This option adds a list of prefixes (separated by space) to the command - line that ccache uses when invoking the preprocessor. -

-
-
-read_only (CCACHE_READONLY or CCACHE_NOREADONLY, see Boolean values above) -
-
-

- If true, ccache will attempt to use existing cached object files, but it - will not add new results to the cache. Statistics counters will still be - updated, though, unless the stats option is set to false. -

-

If you are using this because your ccache directory is read-only, you need to -set temporary_dir since ccache will fail to create temporary files otherwise. -You may also want to set stats = false to make ccache not even try to update -stats files.

-
-
-read_only_direct (CCACHE_READONLY_DIRECT or CCACHE_NOREADONLY_DIRECT, see Boolean values above) -
-
-

- Just like read_only except that ccache will only try to retrieve results - from the cache using the direct mode, not the preprocessor mode. See - documentation for read_only regarding using a read-only ccache directory. -

-
-
-recache (CCACHE_RECACHE or CCACHE_NORECACHE, see Boolean values above) -
-
-

- If true, ccache will not use any previously stored result. New results will - still be cached, possibly overwriting any pre-existing results. -

-
-
-run_second_cpp (CCACHE_CPP2 or CCACHE_NOCPP2, see Boolean values above) -
-
-

- If true, ccache will first run the preprocessor to preprocess the source - code (see THE PREPROCESSOR MODE) and then on a - cache miss run the compiler on the source code to get hold of the object - file. This is the default. -

-

If false, ccache will first run preprocessor to preprocess the source code and -then on a cache miss run the compiler on the preprocessed source code instead -of the original source code. This makes cache misses slightly faster since the -source code only has to be preprocessed once. The downside is that some -compilers won’t produce the same result (for instance diagnostics warnings) -when compiling preprocessed source code.

-

A solution to the above mentioned downside is to set run_second_cpp to false -and pass -fdirectives-only (for GCC) or -frewrite-includes (for Clang) to -the compiler. This will cause the compiler to leave the macros and other -preprocessor information, and only process the #include directives. When run -in this way, the preprocessor arguments will be passed to the compiler since it -still has to do some preprocessing (like macros).

-
-
-sloppiness (CCACHE_SLOPPINESS) -
-
-

- By default, ccache tries to give as few false cache hits as possible. - However, in certain situations it’s possible that you know things that - ccache can’t take for granted. This setting makes it possible to tell - ccache to relax some checks in order to increase the hit rate. The value - should be a comma-separated string with options. Available options are: -

-
-
-
-
-clang_index_store -
-
-

- Ignore the Clang compiler option -index-store-path and its argument when - computing the manifest hash. This is useful if you use Xcode, which uses an - index store path derived from the local project path. Note that the index - store won’t be updated correctly on cache hits if you enable this option. -

-
-
-file_stat_matches -
-
-

- ccache normally examines a file’s contents to determine whether it matches - the cached version. With this option set, ccache will consider a file as - matching its cached version if the mtimes and ctimes match. -

-
-
-file_stat_matches_ctime -
-
-

- Ignore ctimes when file_stat_matches is enabled. This can be useful when - backdating files' mtimes in a controlled way. -

-
-
-include_file_ctime -
-
-

- By default, ccache will not cache a file if it includes a header whose - ctime is too new. This option disables that check. -

-
-
-include_file_mtime -
-
-

- By default, ccache will not cache a file if it includes a header whose - mtime is too new. This option disables that check. -

-
-
-locale -
-
-

- ccache includes the environment variables LANG, LC_ALL, LC_CTYPE and - LC_MESSAGES in the hash by default since they may affect localization of - compiler warning messages. Set this option to tell ccache not to do that. -

-
-
-pch_defines -
-
-

- Be sloppy about #defines when precompiling a header file. See - PRECOMPILED HEADERS for more information. -

-
-
-system_headers -
-
-

- By default, ccache will also include all system headers in the manifest. - With this option set, ccache will only include system headers in the hash - but not add the system header files to the list of include files. -

-
-
-time_macros -
-
-

- Ignore __DATE__ and __TIME__ being present in the source code. -

-
-
-
-

See the discussion under TROUBLESHOOTING for more -information.

-
-
-stats (CCACHE_STATS or CCACHE_NOSTATS, see Boolean values above) -
-
-

- If true, ccache will update the statistics counters on each compilation. - The default is true. -

-
-
-temporary_dir (CCACHE_TEMPDIR) -
-
-

- This setting specifies where ccache will put temporary files. The default - is <cache_dir>/tmp. -

-
- - - -
-
Note
-
In previous versions of ccache, CCACHE_TEMPDIR had to be on the same - filesystem as the CCACHE_DIR path, but this requirement has been - relaxed.)
-
-
-
-umask (CCACHE_UMASK) -
-
-

- This setting specifies the umask for ccache and all child processes (such - as the compiler). This is mostly useful when you wish to share your cache - with other users. Note that this also affects the file permissions set on - the object files created from your compilations. -

-
-
-
-
-
-
-

Cache size management

-
-

By default, ccache has a 5 GB limit on the total size of files in the cache and -no limit on the number of files. You can set different limits using the --M/--max-size and -F/--max-files options. Use ccache -s/--show-stats -to see the cache size and the currently configured limits (in addition to other -various statistics).

-

Cleanup can be triggered in two different ways: automatic and manual.

-
-

Automatic cleanup

-

ccache maintains counters for various statistics about the cache, including the -size and number of all cached files. In order to improve performance and reduce -issues with concurrent ccache invocations, there is one statistics file for -each of the sixteen subdirectories in the cache.

-

After a new compilation result has been written to the cache, ccache will -update the size and file number statistics for the subdirectory (one of -sixteen) to which the result was written. Then, if the size counter for said -subdirectory is greater than max_size / 16 or the file number counter is -greater than max_files / 16, automatic cleanup is triggered.

-

When automatic cleanup is triggered for a subdirectory in the cache, ccache -will:

-
    -
  1. -

    -Count all files in the subdirectory and compute their aggregated size. -

    -
  2. -
  3. -

    -Remove files in LRU (least recently used) order until the size is at most - limit_multiple * max_size / 16 and the number of files is at most - limit_multiple * max_files / 16, where limit_multiple, max_size and - max_files are configuration settings. -

    -
  4. -
  5. -

    -Set the size and file number counters to match the files that were kept. -

    -
  6. -
-

The reason for removing more files than just those needed to not exceed the max -limits is that a cleanup is a fairly slow operation, so it would not be a good -idea to trigger it often, like after each cache miss.

-
-
-

Manual cleanup

-

You can run ccache -c/--cleanup to force cleanup of the whole cache, i.e. all -of the sixteen subdirectories. This will recalculate the statistics counters -and make sure that the max_size and max_files settings are not exceeded. -Note that limit_multiple is not taken into account for manual cleanup.

-
-
-
-
-

Cache compression

-
-

ccache can optionally compress all files it puts into the cache using the -compression library zlib. While this may involve a tiny performance slowdown, -it increases the number of files that fit in the cache. You can turn on -compression with the compression configuration setting and you can also tweak -the compression level with compression_level.

-
-
-
-

Cache statistics

-
-

ccache -s/--show-stats can show the following statistics:

-
- --- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name Description

autoconf compile/link

Uncachable compilation or linking by an autoconf test.

bad compiler arguments

Malformed compiler argument, e.g. missing a value for an option that requires -an argument or failure to read a file specified by an option argument.

cache file missing

A file was unexpectedly missing from the cache. This only happens in rare -situations, e.g. if one ccache instance is about to get a file from the cache -while another instance removed the file as part of cache cleanup.

cache hit (direct)

A result was successfully found using the direct mode.

cache hit (preprocessed)

A result was successfully found using the preprocessor mode.

cache miss

No result was found.

cache size

Current size of the cache.

called for link

The compiler was called for linking, not compiling.

called for preprocessing

The compiler was called for preprocessing, not compiling.

can’t use precompiled header

Preconditions for using precompiled headers were not -fulfilled.

ccache internal error

Unexpected failure, e.g. due to problems reading/writing the cache.

cleanups performed

Number of cleanups performed, either implicitly due to the cache size limit -being reached or due to explicit ccache -c/--cleanup calls.

compile failed

The compilation failed. No result stored in the cache.

compiler check failed

A compiler check program specified by compiler_check (CCACHE_COMPILERCHECK) -failed.

compiler produced empty output

The compiler’s output file (typically an object file) was empty after -compilation.

compiler produced no output

The compiler’s output file (typically an object file) was missing after -compilation.

compiler produced stdout

The compiler wrote data to standard output. This is something that compilers -normally never do, so ccache is not designed to store such output in the cache.

couldn’t find the compiler

The compiler to execute could not be found.

error hashing extra file

Failure reading a file specified by extra_files_to_hash -(CCACHE_EXTRAFILES).

files in cache

Current number of files in the cache.

multiple source files

The compiler was called to compile multiple source files in one go. This is not -supported by ccache.

no input file

No input file was specified to the compiler.

output to a non-regular file

The output path specified with -o is not a file (e.g. a directory or a device -node).

output to stdout

The compiler was instructed to write its output to standard output using -o --. This is not supported by ccache.

preprocessor error

Preprocessing the source code using the compiler’s -E option failed.

stats updated

When statistics were updated the last time.

stats zeroed

When ccache -z was called the last time.

unsupported code directive

Code like the assembler .incbin directive was found. This is not supported -by ccache.

unsupported compiler option

A compiler option not supported by ccache was found.

unsupported source language

A source language e.g. specified with -x was unsupported by ccache.

-
-
-
-
-

How ccache works

-
-

The basic idea is to detect when you are compiling exactly the same code a -second time and reuse the previously produced output. The detection is done by -hashing different kinds of information that should be unique for the -compilation and then using the hash sum to identify the cached output. ccache -uses MD4, a very fast cryptographic hash algorithm, for the hashing. (MD4 is -nowadays too weak to be useful in cryptographic contexts, but it should be safe -enough to be used to identify recompilations.) On a cache hit, ccache is able -to supply all of the correct compiler outputs (including all warnings, -dependency file, etc) from the cache.

-

ccache has two ways of gathering information used to look up results in the -cache:

-
    -
  • -

    -the direct mode, where ccache hashes the source code and include files - directly -

    -
  • -
  • -

    -the preprocessor mode, where ccache runs the preprocessor on the source - code and hashes the result -

    -
  • -
-

The direct mode is generally faster since running the preprocessor has some -overhead.

-

If no previous result is detected (i.e., there is a cache miss) using the -direct mode, ccache will fall back to the preprocessor mode unless the depend -mode is enabled. In the depend mode, ccache never runs the preprocessor, not -even on cache misses. Read more in THE DEPEND MODE -below.

-
-

Common hashed information

-

The following information is always included in the hash:

-
    -
  • -

    -the extension used by the compiler for a file with preprocessor output - (normally .i for C code and .ii for C++ code) -

    -
  • -
  • -

    -the compiler’s size and modification time (or other compiler-specific - information specified by the compiler_check setting) -

    -
  • -
  • -

    -the name of the compiler -

    -
  • -
  • -

    -the current directory (if the hash_dir setting is enabled) -

    -
  • -
  • -

    -contents of files specified by the extra_files_to_hash setting (if any) -

    -
  • -
-
-
-

The direct mode

-

In the direct mode, the hash is formed of the common information and:

-
    -
  • -

    -the input source file -

    -
  • -
  • -

    -the command line options -

    -
  • -
-

Based on the hash, a data structure called “manifest” is looked up in the -cache. The manifest contains:

-
    -
  • -

    -references to cached compilation results (object file, dependency file, etc) - that were produced by previous compilations that matched the hash -

    -
  • -
  • -

    -paths to the include files that were read at the time the compilation results - were stored in the cache -

    -
  • -
  • -

    -hash sums of the include files at the time the compilation results were - stored in the cache -

    -
  • -
-

The current contents of the include files are then hashed and compared to the -information in the manifest. If there is a match, ccache knows the result of -the compilation. If there is no match, ccache falls back to running the -preprocessor. The output from the preprocessor is parsed to find the include -files that were read. The paths and hash sums of those include files are then -stored in the manifest along with information about the produced compilation -result.

-

There is a catch with the direct mode: header files that were used by the -compiler are recorded, but header files that were not used, but would have -been used if they existed, are not. So, when ccache checks if a result can be -taken from the cache, it currently can’t check if the existence of a new header -file should invalidate the result. In practice, the direct mode is safe to use -in the absolute majority of cases.

-

The direct mode will be disabled if any of the following holds:

-
    -
  • -

    -the configuration setting direct_mode is false -

    -
  • -
  • -

    -a modification time of one of the include files is too new (needed to avoid a - race condition) -

    -
  • -
  • -

    -a compiler option not supported by the direct mode is used: -

    -
      -
    • -

      -a -Wp,X compiler option other than -Wp,-MD,path, - -Wp,-MMD,path and -Wp,-D_define_ -

      -
    • -
    • -

      --Xpreprocessor -

      -
    • -
    -
  • -
  • -

    -the string __TIME__ is present in the source code -

    -
  • -
-
-
-

The preprocessor mode

-

In the preprocessor mode, the hash is formed of the common information and:

-
    -
  • -

    -the preprocessor output from running the compiler with -E -

    -
  • -
  • -

    -the command line options except options that affect include files (-I, - -include, -D, etc; the theory is that these options will change the - preprocessor output if they have any effect at all) -

    -
  • -
  • -

    -any standard error output generated by the preprocessor -

    -
  • -
-

Based on the hash, the cached compilation result can be looked up directly in -the cache.

-
-
-

The depend mode

-

If the depend mode is enabled, ccache will not use the preprocessor at all. The -hash used to identify results in the cache will be based on the direct mode -hash described above plus information about include files read from the -dependency file generated by the compiler with -MD or -MMD.

-

Advantages:

-
    -
  • -

    -The ccache overhead of a cache miss will be much smaller. -

    -
  • -
  • -

    -Not running the preprocessor at all can be good if compilation is performed - remotely, for instance when using distcc or similar; ccache then won’t make - potentially costly preprocessor calls on the local machine. -

    -
  • -
-

Disadvantages:

-
    -
  • -

    -The cache hit rate will likely be lower since any change to compiler options - or source code will make the hash different. Compare this with the default - setup where ccache will fall back to the preprocessor mode, which is tolerant - to some types of changes of compiler options and source code changes. -

    -
  • -
  • -

    -If -MD is used, the manifest entries will include system header files as - well, thus slowing down cache hits slightly, just as using -MD slows down - make. -

    -
  • -
  • -

    -If -MMD is used, the manifest entries will not include system header files, - which means ccache will ignore changes in them. -

    -
  • -
-

The depend mode will be disabled if any of the following holds:

-
    -
  • -

    -the configuration setting depend_mode is false -

    -
  • -
  • -

    -the configuration setting run_second_cpp is false -

    -
  • -
  • -

    -the compiler is not generating dependencies using -MD or -MMD -

    -
  • -
-
-
-
-
-

Cache debugging

-
-

To find out what information ccache actually is hashing, you can enable the -debug mode via the configuration setting debug or by setting CCACHE_DEBUG -in the environment. This can be useful if you are investigating why you don’t -get cache hits. Note that performance will be reduced slightly.

-

When the debug mode is enabled, ccache will create up to five additional files -next to the object file:

-
- --- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Filename Description

<objectfile>.ccache-input-c

Binary input hashed by both the direct mode and the preprocessor mode.

<objectfile>.ccache-input-d

Binary input only hashed by the direct mode.

<objectfile>.ccache-input-p

Binary input only hashed by the preprocessor mode.

<objectfile>.ccache-input-text

Human-readable combined diffable text version of the three files above.

<objectfile>.ccache-log

Log for this object file.

-
-

In the direct mode, ccache uses the MD4 hash of the ccache-input-c -+ ccache-input-d data (where + means concatenation), while the -ccache-input-c + ccache-input-p data is used in the preprocessor mode.

-

The ccache-input-text file is a combined text version of the three -binary input files. It has three sections (“COMMON”, “DIRECT MODE” and -“PREPROCESSOR MODE”), which is turn contain annotations that say what kind of -data comes next.

-

To debug why you don’t get an expected cache hit for an object file, you can do -something like this:

-
    -
  1. -

    -Build with debug mode enabled. -

    -
  2. -
  3. -

    -Save the <objectfile>.ccache-* files. -

    -
  4. -
  5. -

    -Build again with debug mode enabled. -

    -
  6. -
  7. -

    -Compare <objectfile>.ccache-input-text for the two builds. This together - with the <objectfile>.ccache-log files should give you some clues about - what is happening. -

    -
  8. -
-
-
-
-

Compiling in different directories

-
-

Some information included in the hash that identifies a unique compilation can -contain absolute paths:

-
    -
  • -

    -The preprocessed source code may contain absolute paths to include files if - the compiler option -g is used or if absolute paths are given to -I and - similar compiler options. -

    -
  • -
  • -

    -Paths specified by compiler options (such as -I, -MF, etc) on the command - line may be absolute. -

    -
  • -
  • -

    -The source code file path may be absolute, and that path may substituted for - __FILE__ macros in the source code or included in warnings emitted to - standard error by the preprocessor. -

    -
  • -
-

This means that if you compile the same code in different locations, you can’t -share compilation results between the different build directories since you get -cache misses because of the absolute build directory paths that are part of the -hash.

-

Here’s what can be done to enable cache hits between different build -directories:

-
    -
  • -

    -If you build with -g (or similar) to add debug information to the object - file, you must either: -

    -
    -
    -
      -
    • -

      -use the -fdebug-prefix-map=old=new option for relocating debug info to - a common prefix (e.g. -fdebug-prefix-map=$PWD=.); or -

      -
    • -
    • -

      -set hash_dir = false. -

      -
    • -
    -
    -
  • -
  • -

    -If you use absolute paths anywhere on the command line (e.g. the source code - file path or an argument to compiler options like -I and -MF), you must - to set base_dir to an absolute path to a “base directory”. ccache will - then rewrite absolute paths under that directory to relative before computing - the hash. -

    -
  • -
-
-
-
-

Precompiled headers

-
-

ccache has support for GCC’s precompiled headers. However, you have to do some -things to make it work properly:

-
    -
  • -

    -You must set sloppiness to pch_defines,time_macros. The reason is that - ccache can’t tell whether __TIME__ or __DATE__ is used when using a - precompiled header. Further, it can’t detect changes in #defines in the - source code because of how preprocessing works in combination with - precompiled headers. -

    -
  • -
  • -

    -You must either: -

    -
    -
    -
      -
    • -

      -use the -include compiler option to include the precompiled header (i.e., - don’t use #include in the source code to include the header; the filename - itself must be sufficient to find the header, i.e. -I paths are not - searched); or -

      -
    • -
    • -

      -(for the Clang compiler) use the -include-pch compiler option to include - the PCH file generated from the precompiled header; or -

      -
    • -
    • -

      -(for the GCC compiler) add the -fpch-preprocess compiler option when - compiling. -

      -
    • -
    -

    If you don’t do this, either the non-precompiled version of the header file -will be used (if available) or ccache will fall back to running the real -compiler and increase the statistics counter “preprocessor error” (if the -non-precompiled header file is not available).

    -
    -
  • -
-
-
-
-

Sharing a cache

-
-

A group of developers can increase the cache hit rate by sharing a cache -directory. To share a cache without unpleasant side effects, the following -conditions should to be met:

-
    -
  • -

    -Use the same cache directory. -

    -
  • -
  • -

    -Make sure that the configuration setting hard_link is false (which is the - default). -

    -
  • -
  • -

    -Make sure that all users are in the same group. -

    -
  • -
  • -

    -Set the configuration setting umask to 002. This ensures that cached files - are accessible to everyone in the group. -

    -
  • -
  • -

    -Make sure that all users have write permission in the entire cache directory - (and that you trust all users of the shared cache). -

    -
  • -
  • -

    -Make sure that the setgid bit is set on all directories in the cache. This - tells the filesystem to inherit group ownership for new directories. The - following command might be useful for this: -

    -
    -
    -
    -
    -
    find $CCACHE_DIR -type d | xargs chmod g+s
    -
    -
    -
  • -
-

The reason to avoid the hard link mode is that the hard links cause unwanted -side effects, as all links to a cached file share the file’s modification -timestamp. This results in false dependencies to be triggered by -timestamp-based build systems whenever another user links to an existing file. -Typically, users will see that their libraries and binaries are relinked -without reason.

-

You may also want to make sure that a base directory is set appropriately, as -discussed in a previous section.

-
-
-
-

Sharing a cache on NFS

-
-

It is possible to put the cache directory on an NFS filesystem (or similar -filesystems), but keep in mind that:

-
    -
  • -

    -Having the cache on NFS may slow down compilation. Make sure to do some - benchmarking to see if it’s worth it. -

    -
  • -
  • -

    -ccache hasn’t been tested very thoroughly on NFS. -

    -
  • -
-

A tip is to set temporary_dir to a directory on the local host to avoid NFS -traffic for temporary files.

-

It is recommended to use the same operating system version when using a shared -cache. If operating system versions are different then system include files -will likely be different and there will be few or no cache hits between the -systems. One way of improving cache hit rate in that case is to set -sloppiness to system_headers to ignore system -headers.

-
-
-
-

Using ccache with other compiler wrappers

-
-

The recommended way of combining ccache with another compiler wrapper (such as -“distcc”) is by letting ccache execute the compiler wrapper. This is -accomplished by defining the configuration setting prefix_command, for -example by setting the environment variable CCACHE_PREFIX to the name of the -wrapper (e.g. distcc). ccache will then prefix the command line with the -specified command when running the compiler. To specify several prefix -commands, set prefix_command to a colon-separated list of commands.

-

Unless you set compiler_check to a suitable command (see the description of -that configuration option), it is not recommended to use the form ccache -anotherwrapper compiler args as the compilation command. It’s also not -recommended to use the masquerading technique for the other compiler wrapper. -The reason is that by default, ccache will in both cases hash the mtime and -size of the other wrapper instead of the real compiler, which means that:

-
    -
  • -

    -Compiler upgrades will not be detected properly. -

    -
  • -
  • -

    -The cached results will not be shared between compilations with and without - the other wrapper. -

    -
  • -
-

Another minor thing is that if prefix_command is used, ccache will not invoke -the other wrapper when running the preprocessor, which increases performance. -You can use the prefix_command_cpp configuration setting if you also want to -invoke the other wrapper when doing preprocessing (normally by adding -E).

-
-
-
-

Caveats

-
-
    -
  • -

    -The direct mode fails to pick up new header files in some rare scenarios. See - THE DIRECT MODE above. -

    -
  • -
  • -

    -When run via ccache, warning messages produced by GCC 4.9 and newer will only - be colored when the environment variable GCC_COLORS is set. An alternative - to setting GCC_COLORS is to pass -fdiagnostics-color explicitly when - compiling (but then color codes will also be present when redirecting stderr - to a file). -

    -
  • -
  • -

    -If ccache guesses that the compiler may emit colored warnings, then a - compilation with stderr referring to a TTY will be considered different from - a compilation with a redirected stderr, thus not sharing cache entries. This - happens for clang by default and for GCC when GCC_COLORS is set as - mentioned above. If you want to share cache hits, you can pass - -f[no-]diagnostics-color (GCC) or -f[no-]color-diagnostics (clang) - explicitly when compiling (but then color codes will be either on or off for - both the TTY and the redirected case). -

    -
  • -
-
-
-
-

Troubleshooting

-
-
-

General

-

A general tip for getting information about what ccache is doing is to enable -debug logging by setting the configuration option debug (or the environment -variable CCACHE_DEBUG); see debugging for more -information. Another way of keeping track of what is happening is to check the -output of ccache -s.

-
-
-

Performance

-

ccache has been written to perform well out of the box, but sometimes you may -have to do some adjustments of how you use the compiler and ccache in order to -improve performance.

-

Since ccache works best when I/O is fast, put the cache directory on a fast -storage device if possible. Having lots of free memory so that files in the -cache directory stay in the disk cache is also preferable.

-

A good way of monitoring how well ccache works is to run ccache -s before and -after your build and then compare the statistics counters. Here are some common -problems and what may be done to increase the hit rate:

-
    -
  • -

    -If “cache hit (preprocessed)” has been incremented instead of “cache hit - (direct)”, ccache has fallen back to preprocessor mode, which is generally - slower. Some possible reasons are: -

    -
      -
    • -

      -The source code has been modified in such a way that the preprocessor output - is not affected. -

      -
    • -
    • -

      -Compiler arguments that are hashed in the direct mode but not in the - preprocessor mode have changed (-I, -include, -D, etc) and they didn’t - affect the preprocessor output. -

      -
    • -
    • -

      -The compiler option -Xpreprocessor or -Wp,X (except -Wp,-MD,path, - -Wp,-MMD,path, and -Wp,-D_define_) is used. -

      -
    • -
    • -

      -This was the first compilation with a new value of the base directory - setting. -

      -
    • -
    • -

      -A modification time of one of the include files is too new (created the same - second as the compilation is being done). This check is made to avoid a race - condition. To fix this, create the include file earlier in the build process, - if possible, or set sloppiness to include_file_ctime, include_file_mtime - if you are willing to take the risk. (The race condition consists of these - events: the preprocessor is run; an include file is modified by someone; the - new include file is hashed by ccache; the real compiler is run on the - preprocessor’s output, which contains data from the old header file; the - wrong object file is stored in the cache.) -

      -
    • -
    • -

      -The __TIME__ preprocessor macro is (potentially) being used. ccache turns - off direct mode if __TIME__ is present in the source code. This is done as - a safety measure since the string indicates that a __TIME__ macro may - affect the output. (To be sure, ccache would have to run the preprocessor, - but the sole point of the direct mode is to avoid that.) If you know that - __TIME__ isn’t used in practise, or don’t care if ccache produces objects - where __TIME__ is expanded to something in the past, you can set - sloppiness to time_macros. -

      -
    • -
    • -

      -The __DATE__ preprocessor macro is (potentially) being used and the date - has changed. This is similar to how __TIME__ is handled. If __DATE__ is - present in the source code, ccache hashes the current date in order to be - able to produce the correct object file if the __DATE__ macro affects the - output. If you know that __DATE__ isn’t used in practise, or don’t care if - ccache produces objects where __DATE__ is expanded to something in the - past, you can set sloppiness to time_macros. -

      -
    • -
    • -

      -The input file path has changed. ccache includes the input file path in the - direct mode hash to be able to take relative include files into account and - to produce a correct object file if the source code includes a __FILE__ - macro. -

      -
    • -
    -
  • -
  • -

    -If “cache miss” has been incremented even though the same code has been - compiled and cached before, ccache has either detected that something has - changed anyway or a cleanup has been performed (either explicitly or - implicitly when a cache limit has been reached). Some perhaps unobvious - things that may result in a cache miss are usage of __TIME__ or - __DATE__ macros, or use of automatically generated code that contains a - timestamp, build counter or other volatile information. -

    -
  • -
  • -

    -If “multiple source files” has been incremented, it’s an indication that - the compiler has been invoked on several source code files at once. ccache - doesn’t support that. Compile the source code files separately if possible. -

    -
  • -
  • -

    -If “unsupported compiler option” has been incremented, enable debug logging - and check which option was rejected. -

    -
  • -
  • -

    -If “preprocessor error” has been incremented, one possible reason is that - precompiled headers are being used. See PRECOMPILED HEADERS for how to remedy this. -

    -
  • -
  • -

    -If “can’t use precompiled header” has been incremented, see - PRECOMPILED HEADERS. -

    -
  • -
-
-
-

Corrupt object files

-

It should be noted that ccache is susceptible to general storage problems. If a -bad object file sneaks into the cache for some reason, it will of course stay -bad. Some possible reasons for erroneous object files are bad hardware (disk -drive, disk controller, memory, etc), buggy drivers or file systems, a bad -prefix_command or compiler wrapper. If this happens, the easiest way of -fixing it is this:

-
    -
  1. -

    -Build so that the bad object file ends up in the build tree. -

    -
  2. -
  3. -

    -Remove the bad object file from the build tree. -

    -
  4. -
  5. -

    -Rebuild with CCACHE_RECACHE set. -

    -
  6. -
-

An alternative is to clear the whole cache with ccache -C if you don’t mind -losing other cached results.

-

There are no reported issues about ccache producing broken object files -reproducibly. That doesn’t mean it can’t happen, so if you find a repeatable -case, please report it.

-
-
-
-
-

More information

-
-

Credits, mailing list information, bug reporting instructions, source code, -etc, can be found on ccache’s web site: https://ccache.dev.

-
-
-
-

Author

-
-

ccache was originally written by Andrew Tridgell and is currently developed and -maintained by Joel Rosdahl. See AUTHORS.txt or AUTHORS.html and -https://ccache.dev/credits.html for a list of contributors.

-
-
-
-

- - - diff --git a/doc/NEWS.adoc b/doc/NEWS.adoc index e7b44a7..d9eba80 100644 --- a/doc/NEWS.adoc +++ b/doc/NEWS.adoc @@ -1,6 +1,255 @@ -ccache news +Ccache news =========== +Ccache 4.0 +---------- +Release date: 2020-10-18 + + +Summary of major changes +~~~~~~~~~~~~~~~~~~~~~~~~ + +- Changed the default cache directory location to follow the XDG base directory + specification. + +- Changed compression algorithm from Deflate (zlib) to Zstandard, enabled by + default. + +- Added functionality for recompressing cache content with a higher compression + level. + +- Changed hash algorithm from MD4 to BLAKE3. + +- Added checksumming with XXH3 to detect data corruption. + +- Improved cache directory structure. + +- Added support for using file cloning (AKA “reflinks”). + +- Added an experimental “inode cache” for file hashes. + + +Compatibility notes +~~~~~~~~~~~~~~~~~~~ + +- The default location of the cache directory has changed to follow the XDG + base directory specification (<<_detailed_functional_changes,more details + below>>). This means that scripts can no longer assume that the cache + directory is `~/.ccache` by default. The `CCACHE_DIR` environment variable + still overrides the default location just like before. + +- The cache directory structure has changed compared to previous versions + (<<_detailed_functional_changes,more details below>>). This means that ccache + 4.0 will not share cache results with earlier versions. It is however safe to + run ccache 4.0 and earlier versions against the same cache directory: cache + bookkeeping, statistics and cleanup are backward compatible, with the minor + exception that some statistics counters incremented by ccache 4.0 won’t be + visible when running `ccache -s` with an older version. + + +Changed tooling +~~~~~~~~~~~~~~~ + +- CMake is now used instead of Autoconf for configuration and building. + +- A C++11 compiler, a C99 compiler and CMake 3.4.3 or newer are now required to + build ccache. + +- Ccache can now be built using Microsoft Visual C++. + + +Detailed functional changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- All data of a cached result is now stored in a single file called “result” + instead of up to seven files. This reduces inode usage and improves data + locality. + +- Added compression of result and manifest files using the + http://zstd.net[Zstandard] algorithm. Compression is enabled by default with + compression level 1. This makes ccache able to store more data in the cache. + Previously compression using Deflate (zlib) was available but disabled by + default. Files can be recompressed with another compression level later with + the `-X/--recompress` option described further below. + +- Changed from MD4 to https://blake3.io[BLAKE3] for hashing input. This + improves performance and reduces the risk of hash collisions. + +- Added checksumming of result and manifest files using the + http://xxhash.com[XXH3] algorithm to detect data corruption. + +- Ccache now follows the + https://specifications.freedesktop.org/basedir-spec/[XDG base directory + specification]. This means that the default cache directory on Unix systems + is `$XDG_CACHE_HOME/ccache` (with `~/.cache/ccache` as the fallback if + `XDG_CACHE_HOME` is not set) and the configuration file is + `$XDG_CONFIG_HOME/ccache/ccache.conf` (with `~/.config/ccache/ccache.conf` as + the fallback). On macOS, the fallbacks are `~/Library/Caches/ccache` and + `~/Library/Preferences/ccache/ccache.conf`. On Windows, the fallbacks are + `%APPDATA%/ccache` and `%APPDATA%/ccache/ccache.conf`. Exception: If the + legacy `~/.ccache` directory exists, that directory is used as the default + cache location and the configuration file is `~/.ccache/ccache.conf`. + +- Cache statistics are now stored in files on cache level 2 to reduce lock + contention when there are many parallel compilations. + +- An appropriate cache directory level structure is now chosen automatically. + The `cache_dir_levels` (`CCACHE_NLEVELS`) configuration option has therefore + been removed. + +- Added an experimental “inode cache” for file hashes, allowing computed hash + values to be reused both within and between builds. The inode cache is off by + default but can be enabled by setting `inode_cache` (`CCACHE_INODECACHE`) to + `true`. + +- Added support for using file cloning (AKA “reflinks”) on Btrfs, XFS and APFS + to copy data to and from the cache very efficiently. + +- Two measures have been implemented to make the hard link mode safer: + hard-linked files are made read-only and inadvertent content changes that + affect file size are detected. + +- Added a command line option `-x/--show-compression` which shows statistics + about cache compression. + +- Added a command line option `-X/--recompress` which recompresses the cache + data with another compression level or makes it uncompressed. If you choose + to disable compression by default, or choose to use a compression level with + a low compression ratio, you can recompress the cache with a higher + compression level after the build or at another time when there are more CPU + cycles available, for instance every night. Only files that are currently + compressed with a different level than the wanted level will be recompressed. + +- Added a command line option `--evict-older-than` which removes cache entries + older than a certain age. + +- Added a command line option `-d/--directory` which specifies a cache + directory to operate on. It can be used instead of setting `CCACHE_DIR` + temporarily. + +- A progress bar has been added to show the progress of time-consuming options + like `-c/--cleanup`, `-C/--clear`, `--evict-older-than`, + `-x/--show-compression` and `-X/--recompress`. + +- When supported by the CPU, a SIMD-friendly (using AVX2) algorithm is now used + to scan input source code for `__DATE__`, `__TIME__` and `__TIMESTAMP__` + macros. This can decrease the number of CPU cycles for a direct cache hit + with up to 15% in some cases. + +- Some unnecessary `stat(2)` system calls are now avoided when verifying header + files. + +- Compiler diagnostic messages are now always cached in color. Ccache then + strips the color codes on the fly when requested explicitly by a command line + option or when stderr does not refer to a TTY. This allows IDEs and terminals + to share cached compilation results. + +- The configuration option `compiler` (`CCACHE_COMPILER`) now always takes + effect if specified. Previously, the configuration option was only used when + the compiler specified on the command line was looked up via `PATH` (i.e., + not when an absolute path was specified). + +- Added optional logging to syslog if `log_file` (`CCACHE_LOGFILE`) is set to + `syslog`. + +- The compiler option `-fmodules` is now handled in the “depend mode”. If + “depend mode” is disabled the option is still considered too hard and ccache + will fall back to running the compiler. + +- Ccache can now cache compilations with coverage notes (`.gcno` files) + produced by GCC 9+ in combination with `-fprofile-dir=dir`. + +- `realpath(3)` is no longer used for normalization when computing relative + paths. This makes it possible to get cache hits when the source or build + directory is a symbolic link to an absolute path that includes unstable + information like build IDs or timestamps. + +- Added an `ignore_options` (`CCACHE_IGNOREOPTIONS`) configuration option which + makes it possible to exclude compiler options from the hash. + +- Added an `absolute_paths_in_stderr` (`CCACHE_ABSSTDERR`) configuration option + which makes ccache rewrite absolute paths in compiler warnings and errors to + relative. + +- Improved handling of umask. The configured `umask` (`CCACHE_UMASK`) is now + only applied to files and directories in the cache directory. Previously the + umask was applied to all files produced by ccache and the executed compiler. + +- Ccache is now able to share cache entries for different object file names + when using `-MD` or `-MMD`. + +- Clang’s `-Xclang` (used by CMake for precompiled headers), + `-fno-pch-timestamp`, `-emit-pch`, `-emit-pth` and `-include-pth` options are + now understood. + +- Added support for the HIP (“C++ Heterogeneous-Compute Interface for + Portability”) language. + +- The manifest format now allows for header files larger than 4 GiB. + +- Made it possible to once again cache compilations with `__DATE__` in the + source code. + +- Added handling of the `__TIMESTAMP__` macro. + +- An absolute input source path is now rewritten to a relative path when using + `base_dir`. + +- `waitpid` system calls interrupted by a signal are now handled correctly. + +- Made handling of `.dwo` files and interaction between `-gsplit-dwarf` and + other `-g*` options more robust. + +- The “couldn't find compiler” statistics counter is no longer incremented when + ccache exits with a fatal error. + +- Failure to run a `compiler_check` command is no longer a fatal error. + +- Added command line options `--dump-result` and `--extract-result` for + inspecting and extracting result files. + +- Added a command line option `--checksum-file` for debugging or evaluating the + checksum algorithm. + +- Improved error message for `ccache -o=K=V` (trying to set a configuration + option named `=K`). + +- Made timestamps in statistics files Y2038-proof. + +- Removed code for populating a newly created configuration file with max cache + size and max files values for cache directories created by ccache versions + older than 3.2 (released 2014). + +- Removed knowledge about a top-level `stats` file created by ccache versions + older than 3.1 (released 2010). + + +Other improvements +~~~~~~~~~~~~~~~~~~ + +- Improved help text and documentation of command line options. + +- Improved documentation of the `base_dir` configuration option. + +- Improved documentation of preprocessor and direct modes. + +- Added HTML anchors to configuration options in the manual so that it is + possible link to a specific option. + +- Tweaked placement of “(readonly)” in output of `ccache -s`. + +- Improved visibility of color output from the test suite. + +- Fixed a problem when running the test suite with Clang without a libgcov + library available. + +- Fixed test suite problems on macOS. + +- Disabled hardlink tests on AFS since it lacks such support. + +- Disabled read-only tests on file systems that lack such support. + + ccache 3.7.12 ------------- @@ -147,7 +396,6 @@ Other manual. - ccache 3.7.7 ------------ Release date: 2020-01-05 @@ -238,11 +486,11 @@ Release date: 2019-09-12 Improvements ~~~~~~~~~~~~ -- Added support for GCC 9’s `-gz[=type]` option (previously ccache would think - that “-gz” alone would enable debug information, thus potentially including - the current directory in the hash). +- Added support for the `-gz[=type]` compiler option (previously ccache would + think that “-gz” alone would enable debug information, thus potentially + including the current directory in the hash). -- Added support for converting paths like “/c/users” into relative paths on +- Added support for converting paths like “/c/users/...” into relative paths on Windows. diff --git a/doc/NEWS.html b/doc/NEWS.html deleted file mode 100644 index 2ea77a4..0000000 --- a/doc/NEWS.html +++ /dev/null @@ -1,3948 +0,0 @@ - - - - - - -ccache news - - - - - -
-
-

ccache 3.7.12

-
-

Release date: 2020-10-01

-
-

Bug fixes

-
    -
  • -

    -Coverage files (.gcno) produced by GCC 9+ when using -fprofile-dir=dir - are now handled gracefully by falling back to running the compiler. -

    -
  • -
  • -

    -Fixed writing to log file larger than 2 GiB when running ccache compiled in - 32-bit mode. -

    -
  • -
-
-
-

Other

-
    -
  • -

    -Improved documentation about sharing a cache on NFS. -

    -
  • -
  • -

    -Fixed test case failures with old objdump versions. -

    -
  • -
  • -

    -Fixed test case failures with GCC 4.4. -

    -
  • -
-
-
-
-
-

ccache 3.7.11

-
-

Release date: 2020-07-21

-
-

Bug fixes

-
    -
  • -

    -Added knowledge about -fprofile-{correction,reorder-functions,values}. -

    -
  • -
  • -

    -ccache now handles the Intel compiler option -xCODE (where CODE is a - processor feature code) correctly. -

    -
  • -
  • -

    -Added support for NVCC’s -Werror and --Werror options. -

    -
  • -
-
-
-

Other

-
    -
  • -

    -ccache’s “Directory is not hashed if using -gz[=zlib]” tests are now skipped - for GCC 6. -

    -
  • -
-
-
-
-
-

ccache 3.7.10

-
-

Release date: 2020-06-22

-
-

Bug fixes

-
    -
  • -

    -Improved handling of profiling options. ccache should now work correctly for - profiling options like -fprofile-{generate,use}[=path] for GCC ≥ 9 and - Clang as well as -fauto-profile[=path] and the Clang-specific - -fprofile-instr-{generate,use}[=path] and -fprofile-sample-{use,accurate} - options. -

    -
  • -
  • -

    -ccache now copies files directly from the cache to the destination file - instead of via a temporary file. This avoids problems when using filenames - long enough to be near the file system’s filename max limit. -

    -
  • -
  • -

    -When the hard-link mode is enabled, ccache now only uses hard links for - object files, not other files like dependency files. This is because - compilers unlink object files before writing to them but they don’t do that - for dependency files, so the latter can become overwritten and therefore - corrupted in the cache. -

    -
  • -
  • -

    -Fixed a glitch related to hard-link mode and an empty cache. -

    -
  • -
  • -

    -ccache now supports the ccache.conf file to be a symlink. -

    -
  • -
  • -

    -Temporary files are now deleted immediately on signals like SIGTERM and - SIGINT instead of some time later in a cleanup phase. -

    -
  • -
  • -

    -Fixed a bug that affected ccache’s -o/--set-config option for the - base_dir and cache_dir_levels keys. -

    -
  • -
-
-
-
-
-

ccache 3.7.9

-
-

Release date: 2020-03-29

-
-

Bug fixes

-
    -
  • -

    -Fixed replacing of /dev/null when building as root with hard link mode - enabled and using -o /dev/null. -

    -
  • -
  • -

    -Removed incorrect assertion resulting in “ccache: error: Internal error in - format” when using -fdebug-prefix-map=X= with X equal to $PWD. -

    -
  • -
-
-
-

Other

-
    -
  • -

    -Improved CUDA/NVCC support: Recognize -dc and -x cu options. -

    -
  • -
  • -

    -Improved name of temporary file used in NFS-safe unlink. -

    -
  • -
-
-
-
-
-

ccache 3.7.8

-
-

Release date: 2020-03-16

-
-

Bug fixes

-
    -
  • -

    -Use $PWD instead of the real CWD (current working directory) when checking - for CWD in preprocessed output. This fixes a problem when $PWD includes a - symlink part and the user has set hash_dir = false. -

    -
  • -
  • -

    -Rewrote the Windows version of the lockfile routines. This should mitigate - several problems with the old implementation. -

    -
  • -
  • -

    -If localtime_r fails the epoch time is now logged instead of garbage. -

    -
  • -
-
-
-

Other

-
    -
  • -

    -Improved error message when a boolean environment variable has an invalid - value. -

    -
  • -
  • -

    -Improved the regression fix in ccache 3.7.5 related to not passing - compilation-only options to the preprocessor. -

    -
  • -
  • -

    -ccache’s PCH test suite now skips running the tests if it detects broken PCH - compiler support. -

    -
  • -
  • -

    -Fixed unit test failure on Windows. -

    -
  • -
  • -

    -Fixed “stringop-truncation” build warning on Windows. -

    -
  • -
  • -

    -Improved “x_rename” implementation on Windows. -

    -
  • -
  • -

    -Improved removal of temporary file when rewriting absolute paths to relative - in the dependency file. -

    -
  • -
  • -

    -Clarified “include_file_ctime sloppiness” in the Performance section in the - manual. -

    -
  • -
-
-
-
-
-

ccache 3.7.7

-
-

Release date: 2020-01-05

-
-

Bug fixes

-
    -
  • -

    -Fixed a bug related to object file location in the dependency file (if using - -MD or -MMD but not -MF and the build directory is not the same as the - source directory then the object file location in the .d file would become - incorrect). This fixes regression in ccache 3.7.5 introduced by the bug fix - related to EDG-based compilers. Note that this removes support for EDG-based - compilers again. (A better fix for this is planned for ccache 4.0.) -

    -
  • -
  • -

    -Removed the unify mode since it has bugs and shortcomings that are non-trivial - or impossible to fix: it doesn’t work with the direct mode, it doesn’t handle - C++ raw strings correctly, it can give false cache hits for .incbin - directives, it’s turned off when using -g and it can make line numbers in - warning messages and __LINE__ macros incorrect. -

    -
  • -
  • -

    -mtime and ctime values are now stored in the manifest files only when - sloppy_file_stat is set. This avoids adding superfluous manifest file entries - on direct mode cache misses. -

    -
  • -
  • -

    -A “Result:” line is now always printed to the log. -

    -
  • -
  • -

    -The “cache miss” statistics counter will now be updated for read-only cache - misses, making it consistent with the cache hit case. -

    -
  • -
-
-
-
-
-

ccache 3.7.6

-
-

Release date: 2019-11-17

-
-

Bug fixes

-
    -
  • -

    -The opt-in “file_macro sloppiness” mode has been removed so that the input - file path now is always included in the direct mode hash. This fixes a bug - that could result in false cache hits in an edge case when “file_macro - sloppiness” is enabled and several identical source files include a relative - header file with the same name but in different directories. -

    -
  • -
  • -

    -Statistics files are no longer lost when the filesystem of the cache is full. -

    -
  • -
  • -

    -Bail out on too hard Clang option -MJarg (in addition to the previous - bailout of -MJ arg). -

    -
  • -
  • -

    -Properly handle color diagnostics in the depend mode as well. -

    -
  • -
-
-
-
-
-

ccache 3.7.5

-
-

Release date: 2019-10-22

-
-

New features

-
    -
  • -

    -Added support for -MF=arg (with an extra equal sign) as understood by - EDG-based compilers. -

    -
  • -
-
-
-

Bug fixes

-
    -
  • -

    -Fixed a regression in 3.7.2 that could result in a warning message instead of - an error in an edge case related to usage of “-Werror”. -

    -
  • -
  • -

    -An implicit -MQ is now passed to the preprocessor only if the object file - extension is non-standard. This will make it easier to use EDG-based - compilers (e.g. GHS) which don’t understand -MQ. (This is a bug fix of the - corresponding improvement implemented in ccache 3.4.) -

    -
  • -
  • -

    -ccache now falls back to running the real compiler instead of failing fataly - if an internal temporary file is missing after compilation. -

    -
  • -
  • -

    -Fixed a crash if localtime returns null pointer in localtime_r replacement. -

    -
  • -
  • -

    -Fixed header file dependency tracking when building ccache itself. -

    -
  • -
  • -

    -Fixed warning during configure in out-of-tree build in developer mode. -

    -
  • -
-
-
-
-
-

ccache 3.7.4

-
-

Release date: 2019-09-12

-
-

Improvements

-
    -
  • -

    -Added support for GCC 9’s -gz[=type] option (previously ccache would think - that “-gz” alone would enable debug information, thus potentially including - the current directory in the hash). -

    -
  • -
  • -

    -Added support for converting paths like “/c/users” into relative paths on - Windows. -

    -
  • -
-
-
-
-
-

ccache 3.7.3

-
-

Release date: 2019-08-17

-
-

Bug fixes

-
    -
  • -

    -The cache size (which is counted in “used disk blocks”) is now correct on - filesystems that use more or less disk blocks than conventional filesystems, - e.g. ecryptfs or btrfs/zfs with transparent compression. This also fixes a - related problem with ccache’s own test suite when run on such file systems. -

    -
  • -
  • -

    -Fixed a regression in 3.7.2 when using the compiler option “-Werror” and then - “-Wno-error” later on the command line. -

    -
  • -
-
-
-
-
-

ccache 3.7.2

-
-

Release date: 2019-07-19

-
-

Bug fixes

-
    -
  • -

    -The compiler option -gdwarf* no longer forces “run_second_cpp = true”. -

    -
  • -
  • -

    -Added verification that the value passed to the -o/--set-config option is - valid. -

    -
  • -
  • -

    -Fixed detection of precompiled headers in the depend mode. -

    -
  • -
  • -

    -Bail out on too hard Clang option -ftime-trace. -

    -
  • -
  • -

    -ccache now updates the correct stats file when adding/updating manifest - files. This bug previously made the file and size statistics counters - incorrect over time. -

    -
  • -
  • -

    -Fixed warnings from Clang about unused arguments during preprocessing. -

    -
  • -
  • -

    -Unknown manifest versions are now handled gracefully in --dump-manifest. -

    -
  • -
  • -

    -Fixed make check with “funny” locales. -

    -
  • -
-
-
-

Documentation

-
    -
  • -

    -Added a hint about not running autogen.sh when building from a release - archive. -

    -
  • -
  • -

    -Mention that xsltproc is needed when building from the source repository. -

    -
  • -
-
-
-
-
-

ccache 3.7.1

-
-

Release date: 2019-05-01

-
-

Changes

-
    -
  • -

    -Fixed a problem when using the compiler option -MF /dev/null. -

    -
  • -
  • -

    -Long commandlines are now handled gracefully on Windows by using the @file - syntax to avoid hitting the commandline size limit. -

    -
  • -
  • -

    -Fixed complaint from GCC 9’s -Werror=format-overflow when compiling ccache - itself. -

    -
  • -
-
-
-
-
-

ccache 3.7

-
-

Release date: 2019-04-23

-
-

Changes

-
    -
  • -

    -Fixed crash when the debug mode is enabled and the output file is in a - non-writable directory, e.g. when the output file is /dev/null. -

    -
  • -
  • -

    -Fixed an issue when printing very large log messages to the debug log. -

    -
  • -
  • -

    -Fixed bugs related to support for -gsplit-dwarf. Previously ccache could - produce an incorrect link to the .dwo file in the .o file. -

    -
  • -
  • -

    -Compilations with /dev/null as the input file are now cached. -

    -
  • -
  • -

    -ccache has learned how to construct the object filename if no -o option is - given and the source filename does not include a . or ends with a .. -

    -
  • -
  • -

    -Fixed a temporary file leak when the depend mode is enabled and the compiler - produces standard error output. -

    -
  • -
  • -

    -Fixed a bug in the depend mode where a manifest hash only could be associated - with one set of header dependencies. -

    -
  • -
  • -

    -Manifest files did not get marked as used on direct cache hits, so the LRU - cache cleanup would incorrectly remove them eventually. This has been fixed. -

    -
  • -
  • -

    -The rewriting of absolute paths into relative paths in the dependency file - has been enabled in the depend mode as well. -

    -
  • -
  • -

    -ccache now ignores unknown keys in configuration files for forward - compatibility. -

    -
  • -
  • -

    -Rearranged command-line options into sections in the help text. -

    -
  • -
  • -

    -Documented the previously undocumented --dump-manifest and --hash-file - options (only useful for debugging ccache itself). -

    -
  • -
  • -

    -Added missing documentation for the command-line option -k/--get-config - added in ccache 3.5. -

    -
  • -
  • -

    -Renamed the --print-config command to --show-config. -

    -
  • -
  • -

    -Added a new --print-stats command that prints statistics counters in - machine-parsable (tab-separated) format. -

    -
  • -
  • -

    -ccache no longer creates a missing output directory, thus mimicking the - compiler behavior for -o out/obj.o when “out” doesn’t exist. -

    -
  • -
  • -

    --fdebug-prefix-map=ARG, -ffile-prefix-map=ARG and - -fmacro-prefix-map=ARG are now included in the hash, but only the part - before “ARG”. This fixes a bug where compiler feature detection of said flags - would not work correctly with ccache. -

    -
  • -
  • -

    -Bail out on too hard compiler option -gtoggle. -

    -
  • -
  • -

    -Bail out on too hard Clang options --analyze and -analyze. -

    -
  • -
  • -

    -Improved debug logging of file hashes in depend mode. -

    -
  • -
  • -

    -Improved handling of various -g* options. In particular, ccache now - understands that -g0 cancels out previous -g* options. -

    -
  • -
  • -

    -Worked around a problem with Automake related to .d files when using the - hard link mode. -

    -
  • -
  • -

    -Added opt-in (at configure time) support for enabling trace logs for - profiling ccache itself. See doc/DEVELOPER.md in the code tree for more - information -

    -
  • -
  • -

    -Removed support for Fortran 77 again. Some Fortran support was added in - ccache 3.3, but the implementation did not work when Fortran modules are - involved. -

    -
  • -
-
-
-
-
-

ccache 3.6

-
-

Release date: 2019-01-14

-
-

Changes

-
    -
  • -

    -ccache now has an opt-in “depend mode”. When enabled, ccache never executes - the preprocessor, which results in much lower cache miss overhead at the - expense of a lower potential cache hit rate. The depend mode is only possible - to use when the compiler option -MD or -MMD is used. -

    -
  • -
  • -

    -Added support for GCC’s -ffile-prefix-map option. The -fmacro-prefix-map - option is now also skipped from the hash. -

    -
  • -
  • -

    -Added support for multiple -fsanitize-blacklist arguments. -

    -
  • -
  • -

    -ccache now includes the environment variables LANG, LC_ALL, LC_CTYPE - and LC_MESSAGES in the hash since they may affect localization of compiler - warning messages. Set sloppiness to locale to opt out of this. -

    -
  • -
  • -

    -Fixed a problem due to Clang overwriting the output file when compiling an - assembler file. -

    -
  • -
  • -

    -Clarified the manual to explain the reasoning behind the “file_macro” - sloppiness setting in a better way. -

    -
  • -
  • -

    -ccache now handles several levels of nonexistent directories when rewriting - absolute paths to relative. -

    -
  • -
  • -

    -A new sloppiness setting clang_index_store makes ccache skip the Clang - compiler option -index-store-path and its argument when computing the - manifest hash. This is useful if you use Xcode, which uses an index store - path derived from the local project path. Note that the index store won’t be - updated correctly on cache hits if you enable this option. -

    -
  • -
  • -

    -Rename sloppiness no_system_headers to system_headers for consistency - with other options. no_system_headers can still be used as an - (undocumented) alias. -

    -
  • -
  • -

    -The GCC variables “DEPENDENCIES_OUTPUT” and “SUNPRO_DEPENDENCIES” are now - supported correctly. -

    -
  • -
  • -

    -The algorithm that scans for __DATE_ and __TIME__ tokens in the hashed - source code now doesn’t produce false positives for tokens where __DATE__ - or __TIME__ is a substring. -

    -
  • -
-
-
-
-
-

ccache 3.5.1

-
-

Release date: 2019-01-02

-
-

Changes

-
    -
  • -

    -Added missing getopt_long.c source file to release archive. -

    -
  • -
  • -

    -Fixed (harmless) compiler warnings when building ccache object files. -

    -
  • -
  • -

    -CFLAGS is no longer passed to the linker when linking ccache. -

    -
  • -
  • -

    -Improved development mode build flags. -

    -
  • -
-
-
-
-
-

ccache 3.5

-
-

Release date: 2018-10-15

-
-

Changes

-
    -
  • -

    -Added a boolean debug (CCACHE_DEBUG) configuration option. When enabled, - ccache will create per-object debug files that are helpful e.g. when - debugging unexpected cache misses. See also the new “Cache debugging” section - in the manual. -

    -
  • -
  • -

    -Renamed CCACHE_CC to CCACHE_COMPILER (keeping the former as a deprecated - alias). -

    -
  • -
  • -

    -Added a new command-line option -k/--get-config that prints the value of a - config key. -

    -
  • -
  • -

    -It is now possible to let ccache hash a precomputed checksum file instead of - the full content of a precompiled header. This can save time for large - precompiled headers. Note that the build system needs to keep the checksum - file in sync with the precompiled header for this to work. -

    -
  • -
  • -

    -Improved performance substantially when using hash_dir = false on platforms - like macOS where getcwd() is slow. -

    -
  • -
  • -

    -Added “stats updated” timestamp in ccache -s output. This can be useful if - you wonder whether ccache actually was used for your last build. -

    -
  • -
  • -

    -Renamed “stats zero time” to “stats zeroed” and documented it. The counter is - also now only present in ccache -s output when ccache -z actually has - been called. -

    -
  • -
  • -

    -The content of the -fsanitize-blacklist file is now included in the hash, - so updates to the file will now correctly result in separate cache entries. -

    -
  • -
  • -

    -It’s now possible to opt out of building and installing man pages when - running make install in the source repository. -

    -
  • -
  • -

    -If the compiler type can’t be detected (e.g. if it is named cc), use safer - defaults that won’t trip up Clang. -

    -
  • -
  • -

    -Made the ccache test suite work on FreeBSD. -

    -
  • -
  • -

    -Added file_stat_matches_ctime option to disable ctime check if - file_stat_matches is enabled. -

    -
  • -
  • -

    -Made “./configure --without-bundled-zlib” do what’s intended. -

    -
  • -
-
-
-
-
-

ccache 3.4.3

-
-

Release date: 2018-09-02

-
-

Bug fixes

-
    -
  • -

    -Fixed a race condition when creating the initial config file in the cache - directory. -

    -
  • -
  • -

    -Bail out on too hard Clang option -MJ. -

    -
  • -
  • -

    -Bail out on too hard option -save-temps=obj. -

    -
  • -
  • -

    -Handle separate parameter to Clang option -target correctly. -

    -
  • -
  • -

    -Upgraded bundled zlib to version 1.2.11. -

    -
  • -
-
-
-
-
-

ccache 3.4.2

-
-

Release date: 2018-03-25

-
-

Bug fixes

-
    -
  • -

    -The cleanup algorithm has been fixed to not misbehave when files are removed - by another process while the cleanup process is running. Previously, too many - files could be removed from the cache if multiple cleanup processes were - triggered at the same time, in extreme cases trimming the cache to a much - smaller size than the configured limits. -

    -
  • -
  • -

    -Correctly hash preprocessed headers located in a “.gch directory”. - Previously, ccache would not pick up changes to such precompiled headers, - risking false positive cache hits. -

    -
  • -
  • -

    -Fixed build failure when using the bundled zlib sources. -

    -
  • -
  • -

    -ccache 3.3.5 added a workaround for not triggering Clang errors when a - precompiled header’s dependency has an updated timestamp (but identical - content). That workaround is now only applied when the compiler is Clang. -

    -
  • -
  • -

    -Made it possible to perform out-of-source builds in dev mode again. -

    -
  • -
-
-
-
-
-

ccache 3.4.1

-
-

Release date: 2018-02-11

-
-

Bug fixes

-
    -
  • -

    -Fixed printing of version number in ccache --version. -

    -
  • -
-
-
-
-
-

ccache 3.4

-
-

Release date: 2018-02-11

-
-

New features and enhancements

-
    -
  • -

    -The compiler option form --sysroot arg is now handled like the documented - --sysroot=arg form. -

    -
  • -
  • -

    -Added support for caching .su files generated by GCC flag -fstack-usage. -

    -
  • -
  • -

    -ccache should now work with distcc’s “pump” wrapper. -

    -
  • -
  • -

    -The optional unifier is no longer disabled when the direct mode is enabled. -

    -
  • -
  • -

    -Added support for NVCC compiler options --compiler-bindir/-ccbin, - --output-directory/-odir and --libdevice-directory/-ldir. -

    -
  • -
  • -

    -Boolean environment variable settings no longer accept the following - (case-insensitive) values: 0, false, disable and no. All other values - are accepted and taken to mean “true”. This is to stop users from setting - e.g. CCACHE_DISABLE=0 and then expect the cache to be used. -

    -
  • -
  • -

    -Improved support for run_second_cpp = false: If combined with passing - -fdirectives-only (GCC) or frewrite-includes (Clang) to the compiler, - diagnostics warnings and similar will be correct. -

    -
  • -
  • -

    -An implicit -MQ is now passed to the preprocessor only if the object file - extension is non-standard. This should make it easier to use EDG-based - compilers (e.g. GHS) which don’t understand -MQ. -

    -
  • -
  • -

    -ccache now treats an unreadable configuration file just like a missing - configuration file. -

    -
  • -
  • -

    -Documented more pitfalls with enabling hard_links (CCACHE_HARDLINK). -

    -
  • -
  • -

    -Documented caveats related to colored warnings from compilers. -

    -
  • -
-
-
-

Bug fixes

-
    -
  • -

    -File size and number counters are now updated correctly when files are - overwritten in the cache, e.g. when using CCACHE_RECACHE. -

    -
  • -
  • -

    -run_second_cpp is now forced for NVCC. -

    -
  • -
  • -

    -Fixed how the NVCC options -optf and -odir are handled. -

    -
  • -
-
-
-
-
-

ccache 3.3.6

-
-

Release date: 2018-01-28

-
-

New features and enhancements

-
    -
  • -

    -Improved instructions on how to get cache hits between different working - directories. -

    -
  • -
-
-
-

Bug fixes

-
    -
  • -

    -Fixed regression in ccache 3.3.5 related to the UNCACHED_ERR_FD feature. -

    -
  • -
-
-
-
-
-

ccache 3.3.5

-
-

Release date: 2018-01-13

-
-

New features and enhancements

-
    -
  • -

    -Documented how automatic cache cleanup works. -

    -
  • -
-
-
-

Bug fixes

-
    -
  • -

    -Fixed a regression where the original order of debug options could be lost. - This reverts the “Improved parsing of -g* options” feature in ccache 3.3. -

    -
  • -
  • -

    -Multiple -fdebug-prefix-map options should now be handled correctly. -

    -
  • -
  • -

    -Fixed matching of directories in the ignore_headers_in_manifest - configuration option. -

    -
  • -
  • -

    -Fixed detection of missing argument to -opt/--options-file. -

    -
  • -
  • -

    -ccache now bails out when building a precompiled header if any of the - corresponding header files has an updated timestamp. This fixes complaints - from Clang. -

    -
  • -
  • -

    -Fixed a bug related to erroneously storing a dependency file with absolute - paths in the cache on a preprocessed hit. -

    -
  • -
  • -

    -ccache -c/--cleanup now works like documented: it just recalculates size - counters and trims the cache to not exceed the max size and file number - limits. Previously, the forced cleanup took “limit_multiple” into account, so - that ccache -c/--cleanup by default would trim the cache to 80% of the max - limit. -

    -
  • -
  • -

    -ccache no longer ignores linker arguments for Clang since Clang warns about - them. -

    -
  • -
  • -

    -Plugged a couple of file descriptor leaks. -

    -
  • -
  • -

    -Fixed a bug where ccache would skip hashing the compiler argument following a - -fno-working-directory, -fworking-directory, -nostdinc, -nostdinc++, - -remap or -trigraphs option in preprocessor mode. -

    -
  • -
-
-
-
-
-

ccache 3.3.4

-
-

Release date: 2017-02-17

-
-

New features and enhancements

-
    -
  • -

    -Documented the different cache statistics counters. -

    -
  • -
-
-
-

Bug fixes

-
    -
  • -

    -Fixed a regression in ccache 3.3 related to potentially bad content of - dependency files when compiling identical source code but with different - source paths. This was only partially fixed in 3.3.2 and reverts the new - “Names of included files are no longer included in the hash of the compiler’s - preprocessed output” feature in 3.3. -

    -
  • -
  • -

    -Corrected statistics counter for -optf/--options-file failure. -

    -
  • -
  • -

    -Fixed undefined behavior warnings in ccache found by -fsanitize=undefined. -

    -
  • -
-
-
-
-
-

ccache 3.3.3

-
-

Release date: 2016-10-26

-
-

Bug fixes

-
    -
  • -

    -ccache now detects usage of .incbin assembler directives in the source code - and avoids caching such compilations. -

    -
  • -
-
-
-
-
-

ccache 3.3.2

-
-

Release date: 2016-09-28

-
-

Bug fixes

-
    -
  • -

    -Fixed a regression in ccache 3.3 related to potentially bad content of - dependency files when compiling identical source code but with different - source paths. -

    -
  • -
  • -

    -Fixed a regression in ccache 3.3.1: ccache could get confused when using the - compiler option -Wp, to pass multiple options to the preprocessor, - resulting in missing dependency files from direct mode cache hits. -

    -
  • -
-
-
-
-
-

ccache 3.3.1

-
-

Release date: 2016-09-07

-
-

Bug fixes

-
    -
  • -

    -Fixed a problem in the “multiple -arch options” support introduced in 3.3. - When using the direct mode (the default), different combinations of -arch - options were not detected properly. -

    -
  • -
  • -

    -Fixed an issue when compiler option -Wp,-MT,path is used instead of -MT - path (and similar for -MF, -MP and -MQ) and run_second_cpp - (CCACHE_CPP2) is enabled. -

    -
  • -
-
-
-
-
-

ccache 3.3

-
-

Release date: 2016-08-27

-
-

Notes

-
    -
  • -

    -A C99-compatible compiler is now required to build ccache. -

    -
  • -
-
-
-

New features and enhancements

-
    -
  • -

    -The configuration option run_second_cpp (CCACHE_CPP2) now defaults to - true. This improves ccache’s out-of-the-box experience for compilers that - can’t compile their own preprocessed output with the same outcome as if they - compiled the real source code directly, e.g. newer versions of GCC and Clang. -

    -
  • -
  • -

    -The configuration option hash_dir (CCACHE_HASHDIR) now defaults to true. -

    -
  • -
  • -

    -Added a new ignore_headers_in_manifest configuration option, which - specifies headers that should be ignored in the direct mode. -

    -
  • -
  • -

    -Added a new prefix_command_cpp (CCACHE_PREFIX_CPP) configuration option, - which specifies one or several prefixes to add to the command line ccache - uses when invoking the preprocessor. -

    -
  • -
  • -

    -Added a new limit_multiple (CCACHE_LIMIT_MULTIPLE) configuration option, - which specifies how much of the cache to remove when cleaning. -

    -
  • -
  • -

    -Added a new keep_comments_cpp (CCACHE_COMMENTS) configuration option, - which tells ccache not to discard the comments before hashing preprocessor - output. This can be used to check documentation with -Wdocumentation. -

    -
  • -
  • -

    -Added a new sloppiness option no_system_headers, which tells ccache not to - include system headers in manifest files. -

    -
  • -
  • -

    -Added a new statistics counter that tracks the number of performed cleanups - due to the cache size being over the limit. The value is shown in the output - of “ccache -s”. -

    -
  • -
  • -

    -Added support for relocating debug info directory using -fdebug-prefix-map. - This allows for cache hits even when hash_dir is used in combination with - base_dir. -

    -
  • -
  • -

    -Added a new “cache hit rate” field to the output of “ccache -s”. -

    -
  • -
  • -

    -Added support for caching compilation of assembler code produced by e.g. “gcc - -S file.c”. -

    -
  • -
  • -

    -Added support for cuda including the -optf/--options-file option. -

    -
  • -
  • -

    -Added support for Fortran 77. -

    -
  • -
  • -

    -Added support for multiple -arch options to produce “fat binaries”. -

    -
  • -
  • -

    -Multiple identical -arch arguments are now handled without bailing. -

    -
  • -
  • -

    -The concatenated form of some long compiler options is now recognized, for - example when using -isystemPATH instead of -isystem PATH. -

    -
  • -
  • -

    -If hard-linking is enabled and but fails (e.g. due to cross-device linking), - ccache now falls back to copying instead of running the compiler. -

    -
  • -
  • -

    -Made the hash_dir option only have effect when generating debug info. -

    -
  • -
  • -

    -ccache now knows how to convert absolute paths to relative paths inside - dependency files when using base_dir. -

    -
  • -
  • -

    -Improved parsing of -g* options. -

    -
  • -
  • -

    -Made ccache understand -Wp,-D* options. -

    -
  • -
  • -

    -ccache now understands the undocumented -coverage (only one dash) GCC - option. -

    -
  • -
  • -

    -Names of included files are no longer included in the hash of the compiler’s - preprocessed output. This leads to more potential cache hits when not using - the direct mode. -

    -
  • -
  • -

    -Increased buffer size used when reading file data. This improves performance - slightly. -

    -
  • -
-
-
-

Bug fixes

-
    -
  • -

    -Bail out on too hard compiler option -P. -

    -
  • -
  • -

    -Fixed Clang test suite when running on Linux. -

    -
  • -
  • -

    -Fixed build and test for MinGW32 and Windows. -

    -
  • -
-
-
-
-
-

ccache 3.2.9

-
-

Release date: 2016-09-28

-
-

Bug fixes

-
    -
  • -

    -Fixed a regression in ccache 3.2.8: ccache could get confused when using the - compiler option -Wp, to pass multiple options to the preprocessor, - resulting in missing dependency files from direct mode cache hits. -

    -
  • -
-
-
-
-
-

ccache 3.2.8

-
-

Release date: 2016-09-07

-
-

Bug fixes

-
    -
  • -

    -Fixed an issue when compiler option -Wp,-MT,path is used instead of -MT - path (and similar for -MF, -MP and -MQ) and run_second_cpp - (CCACHE_CPP2) is enabled. -

    -
  • -
  • -

    -ccache now understands the undocumented -coverage (only one dash) GCC - option. -

    -
  • -
-
-
-
-
-

ccache 3.2.7

-
-

Release date: 2016-07-20

-
-

Bug fixes

-
    -
  • -

    -Fixed a bug which could lead to false cache hits for compiler command lines - with a missing argument to an option that takes an argument. -

    -
  • -
  • -

    -ccache now knows how to work around a glitch in the output of GCC 6’s - preprocessor. -

    -
  • -
-
-
-
-
-

ccache 3.2.6

-
-

Release date: 2016-07-12

-
-

Bug fixes

-
    -
  • -

    -Fixed build problem on QNX, which lacks “SA_RESTART”. -

    -
  • -
  • -

    -Bail out on compiler option -fstack-usage since it creates a .su file - which ccache currently doesn’t handle. -

    -
  • -
  • -

    -Fixed a bug where (due to ccache rewriting paths) the compiler could choose - incorrect include files if CCACHE_BASEDIR is used and the source file path - is absolute and is a symlink. -

    -
  • -
-
-
-
-
-

ccache 3.2.5

-
-

Release date: 2016-04-17

-
-

New features and enhancements

-
    -
  • -

    -Only pass Clang-specific -stdlib= to the preprocessor. -

    -
  • -
  • -

    -Improved handling of stale NFS handles. -

    -
  • -
  • -

    -Made it harder to misinterpret documentation of boolean environment settings’ - semantics. -

    -
  • -
-
-
-

Bug fixes

-
    -
  • -

    -Include m4 files used by configure.ac in the source dist archives. -

    -
  • -
  • -

    -Corrected “Performance” section in the manual regarding __DATE_, __TIME__ - and __FILE__ macros. -

    -
  • -
  • -

    -Fixed build on Solaris 10+ and AIX 7. -

    -
  • -
  • -

    -Fixed failure to create directories on QNX. -

    -
  • -
  • -

    -Don’t (try to) update manifest file in “read-only” and “read-only direct” - modes. -

    -
  • -
  • -

    -Fixed a bug in caching of stat system calls in “file_stat_matches - sloppiness mode”. -

    -
  • -
  • -

    -Fixed bug in hashing of Clang plugins, leading to unnecessary cache misses. -

    -
  • -
  • -

    -Fixed --print-config to show “pch_defines sloppiness”. -

    -
  • -
  • -

    -The man page is now built when running “make install” from Git repository - sources. -

    -
  • -
-
-
-
-
-

ccache 3.2.4

-
-

Release date: 2015-10-08

-
-

Bug fixes

-
    -
  • -

    -Fixed build error related to zlib on systems with older make versions - (regression in ccache 3.2.3). -

    -
  • -
  • -

    -Made conversion-to-bool explicit to avoid build warnings (and potential - runtime errors) on legacy systems. -

    -
  • -
  • -

    -Improved signal handling: Kill compiler on SIGTERM; wait for compiler to exit - before exiting; die appropriately. -

    -
  • -
  • -

    -Minor fixes related to Windows support. -

    -
  • -
  • -

    -The correct compression level is now used if compression is requested. -

    -
  • -
  • -

    -Fixed a bug where cache cleanup could be run too early for caches larger than - 64 GiB on 32-bit systems. -

    -
  • -
-
-
-
-
-

ccache 3.2.3

-
-

Release date: 2015-08-16

-
-

New features and enhancements

-
    -
  • -

    -Added support for compiler option -gsplit-dwarf. -

    -
  • -
-
-
-

Bug fixes

-
    -
  • -

    -Support external zlib in nonstandard directory. -

    -
  • -
  • -

    -Avoid calling exit() inside an exit handler. -

    -
  • -
  • -

    -Let exit handler terminate properly. -

    -
  • -
  • -

    -Bail out on compiler option --save-temps in addition to -save-temps. -

    -
  • -
  • -

    -Only log “Disabling direct mode” once when failing to read potential include - files. -

    -
  • -
-
-
-
-
-

ccache 3.2.2

-
-

Release date: 2015-05-10

-
-

New features and enhancements

-
    -
  • -

    -Added support for CCACHE_COMPILERCHECK=string:<value>. This is a faster - alternative to CCACHE_COMPILERCHECK=<command> if the command’s output can - be precalculated by the build system. -

    -
  • -
  • -

    -Add support for caching code coverage results (compiling for gcov). -

    -
  • -
-
-
-

Bug fixes

-
    -
  • -

    -Made hash of cached result created with and without CCACHE_CPP2 different. - This makes it possible to rebuild with CCACHE_CPP2 set without having to - clear the cache to get new results. -

    -
  • -
  • -

    -Don’t try to reset a nonexistent stats file. This avoids “No such file or - directory” messages in the ccache log when the cache directory doesn’t exist. -

    -
  • -
  • -

    -Fixed a bug where ccache deleted Clang diagnostics after compiler failures. -

    -
  • -
  • -

    -Avoid performing an unnecessary copy of the object file on a cache miss. -

    -
  • -
  • -

    -Bail out on too hard compiler option -fmodules. -

    -
  • -
  • -

    -Bail out on too hard compiler option -fplugin=libcc1plugin (interaction - with GDB). -

    -
  • -
  • -

    -Fixed build error when compiling ccache with recent Clang versions. -

    -
  • -
  • -

    -Removed signal-unsafe code from signal handler. -

    -
  • -
  • -

    -Corrected logic for when to output cached stderr. -

    -
  • -
  • -

    -Wipe the whole cached result on failure retrieving a cached file. -

    -
  • -
  • -

    -Fixed build error when compiling ccache with recent Clang versions. -

    -
  • -
-
-
-
-
-

ccache 3.2.1

-
-

Release date: 2014-12-10

-
-

Bug fixes

-
    -
  • -

    -Fixed regression in temporary file handling, which lead to incorrect - permissions for stats, manifest and ccache.conf files in the cache. -

    -
  • -
  • -

    -CACHEDIR.TAG files are now created in the [0-9a-f] subdirectories so that - ccache.conf is not lost in backups. -

    -
  • -
  • -

    -Made the default cache size suffix G, as previously documented. -

    -
  • -
  • -

    --fdiagnostics-color=auto is now passed to the compiler even if stderr is - redirected. This fixes a problem when, for instance, a configure test probes - if the compiler (wrapped via ccache) supports -fdiagnostics-color=auto. -

    -
  • -
  • -

    -Added missing documentation for max_files and max_size configuration - options. -

    -
  • -
-
-
-
-
-

ccache 3.2

-
-

Release date: 2014-11-17

-
-

New features and enhancements

-
    -
  • -

    -Added support for configuring ccache via one or several configuration files - instead of via environment variables. Environment variables still have - priority but are no longer the recommended way of customizing ccache - behavior. See the manual for more information. -

    -
  • -
  • -

    -Added support for compiler error/warning messages with color. -

    -
  • -
  • -

    -Made creation of temporary directories and cache directories smarter to avoid - unnecessary stat calls. -

    -
  • -
  • -

    -Improved efficiency of the algorithm that scans for __DATE_ and __TIME__ - tokens in the hashed source code. -

    -
  • -
  • -

    -Added support for several binaries (separated by space) in CCACHE_PREFIX. -

    -
  • -
  • -

    -The -c option is no longer passed to the preprocessor. This fixes problems - with Clang and Solaris’s C++ compiler. -

    -
  • -
  • -

    -ccache no longer passes preprocessor options like -D and -I to the - compiler when compiling preprocessed output. This fixes warnings emitted by - Clang. -

    -
  • -
  • -

    -Compiler options -fprofile-generate, -fprofile-arcs, -fprofile-use and - -fbranch-probabilities are now handled without bailing. -

    -
  • -
  • -

    -Added support for Clang’s --serialize-diagnostic option, storing the - diagnostic file (.dia) in the cache. -

    -
  • -
  • -

    -Added support for precompiled headers when using Clang. -

    -
  • -
  • -

    -Added support for Clang .pth (pretokenized header) files. -

    -
  • -
  • -

    -Changed the -x language option to use the new objective C standard for GCC - and Clang. -

    -
  • -
  • -

    -On a cache miss, ccache now instructs the compiler to create the object file - at the real destination and then copies the file into the cache instead of - the other way around. This is needed to support compiler options like - -fprofile-arcs and --serialize-diagnostics. -

    -
  • -
  • -

    -ccache now checks that included files’ ctimes aren’t too new. This check can - be turned off by adding include_file_ctime to the “ccache sloppiness” - setting. -

    -
  • -
  • -

    -Added possibility to get cache hits based on filename, size, mtime and ctime - only. On other words, source code files are not even read, only stat-ed. This - operation mode is opt-in by adding file_stat_matches to the “ccache - sloppiness” setting. -

    -
  • -
  • -

    -The filename part of options like -Wp,-MDfilename is no longer included in - the hash since the filename doesn’t have any bearing on the result. -

    -
  • -
  • -

    -Added a “read-only direct” configuration setting, which is like the ordinary - read-only setting except that ccache will only try to retrieve results from - the cache using the direct mode, not the preprocessor mode. -

    -
  • -
  • -

    -The display and interpretation of cache size has been changed to use SI - units. -

    -
  • -
  • -

    -Default cache size is now 5 GB (was previously 1 GiB). -

    -
  • -
  • -

    -Added configuration option to set the compression level of compressed object - files in the cache. -

    -
  • -
  • -

    -Added support for @file and -@file arguments (reading options from a - file). -

    -
  • -
  • -

    --Wl, options are no longer included in the hash since they don’t affect - compilation. -

    -
  • -
  • -

    -Bail out on too hard compiler option -Wp,-P. -

    -
  • -
  • -

    -Optimized MD4 calculation code on little-endian systems. -

    -
  • -
  • -

    -Various improvements and fixes on win32. -

    -
  • -
  • -

    -Improved logging to the ccache log file. -

    -
  • -
  • -

    -Added --dump-manifest command-line option for debugging purposes. -

    -
  • -
  • -

    -Added --with-bundled-zlib configure option. -

    -
  • -
  • -

    -Upgraded bundled zlib to version 1.2.8. -

    -
  • -
  • -

    -Improved dev.mk to be more platform independent. -

    -
  • -
  • -

    -Made the test suite work with Clang and gcc-llvm on OS X. -

    -
  • -
  • -

    -Various other improvements of the test suite. -

    -
  • -
-
-
-

Bug fixes

-
    -
  • -

    -Any previous .stderr is now removed from the cache when recaching. -

    -
  • -
  • -

    -Fixed an issue when handling the -arch compiler option with an argument. -

    -
  • -
  • -

    -Fixed race condition when creating the initial cache directory. -

    -
  • -
  • -

    -Fixed test suite failures when CC is a ccache-wrapped compiler. -

    -
  • -
-
-
-
-
-

ccache 3.1.12

-
-

Release date: 2016-07-12

-
-

Bug fixes

-
    -
  • -

    -Fixed a bug where (due to ccache rewriting paths) the compiler could choose - incorrect include files if CCACHE_BASEDIR is used and the source file path - is absolute and is a symlink. -

    -
  • -
-
-
-
-
-

ccache 3.1.11

-
-

Release date: 2015-03-07

-
-

Bug fixes

-
    -
  • -

    -Fixed bug which could result in false cache hits when source code contains - '"' followed by " /*" or " //" (with variations). -

    -
  • -
  • -

    -Made hash of cached result created with and without CCACHE_CPP2 different. - This makes it possible to rebuild with CCACHE_CPP2 set without having to - clear the cache to get new results. -

    -
  • -
  • -

    -Don’t try to reset a nonexistent stats file. This avoids “No such file or - directory” messages in the ccache log when the cache directory doesn’t exist. -

    -
  • -
-
-
-
-
-

ccache 3.1.10

-
-

Release date: 2014-10-19

-
-

New features and enhancements

-
    -
  • -

    -Added support for the -Xclang compiler option. -

    -
  • -
  • -

    -Improved handling of exit code of internally executed processes. -

    -
  • -
  • -

    -Zero length object files in the cache are now rejected as invalid. -

    -
  • -
  • -

    -Bail out on option -gsplit-dwarf (since it produces multiple output files). -

    -
  • -
  • -

    -Compiler option -fdebug-prefix-map is now ignored (not part of the hash). - (The -fdebug-prefix-map option may be used in combination with - CCACHE_BASEDIR to reuse results across different directories.) -

    -
  • -
  • -

    -Added note in documentation that --ccache-skip currently does not mean - “don’t hash the following option”. -

    -
  • -
  • -

    -To enable support for precompiled headers (PCH), CCACHE_SLOPPINESS now also - needs to include the new pch_defines sloppiness. This is because ccache - can’t detect changes in the source code when only defined macros have been - changed. -

    -
  • -
  • -

    -Stale files in the internal temporary directory (<ccache_dir>/tmp) are now - cleaned up if they are older than one hour. -

    -
  • -
-
-
-

Bug fixes

-
    -
  • -

    -Fixed path canonicalization in make_relative_path() when path doesn’t - exist. -

    -
  • -
  • -

    -Fixed bug in common_dir_prefix_length(). This corrects the CCACHE_BASEDIR - behavior. -

    -
  • -
  • -

    -ccache no longer tries to create the cache directory when CCACHE_DISABLE is - set. -

    -
  • -
  • -

    -Fixed bug when reading manifests with a very large number of file info - entries. -

    -
  • -
  • -

    -Fixed problem with logging of current working directory. -

    -
  • -
-
-
-
-
-

ccache 3.1.9

-
-

Release date: 2013-01-06

-
-

Bug fixes

-
    -
  • -

    -The EAGAIN signal is now handled correctly when emitting cached stderr - output. This fixes a problem triggered by large error outputs from the - compiler. -

    -
  • -
  • -

    -Subdirectories in the cache are no longer created in read-only mode. -

    -
  • -
  • -

    -Fixed so that ccache’s log file descriptor is not made available to the - compiler. -

    -
  • -
  • -

    -Improved error reporting when failing to create temporary stdout/stderr files - when executing the compiler. -

    -
  • -
  • -

    -Disappearing temporary stdout/stderr files are now handled gracefully. -

    -
  • -
-
-
-

Other

-
    -
  • -

    -Fixed test suite to work on ecryptfs. -

    -
  • -
-
-
-
-
-

ccache 3.1.8

-
-

Release date: 2012-08-11

-
-

New features and enhancements

-
    -
  • -

    -Made paths to dependency files relative in order to increase cache hits. -

    -
  • -
  • -

    -Added work-around to make ccache work with buggy GCC 4.1 when creating a - pre-compiled header. -

    -
  • -
  • -

    -Clang plugins are now hashed to catch plugin upgrades. -

    -
  • -
-
-
-

Bug fixes

-
    -
  • -

    -Fixed crash when the current working directory has been removed. -

    -
  • -
  • -

    -Fixed crash when stderr is closed. -

    -
  • -
  • -

    -Corrected a corner case when parsing backslash escapes in string - literals. -

    -
  • -
  • -

    -Paths are now correctly canonicalized when computing paths relative to the - base directory. -

    -
  • -
-
-
-

Other

-
    -
  • -

    -Made git version macro work when compiling outside of the source directory. -

    -
  • -
  • -

    -Fixed static_assert macro definition clash with GCC 4.7. -

    -
  • -
-
-
-
-
-

ccache 3.1.7

-
-

Release date: 2012-01-08

-
-

Bug fixes

-
    -
  • -

    -Non-writable CCACHE_DIR is now handled gracefully when CCACHE_READONLY is - set. -

    -
  • -
  • -

    -Made failure to create files (typically due to bad directory permissions) in - the cache directory fatal. Previously, such failures were silently and - erroneously flagged as “compiler produced stdout”. -

    -
  • -
  • -

    -Both the -specs=file and --specs=file forms are now recognized. -

    -
  • -
  • -

    -Added recognition and hashing of GCC plugins specified with -fplugin=file. -

    -
  • -
  • -

    -CCACHE_COMPILERCHECK now also determines how to hash explicit specs files - (-specs=file). -

    -
  • -
  • -

    -Added CPATH, C_INCLUDE_PATH and similar environment variables to the hash - to avoid false cache hits when such variables have changed. -

    -
  • -
  • -

    -Corrected log message when unify mode is enabled. -

    -
  • -
  • -

    -Reverted the GCC bug compatibility introduced in ccache 3.1.5 for -MT/-MQ - options with concatenated arguments. (The bug is fixed in recent GCC - versions.) -

    -
  • -
-
-
-

Other

-
    -
  • -

    -Corrected license header for mdfour.c. -

    -
  • -
  • -

    -Improved documentation on how to fix bad object files in the cache. -

    -
  • -
-
-
-
-
-

ccache 3.1.6

-
-

Release date: 2011-08-21

-
-

New features and enhancements

-
    -
  • -

    -Rewrite argument to --sysroot if CCACHE_BASEDIR is used. -

    -
  • -
-
-
-

Bug fixes

-
    -
  • -

    -Don’t crash if getcwd() fails. -

    -
  • -
  • -

    -Fixed alignment of “called for preprocessing” counter. -

    -
  • -
-
-
-
-
-

ccache 3.1.5

-
-

Release date: 2011-05-29

-
-

New features and enhancements

-
    -
  • -

    -Added a new statistics counter named “called for preprocessing”. -

    -
  • -
  • -

    -The original command line is now logged to the file specified with - CCACHE_LOGFILE. -

    -
  • -
  • -

    -Improved error logging when system calls fail. -

    -
  • -
  • -

    -Added support for rewriting absolute paths in -F/-iframework GCC - options. -

    -
  • -
  • -

    -Improved order of statistics counters in ccache -s output. -

    -
  • -
-
-
-

Bug fixes

-
    -
  • -

    -The -MF/-MT/-MQ options with concatenated argument are now handled - correctly when they are last on the command line. -

    -
  • -
  • -

    -ccache is now bug compatible with GCC for the -MT/-MQ options with - concatenated arguments. -

    -
  • -
  • -

    -Fixed a minor memory leak. -

    -
  • -
  • -

    -Systems that lack (and don’t need to be linked with) libm are now supported. -

    -
  • -
-
-
-
-
-

ccache 3.1.4

-
-

Release date: 2011-01-09

-
-

Bug fixes

-
    -
  • -

    -Made a work-around for a bug in gzputc() in zlib 1.2.5. -

    -
  • -
  • -

    -Corrupt manifest files are now removed so that they won’t block direct mode - hits. -

    -
  • -
  • -

    -ccache now copes with file systems that don’t know about symbolic links. -

    -
  • -
  • -

    -The file handle in now correctly closed on write error when trying to create - a cache dir tag. -

    -
  • -
-
-
-
-
-

ccache 3.1.3

-
-

Release date: 2010-11-28

-
-

Bug fixes

-
    -
  • -

    -The -MFarg, -MTarg and -MQarg compiler options (i.e, without space between - option and argument) are now handled correctly. -

    -
  • -
-
-
-

Other

-
    -
  • -

    -Portability fixes for HP-UX 11.00 and other esoteric systems. -

    -
  • -
-
-
-
-
-

ccache 3.1.2

-
-

Release date: 2010-11-21

-
-

Bug fixes

-
    -
  • -

    -Bail out on too hard compiler options -fdump-*. -

    -
  • -
  • -

    -NULL return values from malloc/calloc of zero bytes are now handled - correctly. -

    -
  • -
  • -

    -Fixed issue when parsing precompiler output on AIX. -

    -
  • -
-
-
-

Other

-
    -
  • -

    -Improved documentation on which information is included in the hash sum. -

    -
  • -
  • -

    -Made the “too new header file” test case work on file systems with - unsynchronized clocks. -

    -
  • -
  • -

    -The test suite now also works on systems that lack a /dev/zero. -

    -
  • -
-
-
-
-
-

ccache 3.1.1

-
-

Release date: 2010-11-07

-
-

Bug fixes

-
    -
  • -

    -ccache now falls back to preprocessor mode when a non-regular include file - (device, socket, etc) has been detected so that potential hanging due to - blocking reads is avoided. -

    -
  • -
  • -

    -CRC errors are now detected when decompressing compressed files in the cache. -

    -
  • -
  • -

    -Fixed potential object file corruption race on NFS. -

    -
  • -
  • -

    -Minor documentation corrections. -

    -
  • -
  • -

    -Fixed configure detection of ar. -

    -
  • -
  • -

    -ccache development version (set by dev.mk) now works with gits whose - describe command doesn’t understand --dirty. -

    -
  • -
-
-
-

Other

-
    -
  • -

    -Minor debug log message improvements. -

    -
  • -
-
-
-
-
-

ccache 3.1

-
-

Release date: 2010-09-16

-
-

New features and enhancements

-
    -
  • -

    -Added support for hashing the output of a custom command (e.g. %compiler% - --version) to identify the compiler instead of stat-ing or hashing the - compiler binary. This can improve robustness when the compiler (as seen by - ccache) actually isn’t the real compiler but another compiler wrapper. -

    -
  • -
  • -

    -Added support for caching compilations that use precompiled headers. (See the - manual for important instructions regarding this.) -

    -
  • -
  • -

    -Locking of the files containing statistics counters is now done using - symlinks instead of POSIX locks. This should make ccache behave a lot better - on file systems where POSIX locks are slow or broken (e.g. NFS on some - systems). -

    -
  • -
  • -

    -Manifest files are now updated without the need of taking locks. -

    -
  • -
  • -

    -Updates of statistics counters are now always done in one of the sub-level - statistics files. This reduces lock contention, which especially improves - performance on slow NFS mounts. -

    -
  • -
  • -

    -Reading and writing of statistics counters has been made forward-compatible - (unknown counters are retained). -

    -
  • -
  • -

    -Files are now read without using mmap(). This has two benefits: it’s more - robust against file changes during reading and it improves performance on - poor systems where mmap() doesn’t use the disk cache. -

    -
  • -
  • -

    -Added .cp and .CP as known C++ suffixes. -

    -
  • -
  • -

    -Improved logging. -

    -
  • -
  • -

    -Added -install_name as an option known to take an argument. (This improves - statistics when using the Darwin linker.) -

    -
  • -
-
-
-

Bug fixes

-
    -
  • -

    -Non-fatal error messages are now never printed to stderr but logged instead. -

    -
  • -
  • -

    -Fixed a bug affecting failing commands when --ccache-skip is used. -

    -
  • -
  • -

    -Made --ccache-skip work for all options. -

    -
  • -
  • -

    -EINTR is now handled correctly. -

    -
  • -
-
-
-

Other

-
    -
  • -

    -Work on porting ccache to win32 (native), mostly done by Ramiro Polla. The - port is not yet finished, but will hopefully be complete in some subsequent - release. -

    -
  • -
  • -

    -Added a --nostats flag to the performance benchmark program. -

    -
  • -
  • -

    -Made the performance benchmark program more accurate when measuring cache - hits. -

    -
  • -
  • -

    -Added a new test framework for unit tests written in C. -

    -
  • -
  • -

    -Got rid of configure-dev; dev mode is now given by dev.mk.in presence. -

    -
  • -
  • -

    -Improved documentation on how to combine ccache with other compiler wrappers - (like distcc). -

    -
  • -
  • -

    -New LICENSE.txt file with licensing and copyright details about bundled - source code. -

    -
  • -
  • -

    -New AUTHORS.txt file with a list of ccache contributors. -

    -
  • -
  • -

    -New HACKING.txt file with some notes about ccache code conventions. -

    -
  • -
-
-
-
-
-

ccache 3.0.1

-
-

Release date: 2010-07-15

-
-

Bug fixes

-
    -
  • -

    -The statistics counter “called for link” is now correctly updated when - linking with a single object file. -

    -
  • -
  • -

    -Fixed a problem with out-of-source builds. -

    -
  • -
-
-
-
-
-

ccache 3.0

-
-

Release date: 2010-06-20

-
-

General

-
    -
  • -

    -ccache is now licensed under the GNU General Public License (GPL) version 3 - or later. -

    -
  • -
-
-
-

Upgrade notes

-
    -
  • -

    -The way the hashes are calculated has changed, so you won’t get cache hits - for compilation results stored by older ccache versions. Because of this, you - might as well clear the old cache directory with ccache --clear if you - want, unless you plan to keep using an older ccache version. -

    -
  • -
-
-
-

New features and enhancements

-
    -
  • -

    -ccache now has a “direct mode” where it computes a hash of the source code - (including all included files) and compiler options without running the - preprocessor. By not running the preprocessor, CPU usage is reduced; the - speed is somewhere between 1 and 5 times that of ccache running in - traditional mode, depending on the circumstances. The speedup will be higher - when I/O is fast (e.g., when files are in the disk cache). The direct mode - can be disabled by setting CCACHE_NODIRECT. -

    -
  • -
  • -

    -Support has been added for rewriting absolute paths to relative paths when - hashing, in order to increase cache hit rate when building the same source - code in different directories even when compiling with -g and when using - absolute include directory paths. This is done by setting the - CCACHE_BASEDIR environment variable to an absolute path that specifies - which paths to rewrite. -

    -
  • -
  • -

    -Object files are now optionally stored compressed in the cache. The runtime - cost is negligible, and more files will fit in the ccache directory and in - the disk cache. Set CCACHE_COMPRESS to enable object file compression. Note - that you can’t use compression in combination with the hard link feature. -

    -
  • -
  • -

    -A CCACHE_COMPILERCHECK option has been added. This option tells ccache what - compiler-identifying information to hash to ensure that results retrieved - from the cache are accurate. Possible values are: none (don’t hash anything), - mtime (hash the compiler’s mtime and size) and content (hash the content of - the compiler binary). The default is mtime. -

    -
  • -
  • -

    -It is now possible to specify extra files whose contents should be included - in the hash sum by setting the CCACHE_EXTRAFILES option. -

    -
  • -
  • -

    -Added support for Objective-C and Objective-C++. The statistics counter - “not a C/C++ file” has been renamed to “unsupported source language”. -

    -
  • -
  • -

    -Added support for the -x compiler option. -

    -
  • -
  • -

    -Added support for long command-line options. -

    -
  • -
  • -

    -A CACHEDIR.TAG file is now created in the cache directory. See - http://www.brynosaurus.com/cachedir/. -

    -
  • -
  • -

    -Messages printed to the debug log (specified by CCACHE_LOGFILE) have been - improved. -

    -
  • -
  • -

    -You can relax some checks that ccache does in direct mode by setting - CCACHE_SLOPPINESS. See the manual for more information. -

    -
  • -
  • -

    -CCACHE_TEMPDIR no longer needs to be on the same filesystem as - CCACHE_DIR. -

    -
  • -
  • -

    -The default value of CCACHE_TEMPDIR has been changed to $CCACHE_DIR/tmp - to avoid cluttering the top directory. -

    -
  • -
  • -

    -Temporary files that later will be moved into the cache are now created in - the cache directory they will end up in. This makes ccache more friendly to - Linux’s directory layout. -

    -
  • -
  • -

    -Improved the test suite and added tests for most of the new functionality. - It’s now also possible to specify a subset of tests to run. -

    -
  • -
  • -

    -Standard error output from the compiler is now only stored in the cache if - it’s non-empty. -

    -
  • -
  • -

    -If the compiler produces no object file or an empty object file, but gives a - zero exit status (could be due to a file system problem, a buggy program - specified by CCACHE_PREFIX, etc.), ccache copes with it properly. -

    -
  • -
  • -

    -Added installcheck and distcheck make targets. -

    -
  • -
  • -

    -Clarified cache size limit options’ and cleanup semantics. -

    -
  • -
  • -

    -Improved display of cache max size values. -

    -
  • -
  • -

    -The following options are no longer hashed in the preprocessor mode: - -imacros, -imultilib, -iprefix, -iquote, -isysroot, -iwithprefix, - -iwithprefixbefore, -nostdinc, -nostdinc++ and -U. -

    -
  • -
-
-
-

Bug fixes

-
    -
  • -

    -Various portability improvements. -

    -
  • -
  • -

    -Improved detection of home directory. -

    -
  • -
  • -

    -User-defined CPPFLAGS and LDFLAGS are now respected in the Makefile. -

    -
  • -
  • -

    -Fixed NFS issues. -

    -
  • -
  • -

    -Computation of the hash sum has been improved to decrease the risk of hash - collisions. For instance, the compiler options -X -Y and -X-Y previously - contributed equally to the hash sum. -

    -
  • -
  • -

    -Bail out on too hard compiler options --coverage, -fprofile-arcs, - -fprofile-generate, -fprofile-use, -frepo, -ftest-coverage and - -save-temps. Also bail out on @file style options. -

    -
  • -
  • -

    -Errors when using multiple -arch compiler options are now noted as - “unsupported compiler option”. -

    -
  • -
  • -

    --MD/-MMD options without -MT/-MF are now handled correctly. -

    -
  • -
  • -

    -The -finput-charset option is now handled correctly. -

    -
  • -
  • -

    -Added support for -Wp,-MD and -Wp,-MMD options. -

    -
  • -
  • -

    -The compiler options -Xassembler, -b, -G and -V are now correctly - recognized as taking an argument. -

    -
  • -
  • -

    -Debug information containing line numbers of predefined and command-line - macros (enabled with the compiler option -g3) will now be correct. -

    -
  • -
  • -

    -Corrected LRU cleanup handling of object files. -

    -
  • -
  • -

    -utimes() is now used instead of utime() when available. -

    -
  • -
  • -

    -Non-writable cache directories are now handled gracefully. -

    -
  • -
  • -

    -Corrected documentation about sharing the cache directory. -

    -
  • -
  • -

    -Fixed compilation warnings from GCC 4.3. -

    -
  • -
  • -

    -The command specified by CCACHE_PREFIX is no longer part of the hash. -

    -
  • -
  • -

    -Fixed bad memory access spotted by Valgrind. -

    -
  • -
  • -

    -Fixed a bug in x_realloc. -

    -
  • -
  • -

    -Freed memory is no longer referenced when compiling a .i/.ii file and - falling back to running the real compiler. -

    -
  • -
  • -

    -The test suite is now immune to external values of the CCACHE_* environment - variables. -

    -
  • -
  • -

    -Improved detection of recursive invocation. -

    -
  • -
  • -

    -The ccache binary is now not unconditionally stripped when installing. -

    -
  • -
  • -

    -Statistics counters are now correctly updated for -E option failures and - internal errors. -

    -
  • -
-
-
-
-
-

- - - diff --git a/doc/ccache.1 b/doc/ccache.1 deleted file mode 100644 index ca71648..0000000 --- a/doc/ccache.1 +++ /dev/null @@ -1,2258 +0,0 @@ -'\" t -.\" Title: ccache -.\" Author: [see the "Author" section] -.\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 10/01/2020 -.\" Manual: ccache Manual -.\" Source: ccache 3.7.12 -.\" Language: English -.\" -.TH "CCACHE" "1" "10/01/2020" "ccache 3\&.7\&.12" "ccache Manual" -.\" ----------------------------------------------------------------- -.\" * Define some portability stuff -.\" ----------------------------------------------------------------- -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.\" http://bugs.debian.org/507673 -.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.ie \n(.g .ds Aq \(aq -.el .ds Aq ' -.\" ----------------------------------------------------------------- -.\" * set default formatting -.\" ----------------------------------------------------------------- -.\" disable hyphenation -.nh -.\" disable justification (adjust text to left margin only) -.ad l -.\" ----------------------------------------------------------------- -.\" * MAIN CONTENT STARTS HERE * -.\" ----------------------------------------------------------------- -.SH "NAME" -ccache \- a fast C/C++ compiler cache -.SH "SYNOPSIS" -.sp -.nf -\fBccache\fR [\fIoptions\fR] -\fBccache\fR \fIcompiler\fR [\fIcompiler options\fR] -\fIcompiler\fR [\fIcompiler options\fR] (via symbolic link) -.fi -.SH "DESCRIPTION" -.sp -ccache is a compiler cache\&. It speeds up recompilation by caching the result of previous compilations and detecting when the same compilation is being done again\&. Supported languages are C, C++, Objective\-C and Objective\-C++\&. -.sp -ccache has been carefully written to always produce exactly the same compiler output that you would get without the cache\&. The only way you should be able to tell that you are using ccache is the speed\&. Currently known exceptions to this goal are listed under CAVEATS\&. If you ever discover an undocumented case where ccache changes the output of your compiler, please let us know\&. -.SS "Features" -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -Keeps statistics on hits/misses\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -Automatic cache size management\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -Can cache compilations that generate warnings\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -Easy installation\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -Low overhead\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -Optionally compresses files in the cache to reduce disk space\&. -.RE -.SS "Limitations" -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -Only knows how to cache the compilation of a single C/C++/Objective\-C/Objective\-C++ file\&. Other types of compilations (multi\-file compilation, linking, etc) will silently fall back to running the real compiler\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -Only works with GCC and compilers that behave similar enough\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -Some compiler flags are not supported\&. If such a flag is detected, ccache will silently fall back to running the real compiler\&. -.RE -.SH "RUN MODES" -.sp -There are two ways to use ccache\&. You can either prefix your compilation commands with \fBccache\fR or you can let ccache masquerade as the compiler by creating a symbolic link (named as the compiler) to ccache\&. The first method is most convenient if you just want to try out ccache or wish to use it for some specific projects\&. The second method is most useful for when you wish to use ccache for all your compilations\&. -.sp -To use the first method, just make sure that \fBccache\fR is in your \fBPATH\fR\&. -.sp -To use the symlinks method, do something like this: -.sp -.if n \{\ -.RS 4 -.\} -.nf -cp ccache /usr/local/bin/ -ln \-s ccache /usr/local/bin/gcc -ln \-s ccache /usr/local/bin/g++ -ln \-s ccache /usr/local/bin/cc -ln \-s ccache /usr/local/bin/c++ -.fi -.if n \{\ -.RE -.\} -.sp -And so forth\&. This will work as long as the directory with symlinks comes before the path to the compiler (which is usually in \fB/usr/bin\fR)\&. After installing you may wish to run \(lqwhich gcc\(rq to make sure that the correct link is being used\&. -.if n \{\ -.sp -.\} -.RS 4 -.it 1 an-trap -.nr an-no-space-flag 1 -.nr an-break-flag 1 -.br -.ps +1 -\fBWarning\fR -.ps -1 -.br -.sp -The technique of letting ccache masquerade as the compiler works well, but currently doesn\(cqt interact well with other tools that do the same thing\&. See USING CCACHE WITH OTHER COMPILER WRAPPERS\&. -.sp .5v -.RE -.if n \{\ -.sp -.\} -.RS 4 -.it 1 an-trap -.nr an-no-space-flag 1 -.nr an-break-flag 1 -.br -.ps +1 -\fBWarning\fR -.ps -1 -.br -.sp -Do not use a hard link, use a symbolic link\&. A hard link will cause \(lqinteresting\(rq problems\&. -.sp .5v -.RE -.SH "OPTIONS" -.sp -These options only apply when you invoke ccache as \(lqccache\(rq\&. When invoked as a compiler (via a symlink as described in the previous section), the normal compiler options apply and you should refer to the compiler\(cqs documentation\&. -.PP -\fB\fB\-c, \-\-cleanup\fR\fR -.RS 4 -Clean up the cache by removing old cached files until the specified file number and cache size limits are not exceeded\&. This also recalculates the cache file count and size totals\&. Normally, there is no need to initiate cleanup manually as ccache keeps the cache below the specified limits at runtime and keeps statistics up to date on each compilation\&. Forcing a cleanup is mostly useful if you manually modify the cache contents or believe that the cache size statistics may be inaccurate\&. -.RE -.PP -\fB\fB\-C, \-\-clear\fR\fR -.RS 4 -Clear the entire cache, removing all cached files, but keeping the configuration file\&. -.RE -.PP -\fB\fB\-\-dump\-manifest\fR\fR=\fIPATH\fR -.RS 4 -Dump manifest file at PATH in text format\&. This is only useful when debugging ccache and its behavior\&. -.RE -.PP -\fB\fB\-k, \-\-get\-config\fR\fR=\fIKEY\fR -.RS 4 -Print the value of configuration option -\fIKEY\fR\&. See -CONFIGURATION -for more information\&. -.RE -.PP -\fB\fB\-\-hash\-file\fR\fR=\fIPATH\fR -.RS 4 -Print the hash (in format -\fB\-\fR) of the file at PATH\&. This is only useful when debugging ccache and its behavior\&. -.RE -.PP -\fB\fB\-h, \-\-help\fR\fR -.RS 4 -Print an options summary page\&. -.RE -.PP -\fB\fB\-F, \-\-max\-files\fR\fR=\fIN\fR -.RS 4 -Set the maximum number of files allowed in the cache\&. Use 0 for no limit\&. The value is stored in a configuration file in the cache directory and applies to all future compilations\&. -.RE -.PP -\fB\fB\-M, \-\-max\-size\fR\fR=\fISIZE\fR -.RS 4 -Set the maximum size of the files stored in the cache\&. -\fISIZE\fR -should be a number followed by an optional suffix: k, M, G, T (decimal), Ki, Mi, Gi or Ti (binary)\&. The default suffix is G\&. Use 0 for no limit\&. The value is stored in a configuration file in the cache directory and applies to all future compilations\&. -.RE -.PP -\fB\fB\-\-print\-stats\fR\fR -.RS 4 -Print statistics counter IDs and corresponding values machine\-parsable (tab\-separated) format\&. -.RE -.PP -\fB\fB\-o, \-\-set\-config\fR\fR=\fIKEY=VALUE\fR -.RS 4 -Set configuration option -\fIKEY\fR -to -\fIVALUE\fR\&. See -CONFIGURATION -for more information\&. -.RE -.PP -\fB\fB\-p, \-\-show\-config\fR\fR -.RS 4 -Print current configuration options and from where they originate (environment variable, configuration file or compile\-time default) in human\-readable format\&. -.RE -.PP -\fB\fB\-s, \-\-show\-stats\fR\fR -.RS 4 -Print a summary of configuration and statistics counters in human\-readable format\&. -.RE -.PP -\fB\fB\-V, \-\-version\fR\fR -.RS 4 -Print version and copyright information\&. -.RE -.PP -\fB\fB\-z, \-\-zero\-stats\fR\fR -.RS 4 -Zero the cache statistics (but not the configuration options)\&. -.RE -.SH "EXTRA OPTIONS" -.sp -When run as a compiler, ccache usually just takes the same command line options as the compiler you are using\&. The only exception to this is the option \fB\-\-ccache\-skip\fR\&. That option can be used to tell ccache to avoid interpreting the next option in any way and to pass it along to the compiler as\-is\&. -.if n \{\ -.sp -.\} -.RS 4 -.it 1 an-trap -.nr an-no-space-flag 1 -.nr an-break-flag 1 -.br -.ps +1 -\fBNote\fR -.ps -1 -.br -.sp -\fB\-\-ccache\-skip\fR currently only tells ccache not to interpret the next option as a special compiler option \(em the option will still be included in the direct mode hash\&. -.sp .5v -.RE -.sp -The reason this can be important is that ccache does need to parse the command line and determine what is an input filename and what is a compiler option, as it needs the input filename to determine the name of the resulting object file (among other things)\&. The heuristic ccache uses when parsing the command line is that any argument that exists as a file is treated as an input file name\&. By using \fB\-\-ccache\-skip\fR you can force an option to not be treated as an input file name and instead be passed along to the compiler as a command line option\&. -.sp -Another case where \fB\-\-ccache\-skip\fR can be useful is if ccache interprets an option specially but shouldn\(cqt, since the option has another meaning for your compiler than what ccache thinks\&. -.SH "CONFIGURATION" -.sp -ccache\(cqs default behavior can be overridden by configuration file settings, which in turn can be overridden by environment variables with names starting with \fBCCACHE_\fR\&. ccache normally reads configuration from two files: first a system\-level configuration file and secondly a cache\-specific configuration file\&. The priority of configuration settings is as follows (where 1 is highest): -.sp -.RS 4 -.ie n \{\ -\h'-04' 1.\h'+01'\c -.\} -.el \{\ -.sp -1 -.IP " 1." 4.2 -.\} -Environment variables\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04' 2.\h'+01'\c -.\} -.el \{\ -.sp -1 -.IP " 2." 4.2 -.\} -The cache\-specific configuration file -\fB\fI\fR\fR\fB/ccache\&.conf\fR -(typically -\fB$HOME/\&.ccache/ccache\&.conf\fR)\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04' 3.\h'+01'\c -.\} -.el \{\ -.sp -1 -.IP " 3." 4.2 -.\} -The system\-wide configuration file -\fB\fI\fR\fR\fB/ccache\&.conf\fR -(typically -\fB/etc/ccache\&.conf\fR -or -\fB/usr/local/etc/ccache\&.conf\fR)\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04' 4.\h'+01'\c -.\} -.el \{\ -.sp -1 -.IP " 4." 4.2 -.\} -Compile\-time defaults\&. -.RE -.sp -As a special case, if the environment variable \fBCCACHE_CONFIGPATH\fR is set, ccache reads configuration from the specified path instead of the default paths\&. -.SS "Configuration file syntax" -.sp -Configuration files are in a simple \(lqkey = value\(rq format, one setting per line\&. Lines starting with a hash sign are comments\&. Blank lines are ignored, as is whitespace surrounding keys and values\&. Example: -.sp -.if n \{\ -.RS 4 -.\} -.nf -# Set maximum cache size to 10 GB: -max_size = 10G -.fi -.if n \{\ -.RE -.\} -.SS "Boolean values" -.sp -Some settings are boolean values (i\&.e\&. truth values)\&. In a configuration file, such values must be set to the string \fBtrue\fR or \fBfalse\fR\&. For the corresponding environment variables, the semantics are a bit different: a set environment variable means \(lqtrue\(rq (even if set to the empty string), the following case\-insensitive negative values are considered an error (rather than surprising the user): \fB0\fR, \fBfalse\fR, \fBdisable\fR and \fBno\fR, and an unset environment variable means \(lqfalse\(rq\&. Each boolean environment variable also has a negated form starting with \fBCCACHE_NO\fR\&. For example, \fBCCACHE_COMPRESS\fR can be set to force compression and \fBCCACHE_NOCOMPRESS\fR can be set to force no compression\&. -.SS "Configuration settings" -.sp -Below is a list of available configuration settings\&. The corresponding environment variable name is indicated in parentheses after each configuration setting key\&. -.PP -\fBbase_dir\fR (\fBCCACHE_BASEDIR\fR) -.RS 4 -This setting should be an absolute path to a directory\&. ccache then rewrites absolute paths into relative paths before computing the hash that identifies the compilation, but only for paths under the specified directory\&. If set to the empty string (which is the default), no rewriting is done\&. A typical path to use as the base directory is your home directory or another directory that is a parent of your build directories\&. Don\(cqt use -\fB/\fR -as the base directory since that will make ccache also rewrite paths to system header files, which doesn\(cqt gain anything\&. -.sp -See also the discussion under -COMPILING IN DIFFERENT DIRECTORIES\&. -.RE -.PP -\fBcache_dir\fR (\fBCCACHE_DIR\fR) -.RS 4 -This setting specifies where ccache will keep its cached compiler outputs\&. It will only take effect if set in the system\-wide configuration file or as an environment variable\&. The default is -\fB$HOME/\&.ccache\fR\&. -.RE -.PP -\fBcache_dir_levels\fR (\fBCCACHE_NLEVELS\fR) -.RS 4 -This setting allows you to choose the number of directory levels in the cache directory\&. The default is 2\&. The minimum is 1 and the maximum is 8\&. -.RE -.PP -\fBcompiler\fR (\fBCCACHE_COMPILER\fR or (deprecated) \fBCCACHE_CC\fR) -.RS 4 -This setting can be used to force the name of the compiler to use\&. If set to the empty string (which is the default), ccache works it out from the command line\&. -.RE -.PP -\fBcompiler_check\fR (\fBCCACHE_COMPILERCHECK\fR) -.RS 4 -By default, ccache includes the modification time (\(lqmtime\(rq) and size of the compiler in the hash to ensure that results retrieved from the cache are accurate\&. This setting can be used to select another strategy\&. Possible values are: -.PP -\fBcontent\fR -.RS 4 -Hash the content of the compiler binary\&. This makes ccache very slightly slower compared to the -\fBmtime\fR -setting, but makes it cope better with compiler upgrades during a build bootstrapping process\&. -.RE -.PP -\fBmtime\fR -.RS 4 -Hash the compiler\(cqs mtime and size, which is fast\&. This is the default\&. -.RE -.PP -\fBnone\fR -.RS 4 -Don\(cqt hash anything\&. This may be good for situations where you can safely use the cached results even though the compiler\(cqs mtime or size has changed (e\&.g\&. if the compiler is built as part of your build system and the compiler\(cqs source has not changed, or if the compiler only has changes that don\(cqt affect code generation)\&. You should only use the -\fBnone\fR -setting if you know what you are doing\&. -.RE -.PP -\fBstring:value\fR -.RS 4 -Use -\fBvalue\fR -as the string to calculate hash from\&. This can be the compiler revision number you retrieved earlier and set here via environment variable\&. -.RE -.PP -\fIa command string\fR -.RS 4 -Hash the standard output and standard error output of the specified command\&. The string will be split on whitespace to find out the command and arguments to run\&. No other interpretation of the command string will be done, except that the special word -\fB%compiler%\fR -will be replaced with the path to the compiler\&. Several commands can be specified with semicolon as separator\&. Examples: -.sp -.if n \{\ -.RS 4 -.\} -.nf -%compiler% \-v -.fi -.if n \{\ -.RE -.\} -.sp -.if n \{\ -.RS 4 -.\} -.nf -%compiler% \-dumpmachine; %compiler% \-dumpversion -.fi -.if n \{\ -.RE -.\} -.sp -You should make sure that the specified command is as fast as possible since it will be run once for each ccache invocation\&. -.sp -Identifying the compiler using a command is useful if you want to avoid cache misses when the compiler has been rebuilt but not changed\&. -.sp -Another case is when the compiler (as seen by ccache) actually isn\(cqt the real compiler but another compiler wrapper \(em in that case, the default -\fBmtime\fR -method will hash the mtime and size of the other compiler wrapper, which means that ccache won\(cqt be able to detect a compiler upgrade\&. Using a suitable command to identify the compiler is thus safer, but it\(cqs also slower, so you should consider continue using the -\fBmtime\fR -method in combination with the -\fBprefix_command\fR -setting if possible\&. See -USING CCACHE WITH OTHER COMPILER WRAPPERS\&. -.RE -.RE -.PP -\fBcompression\fR (\fBCCACHE_COMPRESS\fR or \fBCCACHE_NOCOMPRESS\fR, see Boolean values above) -.RS 4 -If true, ccache will compress object files and other compiler output it puts in the cache\&. However, this setting has no effect on how files are retrieved from the cache; compressed and uncompressed results will still be usable regardless of this setting\&. The default is false\&. -.RE -.PP -\fBcompression_level\fR (\fBCCACHE_COMPRESSLEVEL\fR) -.RS 4 -This setting determines the level at which ccache will compress object files\&. It only has effect if -\fBcompression\fR -is enabled\&. The value defaults to 6, and must be no lower than 1 (fastest, worst compression) and no higher than 9 (slowest, best compression)\&. -.RE -.PP -\fBcpp_extension\fR (\fBCCACHE_EXTENSION\fR) -.RS 4 -This setting can be used to force a certain extension for the intermediate preprocessed file\&. The default is to automatically determine the extension to use for intermediate preprocessor files based on the type of file being compiled, but that sometimes doesn\(cqt work\&. For example, when using the \(lqaCC\(rq compiler on HP\-UX, set the cpp extension to -\fBi\fR\&. -.RE -.PP -\fBdebug\fR (\fBCCACHE_DEBUG\fR or \fBCCACHE_NODEBUG\fR, see Boolean values above) -.RS 4 -If true, enable the debug mode\&. The debug mode creates per\-object debug files that are helpful when debugging unexpected cache misses\&. Note however that ccache performance will be reduced slightly\&. See -debugging -for more information\&. The default is false\&. -.RE -.PP -\fBdepend_mode\fR (\fBCCACHE_DEPEND\fR or \fBCCACHE_NODEPEND\fR, see Boolean values above) -.RS 4 -If true, the depend mode will be used\&. The default is false\&. See -THE DEPEND MODE\&. -.RE -.PP -\fBdirect_mode\fR (\fBCCACHE_DIRECT\fR or \fBCCACHE_NODIRECT\fR, see Boolean values above) -.RS 4 -If true, the direct mode will be used\&. The default is true\&. See -THE DIRECT MODE\&. -.RE -.PP -\fBdisable\fR (\fBCCACHE_DISABLE\fR or \fBCCACHE_NODISABLE\fR, see Boolean values above) -.RS 4 -When true, ccache will just call the real compiler, bypassing the cache completely\&. The default is false\&. -.RE -.PP -\fBextra_files_to_hash\fR (\fBCCACHE_EXTRAFILES\fR) -.RS 4 -This setting is a list of paths to files that ccache will include in the the hash sum that identifies the build\&. The list separator is semicolon on Windows systems and colon on other systems\&. -.RE -.PP -\fBhard_link\fR (\fBCCACHE_HARDLINK\fR or \fBCCACHE_NOHARDLINK\fR, see Boolean values above) -.RS 4 -If true, ccache will attempt to use hard links from the cache directory when creating the compiler output rather than using a file copy\&. Hard links are never made for compressed cache files\&. This means that you should not enable compression if you want to use hard links\&. The default is false\&. -.if n \{\ -.sp -.\} -.RS 4 -.it 1 an-trap -.nr an-no-space-flag 1 -.nr an-break-flag 1 -.br -.ps +1 -\fBWarning\fR -.ps -1 -.br -Do not enable this option unless you are aware of the consequences\&. Using hard links may be slightly faster in some situations, but there are several pitfalls since the resulting object file will share i\-node with the cached object file: -.sp .5v -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04' 1.\h'+01'\c -.\} -.el \{\ -.sp -1 -.IP " 1." 4.2 -.\} -If the resulting object file is modified in any way, the cached object file will be modified as well\&. For instance, if you run -\fBstrip object\&.o\fR -or -echo >object\&.o, you will corrupt the cache\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04' 2.\h'+01'\c -.\} -.el \{\ -.sp -1 -.IP " 2." 4.2 -.\} -Programs that rely on modification times (like \(lqmake\(rq) can be confused since ccache updates the cached files\*(Aq modification times as part of the automatic cache size management\&. This will affect object files in the build tree as well, which can retrigger the linking step even though nothing really has changed\&. -.RE -.RE -.PP -\fBhash_dir\fR (\fBCCACHE_HASHDIR\fR or \fBCCACHE_NOHASHDIR\fR, see Boolean values above) -.RS 4 -If true (which is the default), ccache will include the current working directory (CWD) in the hash that is used to distinguish two compilations when generating debug info (compiler option -\fB\-g\fR -with variations)\&. Exception: The CWD will not be included in the hash if -\fBbase_dir\fR -is set (and matches the CWD) and the compiler option -\fB\-fdebug\-prefix\-map\fR -is used\&. See also the discussion under -COMPILING IN DIFFERENT DIRECTORIES\&. -.sp -The reason for including the CWD in the hash by default is to prevent a problem with the storage of the current working directory in the debug info of an object file, which can lead ccache to return a cached object file that has the working directory in the debug info set incorrectly\&. -.sp -You can disable this setting to get cache hits when compiling the same source code in different directories if you don\(cqt mind that CWD in the debug info might be incorrect\&. -.RE -.PP -\fBignore_headers_in_manifest\fR (\fBCCACHE_IGNOREHEADERS\fR) -.RS 4 -This setting is a list of paths to files (or directories with headers) that ccache will -\fBnot\fR -include in the manifest list that makes up the direct mode\&. Note that this can cause stale cache hits if those headers do indeed change\&. The list separator is semicolon on Windows systems and colon on other systems\&. -.RE -.PP -\fBkeep_comments_cpp\fR (\fBCCACHE_COMMENTS\fR or \fBCCACHE_NOCOMMENTS\fR, see Boolean values above) -.RS 4 -If true, ccache will not discard the comments before hashing preprocessor output\&. This can be used to check documentation with -\fB\-Wdocumentation\fR\&. -.RE -.PP -\fBlimit_multiple\fR (\fBCCACHE_LIMIT_MULTIPLE\fR) -.RS 4 -Sets the limit when cleaning up\&. Files are deleted (in LRU order) until the levels are below the limit\&. The default is 0\&.8 (= 80%)\&. See -AUTOMATIC CLEANUP -for more information\&. -.RE -.PP -\fBlog_file\fR (\fBCCACHE_LOGFILE\fR) -.RS 4 -If set to a file path, ccache will write information on what it is doing to the specified file\&. This is useful for tracking down problems\&. -.RE -.PP -\fBmax_files\fR (\fBCCACHE_MAXFILES\fR) -.RS 4 -This option specifies the maximum number of files to keep in the cache\&. Use 0 for no limit (which is the default)\&. See also -CACHE SIZE MANAGEMENT\&. -.RE -.PP -\fBmax_size\fR (\fBCCACHE_MAXSIZE\fR) -.RS 4 -This option specifies the maximum size of the cache\&. Use 0 for no limit\&. The default value is 5G\&. Available suffixes: k, M, G, T (decimal) and Ki, Mi, Gi, Ti (binary)\&. The default suffix is G\&. See also -CACHE SIZE MANAGEMENT\&. -.RE -.PP -\fBpath\fR (\fBCCACHE_PATH\fR) -.RS 4 -If set, ccache will search directories in this list when looking for the real compiler\&. The list separator is semicolon on Windows systems and colon on other systems\&. If not set, ccache will look for the first executable matching the compiler name in the normal -\fBPATH\fR -that isn\(cqt a symbolic link to ccache itself\&. -.RE -.PP -\fBpch_external_checksum\fR (\fBCCACHE_PCH_EXTSUM\fR or \fBCCACHE_NOPCH_EXTSUM\fR, see Boolean values above) -.RS 4 -When this option is set, and ccache finds a precompiled header file, ccache will look for a file with the extension \(lq\&.sum\(rq added (e\&.g\&. \(lqpre\&.h\&.gch\&.sum\(rq), and if found, it will hash this file instead of the precompiled header itself to work around the performance penalty of hashing very large files\&. -.RE -.PP -\fBprefix_command\fR (\fBCCACHE_PREFIX\fR) -.RS 4 -This option adds a list of prefixes (separated by space) to the command line that ccache uses when invoking the compiler\&. See also -USING CCACHE WITH OTHER COMPILER WRAPPERS\&. -.RE -.PP -\fBprefix_command_cpp\fR (\fBCCACHE_PREFIX_CPP\fR) -.RS 4 -This option adds a list of prefixes (separated by space) to the command line that ccache uses when invoking the preprocessor\&. -.RE -.PP -\fBread_only\fR (\fBCCACHE_READONLY\fR or \fBCCACHE_NOREADONLY\fR, see Boolean values above) -.RS 4 -If true, ccache will attempt to use existing cached object files, but it will not add new results to the cache\&. Statistics counters will still be updated, though, unless the -\fBstats\fR -option is set to -\fBfalse\fR\&. -.sp -If you are using this because your ccache directory is read\-only, you need to set -\fBtemporary_dir\fR -since ccache will fail to create temporary files otherwise\&. You may also want to set -\fBstats = false\fR -to make ccache not even try to update stats files\&. -.RE -.PP -\fBread_only_direct\fR (\fBCCACHE_READONLY_DIRECT\fR or \fBCCACHE_NOREADONLY_DIRECT\fR, see Boolean values above) -.RS 4 -Just like -\fBread_only\fR -except that ccache will only try to retrieve results from the cache using the direct mode, not the preprocessor mode\&. See documentation for -\fBread_only\fR -regarding using a read\-only ccache directory\&. -.RE -.PP -\fBrecache\fR (\fBCCACHE_RECACHE\fR or \fBCCACHE_NORECACHE\fR, see Boolean values above) -.RS 4 -If true, ccache will not use any previously stored result\&. New results will still be cached, possibly overwriting any pre\-existing results\&. -.RE -.PP -\fBrun_second_cpp\fR (\fBCCACHE_CPP2\fR or \fBCCACHE_NOCPP2\fR, see Boolean values above) -.RS 4 -If true, ccache will first run the preprocessor to preprocess the source code (see -THE PREPROCESSOR MODE) and then on a cache miss run the compiler on the source code to get hold of the object file\&. This is the default\&. -.sp -If false, ccache will first run preprocessor to preprocess the source code and then on a cache miss run the compiler on the -\fIpreprocessed source code\fR -instead of the original source code\&. This makes cache misses slightly faster since the source code only has to be preprocessed once\&. The downside is that some compilers won\(cqt produce the same result (for instance diagnostics warnings) when compiling preprocessed source code\&. -.sp -A solution to the above mentioned downside is to set -\fBrun_second_cpp\fR -to false and pass -\fB\-fdirectives\-only\fR -(for GCC) or -\fB\-frewrite\-includes\fR -(for Clang) to the compiler\&. This will cause the compiler to leave the macros and other preprocessor information, and only process the -\fB#include\fR -directives\&. When run in this way, the preprocessor arguments will be passed to the compiler since it still has to do -\fIsome\fR -preprocessing (like macros)\&. -.RE -.PP -\fBsloppiness\fR (\fBCCACHE_SLOPPINESS\fR) -.RS 4 -By default, ccache tries to give as few false cache hits as possible\&. However, in certain situations it\(cqs possible that you know things that ccache can\(cqt take for granted\&. This setting makes it possible to tell ccache to relax some checks in order to increase the hit rate\&. The value should be a comma\-separated string with options\&. Available options are: -.PP -\fBclang_index_store\fR -.RS 4 -Ignore the Clang compiler option -\fB\-index\-store\-path\fR -and its argument when computing the manifest hash\&. This is useful if you use Xcode, which uses an index store path derived from the local project path\&. Note that the index store won\(cqt be updated correctly on cache hits if you enable this option\&. -.RE -.PP -\fBfile_stat_matches\fR -.RS 4 -ccache normally examines a file\(cqs contents to determine whether it matches the cached version\&. With this option set, ccache will consider a file as matching its cached version if the mtimes and ctimes match\&. -.RE -.PP -\fBfile_stat_matches_ctime\fR -.RS 4 -Ignore ctimes when -\fBfile_stat_matches\fR -is enabled\&. This can be useful when backdating files\*(Aq mtimes in a controlled way\&. -.RE -.PP -\fBinclude_file_ctime\fR -.RS 4 -By default, ccache will not cache a file if it includes a header whose ctime is too new\&. This option disables that check\&. -.RE -.PP -\fBinclude_file_mtime\fR -.RS 4 -By default, ccache will not cache a file if it includes a header whose mtime is too new\&. This option disables that check\&. -.RE -.PP -\fBlocale\fR -.RS 4 -ccache includes the environment variables -\fBLANG\fR, -\fBLC_ALL\fR, -\fBLC_CTYPE\fR -and -\fBLC_MESSAGES\fR -in the hash by default since they may affect localization of compiler warning messages\&. Set this option to tell ccache not to do that\&. -.RE -.PP -\fBpch_defines\fR -.RS 4 -Be sloppy about -\fB#define\fRs when precompiling a header file\&. See -PRECOMPILED HEADERS -for more information\&. -.RE -.PP -\fBsystem_headers\fR -.RS 4 -By default, ccache will also include all system headers in the manifest\&. With this option set, ccache will only include system headers in the hash but not add the system header files to the list of include files\&. -.RE -.PP -\fBtime_macros\fR -.RS 4 -Ignore -\fB__DATE__\fR -and -\fB__TIME__\fR -being present in the source code\&. -.RE -.sp -See the discussion under -TROUBLESHOOTING -for more information\&. -.RE -.PP -\fBstats\fR (\fBCCACHE_STATS\fR or \fBCCACHE_NOSTATS\fR, see Boolean values above) -.RS 4 -If true, ccache will update the statistics counters on each compilation\&. The default is true\&. -.RE -.PP -\fBtemporary_dir\fR (\fBCCACHE_TEMPDIR\fR) -.RS 4 -This setting specifies where ccache will put temporary files\&. The default is -\fB/tmp\fR\&. -.if n \{\ -.sp -.\} -.RS 4 -.it 1 an-trap -.nr an-no-space-flag 1 -.nr an-break-flag 1 -.br -.ps +1 -\fBNote\fR -.ps -1 -.br -In previous versions of ccache, -\fBCCACHE_TEMPDIR\fR -had to be on the same filesystem as the -\fBCCACHE_DIR\fR -path, but this requirement has been relaxed\&.) -.sp .5v -.RE -.RE -.PP -\fBumask\fR (\fBCCACHE_UMASK\fR) -.RS 4 -This setting specifies the umask for ccache and all child processes (such as the compiler)\&. This is mostly useful when you wish to share your cache with other users\&. Note that this also affects the file permissions set on the object files created from your compilations\&. -.RE -.SH "CACHE SIZE MANAGEMENT" -.sp -By default, ccache has a 5 GB limit on the total size of files in the cache and no limit on the number of files\&. You can set different limits using the \fB\-M\fR/\fB\-\-max\-size\fR and \fB\-F\fR/\fB\-\-max\-files\fR options\&. Use \fBccache \-s/\-\-show\-stats\fR to see the cache size and the currently configured limits (in addition to other various statistics)\&. -.sp -Cleanup can be triggered in two different ways: automatic and manual\&. -.SS "Automatic cleanup" -.sp -ccache maintains counters for various statistics about the cache, including the size and number of all cached files\&. In order to improve performance and reduce issues with concurrent ccache invocations, there is one statistics file for each of the sixteen subdirectories in the cache\&. -.sp -After a new compilation result has been written to the cache, ccache will update the size and file number statistics for the subdirectory (one of sixteen) to which the result was written\&. Then, if the size counter for said subdirectory is greater than \fBmax_size / 16\fR or the file number counter is greater than \fBmax_files / 16\fR, automatic cleanup is triggered\&. -.sp -When automatic cleanup is triggered for a subdirectory in the cache, ccache will: -.sp -.RS 4 -.ie n \{\ -\h'-04' 1.\h'+01'\c -.\} -.el \{\ -.sp -1 -.IP " 1." 4.2 -.\} -Count all files in the subdirectory and compute their aggregated size\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04' 2.\h'+01'\c -.\} -.el \{\ -.sp -1 -.IP " 2." 4.2 -.\} -Remove files in LRU (least recently used) order until the size is at most -\fBlimit_multiple * max_size / 16\fR -and the number of files is at most -\fBlimit_multiple * max_files / 16\fR, where -\fBlimit_multiple\fR, -\fBmax_size\fR -and -\fBmax_files\fR -are configuration settings\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04' 3.\h'+01'\c -.\} -.el \{\ -.sp -1 -.IP " 3." 4.2 -.\} -Set the size and file number counters to match the files that were kept\&. -.RE -.sp -The reason for removing more files than just those needed to not exceed the max limits is that a cleanup is a fairly slow operation, so it would not be a good idea to trigger it often, like after each cache miss\&. -.SS "Manual cleanup" -.sp -You can run \fBccache \-c/\-\-cleanup\fR to force cleanup of the whole cache, i\&.e\&. all of the sixteen subdirectories\&. This will recalculate the statistics counters and make sure that the \fBmax_size\fR and \fBmax_files\fR settings are not exceeded\&. Note that \fBlimit_multiple\fR is not taken into account for manual cleanup\&. -.SH "CACHE COMPRESSION" -.sp -ccache can optionally compress all files it puts into the cache using the compression library zlib\&. While this may involve a tiny performance slowdown, it increases the number of files that fit in the cache\&. You can turn on compression with the \fBcompression\fR configuration setting and you can also tweak the compression level with \fBcompression_level\fR\&. -.SH "CACHE STATISTICS" -.sp -\fBccache \-s/\-\-show\-stats\fR can show the following statistics: -.TS -allbox tab(:); -ltB ltB. -T{ -Name -T}:T{ -Description -T} -.T& -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt -lt lt. -T{ -.sp -autoconf compile/link -T}:T{ -.sp -Uncachable compilation or linking by an autoconf test\&. -T} -T{ -.sp -bad compiler arguments -T}:T{ -.sp -Malformed compiler argument, e\&.g\&. missing a value for an option that requires an argument or failure to read a file specified by an option argument\&. -T} -T{ -.sp -cache file missing -T}:T{ -.sp -A file was unexpectedly missing from the cache\&. This only happens in rare situations, e\&.g\&. if one ccache instance is about to get a file from the cache while another instance removed the file as part of cache cleanup\&. -T} -T{ -.sp -cache hit (direct) -T}:T{ -.sp -A result was successfully found using the direct mode\&. -T} -T{ -.sp -cache hit (preprocessed) -T}:T{ -.sp -A result was successfully found using the preprocessor mode\&. -T} -T{ -.sp -cache miss -T}:T{ -.sp -No result was found\&. -T} -T{ -.sp -cache size -T}:T{ -.sp -Current size of the cache\&. -T} -T{ -.sp -called for link -T}:T{ -.sp -The compiler was called for linking, not compiling\&. -T} -T{ -.sp -called for preprocessing -T}:T{ -.sp -The compiler was called for preprocessing, not compiling\&. -T} -T{ -.sp -can\(cqt use precompiled header -T}:T{ -.sp -Preconditions for using precompiled headers were not fulfilled\&. -T} -T{ -.sp -ccache internal error -T}:T{ -.sp -Unexpected failure, e\&.g\&. due to problems reading/writing the cache\&. -T} -T{ -.sp -cleanups performed -T}:T{ -.sp -Number of cleanups performed, either implicitly due to the cache size limit being reached or due to explicit \fBccache \-c/\-\-cleanup\fR calls\&. -T} -T{ -.sp -compile failed -T}:T{ -.sp -The compilation failed\&. No result stored in the cache\&. -T} -T{ -.sp -compiler check failed -T}:T{ -.sp -A compiler check program specified by \fBcompiler_check\fR (\fBCCACHE_COMPILERCHECK\fR) failed\&. -T} -T{ -.sp -compiler produced empty output -T}:T{ -.sp -The compiler\(cqs output file (typically an object file) was empty after compilation\&. -T} -T{ -.sp -compiler produced no output -T}:T{ -.sp -The compiler\(cqs output file (typically an object file) was missing after compilation\&. -T} -T{ -.sp -compiler produced stdout -T}:T{ -.sp -The compiler wrote data to standard output\&. This is something that compilers normally never do, so ccache is not designed to store such output in the cache\&. -T} -T{ -.sp -couldn\(cqt find the compiler -T}:T{ -.sp -The compiler to execute could not be found\&. -T} -T{ -.sp -error hashing extra file -T}:T{ -.sp -Failure reading a file specified by \fBextra_files_to_hash\fR (\fBCCACHE_EXTRAFILES\fR)\&. -T} -T{ -.sp -files in cache -T}:T{ -.sp -Current number of files in the cache\&. -T} -T{ -.sp -multiple source files -T}:T{ -.sp -The compiler was called to compile multiple source files in one go\&. This is not supported by ccache\&. -T} -T{ -.sp -no input file -T}:T{ -.sp -No input file was specified to the compiler\&. -T} -T{ -.sp -output to a non\-regular file -T}:T{ -.sp -The output path specified with \fB\-o\fR is not a file (e\&.g\&. a directory or a device node)\&. -T} -T{ -.sp -output to stdout -T}:T{ -.sp -The compiler was instructed to write its output to standard output using \fB\-o \-\fR\&. This is not supported by ccache\&. -T} -T{ -.sp -preprocessor error -T}:T{ -.sp -Preprocessing the source code using the compiler\(cqs \fB\-E\fR option failed\&. -T} -T{ -.sp -stats updated -T}:T{ -.sp -When statistics were updated the last time\&. -T} -T{ -.sp -stats zeroed -T}:T{ -.sp -When \fBccache \-z\fR was called the last time\&. -T} -T{ -.sp -unsupported code directive -T}:T{ -.sp -Code like the assembler \fB\&.incbin\fR directive was found\&. This is not supported by ccache\&. -T} -T{ -.sp -unsupported compiler option -T}:T{ -.sp -A compiler option not supported by ccache was found\&. -T} -T{ -.sp -unsupported source language -T}:T{ -.sp -A source language e\&.g\&. specified with \fB\-x\fR was unsupported by ccache\&. -T} -.TE -.sp 1 -.SH "HOW CCACHE WORKS" -.sp -The basic idea is to detect when you are compiling exactly the same code a second time and reuse the previously produced output\&. The detection is done by hashing different kinds of information that should be unique for the compilation and then using the hash sum to identify the cached output\&. ccache uses MD4, a very fast cryptographic hash algorithm, for the hashing\&. (MD4 is nowadays too weak to be useful in cryptographic contexts, but it should be safe enough to be used to identify recompilations\&.) On a cache hit, ccache is able to supply all of the correct compiler outputs (including all warnings, dependency file, etc) from the cache\&. -.sp -ccache has two ways of gathering information used to look up results in the cache: -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -the -\fBdirect mode\fR, where ccache hashes the source code and include files directly -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -the -\fBpreprocessor mode\fR, where ccache runs the preprocessor on the source code and hashes the result -.RE -.sp -The direct mode is generally faster since running the preprocessor has some overhead\&. -.sp -If no previous result is detected (i\&.e\&., there is a cache miss) using the direct mode, ccache will fall back to the preprocessor mode unless the \fBdepend mode\fR is enabled\&. In the depend mode, ccache never runs the preprocessor, not even on cache misses\&. Read more in THE DEPEND MODE below\&. -.SS "Common hashed information" -.sp -The following information is always included in the hash: -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -the extension used by the compiler for a file with preprocessor output (normally -\fB\&.i\fR -for C code and -\fB\&.ii\fR -for C++ code) -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -the compiler\(cqs size and modification time (or other compiler\-specific information specified by the -\fBcompiler_check\fR -setting) -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -the name of the compiler -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -the current directory (if the -\fBhash_dir\fR -setting is enabled) -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -contents of files specified by the -\fBextra_files_to_hash\fR -setting (if any) -.RE -.SS "The direct mode" -.sp -In the direct mode, the hash is formed of the common information and: -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -the input source file -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -the command line options -.RE -.sp -Based on the hash, a data structure called \(lqmanifest\(rq is looked up in the cache\&. The manifest contains: -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -references to cached compilation results (object file, dependency file, etc) that were produced by previous compilations that matched the hash -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -paths to the include files that were read at the time the compilation results were stored in the cache -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -hash sums of the include files at the time the compilation results were stored in the cache -.RE -.sp -The current contents of the include files are then hashed and compared to the information in the manifest\&. If there is a match, ccache knows the result of the compilation\&. If there is no match, ccache falls back to running the preprocessor\&. The output from the preprocessor is parsed to find the include files that were read\&. The paths and hash sums of those include files are then stored in the manifest along with information about the produced compilation result\&. -.sp -There is a catch with the direct mode: header files that were used by the compiler are recorded, but header files that were \fBnot\fR used, but would have been used if they existed, are not\&. So, when ccache checks if a result can be taken from the cache, it currently can\(cqt check if the existence of a new header file should invalidate the result\&. In practice, the direct mode is safe to use in the absolute majority of cases\&. -.sp -The direct mode will be disabled if any of the following holds: -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -the configuration setting -\fBdirect_mode\fR -is false -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -a modification time of one of the include files is too new (needed to avoid a race condition) -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -a compiler option not supported by the direct mode is used: -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -a -\fB\-Wp,\fR\fB\fIX\fR\fR -compiler option other than -\fB\-Wp,\-MD,\fR\fB\fIpath\fR\fR, -\fB\-Wp,\-MMD,\fR\fB\fIpath\fR\fR -and -\fB\-Wp,\-D_define_\fR -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -\fB\-Xpreprocessor\fR -.RE -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -the string -\fB__TIME__\fR -is present in the source code -.RE -.SS "The preprocessor mode" -.sp -In the preprocessor mode, the hash is formed of the common information and: -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -the preprocessor output from running the compiler with -\fB\-E\fR -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -the command line options except options that affect include files (\fB\-I\fR, -\fB\-include\fR, -\fB\-D\fR, etc; the theory is that these options will change the preprocessor output if they have any effect at all) -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -any standard error output generated by the preprocessor -.RE -.sp -Based on the hash, the cached compilation result can be looked up directly in the cache\&. -.SS "The depend mode" -.sp -If the depend mode is enabled, ccache will not use the preprocessor at all\&. The hash used to identify results in the cache will be based on the direct mode hash described above plus information about include files read from the dependency file generated by the compiler with \fB\-MD\fR or \fB\-MMD\fR\&. -.sp -Advantages: -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -The ccache overhead of a cache miss will be much smaller\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -Not running the preprocessor at all can be good if compilation is performed remotely, for instance when using distcc or similar; ccache then won\(cqt make potentially costly preprocessor calls on the local machine\&. -.RE -.sp -Disadvantages: -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -The cache hit rate will likely be lower since any change to compiler options or source code will make the hash different\&. Compare this with the default setup where ccache will fall back to the preprocessor mode, which is tolerant to some types of changes of compiler options and source code changes\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -If \-MD is used, the manifest entries will include system header files as well, thus slowing down cache hits slightly, just as using \-MD slows down make\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -If \-MMD is used, the manifest entries will not include system header files, which means ccache will ignore changes in them\&. -.RE -.sp -The depend mode will be disabled if any of the following holds: -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -the configuration setting -\fBdepend_mode\fR -is false -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -the configuration setting -\fBrun_second_cpp\fR -is false -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -the compiler is not generating dependencies using -\fB\-MD\fR -or -\fB\-MMD\fR -.RE -.SH "CACHE DEBUGGING" -.sp -To find out what information ccache actually is hashing, you can enable the debug mode via the configuration setting \fBdebug\fR or by setting \fBCCACHE_DEBUG\fR in the environment\&. This can be useful if you are investigating why you don\(cqt get cache hits\&. Note that performance will be reduced slightly\&. -.sp -When the debug mode is enabled, ccache will create up to five additional files next to the object file: -.TS -allbox tab(:); -ltB ltB. -T{ -Filename -T}:T{ -Description -T} -.T& -lt lt -lt lt -lt lt -lt lt -lt lt. -T{ -.sp -\fB\&.ccache\-input\-c\fR -T}:T{ -.sp -Binary input hashed by both the direct mode and the preprocessor mode\&. -T} -T{ -.sp -\fB\&.ccache\-input\-d\fR -T}:T{ -.sp -Binary input only hashed by the direct mode\&. -T} -T{ -.sp -\fB\&.ccache\-input\-p\fR -T}:T{ -.sp -Binary input only hashed by the preprocessor mode\&. -T} -T{ -.sp -\fB\&.ccache\-input\-text\fR -T}:T{ -.sp -Human\-readable combined diffable text version of the three files above\&. -T} -T{ -.sp -\fB\&.ccache\-log\fR -T}:T{ -.sp -Log for this object file\&. -T} -.TE -.sp 1 -.sp -In the direct mode, ccache uses the MD4 hash of the \fBccache\-input\-c\fR + \fBccache\-input\-d\fR data (where \fB+\fR means concatenation), while the \fBccache\-input\-c\fR + \fBccache\-input\-p\fR data is used in the preprocessor mode\&. -.sp -The \fBccache\-input\-text\fR file is a combined text version of the three binary input files\&. It has three sections (\(lqCOMMON\(rq, \(lqDIRECT MODE\(rq and \(lqPREPROCESSOR MODE\(rq), which is turn contain annotations that say what kind of data comes next\&. -.sp -To debug why you don\(cqt get an expected cache hit for an object file, you can do something like this: -.sp -.RS 4 -.ie n \{\ -\h'-04' 1.\h'+01'\c -.\} -.el \{\ -.sp -1 -.IP " 1." 4.2 -.\} -Build with debug mode enabled\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04' 2.\h'+01'\c -.\} -.el \{\ -.sp -1 -.IP " 2." 4.2 -.\} -Save the -\fB\&.ccache\-*\fR -files\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04' 3.\h'+01'\c -.\} -.el \{\ -.sp -1 -.IP " 3." 4.2 -.\} -Build again with debug mode enabled\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04' 4.\h'+01'\c -.\} -.el \{\ -.sp -1 -.IP " 4." 4.2 -.\} -Compare -\fB\&.ccache\-input\-text\fR -for the two builds\&. This together with the -\fB\&.ccache\-log\fR -files should give you some clues about what is happening\&. -.RE -.SH "COMPILING IN DIFFERENT DIRECTORIES" -.sp -Some information included in the hash that identifies a unique compilation can contain absolute paths: -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -The preprocessed source code may contain absolute paths to include files if the compiler option -\fB\-g\fR -is used or if absolute paths are given to -\fB\-I\fR -and similar compiler options\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -Paths specified by compiler options (such as -\fB\-I\fR, -\fB\-MF\fR, etc) on the command line may be absolute\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -The source code file path may be absolute, and that path may substituted for -\fB__FILE__\fR -macros in the source code or included in warnings emitted to standard error by the preprocessor\&. -.RE -.sp -This means that if you compile the same code in different locations, you can\(cqt share compilation results between the different build directories since you get cache misses because of the absolute build directory paths that are part of the hash\&. -.sp -Here\(cqs what can be done to enable cache hits between different build directories: -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -If you build with -\fB\-g\fR -(or similar) to add debug information to the object file, you must either: -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -use the -\fB\-fdebug\-prefix\-map=\fR\fB\fIold\fR\fR\fB=\fR\fB\fInew\fR\fR -option for relocating debug info to a common prefix (e\&.g\&. -\fB\-fdebug\-prefix\-map=$PWD=\&.\fR); or -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -set -\fBhash_dir = false\fR\&. -.RE -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -If you use absolute paths anywhere on the command line (e\&.g\&. the source code file path or an argument to compiler options like -\fB\-I\fR -and -\fB\-MF\fR), you must to set -\fBbase_dir\fR -to an absolute path to a \(lqbase directory\(rq\&. ccache will then rewrite absolute paths under that directory to relative before computing the hash\&. -.RE -.SH "PRECOMPILED HEADERS" -.sp -ccache has support for GCC\(cqs precompiled headers\&. However, you have to do some things to make it work properly: -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -You must set -\fBsloppiness\fR -to -\fBpch_defines,time_macros\fR\&. The reason is that ccache can\(cqt tell whether -\fB__TIME__\fR -or -\fB__DATE__\fR -is used when using a precompiled header\&. Further, it can\(cqt detect changes in -\fB#define\fRs in the source code because of how preprocessing works in combination with precompiled headers\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -You must either: -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -use the -\fB\-include\fR -compiler option to include the precompiled header (i\&.e\&., don\(cqt use -\fB#include\fR -in the source code to include the header; the filename itself must be sufficient to find the header, i\&.e\&. -\fB\-I\fR -paths are not searched); or -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -(for the Clang compiler) use the -\fB\-include\-pch\fR -compiler option to include the PCH file generated from the precompiled header; or -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -(for the GCC compiler) add the -\fB\-fpch\-preprocess\fR -compiler option when compiling\&. -.RE -.sp -If you don\(cqt do this, either the non\-precompiled version of the header file will be used (if available) or ccache will fall back to running the real compiler and increase the statistics counter \(lqpreprocessor error\(rq (if the non\-precompiled header file is not available)\&. -.RE -.SH "SHARING A CACHE" -.sp -A group of developers can increase the cache hit rate by sharing a cache directory\&. To share a cache without unpleasant side effects, the following conditions should to be met: -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -Use the same cache directory\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -Make sure that the configuration setting -\fBhard_link\fR -is false (which is the default)\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -Make sure that all users are in the same group\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -Set the configuration setting -\fBumask\fR -to 002\&. This ensures that cached files are accessible to everyone in the group\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -Make sure that all users have write permission in the entire cache directory (and that you trust all users of the shared cache)\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -Make sure that the setgid bit is set on all directories in the cache\&. This tells the filesystem to inherit group ownership for new directories\&. The following command might be useful for this: -.sp -.if n \{\ -.RS 4 -.\} -.nf -find $CCACHE_DIR \-type d | xargs chmod g+s -.fi -.if n \{\ -.RE -.\} -.RE -.sp -The reason to avoid the hard link mode is that the hard links cause unwanted side effects, as all links to a cached file share the file\(cqs modification timestamp\&. This results in false dependencies to be triggered by timestamp\-based build systems whenever another user links to an existing file\&. Typically, users will see that their libraries and binaries are relinked without reason\&. -.sp -You may also want to make sure that a base directory is set appropriately, as discussed in a previous section\&. -.SH "SHARING A CACHE ON NFS" -.sp -It is possible to put the cache directory on an NFS filesystem (or similar filesystems), but keep in mind that: -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -Having the cache on NFS may slow down compilation\&. Make sure to do some benchmarking to see if it\(cqs worth it\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -ccache hasn\(cqt been tested very thoroughly on NFS\&. -.RE -.sp -A tip is to set \fBtemporary_dir\fR to a directory on the local host to avoid NFS traffic for temporary files\&. -.sp -It is recommended to use the same operating system version when using a shared cache\&. If operating system versions are different then system include files will likely be different and there will be few or no cache hits between the systems\&. One way of improving cache hit rate in that case is to set \fBsloppiness\fR to \fBsystem_headers\fR to ignore system headers\&. -.SH "USING CCACHE WITH OTHER COMPILER WRAPPERS" -.sp -The recommended way of combining ccache with another compiler wrapper (such as \(lqdistcc\(rq) is by letting ccache execute the compiler wrapper\&. This is accomplished by defining the configuration setting \fBprefix_command\fR, for example by setting the environment variable \fBCCACHE_PREFIX\fR to the name of the wrapper (e\&.g\&. \fBdistcc\fR)\&. ccache will then prefix the command line with the specified command when running the compiler\&. To specify several prefix commands, set \fBprefix_command\fR to a colon\-separated list of commands\&. -.sp -Unless you set \fBcompiler_check\fR to a suitable command (see the description of that configuration option), it is not recommended to use the form \fBccache anotherwrapper compiler args\fR as the compilation command\&. It\(cqs also not recommended to use the masquerading technique for the other compiler wrapper\&. The reason is that by default, ccache will in both cases hash the mtime and size of the other wrapper instead of the real compiler, which means that: -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -Compiler upgrades will not be detected properly\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -The cached results will not be shared between compilations with and without the other wrapper\&. -.RE -.sp -Another minor thing is that if \fBprefix_command\fR is used, ccache will not invoke the other wrapper when running the preprocessor, which increases performance\&. You can use the \fBprefix_command_cpp\fR configuration setting if you also want to invoke the other wrapper when doing preprocessing (normally by adding \fB\-E\fR)\&. -.SH "CAVEATS" -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -The direct mode fails to pick up new header files in some rare scenarios\&. See -THE DIRECT MODE -above\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -When run via ccache, warning messages produced by GCC 4\&.9 and newer will only be colored when the environment variable -\fBGCC_COLORS\fR -is set\&. An alternative to setting -\fBGCC_COLORS\fR -is to pass -\fB\-fdiagnostics\-color\fR -explicitly when compiling (but then color codes will also be present when redirecting stderr to a file)\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -If ccache guesses that the compiler may emit colored warnings, then a compilation with stderr referring to a TTY will be considered different from a compilation with a redirected stderr, thus not sharing cache entries\&. This happens for clang by default and for GCC when -\fBGCC_COLORS\fR -is set as mentioned above\&. If you want to share cache hits, you can pass -\fB\-f[no\-]diagnostics\-color\fR -(GCC) or -\fB\-f[no\-]color\-diagnostics\fR -(clang) explicitly when compiling (but then color codes will be either on or off for both the TTY and the redirected case)\&. -.RE -.SH "TROUBLESHOOTING" -.SS "General" -.sp -A general tip for getting information about what ccache is doing is to enable debug logging by setting the configuration option \fBdebug\fR (or the environment variable \fBCCACHE_DEBUG\fR); see debugging for more information\&. Another way of keeping track of what is happening is to check the output of \fBccache \-s\fR\&. -.SS "Performance" -.sp -ccache has been written to perform well out of the box, but sometimes you may have to do some adjustments of how you use the compiler and ccache in order to improve performance\&. -.sp -Since ccache works best when I/O is fast, put the cache directory on a fast storage device if possible\&. Having lots of free memory so that files in the cache directory stay in the disk cache is also preferable\&. -.sp -A good way of monitoring how well ccache works is to run \fBccache \-s\fR before and after your build and then compare the statistics counters\&. Here are some common problems and what may be done to increase the hit rate: -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -If \(lqcache hit (preprocessed)\(rq has been incremented instead of \(lqcache hit (direct)\(rq, ccache has fallen back to preprocessor mode, which is generally slower\&. Some possible reasons are: -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -The source code has been modified in such a way that the preprocessor output is not affected\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -Compiler arguments that are hashed in the direct mode but not in the preprocessor mode have changed (\fB\-I\fR, -\fB\-include\fR, -\fB\-D\fR, etc) and they didn\(cqt affect the preprocessor output\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -The compiler option -\fB\-Xpreprocessor\fR -or -\fB\-Wp,\fR\fB\fIX\fR\fR -(except -\fB\-Wp,\-MD,\fR\fB\fIpath\fR\fR, -\fB\-Wp,\-MMD,\fR\fB\fIpath\fR\fR, and -\fB\-Wp,\-D_define_\fR) is used\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -This was the first compilation with a new value of the base directory setting\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -A modification time of one of the include files is too new (created the same second as the compilation is being done)\&. This check is made to avoid a race condition\&. To fix this, create the include file earlier in the build process, if possible, or set -\fBsloppiness\fR -to -\fBinclude_file_ctime, include_file_mtime\fR -if you are willing to take the risk\&. (The race condition consists of these events: the preprocessor is run; an include file is modified by someone; the new include file is hashed by ccache; the real compiler is run on the preprocessor\(cqs output, which contains data from the old header file; the wrong object file is stored in the cache\&.) -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -The -\fB__TIME__\fR -preprocessor macro is (potentially) being used\&. ccache turns off direct mode if -\fB__TIME__\fR -is present in the source code\&. This is done as a safety measure since the string indicates that a -\fB__TIME__\fR -macro -\fImay\fR -affect the output\&. (To be sure, ccache would have to run the preprocessor, but the sole point of the direct mode is to avoid that\&.) If you know that -\fB__TIME__\fR -isn\(cqt used in practise, or don\(cqt care if ccache produces objects where -\fB__TIME__\fR -is expanded to something in the past, you can set -\fBsloppiness\fR -to -\fBtime_macros\fR\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -The -\fB__DATE__\fR -preprocessor macro is (potentially) being used and the date has changed\&. This is similar to how -\fB__TIME__\fR -is handled\&. If -\fB__DATE__\fR -is present in the source code, ccache hashes the current date in order to be able to produce the correct object file if the -\fB__DATE__\fR -macro affects the output\&. If you know that -\fB__DATE__\fR -isn\(cqt used in practise, or don\(cqt care if ccache produces objects where -\fB__DATE__\fR -is expanded to something in the past, you can set -\fBsloppiness\fR -to -\fBtime_macros\fR\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -The input file path has changed\&. ccache includes the input file path in the direct mode hash to be able to take relative include files into account and to produce a correct object file if the source code includes a -\fB__FILE__\fR -macro\&. -.RE -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -If \(lqcache miss\(rq has been incremented even though the same code has been compiled and cached before, ccache has either detected that something has changed anyway or a cleanup has been performed (either explicitly or implicitly when a cache limit has been reached)\&. Some perhaps unobvious things that may result in a cache miss are usage of -\fB__TIME__\fR -or -\fB__DATE__\fR -macros, or use of automatically generated code that contains a timestamp, build counter or other volatile information\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -If \(lqmultiple source files\(rq has been incremented, it\(cqs an indication that the compiler has been invoked on several source code files at once\&. ccache doesn\(cqt support that\&. Compile the source code files separately if possible\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -If \(lqunsupported compiler option\(rq has been incremented, enable debug logging and check which option was rejected\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -If \(lqpreprocessor error\(rq has been incremented, one possible reason is that precompiled headers are being used\&. See -PRECOMPILED HEADERS -for how to remedy this\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04'\(bu\h'+03'\c -.\} -.el \{\ -.sp -1 -.IP \(bu 2.3 -.\} -If \(lqcan\(cqt use precompiled header\(rq has been incremented, see -PRECOMPILED HEADERS\&. -.RE -.SS "Corrupt object files" -.sp -It should be noted that ccache is susceptible to general storage problems\&. If a bad object file sneaks into the cache for some reason, it will of course stay bad\&. Some possible reasons for erroneous object files are bad hardware (disk drive, disk controller, memory, etc), buggy drivers or file systems, a bad \fBprefix_command\fR or compiler wrapper\&. If this happens, the easiest way of fixing it is this: -.sp -.RS 4 -.ie n \{\ -\h'-04' 1.\h'+01'\c -.\} -.el \{\ -.sp -1 -.IP " 1." 4.2 -.\} -Build so that the bad object file ends up in the build tree\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04' 2.\h'+01'\c -.\} -.el \{\ -.sp -1 -.IP " 2." 4.2 -.\} -Remove the bad object file from the build tree\&. -.RE -.sp -.RS 4 -.ie n \{\ -\h'-04' 3.\h'+01'\c -.\} -.el \{\ -.sp -1 -.IP " 3." 4.2 -.\} -Rebuild with -\fBCCACHE_RECACHE\fR -set\&. -.RE -.sp -An alternative is to clear the whole cache with \fBccache \-C\fR if you don\(cqt mind losing other cached results\&. -.sp -There are no reported issues about ccache producing broken object files reproducibly\&. That doesn\(cqt mean it can\(cqt happen, so if you find a repeatable case, please report it\&. -.SH "MORE INFORMATION" -.sp -Credits, mailing list information, bug reporting instructions, source code, etc, can be found on ccache\(cqs web site: https://ccache\&.dev\&. -.SH "AUTHOR" -.sp -ccache was originally written by Andrew Tridgell and is currently developed and maintained by Joel Rosdahl\&. See AUTHORS\&.txt or AUTHORS\&.html and https://ccache\&.dev/credits\&.html for a list of contributors\&. diff --git a/dockerfiles/README b/dockerfiles/README new file mode 100644 index 0000000..c792398 --- /dev/null +++ b/dockerfiles/README @@ -0,0 +1,11 @@ +This directory contains Dockerfiles for building and testing ccache in +different build environments. + +For instance, run something like this to build ccache in Ubuntu 20.04: + + misc/build-in-docker ubuntu-20-focal + +The above command will first build the Ubuntu 20.04 Docker image if needed and +finally build ccache and run the ccache test suite. + +See also misc/test-all-systems. diff --git a/dockerfiles/alpine-3.12/Dockerfile b/dockerfiles/alpine-3.12/Dockerfile new file mode 100644 index 0000000..324921a --- /dev/null +++ b/dockerfiles/alpine-3.12/Dockerfile @@ -0,0 +1,17 @@ +FROM alpine:3.12 + +RUN apk add --no-cache \ + bash \ + ccache \ + clang \ + cmake \ + elfutils \ + g++ \ + gcc \ + libc-dev \ + make \ + perl \ + zstd-dev + +# Redirect all compilers to ccache. +RUN for t in gcc g++ cc c++ clang clang++; do ln -vs /usr/bin/ccache /usr/local/bin/$t; done diff --git a/dockerfiles/alpine-3.4/Dockerfile b/dockerfiles/alpine-3.4/Dockerfile new file mode 100644 index 0000000..ad42a00 --- /dev/null +++ b/dockerfiles/alpine-3.4/Dockerfile @@ -0,0 +1,16 @@ +# Released 2016, this is the first release to contain cmake >= 3.4.3 + +FROM alpine:3.4 + +RUN apk add --no-cache \ + bash \ + ccache \ + cmake \ + g++ \ + gcc \ + libc-dev \ + make \ + perl + +# Redirect all compilers to ccache. +RUN for t in gcc g++ cc c++ clang clang++; do ln -vs /usr/bin/ccache /usr/local/bin/$t; done diff --git a/dockerfiles/centos-7/Dockerfile b/dockerfiles/centos-7/Dockerfile new file mode 100644 index 0000000..89a122f --- /dev/null +++ b/dockerfiles/centos-7/Dockerfile @@ -0,0 +1,21 @@ +FROM centos:7 + +RUN yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm \ + && yum install -y \ + asciidoc \ + autoconf \ + bash \ + ccache \ + clang \ + cmake3 \ + elfutils \ + gcc \ + gcc-c++ \ + libzstd-devel \ + make \ +# Remove superfluous dependencies brought in by asciidoc: + && rpm -e --nodeps graphviz \ + && yum autoremove -y \ + && yum clean all \ + && cp /usr/bin/cmake3 /usr/bin/cmake \ + && cp /usr/bin/ctest3 /usr/bin/ctest diff --git a/dockerfiles/centos-8/Dockerfile b/dockerfiles/centos-8/Dockerfile new file mode 100644 index 0000000..b7ab38f --- /dev/null +++ b/dockerfiles/centos-8/Dockerfile @@ -0,0 +1,17 @@ +FROM centos:8 + +RUN dnf install -y epel-release \ + && dnf install -y \ + asciidoc \ + autoconf \ + bash \ + ccache \ + clang \ + cmake \ + diffutils \ + elfutils \ + gcc \ + gcc-c++ \ + libzstd-devel \ + make \ + && dnf clean all diff --git a/dockerfiles/debian-10/Dockerfile b/dockerfiles/debian-10/Dockerfile new file mode 100644 index 0000000..703bdc9 --- /dev/null +++ b/dockerfiles/debian-10/Dockerfile @@ -0,0 +1,17 @@ +FROM debian:10 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + bash \ + build-essential \ + ccache \ + clang \ + cmake \ + elfutils \ + gcc-multilib \ + libzstd-dev \ + xsltproc \ + && rm -rf /var/lib/apt/lists/* + +# Redirect all compilers to ccache. +RUN for t in gcc g++ cc c++ clang clang++; do ln -vs /usr/bin/ccache /usr/local/bin/$t; done diff --git a/dockerfiles/debian-9/Dockerfile b/dockerfiles/debian-9/Dockerfile new file mode 100644 index 0000000..b4c6585 --- /dev/null +++ b/dockerfiles/debian-9/Dockerfile @@ -0,0 +1,17 @@ +FROM debian:9 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + bash \ + build-essential \ + ccache \ + clang \ + cmake \ + elfutils \ + gcc-multilib \ + libzstd-dev \ + xsltproc \ + && rm -rf /var/lib/apt/lists/* + +# Redirect all compilers to ccache. +RUN for t in gcc g++ cc c++ clang clang++; do ln -vs /usr/bin/ccache /usr/local/bin/$t; done diff --git a/dockerfiles/fedora-32/Dockerfile b/dockerfiles/fedora-32/Dockerfile new file mode 100644 index 0000000..65c883d --- /dev/null +++ b/dockerfiles/fedora-32/Dockerfile @@ -0,0 +1,16 @@ +FROM fedora:32 + +RUN dnf install -y \ + autoconf \ + bash \ + ccache \ + clang \ + cmake \ + diffutils \ + elfutils \ + findutils \ + gcc \ + gcc-c++ \ + libzstd-devel \ + make \ + && dnf clean all diff --git a/dockerfiles/ubuntu-14.04/Dockerfile b/dockerfiles/ubuntu-14.04/Dockerfile new file mode 100644 index 0000000..2b2c297 --- /dev/null +++ b/dockerfiles/ubuntu-14.04/Dockerfile @@ -0,0 +1,23 @@ +FROM ubuntu:14.04 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + asciidoc \ + bash \ + build-essential \ + ccache \ + clang \ + curl \ + elfutils \ + gcc-multilib \ + wget \ + xsltproc \ + && rm -rf /var/lib/apt/lists/* + +# Redirect all compilers to ccache. +RUN for t in gcc g++ cc c++ clang clang++; do ln -vs /usr/bin/ccache /usr/local/bin/$t; done + +# The distribution's CMake it too old (2.8.12.2). +RUN curl -sSL https://cmake.org/files/v3.5/cmake-3.5.2-Linux-x86_64.tar.gz | sudo tar -xzC /opt \ + && cp -a /opt/cmake-3.5.2-Linux-x86_64/bin /usr/local \ + && cp -a /opt/cmake-3.5.2-Linux-x86_64/share /usr/local diff --git a/dockerfiles/ubuntu-16.04/Dockerfile b/dockerfiles/ubuntu-16.04/Dockerfile new file mode 100644 index 0000000..a7f096c --- /dev/null +++ b/dockerfiles/ubuntu-16.04/Dockerfile @@ -0,0 +1,18 @@ +FROM ubuntu:16.04 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + asciidoc \ + bash \ + build-essential \ + ccache \ + clang \ + cmake \ + elfutils \ + gcc-multilib \ + libzstd1-dev \ + xsltproc \ + && rm -rf /var/lib/apt/lists/* + +# Redirect all compilers to ccache. +RUN for t in gcc g++ cc c++ clang clang++; do ln -vs /usr/bin/ccache /usr/local/bin/$t; done diff --git a/dockerfiles/ubuntu-20.04/Dockerfile b/dockerfiles/ubuntu-20.04/Dockerfile new file mode 100644 index 0000000..c5ec49c --- /dev/null +++ b/dockerfiles/ubuntu-20.04/Dockerfile @@ -0,0 +1,19 @@ +FROM ubuntu:20.04 + +# Non-interactive: do not set up timezone settings. +RUN apt-get update \ + && DEBIAN_FRONTEND="noninteractive" apt-get install -y --no-install-recommends \ + asciidoc \ + bash \ + build-essential \ + ccache \ + clang \ + cmake \ + elfutils \ + gcc-multilib \ + libzstd-dev \ + xsltproc \ + && rm -rf /var/lib/apt/lists/* + +# Redirect all compilers to ccache. +RUN for t in gcc g++ cc c++ clang clang++; do ln -vs /usr/bin/ccache /usr/local/bin/$t; done diff --git a/install-sh b/install-sh deleted file mode 100755 index ba5e22a..0000000 --- a/install-sh +++ /dev/null @@ -1,238 +0,0 @@ -#! /bin/sh -# -# install - install a program, script, or datafile -# This comes from X11R5. -# -# Calling this script install-sh is preferred over install.sh, to prevent -# `make' implicit rules from creating a file called install from it -# when there is no Makefile. -# -# This script is compatible with the BSD install script, but was written -# from scratch. -# - - -# set DOITPROG to echo to test this script - -# Don't use :- since 4.3BSD and earlier shells don't like it. -doit="${DOITPROG-}" - - -# put in absolute paths if you don't have them in your path; or use env. vars. - -mvprog="${MVPROG-mv}" -cpprog="${CPPROG-cp}" -chmodprog="${CHMODPROG-chmod}" -chownprog="${CHOWNPROG-chown}" -chgrpprog="${CHGRPPROG-chgrp}" -stripprog="${STRIPPROG-strip}" -rmprog="${RMPROG-rm}" -mkdirprog="${MKDIRPROG-mkdir}" - -transformbasename="" -transform_arg="" -instcmd="$mvprog" -chmodcmd="$chmodprog 0755" -chowncmd="" -chgrpcmd="" -stripcmd="" -rmcmd="$rmprog -f" -mvcmd="$mvprog" -src="" -dst="" -dir_arg="" - -while [ x"$1" != x ]; do - case $1 in - -c) instcmd="$cpprog" - shift - continue;; - - -d) dir_arg=true - shift - continue;; - - -m) chmodcmd="$chmodprog $2" - shift - shift - continue;; - - -o) chowncmd="$chownprog $2" - shift - shift - continue;; - - -g) chgrpcmd="$chgrpprog $2" - shift - shift - continue;; - - -s) stripcmd="$stripprog" - shift - continue;; - - -t=*) transformarg=`echo $1 | sed 's/-t=//'` - shift - continue;; - - -b=*) transformbasename=`echo $1 | sed 's/-b=//'` - shift - continue;; - - *) if [ x"$src" = x ] - then - src=$1 - else - # this colon is to work around a 386BSD /bin/sh bug - : - dst=$1 - fi - shift - continue;; - esac -done - -if [ x"$src" = x ] -then - echo "install: no input file specified" - exit 1 -else - true -fi - -if [ x"$dir_arg" != x ]; then - dst=$src - src="" - - if [ -d $dst ]; then - instcmd=: - else - instcmd=mkdir - fi -else - -# Waiting for this to be detected by the "$instcmd $src $dsttmp" command -# might cause directories to be created, which would be especially bad -# if $src (and thus $dsttmp) contains '*'. - - if [ -f $src -o -d $src ] - then - true - else - echo "install: $src does not exist" - exit 1 - fi - - if [ x"$dst" = x ] - then - echo "install: no destination specified" - exit 1 - else - true - fi - -# If destination is a directory, append the input filename; if your system -# does not like double slashes in filenames, you may need to add some logic - - if [ -d $dst ] - then - dst="$dst"/`basename $src` - else - true - fi -fi - -## this sed command emulates the dirname command -dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` - -# Make sure that the destination directory exists. -# this part is taken from Noah Friedman's mkinstalldirs script - -# Skip lots of stat calls in the usual case. -if [ ! -d "$dstdir" ]; then -defaultIFS=' -' -IFS="${IFS-${defaultIFS}}" - -oIFS="${IFS}" -# Some sh's can't handle IFS=/ for some reason. -IFS='%' -set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` -IFS="${oIFS}" - -pathcomp='' - -while [ $# -ne 0 ] ; do - pathcomp="${pathcomp}${1}" - shift - - if [ ! -d "${pathcomp}" ] ; - then - $mkdirprog "${pathcomp}" - else - true - fi - - pathcomp="${pathcomp}/" -done -fi - -if [ x"$dir_arg" != x ] -then - $doit $instcmd $dst && - - if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && - if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && - if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && - if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi -else - -# If we're going to rename the final executable, determine the name now. - - if [ x"$transformarg" = x ] - then - dstfile=`basename $dst` - else - dstfile=`basename $dst $transformbasename | - sed $transformarg`$transformbasename - fi - -# don't allow the sed command to completely eliminate the filename - - if [ x"$dstfile" = x ] - then - dstfile=`basename $dst` - else - true - fi - -# Make a temp file name in the proper directory. - - dsttmp=$dstdir/#inst.$$# - -# Move or copy the file name to the temp name - - $doit $instcmd $src $dsttmp && - - trap "rm -f ${dsttmp}" 0 && - -# and set any options; do chmod last to preserve setuid bits - -# If any of these fail, we abort the whole thing. If we want to -# ignore errors from any of these, just make sure not to ignore -# errors from the above "$doit $instcmd $src $dsttmp" command. - - if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && - if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && - if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && - if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && - -# Now rename the file to the real destination. - - $doit $rmcmd -f $dstdir/$dstfile && - $doit $mvcmd $dsttmp $dstdir/$dstfile - -fi && - - -exit 0 diff --git a/m4/clang.m4 b/m4/clang.m4 deleted file mode 100644 index 11911ce..0000000 --- a/m4/clang.m4 +++ /dev/null @@ -1,19 +0,0 @@ -# _AC_LANG_COMPILER_CLANG -# --------------------- -# Check whether the compiler for the current language is clang. -# Adapted from standard autoconf function: _AC_LANG_COMPILER_GNU -# -# Note: clang also identifies itself as a GNU compiler (gcc 4.2.1) -# for compatibility reasons, so that cannot be used to determine -m4_define([_AC_LANG_COMPILER_CLANG], -[AC_CACHE_CHECK([whether we are using the clang _AC_LANG compiler], - [ac_cv_[]_AC_LANG_ABBREV[]_compiler_clang], -[_AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [[#ifndef __clang__ - choke me -#endif -]])], - [ac_compiler_clang=yes], - [ac_compiler_clang=no]) -ac_cv_[]_AC_LANG_ABBREV[]_compiler_clang=$ac_compiler_clang -])])# _AC_LANG_COMPILER_CLANG - diff --git a/m4/feature_macros.m4 b/m4/feature_macros.m4 deleted file mode 100644 index 05d3e49..0000000 --- a/m4/feature_macros.m4 +++ /dev/null @@ -1,147 +0,0 @@ -dnl =========================================================================== -dnl Feature macro stuff borrowed from Python's configure.in -dnl -dnl For license information, see -dnl . -dnl =========================================================================== - -# The later defininition of _XOPEN_SOURCE disables certain features -# on Linux, so we need _GNU_SOURCE to re-enable them (makedev, tm_zone). -AC_DEFINE(_GNU_SOURCE, 1, [Define on Linux to activate all library features]) - -# The later defininition of _XOPEN_SOURCE and _POSIX_C_SOURCE disables -# certain features on NetBSD, so we need _NETBSD_SOURCE to re-enable -# them. -AC_DEFINE(_NETBSD_SOURCE, 1, [Define on NetBSD to activate all library features]) - -# The later defininition of _XOPEN_SOURCE and _POSIX_C_SOURCE disables -# certain features on FreeBSD, so we need __BSD_VISIBLE to re-enable -# them. -AC_DEFINE(__BSD_VISIBLE, 1, [Define on FreeBSD to activate all library features]) - -# The later defininition of _XOPEN_SOURCE and _POSIX_C_SOURCE disables -# u_int on Irix 5.3. Defining _BSD_TYPES brings it back. -AC_DEFINE(_BSD_TYPES, 1, [Define on Irix to enable u_int]) - -# The later defininition of _XOPEN_SOURCE and _POSIX_C_SOURCE disables -# certain features on Mac OS X, so we need _DARWIN_C_SOURCE to re-enable -# them. -AC_DEFINE(_DARWIN_C_SOURCE, 1, [Define on Darwin to activate all library features]) - -define_xopen_source=yes - -ac_sys_system=`uname -s` -if test "$ac_sys_system" = "AIX" -o "$ac_sys_system" = "Monterey64" \ - -o "$ac_sys_system" = "UnixWare" -o "$ac_sys_system" = "OpenUNIX"; then - ac_sys_release=`uname -v` -else - ac_sys_release=`uname -r` -fi - -# Some systems cannot stand _XOPEN_SOURCE being defined at all; they -# disable features if it is defined, without any means to access these -# features as extensions. For these systems, we skip the definition of -# _XOPEN_SOURCE. Before adding a system to the list to gain access to -# some feature, make sure there is no alternative way to access this -# feature. Also, when using wildcards, make sure you have verified the -# need for not defining _XOPEN_SOURCE on all systems matching the -# wildcard, and that the wildcard does not include future systems -# (which may remove their limitations). -dnl quadrigraphs "@<:@" and "@:>@" produce "[" and "]" in the output -case $ac_sys_system/$ac_sys_release in - # On OpenBSD, select(2) is not available if _XOPEN_SOURCE is defined, - # even though select is a POSIX function. Reported by J. Ribbens. - # Reconfirmed for OpenBSD 3.3 by Zachary Hamm, for 3.4 by Jason Ish. - OpenBSD/2.* | OpenBSD/3.@<:@0123456789@:>@ | OpenBSD/4.@<:@0123@:>@) - define_xopen_source=no - # OpenBSD undoes our definition of __BSD_VISIBLE if _XOPEN_SOURCE is - # also defined. This can be overridden by defining _BSD_SOURCE - # As this has a different meaning on Linux, only define it on OpenBSD - AC_DEFINE(_BSD_SOURCE, 1, [Define on OpenBSD to activate all library features]) - ;; - # Defining _XOPEN_SOURCE on NetBSD version prior to the introduction of - # _NETBSD_SOURCE disables certain features (eg. setgroups). Reported by - # Marc Recht - NetBSD/1.5 | NetBSD/1.5.* | NetBSD/1.6 | NetBSD/1.6.* | NetBSD/1.6@<:@A-S@:>@) - define_xopen_source=no;; - # On Solaris 2.6, sys/wait.h is inconsistent in the usage - # of union __?sigval. Reported by Stuart Bishop. - SunOS/5.6) - define_xopen_source=no;; - # On UnixWare 7, u_long is never defined with _XOPEN_SOURCE, - # but used in /usr/include/netinet/tcp.h. Reported by Tim Rice. - # Reconfirmed for 7.1.4 by Martin v. Loewis. - OpenUNIX/8.0.0| UnixWare/7.1.@<:@0-4@:>@) - define_xopen_source=no;; - # On OpenServer 5, u_short is never defined with _XOPEN_SOURCE, - # but used in struct sockaddr.sa_family. Reported by Tim Rice. - SCO_SV/3.2) - define_xopen_source=no;; - # On FreeBSD 4, the math functions C89 does not cover are never defined - # with _XOPEN_SOURCE and __BSD_VISIBLE does not re-enable them. - FreeBSD/4.*) - define_xopen_source=no;; - # On MacOS X 10.2, a bug in ncurses.h means that it craps out if - # _XOPEN_EXTENDED_SOURCE is defined. Apparently, this is fixed in 10.3, which - # identifies itself as Darwin/7.* - # On Mac OS X 10.4, defining _POSIX_C_SOURCE or _XOPEN_SOURCE - # disables platform specific features beyond repair. - # On Mac OS X 10.3, defining _POSIX_C_SOURCE or _XOPEN_SOURCE - # has no effect, don't bother defining them - Darwin/@<:@6789@:>@.*) - define_xopen_source=no;; - # On AIX 4 and 5.1, mbstate_t is defined only when _XOPEN_SOURCE == 500 but - # used in wcsnrtombs() and mbsnrtowcs() even if _XOPEN_SOURCE is not defined - # or has another value. By not (re)defining it, the defaults come in place. - AIX/4) - define_xopen_source=no;; - AIX/5|AIX/7) - if test `uname -r` -eq 1; then - define_xopen_source=no - fi - ;; - # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from - # defining NI_NUMERICHOST. - QNX/6.3.2) - define_xopen_source=no - ;; - -esac - -if test $define_xopen_source = yes -then - # On Solaris w/ g++ it appears that _XOPEN_SOURCE has to be - # defined precisely as g++ defines it - # Furthermore, on Solaris 10, XPG6 requires the use of a C99 - # compiler - case $ac_sys_system/$ac_sys_release in - SunOS/5.8|SunOS/5.9|SunOS/5.10) - AC_DEFINE(_XOPEN_SOURCE, 500, - Define to the level of X/Open that your system supports) - ;; - *) - AC_DEFINE(_XOPEN_SOURCE, 700, - Define to the level of X/Open that your system supports) - ;; - esac - - # On Tru64 Unix 4.0F, defining _XOPEN_SOURCE also requires - # definition of _XOPEN_SOURCE_EXTENDED and _POSIX_C_SOURCE, or else - # several APIs are not declared. Since this is also needed in some - # cases for HP-UX, we define it globally. - # except for Solaris 10, where it must not be defined, - # as it implies XPG4.2 - case $ac_sys_system/$ac_sys_release in - SunOS/5.10|SunOS/5.11) - AC_DEFINE(__EXTENSIONS__, 1, - Define to activate Unix95-and-earlier features) - ;; - *) - AC_DEFINE(_XOPEN_SOURCE_EXTENDED, 1, - Define to activate Unix95-and-earlier features) - ;; - esac - - AC_DEFINE(_POSIX_C_SOURCE, 200809L, Define to activate features from IEEE Stds 1003.1-2001) - -fi diff --git a/m4/snprintf.m4 b/m4/snprintf.m4 deleted file mode 100644 index 7d3aa06..0000000 --- a/m4/snprintf.m4 +++ /dev/null @@ -1,224 +0,0 @@ -# $Id: snprintf.m4,v 1.1.1.1 2008/01/06 03:24:00 holger Exp $ - -# Copyright (c) 2008 Holger Weiss . -# -# This code may freely be used, modified and/or redistributed for any purpose. -# It would be nice if additions and fixes to this file (including trivial code -# cleanups) would be sent back in order to let me include them in the version -# available at . However, this is -# not a requirement for using or redistributing (possibly modified) versions of -# this file, nor is leaving this notice intact mandatory. - -# HW_HEADER_STDARG_H -# ------------------ -# Define HAVE_STDARG_H to 1 if is available. -AC_DEFUN([HW_HEADER_STDARG_H], -[ - AC_PREREQ([2.60])dnl Older releases should work if AC_CHECK_HEADERS is used. - AC_CHECK_HEADERS_ONCE([stdarg.h]) -])# HW_HEADER_STDARG_H - -# HW_HEADER_VARARGS_H -# ------------------- -# Define HAVE_VARARGS_H to 1 if is available. -AC_DEFUN([HW_HEADER_VARARGS_H], -[ - AC_PREREQ([2.60])dnl Older releases should work if AC_CHECK_HEADERS is used. - AC_CHECK_HEADERS_ONCE([varargs.h]) -])# HW_HEADER_VARARGS_H - -# HW_FUNC_VA_COPY -# --------------- -# Set $hw_cv_func_va_copy to "yes" or "no". Define HAVE_VA_COPY to 1 if -# $hw_cv_func_va_copy is set to "yes". Note that it's "unspecified whether -# va_copy and va_end are macros or identifiers declared with external linkage." -# (C99: 7.15.1, 1) Therefore, the presence of va_copy(3) cannot simply "be -# tested with #ifdef", as suggested by the Autoconf manual (5.5.1). -AC_DEFUN([HW_FUNC_VA_COPY], -[ - AC_REQUIRE([HW_HEADER_STDARG_H])dnl Our check evaluates HAVE_STDARG_H. - AC_REQUIRE([HW_HEADER_VARARGS_H])dnl Our check evaluates HAVE_VARARGS_H. - AC_CACHE_CHECK([for va_copy], - [hw_cv_func_va_copy], - [AC_RUN_IFELSE( - [AC_LANG_PROGRAM( - [[#if HAVE_STDARG_H - #include - #elif HAVE_VARARGS_H - #include - #endif]], - [[va_list ap, aq; va_copy(aq, ap);]])], - [hw_cv_func_va_copy=yes], - [hw_cv_func_va_copy=no], - [hw_cv_func_va_copy=no])]) - AS_IF([test "$hw_cv_func_va_copy" = yes], - [AC_DEFINE([HAVE_VA_COPY], [1], - [Define to 1 if you have the `va_copy' function or macro.])]) -])# HW_FUNC_VA_COPY - -# HW_FUNC___VA_COPY -# ----------------- -# Set $hw_cv_func___va_copy to "yes" or "no". Define HAVE___VA_COPY to 1 if -# $hw_cv_func___va_copy is set to "yes". -AC_DEFUN([HW_FUNC___VA_COPY], -[ - AC_REQUIRE([HW_HEADER_STDARG_H])dnl Our check evaluates HAVE_STDARG_H. - AC_REQUIRE([HW_HEADER_VARARGS_H])dnl Our check evaluates HAVE_VARARGS_H. - AC_CACHE_CHECK([for __va_copy], - [hw_cv_func___va_copy], - [AC_RUN_IFELSE( - [AC_LANG_PROGRAM( - [[#if HAVE_STDARG_H - #include - #elif HAVE_VARARGS_H - #include - #endif]], - [[va_list ap, aq; __va_copy(aq, ap);]])], - [hw_cv_func___va_copy=yes], - [hw_cv_func___va_copy=no], - [hw_cv_func___va_copy=no])]) - AS_IF([test "$hw_cv_func___va_copy" = yes], - [AC_DEFINE([HAVE___VA_COPY], [1], - [Define to 1 if you have the `__va_copy' function or macro.])]) -])# HW_FUNC___VA_COPY - -# HW_FUNC_VSNPRINTF -# ----------------- -# Set $hw_cv_func_vsnprintf and $hw_cv_func_vsnprintf_c99 to "yes" or "no", -# respectively. Define HAVE_VSNPRINTF to 1 only if $hw_cv_func_vsnprintf_c99 -# is set to "yes". Otherwise, define vsnprintf to rpl_vsnprintf and make sure -# the replacement function will be built. -AC_DEFUN([HW_FUNC_VSNPRINTF], -[ - AC_PREREQ([2.60])dnl 2.59 should work if some AC_TYPE_* macros are replaced. - AC_REQUIRE([HW_HEADER_STDARG_H])dnl Our check evaluates HAVE_STDARG_H. - AC_CHECK_FUNC([vsnprintf], - [hw_cv_func_vsnprintf=yes], - [hw_cv_func_vsnprintf=no]) - AS_IF([test "$hw_cv_func_vsnprintf" = yes], - [AC_CACHE_CHECK([whether vsnprintf is C99 compliant], - [hw_cv_func_vsnprintf_c99], - [AC_RUN_IFELSE( - [AC_LANG_PROGRAM( - [[#if HAVE_STDARG_H - #include - #endif - #include - static int testprintf(char *buf, size_t size, const char *format, ...) - { - int result; - va_list ap; - va_start(ap, format); - result = vsnprintf(buf, size, format, ap); - va_end(ap); - return result; - }]], - [[char buf[43]; - if (testprintf(buf, 4, "The answer is %27.2g.", 42.0) != 42 || - testprintf(buf, 0, "No, it's %32zu.", (size_t)42) != 42 || - buf[0] != 'T' || buf[3] != '\0') - return 1;]])], - [hw_cv_func_vsnprintf_c99=yes], - [hw_cv_func_vsnprintf_c99=no], - [hw_cv_func_vsnprintf_c99=no])])], - [hw_cv_func_snprintf_c99=no]) - AS_IF([test "$hw_cv_func_vsnprintf_c99" = yes], - [AC_DEFINE([HAVE_VSNPRINTF], [1], - [Define to 1 if you have a C99 compliant `vsnprintf' function.])], - [AC_CHECK_HEADERS([inttypes.h locale.h stddef.h stdint.h]) - AC_CHECK_MEMBERS([struct lconv.decimal_point, struct lconv.thousands_sep], - [], [], [#include ]) -dnl ccache doesn't link correctly on HP-UX 11.00 when support for long double -dnl is enabled. -dnl AC_TYPE_LONG_DOUBLE - AC_TYPE_LONG_LONG_INT - AC_TYPE_UNSIGNED_LONG_LONG_INT - AC_TYPE_SIZE_T - AC_TYPE_INTMAX_T - AC_TYPE_UINTMAX_T - AC_TYPE_UINTPTR_T - AC_CHECK_TYPES([ptrdiff_t]) - AC_CHECK_FUNCS([localeconv]) - _HW_FUNC_XPRINTF_REPLACE]) -])# HW_FUNC_VSNPRINTF - -# HW_FUNC_SNPRINTF -# ---------------- -# Set $hw_cv_func_snprintf and $hw_cv_func_snprintf_c99 to "yes" or "no", -# respectively. Define HAVE_SNPRINTF to 1 only if $hw_cv_func_snprintf_c99 -# is set to "yes". Otherwise, define snprintf to rpl_snprintf and make sure -# the replacement function will be built. -AC_DEFUN([HW_FUNC_SNPRINTF], -[ - AC_REQUIRE([HW_FUNC_VSNPRINTF])dnl Our snprintf(3) calls vsnprintf(3). - AC_CHECK_FUNC([snprintf], - [hw_cv_func_snprintf=yes], - [hw_cv_func_snprintf=no]) - AS_IF([test "$hw_cv_func_snprintf" = yes], - [AC_CACHE_CHECK([whether snprintf is C99 compliant], - [hw_cv_func_snprintf_c99], - [AC_RUN_IFELSE( - [AC_LANG_PROGRAM([[#include ]], - [[char buf[43]; - if (snprintf(buf, 4, "The answer is %27.2g.", 42.0) != 42 || - snprintf(buf, 0, "No, it's %32zu.", (size_t)42) != 42 || - buf[0] != 'T' || buf[3] != '\0') - return 1;]])], - [hw_cv_func_snprintf_c99=yes], - [hw_cv_func_snprintf_c99=no], - [hw_cv_func_snprintf_c99=no])])], - [hw_cv_func_snprintf_c99=no]) - AS_IF([test "$hw_cv_func_snprintf_c99" = yes], - [AC_DEFINE([HAVE_SNPRINTF], [1], - [Define to 1 if you have a C99 compliant `snprintf' function.])], - [_HW_FUNC_XPRINTF_REPLACE]) -])# HW_FUNC_SNPRINTF - -# HW_FUNC_VASPRINTF -# ----------------- -# Set $hw_cv_func_vasprintf to "yes" or "no". Define HAVE_VASPRINTF to 1 if -# $hw_cv_func_vasprintf is set to "yes". Otherwise, define vasprintf to -# rpl_vasprintf and make sure the replacement function will be built. -AC_DEFUN([HW_FUNC_VASPRINTF], -[ - AC_REQUIRE([HW_FUNC_VSNPRINTF])dnl Our vasprintf(3) calls vsnprintf(3). - AC_CHECK_FUNCS([vasprintf], - [hw_cv_func_vasprintf=yes], - [hw_cv_func_vasprintf=no]) - AS_IF([test "$hw_cv_func_vasprintf" = no], - [AC_CHECK_HEADERS([stdlib.h]) - HW_FUNC_VA_COPY - AS_IF([test "$hw_cv_func_va_copy" = no], - [HW_FUNC___VA_COPY]) - _HW_FUNC_XPRINTF_REPLACE]) -])# HW_FUNC_VASPRINTF - -# HW_FUNC_ASPRINTF -# ---------------- -# Set $hw_cv_func_asprintf to "yes" or "no". Define HAVE_ASPRINTF to 1 if -# $hw_cv_func_asprintf is set to "yes". Otherwise, define asprintf to -# rpl_asprintf and make sure the replacement function will be built. -AC_DEFUN([HW_FUNC_ASPRINTF], -[ - AC_REQUIRE([HW_FUNC_VASPRINTF])dnl Our asprintf(3) calls vasprintf(3). - AC_CHECK_FUNCS([asprintf], - [hw_cv_func_asprintf=yes], - [hw_cv_func_asprintf=no]) - AS_IF([test "$hw_cv_func_asprintf" = no], - [_HW_FUNC_XPRINTF_REPLACE]) -])# HW_FUNC_ASPRINTF - -# _HW_FUNC_XPRINTF_REPLACE -# ------------------------ -# Arrange for building snprintf.c. Must be called if one or more of the -# functions provided by snprintf.c are needed. -AC_DEFUN([_HW_FUNC_XPRINTF_REPLACE], -[ - AS_IF([test "x$_hw_cv_func_xprintf_replace_done" != xyes], - [AC_C_CONST - HW_HEADER_STDARG_H - AC_LIBOBJ([snprintf]) - _hw_cv_func_xprintf_replace_done=yes]) -])# _HW_FUNC_XPRINTF_REPLACE - -dnl vim: set joinspaces textwidth=80: diff --git a/misc/build-in-docker b/misc/build-in-docker new file mode 100755 index 0000000..7603096 --- /dev/null +++ b/misc/build-in-docker @@ -0,0 +1,57 @@ +#!/bin/bash +# +# This script runs ci/build in a Docker container. + +if [ $# -eq 0 ] || [ ${1:-} = -h ] || [ ${1:-} = --help ]; then + cat <"$tmp_file" + if cmp -s "$file" "$tmp_file"; then + continue + fi + + if [ -n "$check" ]; then + status=1 + echo "Error: $file not formatted with Clang-Format" + echo 'Run "make format" or apply this diff:' + git diff $cf_color --no-index "$file" "$tmp_file" \ + | sed -r -e "s!^---.*!--- a/$file!" \ + -e "s!^\+\+\+.*!+++ b/$file!" \ + -e "/diff --/d" -e "/index /d" \ + -e "s/.[0-9]*.clang-format.tmp//" + else + echo "Reformatted $file" + cp "$tmp_file" "$file" + fi +done + +exit $status diff --git a/misc/performance b/misc/performance new file mode 100755 index 0000000..d33ad64 --- /dev/null +++ b/misc/performance @@ -0,0 +1,353 @@ +#! /usr/bin/env python3 +# +# Copyright (C) 2010-2020 Joel Rosdahl and other contributors +# +# See doc/AUTHORS.adoc for a complete list of contributors. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +from optparse import OptionParser +from os import access, environ, mkdir, getpid, X_OK +from os.path import ( + abspath, + basename, + exists, + isabs, + isfile, + join as joinpath, + realpath, + splitext, +) +from shutil import rmtree +from subprocess import call +from statistics import median +from time import time +import sys + +USAGE = """%prog [options] [compiler options] """ + +DESCRIPTION = """\ +This program compiles a C/C++ file with/without ccache a number of times to get +some idea of ccache speedup and overhead in the preprocessor and direct modes. +The arguments to the program should be the compiler, optionally followed by +compiler options, and finally the source file to compile. The compiler options +must not contain -c or -o as these options will be added later. Example: +misc/performance gcc -g -O2 -Idir file.c +""" + +DEFAULT_CCACHE = "./ccache" +DEFAULT_DIRECTORY = "." +DEFAULT_HIT_FACTOR = 1 +DEFAULT_TIMES = 30 + +PHASES = [ + "without ccache", + "with ccache, preprocessor mode, cache miss", + "with ccache, preprocessor mode, cache hit", + "with ccache, direct mode, cache miss", + "with ccache, direct mode, cache hit", + "with ccache, depend mode, cache miss", + "with ccache, depend mode, cache hit", +] + +verbose = False + + +def progress(msg): + if verbose: + sys.stderr.write(msg) + sys.stderr.flush() + + +def recreate_dir(x): + if exists(x): + rmtree(x) + mkdir(x) + + +def test(tmp_dir, options, compiler_args, source_file): + src_dir = "%s/src" % tmp_dir + obj_dir = "%s/obj" % tmp_dir + ccache_dir = "%s/ccache" % tmp_dir + mkdir(src_dir) + mkdir(obj_dir) + + compiler_args += ["-c", "-o"] + extension = splitext(source_file)[1] + hit_factor = options.hit_factor + times = options.times + + progress("Creating source code\n") + for i in range(times): + with open("%s/%d%s" % (src_dir, i, extension), "w") as fp: + with open(source_file) as fp2: + content = fp2.read() + fp.write(content) + fp.write("\nint ccache_perf_test_%d;\n" % i) + + environment = {"CCACHE_DIR": ccache_dir, "PATH": environ["PATH"]} + environment["CCACHE_COMPILERCHECK"] = options.compilercheck + if options.compression_level: + environment["CCACHE_COMPRESSLEVEL"] = str(options.compression_level) + if options.file_clone: + environment["CCACHE_FILECLONE"] = "1" + if options.hardlink: + environment["CCACHE_HARDLINK"] = "1" + if options.no_compression: + environment["CCACHE_NOCOMPRESS"] = "1" + if options.no_cpp2: + environment["CCACHE_NOCPP2"] = "1" + if options.no_stats: + environment["CCACHE_NOSTATS"] = "1" + + results = [] + + def run( + times, *, use_direct, use_depend, use_ccache=True, print_progress=True + ): + timings = [] + for i in range(times): + obj = "%s/%d.o" % (obj_dir, i) + src = "%s/%d%s" % (src_dir, i, extension) + if use_ccache: + args = [options.ccache] + else: + args = [] + args += compiler_args + [obj, src] + env = environment.copy() + if not use_direct: + env["CCACHE_NODIRECT"] = "1" + if use_depend: + env["CCACHE_DEPEND"] = "1" + if print_progress: + progress(".") + t0 = time() + if call(args, env=env) != 0: + sys.stderr.write( + 'Error running "%s"; please correct\n' % " ".join(args) + ) + sys.exit(1) + timings.append(time() - t0) + return timings + + # Warm up the disk cache. + recreate_dir(ccache_dir) + recreate_dir(obj_dir) + run(1, use_direct=True, use_depend=False, print_progress=False) + + ########################################################################### + # Without ccache + recreate_dir(ccache_dir) + recreate_dir(obj_dir) + progress("Compiling %s\n" % PHASES[0]) + results.append( + run(times, use_direct=False, use_depend=False, use_ccache=False) + ) + progress("\n") + + ########################################################################### + # Preprocessor mode + recreate_dir(ccache_dir) + recreate_dir(obj_dir) + progress("Compiling %s\n" % PHASES[1]) + results.append(run(times, use_direct=False, use_depend=False)) + progress("\n") + + recreate_dir(obj_dir) + progress("Compiling %s\n" % PHASES[2]) + res = [] + for j in range(hit_factor): + res += run(times, use_direct=False, use_depend=False) + results.append(res) + progress("\n") + + ########################################################################### + # Direct mode + recreate_dir(ccache_dir) + recreate_dir(obj_dir) + progress("Compiling %s\n" % PHASES[3]) + results.append(run(times, use_direct=True, use_depend=False)) + progress("\n") + + recreate_dir(obj_dir) + progress("Compiling %s\n" % PHASES[4]) + res = [] + for j in range(hit_factor): + res += run(times, use_direct=True, use_depend=False) + results.append(res) + progress("\n") + + ########################################################################### + # Direct+depend mode + recreate_dir(ccache_dir) + recreate_dir(obj_dir) + progress("Compiling %s\n" % PHASES[5]) + results.append(run(times, use_direct=True, use_depend=True)) + progress("\n") + + recreate_dir(obj_dir) + progress("Compiling %s\n" % PHASES[6]) + res = [] + for j in range(hit_factor): + res += run(times, use_direct=True, use_depend=True) + results.append(res) + progress("\n") + + for i, x in enumerate(results): + results[i] = median(x) + return results + + +def print_result_as_text(results): + for i, x in enumerate(PHASES): + print( + "%-43s %6.4f s (%8.4f %%) (%8.4f x)" + % ( + x.capitalize() + ":", + results[i], + 100 * (results[i] / results[0]), + results[0] / results[i], + ) + ) + + +def print_result_as_xml(results): + print('') + print("") + for i, x in enumerate(PHASES): + print("") + print("%s" % x.capitalize()) + print("%.4f" % results[i]) + print("%.4f" % (100 * (results[i] / results[0]))) + print("%.4f" % (results[0] / results[i])) + print("") + print("") + + +def on_off(x): + return "on" if x else "off" + + +def find_in_path(cmd): + if isabs(cmd): + return cmd + else: + for path in environ["PATH"].split(":"): + p = joinpath(path, cmd) + if isfile(p) and access(p, X_OK): + return p + return None + + +def main(argv): + op = OptionParser(usage=USAGE, description=DESCRIPTION) + op.disable_interspersed_args() + op.add_option( + "--ccache", help="location of ccache (default: %s)" % DEFAULT_CCACHE + ) + op.add_option( + "--compilercheck", help="specify compilercheck (default: mtime)" + ) + op.add_option( + "--no-compression", help="disable compression", action="store_true" + ) + op.add_option( + "--compression-level", help="set compression level", type=int + ) + op.add_option( + "-d", + "--directory", + help=( + "where to create the temporary directory with the cache and other" + " files (default: %s)" % DEFAULT_DIRECTORY + ), + ) + op.add_option("--file-clone", help="use file cloning", action="store_true") + op.add_option("--hardlink", help="use hard links", action="store_true") + op.add_option( + "--hit-factor", + help=( + "how many times more to compile the file for cache hits (default:" + " %d)" % DEFAULT_HIT_FACTOR + ), + type="int", + ) + op.add_option( + "--no-cpp2", help="compile preprocessed output", action="store_true" + ) + op.add_option( + "--no-stats", help="don't write statistics", action="store_true" + ) + op.add_option( + "-n", + "--times", + help=( + "number of times to compile the file (default: %d)" % DEFAULT_TIMES + ), + type="int", + ) + op.add_option( + "-v", "--verbose", help="print progress messages", action="store_true" + ) + op.add_option("--xml", help="print results as XML", action="store_true") + op.set_defaults( + ccache=DEFAULT_CCACHE, + compilercheck="mtime", + directory=DEFAULT_DIRECTORY, + hit_factor=DEFAULT_HIT_FACTOR, + times=DEFAULT_TIMES, + ) + options, args = op.parse_args(argv[1:]) + if len(args) < 2: + op.error("Missing arguments; pass -h/--help for help") + + global verbose + verbose = options.verbose + + options.ccache = abspath(options.ccache) + + compiler = find_in_path(args[0]) + if compiler is None: + op.error("Could not find %s in PATH" % args[0]) + if "ccache" in basename(realpath(compiler)): + op.error( + "%s seems to be a symlink to ccache; please specify the path to" + " the real compiler instead" % compiler + ) + + if not options.xml: + print( + "Compilation command: %s -c -o %s.o" + % (" ".join(args), splitext(argv[-1])[0]) + ) + print("Compilercheck:", options.compilercheck) + print("Compression:", on_off(not options.no_compression)) + print("Compression level:", options.compression_level or "default") + print("File cloning:", on_off(options.file_clone)) + print("Hard linking:", on_off(options.hardlink)) + print("No cpp2:", on_off(options.no_cpp2)) + print("No stats:", on_off(options.no_stats)) + + tmp_dir = "%s/perfdir.%d" % (abspath(options.directory), getpid()) + recreate_dir(tmp_dir) + results = test(tmp_dir, options, args[:-1], args[-1]) + rmtree(tmp_dir) + if options.xml: + print_result_as_xml(results) + else: + print_result_as_text(results) + + +main(sys.argv) diff --git a/misc/rsyslog.d/00-ccache.conf b/misc/rsyslog.d/00-ccache.conf new file mode 100644 index 0000000..cb68a12 --- /dev/null +++ b/misc/rsyslog.d/00-ccache.conf @@ -0,0 +1,2 @@ +:programname, isequal, "ccache" /var/log/ccache +& ~ diff --git a/misc/shellcheck-excludes.txt b/misc/shellcheck-excludes.txt new file mode 100644 index 0000000..08d8fbd --- /dev/null +++ b/misc/shellcheck-excludes.txt @@ -0,0 +1,8 @@ +SC2148: Tips depend on target shell and yours is unknown. Add a shebang. +# the below excludes are (mostly) about bourne shell and style issues +SC2001: See if you can use ${variable//search/replace} instead. +SC2006: Use $(..) instead of legacy `..`. +SC2046: Quote this to prevent word splitting. +SC2086: Double quote to prevent globbing and word splitting. +SC2103: Consider using ( subshell ), 'cd foo||exit', or pushd/popd instead. +SC2129: Consider using { cmd1; cmd2; } >> file instead of individual redirects. diff --git a/misc/summarize-trace-files b/misc/summarize-trace-files new file mode 100755 index 0000000..3c65f05 --- /dev/null +++ b/misc/summarize-trace-files @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 + +import json +import sys + +trace = json.load(sys.stdin) + +events = trace["traceEvents"] +slot_events = [] + +events.sort(key=lambda event: event["ts"]) + +jobs = int(sys.argv[1]) + +pids = {} +busy = [None] * jobs + + +def find_slot(pid): + if pid in pids: + return pids[pid] + for slot in range(jobs): + if not busy[slot]: + busy[slot] = pid + pids[pid] = slot + return slot + return None + + +def end_slot(pid): + for slot in range(jobs): + if busy[slot] == pid: + busy[slot] = None + del pids[pid] + return slot + return slot + + +name = {} +slot = -1 +for event in events: + cat = event["cat"] + pid = event["pid"] + phase = event["ph"] + args = event["args"] + + if phase == "M" and event["name"] == "thread_name": + name[pid] = args["name"] + if cat != "program": + continue + + if phase == "B" or phase == "S": + slot = find_slot(pid) + elif phase == "E" or phase == "F": + slot = end_slot(pid) + elif phase == "M": + pass + else: + continue + + event["pid"] = slot + event["tid"] = pid + + slot_events.append(event) + +slot_events.sort(key=lambda event: event["tid"]) + +for event in slot_events: + if event["cat"] == "program": + event["cat"] = "ccache" + if event["tid"] in name: + event["name"] = name[event["tid"]] + elif event["tid"] in name: + event["name"] = event["name"] + ":" + name[event["tid"]] + del event["tid"] + if event["ph"] == "S": + event["ph"] = "B" + elif event["ph"] == "F": + event["ph"] = "E" + +for slot in range(jobs): + slot_events.append( + { + "cat": "", + "pid": slot, + "tid": 0, + "ph": "M", + "name": "process_name", + "args": {"name": "Job %d" % slot}, + } + ) + +json.dump({"traceEvents": slot_events}, sys.stdout, indent=4) diff --git a/misc/test-all-systems b/misc/test-all-systems new file mode 100755 index 0000000..2dc0da9 --- /dev/null +++ b/misc/test-all-systems @@ -0,0 +1,51 @@ +#!/bin/sh +# +# While it's obviously quite impossible to support and test every single +# distribution, this script enables easy checking of the most common standard +# distributions at least. + +set -eu + +build_in_docker=$(dirname $0)/build-in-docker + +build() { + local name=$1 + local cc=$2 + local cxx=$3 + local test_cc=$4 + shift 4 + echo "Build in Docker: $name CC=$cc CXX=$cxx TEST_CC=$test_cc CMAKE_PARAMS=\"$*\"" + ASM=$cc CC=$cc CXX=$cxx TEST_CC=$test_cc CMAKE_PARAMS="$*" $build_in_docker $name +} + +# NAME CC CXX TEST_CC CMAKE_PARAMS + +build debian-9 gcc g++ gcc +build debian-9 clang clang++ clang + +build debian-10 gcc g++ gcc +build debian-10 clang clang++ clang + +build ubuntu-14.04 gcc g++ gcc -DZSTD_FROM_INTERNET=ON +build ubuntu-14.04 gcc g++ clang -DZSTD_FROM_INTERNET=ON + +build ubuntu-16.04 gcc g++ gcc +build ubuntu-16.04 clang clang++ clang + +build ubuntu-20.04 gcc g++ gcc +build ubuntu-20.04 clang clang++ clang + +build centos-7 gcc g++ gcc -DWARNINGS_AS_ERRORS=false +build centos-7 gcc g++ clang -DWARNINGS_AS_ERRORS=false + +build centos-8 gcc g++ gcc +build centos-8 clang clang++ clang + +build fedora-32 gcc g++ gcc +build fedora-32 clang clang++ clang + +build alpine-3.4 gcc g++ gcc -DZSTD_FROM_INTERNET=ON +build alpine-3.4 gcc g++ clang -DZSTD_FROM_INTERNET=ON + +build alpine-3.12 gcc g++ gcc +build alpine-3.12 clang clang++ clang diff --git a/misc/update-authors b/misc/update-authors new file mode 100755 index 0000000..0755026 --- /dev/null +++ b/misc/update-authors @@ -0,0 +1,14 @@ +#!/bin/sh + +if [ -d .git ]; then + # Fetch full Git history if needed, e.g. when run via CI. + git fetch --unshallow 2>/dev/null + + # Update doc/AUTHORS.adoc with Git commit authors plus authors mentioned via + # a "Co-authored-by:" in the commit message. + (git log | grep -Po "(?<=Co-authored-by: )(.*)(?= <)"; \ + git log --format="%aN") \ + | sed 's/^/* /' \ + | LANG=en_US.utf8 sort -uf \ + | perl -00 -p -i -e 's/^\*.*/ . "\n"/es' doc/AUTHORS.adoc +fi diff --git a/src/.clang-tidy b/src/.clang-tidy new file mode 100644 index 0000000..c240850 --- /dev/null +++ b/src/.clang-tidy @@ -0,0 +1,77 @@ +# This .clang-tidy file is used by CI to ensure that commits do not worsen the +# codebase. The checks and values below are the minimum standard for new code. +# (Without claiming that they are 100% correct. They can be modified on demand!) +# If you want to improve the codebase try enabling some additional checks or +# playing with the configuration values. +# +# Some checks are highly style dependent. The goal is NOT to activate all of +# them. + +--- +Checks: '-*, + readability-*, + -readability-implicit-bool-conversion, + -readability-magic-numbers, + -readability-else-after-return, + -readability-qualified-auto, + -readability-magic-numbers, + performance-*, + -performance-unnecessary-value-param, + modernize-*, + -modernize-avoid-c-arrays, + -modernize-pass-by-value, + -modernize-use-auto, + -modernize-use-trailing-return-type, + cppcoreguidelines-*, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-pro-type-vararg, + -cppcoreguidelines-owning-memory, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-avoid-non-const-global-variables, + -cppcoreguidelines-const-correctness, + -cppcoreguidelines-pro-bounds-pointer-arithmetic, + -cppcoreguidelines-no-malloc, + -cppcoreguidelines-init-variables, + -cppcoreguidelines-avoid-c-arrays, + -cppcoreguidelines-pro-bounds-constant-array-index, + -cppcoreguidelines-pro-type-member-init, + -cppcoreguidelines-macro-usage, + -cppcoreguidelines-pro-type-const-cast, + -cppcoreguidelines-pro-type-reinterpret-cast, + -cppcoreguidelines-pro-type-union-access, + -cppcoreguidelines-narrowing-conversions, + bugprone-*, + -bugprone-signed-char-misuse, + -bugprone-branch-clone, + -bugprone-narrowing-conversions, + cert-*, + -cert-err34-c, + -cert-dcl50-cpp, + -cert-err58-cpp, + clang-diagnostic-*, + clang-analyzer-*, + -clang-analyzer-alpha*, + -clang-analyzer-valist.Uninitialized, + -clang-analyzer-optin.performance.Padding' +WarningsAsErrors: '*' +# Only include headers directly in src. +HeaderFilterRegex: 'src/[^/]*$' +CheckOptions: + # Always add braces (added here just in case Clang-Tidy default changes). + - key: readability-braces-around-statements.ShortStatementLines + value: 0 + + # If you hit a limit, please consider changing the code instead of the limit. + - key: readability-function-size.LineThreshold + value: 700 + - key: readability-function-size.StatementThreshold + value: 500 + - key: readability-function-size.BranchThreshold + value: 170 + - key: readability-function-size.ParameterThreshold + value: 6 + - key: readability-function-size.NestingThreshold + value: 6 + - key: readability-function-size.VariableThreshold + value: 80 +... diff --git a/src/Args.cpp b/src/Args.cpp new file mode 100644 index 0000000..92a1e41 --- /dev/null +++ b/src/Args.cpp @@ -0,0 +1,218 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "Args.hpp" + +#include "Util.hpp" + +using nonstd::nullopt; +using nonstd::optional; +using nonstd::string_view; + +Args::Args(Args&& other) noexcept : m_args(std::move(other.m_args)) +{ +} + +Args +Args::from_argv(int argc, const char* const* argv) +{ + Args args; + args.m_args.assign(argv, argv + argc); + return args; +} + +Args +Args::from_string(const std::string& command) +{ + Args args; + for (const std::string& word : Util::split_into_strings(command, " \t\r\n")) { + args.push_back(word); + } + return args; +} + +optional +Args::from_gcc_atfile(const std::string& filename) +{ + std::string argtext; + try { + argtext = Util::read_file(filename); + } catch (Error&) { + return nullopt; + } + + Args args; + auto pos = argtext.c_str(); + std::string argbuf; + argbuf.resize(argtext.length() + 1); + auto argpos = argbuf.begin(); + + // Used to track quoting state; if \0 we are not inside quotes. Otherwise + // stores the quoting character that started it for matching the end quote. + char quoting = '\0'; + + while (true) { + switch (*pos) { + case '\\': + pos++; + if (*pos == '\0') { + continue; + } + break; + + case '"': + case '\'': + if (quoting != '\0') { + if (quoting == *pos) { + quoting = '\0'; + pos++; + continue; + } else { + break; + } + } else { + quoting = *pos; + pos++; + continue; + } + + case '\n': + case '\r': + case '\t': + case ' ': + if (quoting) { + break; + } + // Fall through. + + case '\0': + // End of token + *argpos = '\0'; + if (argbuf[0] != '\0') { + args.push_back(argbuf.substr(0, argbuf.find('\0'))); + } + argpos = argbuf.begin(); + if (*pos == '\0') { + return args; + } else { + pos++; + continue; + } + } + + *argpos = *pos; + pos++; + argpos++; + } +} + +Args& +Args::operator=(Args&& other) noexcept +{ + if (&other != this) { + m_args = std::move(other.m_args); + } + return *this; +} + +std::vector +Args::to_argv() const +{ + std::vector result; + result.reserve(m_args.size() + 1); + for (const auto& arg : m_args) { + result.push_back(arg.c_str()); + } + result.push_back(nullptr); + return result; +} + +std::string +Args::to_string() const +{ + std::string result; + for (const auto& arg : m_args) { + if (!result.empty()) { + result += ' '; + } + result += arg; + } + return result; +} + +void +Args::erase_with_prefix(string_view prefix) +{ + m_args.erase(std::remove_if(m_args.begin(), + m_args.end(), + [&prefix](const std::string& s) { + return Util::starts_with(s, prefix); + }), + m_args.end()); +} + +void +Args::insert(size_t index, const Args& args) +{ + if (args.size() == 0) { + return; + } + m_args.insert(m_args.begin() + index, args.m_args.begin(), args.m_args.end()); +} + +void +Args::pop_back(size_t count) +{ + m_args.erase(m_args.end() - count, m_args.end()); +} + +void +Args::pop_front(size_t count) +{ + m_args.erase(m_args.begin(), m_args.begin() + count); +} + +void +Args::push_back(const std::string& arg) +{ + m_args.push_back(arg); +} + +void +Args::push_back(const Args& args) +{ + m_args.insert(m_args.end(), args.m_args.begin(), args.m_args.end()); +} + +void +Args::push_front(const std::string& arg) +{ + m_args.push_front(arg); +} + +void +Args::replace(size_t index, const Args& args) +{ + if (args.size() == 1) { + // Trivial case; replace with 1 element. + m_args[index] = args[0]; + } else { + m_args.erase(m_args.begin() + index); + insert(index, args); + } +} diff --git a/src/Args.hpp b/src/Args.hpp new file mode 100644 index 0000000..be917ec --- /dev/null +++ b/src/Args.hpp @@ -0,0 +1,129 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "NonCopyable.hpp" +#include "Util.hpp" + +#include "third_party/nonstd/optional.hpp" +#include "third_party/nonstd/string_view.hpp" + +#include +#include + +class Args +{ +public: + Args() = default; + Args(const Args& other) = default; + Args(Args&& other) noexcept; + + static Args from_argv(int argc, const char* const* argv); + static Args from_string(const std::string& command); + static nonstd::optional from_gcc_atfile(const std::string& filename); + + Args& operator=(const Args& other) = default; + Args& operator=(Args&& other) noexcept; + + bool operator==(const Args& other) const; + bool operator!=(const Args& other) const; + + bool empty() const; + size_t size() const; + const std::string& operator[](size_t i) const; + std::string& operator[](size_t i); + + // Return the argument list as a vector of raw string pointers. Callers can + // use `const_cast(args.to_argv().data())` to get an array + // suitable to pass to e.g. execv(2). + std::vector to_argv() const; + + // Return a space-delimited argument list in string form. No quoting of spaces + // in arguments is performed. + std::string to_string() const; + + // Remove all arguments with prefix `prefix`. + void erase_with_prefix(nonstd::string_view prefix); + + // Insert arguments in `args` at position `index`. + void insert(size_t index, const Args& args); + + // Remove the last `count` arguments. + void pop_back(size_t count = 1); + + // Remove the first `count` arguments. + void pop_front(size_t count = 1); + + // Add `arg` to the end. + void push_back(const std::string& arg); + + // Add `args` to the end. + void push_back(const Args& args); + + // Add `arg` to the front. + void push_front(const std::string& arg); + + // Replace the argument at `index` with all arguments in `args`. + void replace(size_t index, const Args& args); + +private: + std::deque m_args; +}; + +inline bool +Args::operator==(const Args& other) const +{ + return m_args == other.m_args; +} + +inline bool +Args::operator!=(const Args& other) const +{ + return m_args != other.m_args; +} + +inline bool +Args::empty() const +{ + return m_args.empty(); +} + +inline size_t +Args::size() const +{ + return m_args.size(); +} + +// clang-format off +inline const std::string& +Args::operator[](size_t i) const +// clang-format on +{ + return m_args[i]; +} + +// clang-format off +inline std::string& +Args::operator[](size_t i) +// clang-format on +{ + return m_args[i]; +} diff --git a/src/ArgsInfo.hpp b/src/ArgsInfo.hpp new file mode 100644 index 0000000..e79b1c9 --- /dev/null +++ b/src/ArgsInfo.hpp @@ -0,0 +1,115 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Args.hpp" + +#include +#include + +// This class holds meta-information derived from the compiler arguments. +struct ArgsInfo +{ + // The source file. + std::string input_file; + + // The output file being compiled to. + std::string output_obj; + + // The path to the dependency file (implicit or specified with -MF). + std::string output_dep; + + // The path to the stack usage (implicit when using -fstack-usage). + std::string output_su; + + // Diagnostic generation information (Clang). Contains pathname if not empty. + std::string output_dia; + + // Split dwarf information (GCC 4.8 and up). Contains pathname if not empty. + std::string output_dwo; + + // Language to use for the compilation target (see language.c). + std::string actual_language; + + // Is the compiler being asked to output debug info? + bool generating_debuginfo = false; + + // Is the compiler being asked to output dependencies? + bool generating_dependencies = false; + + // Seen -MD or -MMD? + bool seen_MD_MMD = false; + + // Is the dependency makefile target name specified with -MT or -MQ? + bool dependency_target_specified = false; + + // Is the compiler being asked to output coverage? + bool generating_coverage = false; + + // Is the compiler being asked to output stack usage? + bool generating_stackusage = false; + + // Us the compiler being asked to generate diagnostics + // (--serialize-diagnostics)? + bool generating_diagnostics = false; + + // Whether to strip color codes from diagnostic messages on output. + bool strip_diagnostics_colors = false; + + // Have we seen -gsplit-dwarf? + bool seen_split_dwarf = false; + + // Are we compiling a .i or .ii file directly? + bool direct_i_file = false; + + // Whether the output is a precompiled header. + bool output_is_precompiled_header = false; + + // Is the compiler being asked to output coverage data (.gcda) at runtime? + bool profile_arcs = false; + + // Name of the custom profile directory or file. + std::string profile_path; + + // Profile generation / usage information. + bool profile_use = false; + bool profile_generate = false; + + // Whether we are using a precompiled header (either via -include, #include or + // Clang's -include-pch or -include-pth). + bool using_precompiled_header = false; + + // Whether Clang is instructed not to include timestamps in the precompiled + // header it generates. + bool fno_pch_timestamp = false; + + // Files referenced by -fsanitize-blacklist options. + std::vector sanitize_blacklists; + + // Architectures from -arch options. + std::vector arch_args; + + // Relocating debuginfo in the format old=new. + std::vector debug_prefix_maps; + + // Argument list to add to compiler invocation in depend mode. + Args depend_extra_args; +}; diff --git a/src/AtomicFile.cpp b/src/AtomicFile.cpp new file mode 100644 index 0000000..d02679b --- /dev/null +++ b/src/AtomicFile.cpp @@ -0,0 +1,69 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "AtomicFile.hpp" + +#include "TemporaryFile.hpp" +#include "Util.hpp" +#include "assertions.hpp" +#include "exceptions.hpp" + +AtomicFile::AtomicFile(const std::string& path, Mode mode) : m_path(path) +{ + TemporaryFile tmp_file(path + ".tmp"); + m_stream = fdopen(tmp_file.fd.release(), mode == Mode::binary ? "w+b" : "w+"); + m_tmp_path = std::move(tmp_file.path); +} + +AtomicFile::~AtomicFile() +{ + if (m_stream) { + // commit() was not called so remove the lingering temporary file. + fclose(m_stream); + Util::unlink_tmp(m_tmp_path); + } +} + +void +AtomicFile::write(const std::string& data) +{ + if (fwrite(data.data(), data.size(), 1, m_stream) != 1) { + throw Error("failed to write data to {}: {}", m_path, strerror(errno)); + } +} + +void +AtomicFile::write(const std::vector& data) +{ + if (fwrite(data.data(), data.size(), 1, m_stream) != 1) { + throw Error("failed to write data to {}: {}", m_path, strerror(errno)); + } +} + +void +AtomicFile::commit() +{ + ASSERT(m_stream); + int result = fclose(m_stream); + m_stream = nullptr; + if (result == EOF) { + Util::unlink_tmp(m_tmp_path); + throw Error("failed to write data to {}: {}", m_path, strerror(errno)); + } + Util::rename(m_tmp_path, m_path); +} diff --git a/src/AtomicFile.hpp b/src/AtomicFile.hpp new file mode 100644 index 0000000..118c310 --- /dev/null +++ b/src/AtomicFile.hpp @@ -0,0 +1,56 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include +#include + +// This class represents a file whose data will be atomically written to a path +// by renaming a temporary file in place. +class AtomicFile +{ +public: + enum class Mode { binary, text }; + + AtomicFile(const std::string& path, Mode mode); + ~AtomicFile(); + + FILE* stream(); + + void write(const std::string& data); + void write(const std::vector& data); + + // Close the temporary file and rename it to the destination file. Note: The + // destructor will not do this automatically to avoid half-written data in the + // file. + void commit(); + +private: + const std::string m_path; + std::string m_tmp_path; + FILE* m_stream; +}; + +inline FILE* +AtomicFile::stream() +{ + return m_stream; +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..1225887 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,74 @@ +set( + source_files + Args.cpp + AtomicFile.cpp + CacheEntryReader.cpp + CacheEntryWriter.cpp + CacheFile.cpp + Compression.cpp + Compressor.cpp + Config.cpp + Context.cpp + Counters.cpp + Decompressor.cpp + Hash.cpp + Lockfile.cpp + Logging.cpp + Manifest.cpp + MiniTrace.cpp + NullCompressor.cpp + NullDecompressor.cpp + ProgressBar.cpp + Result.cpp + ResultDumper.cpp + ResultExtractor.cpp + ResultRetriever.cpp + SignalHandler.cpp + Stat.cpp + Statistics.cpp + TemporaryFile.cpp + ThreadPool.cpp + Util.cpp + ZstdCompressor.cpp + ZstdDecompressor.cpp + argprocessing.cpp + assertions.cpp + ccache.cpp + cleanup.cpp + compopt.cpp + compress.cpp + execute.cpp + hashutil.cpp + language.cpp + version.cpp) + +if(INODE_CACHE_SUPPORTED) + list(APPEND source_files InodeCache.cpp) +endif() + +if(WIN32) + list(APPEND source_files Win32Util.cpp) +endif() + +add_library(ccache_lib STATIC ${source_files}) + +if(WIN32) + target_link_libraries(ccache_lib PRIVATE ws2_32 "psapi") + + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + target_link_libraries( + ccache_lib PRIVATE -static gcc stdc++ winpthread -dynamic) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + target_link_libraries(ccache_lib PRIVATE -static c++ -dynamic) + endif() +endif() + +find_package(Threads REQUIRED) +target_link_libraries( + ccache_lib + PRIVATE standard_settings standard_warnings ZSTD::ZSTD + Threads::Threads third_party_lib) + +target_include_directories(ccache_lib PRIVATE ${CMAKE_BINARY_DIR} .) + +add_subdirectory(third_party) diff --git a/src/CacheEntryReader.cpp b/src/CacheEntryReader.cpp new file mode 100644 index 0000000..5879b51 --- /dev/null +++ b/src/CacheEntryReader.cpp @@ -0,0 +1,93 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "CacheEntryReader.hpp" + +#include "Compressor.hpp" +#include "exceptions.hpp" + +#include "third_party/fmt/core.h" + +CacheEntryReader::CacheEntryReader(FILE* stream, + const uint8_t expected_magic[4], + uint8_t expected_version) +{ + uint8_t header_bytes[15]; + if (fread(header_bytes, sizeof(header_bytes), 1, stream) != 1) { + throw Error("Error reading header"); + } + + memcpy(m_magic, header_bytes, sizeof(m_magic)); + m_version = header_bytes[4]; + m_compression_type = Compression::type_from_int(header_bytes[5]); + m_compression_level = header_bytes[6]; + Util::big_endian_to_int(header_bytes + 7, m_content_size); + + if (memcmp(m_magic, expected_magic, sizeof(m_magic)) != 0) { + throw Error("Bad magic value 0x{:02x}{:02x}{:02x}{:02x}", + m_magic[0], + m_magic[1], + m_magic[2], + m_magic[3]); + } + if (m_version != expected_version) { + throw Error( + "Unknown version (actual {}, expected {})", m_version, expected_version); + } + + m_checksum.update(header_bytes, sizeof(header_bytes)); + m_decompressor = Decompressor::create_from_type(m_compression_type, stream); +} + +void +CacheEntryReader::dump_header(FILE* dump_stream) +{ + fmt::print(dump_stream, "Magic: {:.4}\n", m_magic); + fmt::print(dump_stream, "Version: {}\n", m_version); + fmt::print(dump_stream, + "Compression type: {}\n", + Compression::type_to_string(m_compression_type)); + fmt::print(dump_stream, "Compression level: {}\n", m_compression_level); + fmt::print(dump_stream, "Content size: {}\n", m_content_size); +} + +void +CacheEntryReader::read(void* data, size_t count) +{ + m_decompressor->read(data, count); + m_checksum.update(data, count); +} + +void +CacheEntryReader::finalize() +{ + uint64_t actual_digest = m_checksum.digest(); + + uint8_t buffer[8]; + read(buffer, sizeof(buffer)); + uint64_t expected_digest; + Util::big_endian_to_int(buffer, expected_digest); + + if (actual_digest != expected_digest) { + throw Error("Incorrect checksum (actual 0x{:016x}, expected 0x{:016x})", + actual_digest, + expected_digest); + } + + m_decompressor->finalize(); +} diff --git a/src/CacheEntryReader.hpp b/src/CacheEntryReader.hpp new file mode 100644 index 0000000..8ee7302 --- /dev/null +++ b/src/CacheEntryReader.hpp @@ -0,0 +1,145 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Checksum.hpp" +#include "Decompressor.hpp" +#include "Util.hpp" + +#include + +// This class knows how to read a cache entry with a common header and a +// payload part that is different depending on the cache entry type (result or +// manifest). +class CacheEntryReader +{ +public: + // Constructor. + // + // Parameters: + // - stream: Stream to read header and payload from. + // - expected_magic: Expected magic bytes (first four bytes of the file). + // - expected_version: Expected file format version. + CacheEntryReader(FILE* stream, + const uint8_t expected_magic[4], + uint8_t expected_version); + + // Dump header information in text format. + // + // Parameters: + // - dump_stream: Stream to write to. + void dump_header(FILE* dump_stream); + + // Read data into a buffer from the payload. + // + // Parameters: + // - data: Buffer to write data to. + // - count: How many bytes to write. + // + // Throws Error on failure. + void read(void* data, size_t count); + + // Read an unsigned integer from the payload. + // + // Parameters: + // - value: Variable to write to. + // + // Throws Error on failure. + template void read(T& value); + + // Close for reading. + // + // This method potentially verifies the end state after reading the cache + // entry and throws Error if any integrity issues are found. + void finalize(); + + // Get size of the payload, + uint64_t payload_size() const; + + // Get content magic. + const uint8_t* magic() const; + + // Get content version. + uint8_t version() const; + + // Get compression type. + Compression::Type compression_type() const; + + // Get compression level. + int8_t compression_level() const; + + // Get size of the content (header + payload + checksum). + uint64_t content_size() const; + +private: + std::unique_ptr m_decompressor; + Checksum m_checksum; + uint8_t m_magic[4]; + uint8_t m_version; + Compression::Type m_compression_type; + int8_t m_compression_level; + uint64_t m_content_size; +}; + +template +inline void +CacheEntryReader::read(T& value) +{ + uint8_t buffer[sizeof(T)]; + read(buffer, sizeof(T)); + Util::big_endian_to_int(buffer, value); +} + +inline const uint8_t* +CacheEntryReader::magic() const +{ + return m_magic; +} + +inline uint8_t +CacheEntryReader::version() const +{ + return m_version; +} + +inline Compression::Type +CacheEntryReader::compression_type() const +{ + return m_compression_type; +} + +inline int8_t +CacheEntryReader::compression_level() const +{ + return m_compression_level; +} + +inline uint64_t +CacheEntryReader::payload_size() const +{ + return m_content_size - 15 - 8; +} + +inline uint64_t +CacheEntryReader::content_size() const +{ + return m_content_size; +} diff --git a/src/CacheEntryWriter.cpp b/src/CacheEntryWriter.cpp new file mode 100644 index 0000000..6636bf5 --- /dev/null +++ b/src/CacheEntryWriter.cpp @@ -0,0 +1,60 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "CacheEntryWriter.hpp" + +CacheEntryWriter::CacheEntryWriter(FILE* stream, + const uint8_t magic[4], + uint8_t version, + Compression::Type compression_type, + int8_t compression_level, + uint64_t payload_size) + // clang-format off + : m_compressor( + Compressor::create_from_type(compression_type, stream, compression_level) + ) +{ + // clang-format on + uint8_t header_bytes[15]; + memcpy(header_bytes, magic, 4); + header_bytes[4] = version; + header_bytes[5] = static_cast(compression_type); + header_bytes[6] = m_compressor->actual_compression_level(); + uint64_t content_size = 15 + payload_size + 8; + Util::int_to_big_endian(content_size, header_bytes + 7); + if (fwrite(header_bytes, sizeof(header_bytes), 1, stream) != 1) { + throw Error("Failed to write cache entry header"); + } + m_checksum.update(header_bytes, sizeof(header_bytes)); +} + +void +CacheEntryWriter::write(const void* data, size_t count) +{ + m_compressor->write(data, count); + m_checksum.update(data, count); +} + +void +CacheEntryWriter::finalize() +{ + uint8_t buffer[8]; + Util::int_to_big_endian(m_checksum.digest(), buffer); + m_compressor->write(buffer, sizeof(buffer)); + m_compressor->finalize(); +} diff --git a/src/CacheEntryWriter.hpp b/src/CacheEntryWriter.hpp new file mode 100644 index 0000000..14c0d19 --- /dev/null +++ b/src/CacheEntryWriter.hpp @@ -0,0 +1,86 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Checksum.hpp" +#include "Compressor.hpp" +#include "Util.hpp" + +#include + +// This class knows how to write a cache entry with a common header and a +// payload part that is different depending on the cache entry type (result or +// manifest). +class CacheEntryWriter +{ +public: + // Constructor. + // + // Parameters: + // - stream: Stream to write header + payload to. + // - magic: File format magic bytes. + // - version: File format version. + // - compression_type: Compression type to use. + // - compression_level: Compression level to use. + // - payload_size: Payload size. + CacheEntryWriter(FILE* stream, + const uint8_t magic[4], + uint8_t version, + Compression::Type compression_type, + int8_t compression_level, + uint64_t payload_size); + + // Write data to the payload from a buffer. + // + // Parameters: + // - data: Data to write. + // - count: Size of data to write. + // + // Throws Error on failure. + void write(const void* data, size_t count); + + // Write an unsigned integer to the payload. + // + // Parameters: + // - value: Value to write. + // + // Throws Error on failure. + template void write(T value); + + // Close for writing. + // + // This method potentially verifies the end state after writing the cache + // entry and throws Error if any integrity issues are found. + void finalize(); + +private: + std::unique_ptr m_compressor; + Checksum m_checksum; +}; + +template +inline void +CacheEntryWriter::write(T value) +{ + uint8_t buffer[sizeof(T)]; + Util::int_to_big_endian(value, buffer); + write(buffer, sizeof(T)); +} diff --git a/src/CacheFile.cpp b/src/CacheFile.cpp new file mode 100644 index 0000000..68072df --- /dev/null +++ b/src/CacheFile.cpp @@ -0,0 +1,45 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "CacheFile.hpp" + +#include "Manifest.hpp" +#include "Result.hpp" +#include "Util.hpp" + +const Stat& +CacheFile::lstat() const +{ + if (!m_stat) { + m_stat = Stat::lstat(m_path); + } + + return *m_stat; +} + +CacheFile::Type +CacheFile::type() const +{ + if (Util::ends_with(m_path, Manifest::k_file_suffix)) { + return Type::manifest; + } else if (Util::ends_with(m_path, Result::k_file_suffix)) { + return Type::result; + } else { + return Type::unknown; + } +} diff --git a/src/CacheFile.hpp b/src/CacheFile.hpp new file mode 100644 index 0000000..59c180b --- /dev/null +++ b/src/CacheFile.hpp @@ -0,0 +1,57 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Stat.hpp" +#include "exceptions.hpp" + +#include "third_party/nonstd/optional.hpp" + +#include + +class CacheFile +{ +public: + enum class Type { result, manifest, unknown }; + + explicit CacheFile(const std::string& path); + + CacheFile(const CacheFile&) = delete; + CacheFile& operator=(const CacheFile&) = delete; + + const Stat& lstat() const; + const std::string& path() const; + Type type() const; + +private: + const std::string m_path; + mutable nonstd::optional m_stat; +}; + +inline CacheFile::CacheFile(const std::string& path) : m_path(path) +{ +} + +inline const std::string& +CacheFile::path() const +{ + return m_path; +} diff --git a/src/Checksum.hpp b/src/Checksum.hpp new file mode 100644 index 0000000..e2c6274 --- /dev/null +++ b/src/Checksum.hpp @@ -0,0 +1,69 @@ +// Copyright (C) 2019 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#ifdef USE_XXH_DISPATCH +# include "third_party/xxh_x86dispatch.h" +#else +# include "third_party/xxhash.h" +#endif + +class Checksum +{ +public: + Checksum(); + ~Checksum(); + + void reset(); + void update(const void* data, size_t length); + uint64_t digest() const; + +private: + XXH3_state_t* m_state; +}; + +inline Checksum::Checksum() : m_state(XXH3_createState()) +{ + reset(); +} + +inline Checksum::~Checksum() +{ + XXH3_freeState(m_state); +} + +inline void +Checksum::reset() +{ + XXH3_64bits_reset(m_state); +} + +inline void +Checksum::update(const void* data, size_t length) +{ + XXH3_64bits_update(m_state, data, length); +} + +inline uint64_t +Checksum::digest() const +{ + return XXH3_64bits_digest(m_state); +} diff --git a/src/Compression.cpp b/src/Compression.cpp new file mode 100644 index 0000000..aa2a182 --- /dev/null +++ b/src/Compression.cpp @@ -0,0 +1,68 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "Compression.hpp" + +#include "Config.hpp" +#include "Context.hpp" +#include "assertions.hpp" +#include "exceptions.hpp" + +namespace Compression { + +int8_t +level_from_config(const Config& config) +{ + return config.compression() ? config.compression_level() : 0; +} + +Type +type_from_config(const Config& config) +{ + return config.compression() ? Type::zstd : Type::none; +} + +Type +type_from_int(uint8_t type) +{ + switch (type) { + case static_cast(Type::none): + return Type::none; + + case static_cast(Type::zstd): + return Type::zstd; + } + + throw Error("Unknown type: {}", type); +} + +std::string +type_to_string(Type type) +{ + switch (type) { + case Type::none: + return "none"; + + case Type::zstd: + return "zstd"; + } + + ASSERT(false); +} + +} // namespace Compression diff --git a/src/Compression.hpp b/src/Compression.hpp new file mode 100644 index 0000000..24e1746 --- /dev/null +++ b/src/Compression.hpp @@ -0,0 +1,42 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include + +class Config; + +namespace Compression { + +enum class Type : uint8_t { + none = 0, + zstd = 1, +}; + +int8_t level_from_config(const Config& config); + +Type type_from_config(const Config& config); + +Type type_from_int(uint8_t type); + +std::string type_to_string(Compression::Type type); + +} // namespace Compression diff --git a/src/Compressor.cpp b/src/Compressor.cpp new file mode 100644 index 0000000..efb1257 --- /dev/null +++ b/src/Compressor.cpp @@ -0,0 +1,40 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "Compressor.hpp" + +#include "NullCompressor.hpp" +#include "StdMakeUnique.hpp" +#include "ZstdCompressor.hpp" +#include "assertions.hpp" + +std::unique_ptr +Compressor::create_from_type(Compression::Type type, + FILE* stream, + int8_t compression_level) +{ + switch (type) { + case Compression::Type::none: + return std::make_unique(stream); + + case Compression::Type::zstd: + return std::make_unique(stream, compression_level); + } + + ASSERT(false); +} diff --git a/src/Compressor.hpp b/src/Compressor.hpp new file mode 100644 index 0000000..3295874 --- /dev/null +++ b/src/Compressor.hpp @@ -0,0 +1,67 @@ +// Copyright (C) 2019 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Compression.hpp" + +#include + +class Compressor +{ +public: + virtual ~Compressor() = default; + + // Create a compressor for the specified type. + // + // Parameters: + // - type: The type. + // - stream: The stream to write to. + // - compression_level: Desired compression level. + static std::unique_ptr create_from_type(Compression::Type type, + FILE* stream, + int8_t compression_level); + + // Get the actual compression level used for the compressed stream. + virtual int8_t actual_compression_level() const = 0; + + // Write data from a buffer to the compressed stream. + // + // Parameters: + // - data: Data to write. + // - count: Size of data to write. + // + // Throws Error on failure. + virtual void write(const void* data, size_t count) = 0; + + // Write an unsigned integer to the compressed stream. + // + // Parameters: + // - value: Value to write. + // + // Throws Error on failure. + template void write(T value); + + // Finalize compression. + // + // This method checks that the end state of the compressed stream is correct + // and throws Error if not. + virtual void finalize() = 0; +}; diff --git a/src/Config.cpp b/src/Config.cpp new file mode 100644 index 0000000..c7168e0 --- /dev/null +++ b/src/Config.cpp @@ -0,0 +1,843 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "Config.hpp" + +#include "AtomicFile.hpp" +#include "Compression.hpp" +#include "Util.hpp" +#include "assertions.hpp" +#include "ccache.hpp" +#include "exceptions.hpp" + +#include "third_party/fmt/core.h" + +#include +#include +#include +#include +#include +#include +#include + +using nonstd::nullopt; +using nonstd::optional; + +namespace { + +enum class ConfigItem { + absolute_paths_in_stderr, + base_dir, + cache_dir, + compiler, + compiler_check, + compression, + compression_level, + cpp_extension, + debug, + depend_mode, + direct_mode, + disable, + extra_files_to_hash, + file_clone, + hard_link, + hash_dir, + ignore_headers_in_manifest, + ignore_options, + inode_cache, + keep_comments_cpp, + limit_multiple, + log_file, + max_files, + max_size, + path, + pch_external_checksum, + prefix_command, + prefix_command_cpp, + read_only, + read_only_direct, + recache, + run_second_cpp, + sloppiness, + stats, + temporary_dir, + umask, +}; + +const std::unordered_map k_config_key_table = { + {"absolute_paths_in_stderr", ConfigItem::absolute_paths_in_stderr}, + {"base_dir", ConfigItem::base_dir}, + {"cache_dir", ConfigItem::cache_dir}, + {"compiler", ConfigItem::compiler}, + {"compiler_check", ConfigItem::compiler_check}, + {"compression", ConfigItem::compression}, + {"compression_level", ConfigItem::compression_level}, + {"cpp_extension", ConfigItem::cpp_extension}, + {"debug", ConfigItem::debug}, + {"depend_mode", ConfigItem::depend_mode}, + {"direct_mode", ConfigItem::direct_mode}, + {"disable", ConfigItem::disable}, + {"extra_files_to_hash", ConfigItem::extra_files_to_hash}, + {"file_clone", ConfigItem::file_clone}, + {"hard_link", ConfigItem::hard_link}, + {"hash_dir", ConfigItem::hash_dir}, + {"ignore_headers_in_manifest", ConfigItem::ignore_headers_in_manifest}, + {"ignore_options", ConfigItem::ignore_options}, + {"inode_cache", ConfigItem::inode_cache}, + {"keep_comments_cpp", ConfigItem::keep_comments_cpp}, + {"limit_multiple", ConfigItem::limit_multiple}, + {"log_file", ConfigItem::log_file}, + {"max_files", ConfigItem::max_files}, + {"max_size", ConfigItem::max_size}, + {"path", ConfigItem::path}, + {"pch_external_checksum", ConfigItem::pch_external_checksum}, + {"prefix_command", ConfigItem::prefix_command}, + {"prefix_command_cpp", ConfigItem::prefix_command_cpp}, + {"read_only", ConfigItem::read_only}, + {"read_only_direct", ConfigItem::read_only_direct}, + {"recache", ConfigItem::recache}, + {"run_second_cpp", ConfigItem::run_second_cpp}, + {"sloppiness", ConfigItem::sloppiness}, + {"stats", ConfigItem::stats}, + {"temporary_dir", ConfigItem::temporary_dir}, + {"umask", ConfigItem::umask}, +}; + +const std::unordered_map k_env_variable_table = { + {"ABSSTDERR", "absolute_paths_in_stderr"}, + {"BASEDIR", "base_dir"}, + {"CC", "compiler"}, // Alias for CCACHE_COMPILER + {"COMMENTS", "keep_comments_cpp"}, + {"COMPILER", "compiler"}, + {"COMPILERCHECK", "compiler_check"}, + {"COMPRESS", "compression"}, + {"COMPRESSLEVEL", "compression_level"}, + {"CPP2", "run_second_cpp"}, + {"DEBUG", "debug"}, + {"DEPEND", "depend_mode"}, + {"DIR", "cache_dir"}, + {"DIRECT", "direct_mode"}, + {"DISABLE", "disable"}, + {"EXTENSION", "cpp_extension"}, + {"EXTRAFILES", "extra_files_to_hash"}, + {"FILECLONE", "file_clone"}, + {"HARDLINK", "hard_link"}, + {"HASHDIR", "hash_dir"}, + {"IGNOREHEADERS", "ignore_headers_in_manifest"}, + {"IGNOREOPTIONS", "ignore_options"}, + {"INODECACHE", "inode_cache"}, + {"LIMIT_MULTIPLE", "limit_multiple"}, + {"LOGFILE", "log_file"}, + {"MAXFILES", "max_files"}, + {"MAXSIZE", "max_size"}, + {"PATH", "path"}, + {"PCH_EXTSUM", "pch_external_checksum"}, + {"PREFIX", "prefix_command"}, + {"PREFIX_CPP", "prefix_command_cpp"}, + {"READONLY", "read_only"}, + {"READONLY_DIRECT", "read_only_direct"}, + {"RECACHE", "recache"}, + {"SLOPPINESS", "sloppiness"}, + {"STATS", "stats"}, + {"TEMPDIR", "temporary_dir"}, + {"UMASK", "umask"}, +}; + +bool +parse_bool(const std::string& value, + const optional env_var_key, + bool negate) +{ + if (env_var_key) { + // Special rule for boolean settings from the environment: "0", "false", + // "disable" and "no" (case insensitive) are invalid, and all other values + // mean true. + // + // Previously any value meant true, but this was surprising to users, who + // might do something like CCACHE_DISABLE=0 and expect ccache to be + // enabled. + std::string lower_value = Util::to_lowercase(value); + if (value == "0" || lower_value == "false" || lower_value == "disable" + || lower_value == "no") { + throw Error( + "invalid boolean environment variable value \"{}\" (did you mean to" + " set \"CCACHE_{}{}=true\"?)", + value, + negate ? "" : "NO", + *env_var_key); + } + return !negate; + } else if (value == "true") { + return true; + } else if (value == "false") { + return false; + } else { + throw Error("not a boolean value: \"{}\"", value); + } +} + +std::string +format_bool(bool value) +{ + return value ? "true" : "false"; +} + +double +parse_double(const std::string& value) +{ + size_t end; + double result; + try { + result = std::stod(value, &end); + } catch (std::exception& e) { + throw Error(e.what()); + } + if (end != value.size()) { + throw Error("invalid floating point: \"{}\"", value); + } + return result; +} + +std::string +format_cache_size(uint64_t value) +{ + return Util::format_parsable_size_with_suffix(value); +} + +uint32_t +parse_sloppiness(const std::string& value) +{ + size_t start = 0; + size_t end = 0; + uint32_t result = 0; + while (end != std::string::npos) { + end = value.find_first_of(", ", start); + std::string token = + Util::strip_whitespace(value.substr(start, end - start)); + if (token == "file_stat_matches") { + result |= SLOPPY_FILE_STAT_MATCHES; + } else if (token == "file_stat_matches_ctime") { + result |= SLOPPY_FILE_STAT_MATCHES_CTIME; + } else if (token == "include_file_ctime") { + result |= SLOPPY_INCLUDE_FILE_CTIME; + } else if (token == "include_file_mtime") { + result |= SLOPPY_INCLUDE_FILE_MTIME; + } else if (token == "system_headers" || token == "no_system_headers") { + result |= SLOPPY_SYSTEM_HEADERS; + } else if (token == "pch_defines") { + result |= SLOPPY_PCH_DEFINES; + } else if (token == "time_macros") { + result |= SLOPPY_TIME_MACROS; + } else if (token == "clang_index_store") { + result |= SLOPPY_CLANG_INDEX_STORE; + } else if (token == "locale") { + result |= SLOPPY_LOCALE; + } else if (token == "modules") { + result |= SLOPPY_MODULES; + } // else: ignore unknown value for forward compatibility + start = value.find_first_not_of(", ", end); + } + return result; +} + +std::string +format_sloppiness(uint32_t sloppiness) +{ + std::string result; + if (sloppiness & SLOPPY_INCLUDE_FILE_MTIME) { + result += "include_file_mtime, "; + } + if (sloppiness & SLOPPY_INCLUDE_FILE_CTIME) { + result += "include_file_ctime, "; + } + if (sloppiness & SLOPPY_TIME_MACROS) { + result += "time_macros, "; + } + if (sloppiness & SLOPPY_PCH_DEFINES) { + result += "pch_defines, "; + } + if (sloppiness & SLOPPY_FILE_STAT_MATCHES) { + result += "file_stat_matches, "; + } + if (sloppiness & SLOPPY_FILE_STAT_MATCHES_CTIME) { + result += "file_stat_matches_ctime, "; + } + if (sloppiness & SLOPPY_SYSTEM_HEADERS) { + result += "system_headers, "; + } + if (sloppiness & SLOPPY_CLANG_INDEX_STORE) { + result += "clang_index_store, "; + } + if (sloppiness & SLOPPY_LOCALE) { + result += "locale, "; + } + if (sloppiness & SLOPPY_MODULES) { + result += "modules, "; + } + if (!result.empty()) { + // Strip last ", ". + result.resize(result.size() - 2); + } + return result; +} + +uint32_t +parse_umask(const std::string& value) +{ + if (value.empty()) { + return std::numeric_limits::max(); + } + + size_t end; + uint32_t result = std::stoul(value, &end, 8); + if (end != value.size()) { + throw Error("not an octal integer: \"{}\"", value); + } + return result; +} + +std::string +format_umask(uint32_t umask) +{ + if (umask == std::numeric_limits::max()) { + return {}; + } else { + return fmt::format("{:03o}", umask); + } +} + +void +verify_absolute_path(const std::string& value) +{ + if (!Util::is_absolute_path(value)) { + throw Error("not an absolute path: \"{}\"", value); + } +} + +bool +parse_line(const std::string& line, + std::string* key, + std::string* value, + std::string* error_message) +{ + std::string stripped_line = Util::strip_whitespace(line); + if (stripped_line.empty() || stripped_line[0] == '#') { + return true; + } + size_t equal_pos = stripped_line.find('='); + if (equal_pos == std::string::npos) { + *error_message = "missing equal sign"; + return false; + } + *key = stripped_line.substr(0, equal_pos); + *value = stripped_line.substr(equal_pos + 1); + *key = Util::strip_whitespace(*key); + *value = Util::strip_whitespace(*value); + return true; +} + +// `line` is the full configuration line excluding newline. `key` will be empty +// for comments and blank lines. `value` does not include newline. +using ConfigLineHandler = std::function; + +// Call `config_line_handler` for each line in `path`. +bool +parse_config_file(const std::string& path, + const ConfigLineHandler& config_line_handler) +{ + std::ifstream file(path); + if (!file) { + return false; + } + + std::string line; + + size_t line_number = 0; + while (std::getline(file, line)) { + ++line_number; + + try { + std::string key; + std::string value; + std::string error_message; + if (!parse_line(line, &key, &value, &error_message)) { + throw Error(error_message); + } + config_line_handler(line, key, value); + } catch (const Error& e) { + throw Error("{}:{}: {}", path, line_number, e.what()); + } + } + return true; +} + +} // namespace + +const std::string& +Config::primary_config_path() const +{ + return m_primary_config_path; +} + +const std::string& +Config::secondary_config_path() const +{ + return m_secondary_config_path; +} + +void +Config::set_primary_config_path(std::string path) +{ + m_primary_config_path = std::move(path); +} + +void +Config::set_secondary_config_path(std::string path) +{ + m_secondary_config_path = std::move(path); +} + +bool +Config::update_from_file(const std::string& path) +{ + return parse_config_file(path, + [&](const std::string& /*line*/, + const std::string& key, + const std::string& value) { + if (!key.empty()) { + set_item(key, value, nullopt, false, path); + } + }); +} + +void +Config::update_from_environment() +{ + for (char** env = environ; *env; ++env) { + std::string setting = *env; + const std::string prefix = "CCACHE_"; + if (!Util::starts_with(setting, prefix)) { + continue; + } + size_t equal_pos = setting.find('='); + if (equal_pos == std::string::npos) { + continue; + } + + std::string key = setting.substr(prefix.size(), equal_pos - prefix.size()); + std::string value = setting.substr(equal_pos + 1); + bool negate = Util::starts_with(key, "NO"); + if (negate) { + key = key.substr(2); + } + + auto it = k_env_variable_table.find(key); + if (it == k_env_variable_table.end()) { + // Ignore unknown keys. + continue; + } + const auto& config_key = it->second; + + try { + set_item(config_key, value, key, negate, "environment"); + } catch (const Error& e) { + throw Error("CCACHE_{}{}: {}", negate ? "NO" : "", key, e.what()); + } + } +} + +std::string +Config::get_string_value(const std::string& key) const +{ + auto it = k_config_key_table.find(key); + if (it == k_config_key_table.end()) { + throw Error("unknown configuration option \"{}\"", key); + } + + switch (it->second) { + case ConfigItem::absolute_paths_in_stderr: + return format_bool(m_absolute_paths_in_stderr); + + case ConfigItem::base_dir: + return m_base_dir; + + case ConfigItem::cache_dir: + return m_cache_dir; + + case ConfigItem::compiler: + return m_compiler; + + case ConfigItem::compiler_check: + return m_compiler_check; + + case ConfigItem::compression: + return format_bool(m_compression); + + case ConfigItem::compression_level: + return fmt::format("{}", m_compression_level); + + case ConfigItem::cpp_extension: + return m_cpp_extension; + + case ConfigItem::debug: + return format_bool(m_debug); + + case ConfigItem::depend_mode: + return format_bool(m_depend_mode); + + case ConfigItem::direct_mode: + return format_bool(m_direct_mode); + + case ConfigItem::disable: + return format_bool(m_disable); + + case ConfigItem::extra_files_to_hash: + return m_extra_files_to_hash; + + case ConfigItem::file_clone: + return format_bool(m_file_clone); + + case ConfigItem::hard_link: + return format_bool(m_hard_link); + + case ConfigItem::hash_dir: + return format_bool(m_hash_dir); + + case ConfigItem::ignore_headers_in_manifest: + return m_ignore_headers_in_manifest; + + case ConfigItem::ignore_options: + return m_ignore_options; + + case ConfigItem::inode_cache: + return format_bool(m_inode_cache); + + case ConfigItem::keep_comments_cpp: + return format_bool(m_keep_comments_cpp); + + case ConfigItem::limit_multiple: + return fmt::format("{:.1f}", m_limit_multiple); + + case ConfigItem::log_file: + return m_log_file; + + case ConfigItem::max_files: + return fmt::format("{}", m_max_files); + + case ConfigItem::max_size: + return format_cache_size(m_max_size); + + case ConfigItem::path: + return m_path; + + case ConfigItem::pch_external_checksum: + return format_bool(m_pch_external_checksum); + + case ConfigItem::prefix_command: + return m_prefix_command; + + case ConfigItem::prefix_command_cpp: + return m_prefix_command_cpp; + + case ConfigItem::read_only: + return format_bool(m_read_only); + + case ConfigItem::read_only_direct: + return format_bool(m_read_only_direct); + + case ConfigItem::recache: + return format_bool(m_recache); + + case ConfigItem::run_second_cpp: + return format_bool(m_run_second_cpp); + + case ConfigItem::sloppiness: + return format_sloppiness(m_sloppiness); + + case ConfigItem::stats: + return format_bool(m_stats); + + case ConfigItem::temporary_dir: + return m_temporary_dir; + + case ConfigItem::umask: + return format_umask(m_umask); + } + + ASSERT(false); // Never reached +} + +void +Config::set_value_in_file(const std::string& path, + const std::string& key, + const std::string& value) +{ + if (k_config_key_table.find(key) == k_config_key_table.end()) { + throw Error("unknown configuration option \"{}\"", key); + } + + // Verify that the value is valid; set_item will throw if not. + Config dummy_config; + dummy_config.set_item(key, value, nullopt, false, ""); + + const auto resolved_path = Util::real_path(path); + const auto st = Stat::stat(resolved_path); + if (!st) { + Util::ensure_dir_exists(Util::dir_name(resolved_path)); + try { + Util::write_file(resolved_path, ""); + } catch (const Error& e) { + throw Error("failed to write to {}: {}", resolved_path, e.what()); + } + } + + AtomicFile output(resolved_path, AtomicFile::Mode::text); + bool found = false; + + if (!parse_config_file(path, + [&](const std::string& c_line, + const std::string& c_key, + const std::string& /*c_value*/) { + if (c_key == key) { + output.write(fmt::format("{} = {}\n", key, value)); + found = true; + } else { + output.write(fmt::format("{}\n", c_line)); + } + })) { + throw Error("failed to open {}: {}", path, strerror(errno)); + } + + if (!found) { + output.write(fmt::format("{} = {}\n", key, value)); + } + + output.commit(); +} + +void +Config::visit_items(const ItemVisitor& item_visitor) const +{ + std::vector keys; + keys.reserve(k_config_key_table.size()); + + for (const auto& item : k_config_key_table) { + keys.emplace_back(item.first); + } + std::sort(keys.begin(), keys.end()); + for (const auto& key : keys) { + auto it = m_origins.find(key); + std::string origin = it != m_origins.end() ? it->second : "default"; + item_visitor(key, get_string_value(key), origin); + } +} + +void +Config::set_item(const std::string& key, + const std::string& value, + const optional& env_var_key, + bool negate, + const std::string& origin) +{ + auto it = k_config_key_table.find(key); + if (it == k_config_key_table.end()) { + // Ignore unknown keys. + return; + } + + switch (it->second) { + case ConfigItem::absolute_paths_in_stderr: + m_absolute_paths_in_stderr = parse_bool(value, env_var_key, negate); + break; + + case ConfigItem::base_dir: + m_base_dir = Util::expand_environment_variables(value); + if (!m_base_dir.empty()) { // The empty string means "disable" + verify_absolute_path(m_base_dir); + m_base_dir = Util::normalize_absolute_path(m_base_dir); + } + break; + + case ConfigItem::cache_dir: + set_cache_dir(Util::expand_environment_variables(value)); + break; + + case ConfigItem::compiler: + m_compiler = value; + break; + + case ConfigItem::compiler_check: + m_compiler_check = value; + break; + + case ConfigItem::compression: + m_compression = parse_bool(value, env_var_key, negate); + break; + + case ConfigItem::compression_level: { + m_compression_level = + Util::parse_signed(value, INT8_MIN, INT8_MAX, "compression_level"); + break; + } + + case ConfigItem::cpp_extension: + m_cpp_extension = value; + break; + + case ConfigItem::debug: + m_debug = parse_bool(value, env_var_key, negate); + break; + + case ConfigItem::depend_mode: + m_depend_mode = parse_bool(value, env_var_key, negate); + break; + + case ConfigItem::direct_mode: + m_direct_mode = parse_bool(value, env_var_key, negate); + break; + + case ConfigItem::disable: + m_disable = parse_bool(value, env_var_key, negate); + break; + + case ConfigItem::extra_files_to_hash: + m_extra_files_to_hash = Util::expand_environment_variables(value); + break; + + case ConfigItem::file_clone: + m_file_clone = parse_bool(value, env_var_key, negate); + break; + + case ConfigItem::hard_link: + m_hard_link = parse_bool(value, env_var_key, negate); + break; + + case ConfigItem::hash_dir: + m_hash_dir = parse_bool(value, env_var_key, negate); + break; + + case ConfigItem::ignore_headers_in_manifest: + m_ignore_headers_in_manifest = Util::expand_environment_variables(value); + break; + + case ConfigItem::ignore_options: + m_ignore_options = Util::expand_environment_variables(value); + break; + + case ConfigItem::inode_cache: + m_inode_cache = parse_bool(value, env_var_key, negate); + break; + + case ConfigItem::keep_comments_cpp: + m_keep_comments_cpp = parse_bool(value, env_var_key, negate); + break; + + case ConfigItem::limit_multiple: + m_limit_multiple = parse_double(value); + break; + + case ConfigItem::log_file: + m_log_file = Util::expand_environment_variables(value); + break; + + case ConfigItem::max_files: + m_max_files = Util::parse_unsigned(value, nullopt, nullopt, "max_files"); + break; + + case ConfigItem::max_size: + m_max_size = Util::parse_size(value); + break; + + case ConfigItem::path: + m_path = Util::expand_environment_variables(value); + break; + + case ConfigItem::pch_external_checksum: + m_pch_external_checksum = parse_bool(value, env_var_key, negate); + break; + + case ConfigItem::prefix_command: + m_prefix_command = Util::expand_environment_variables(value); + break; + + case ConfigItem::prefix_command_cpp: + m_prefix_command_cpp = Util::expand_environment_variables(value); + break; + + case ConfigItem::read_only: + m_read_only = parse_bool(value, env_var_key, negate); + break; + + case ConfigItem::read_only_direct: + m_read_only_direct = parse_bool(value, env_var_key, negate); + break; + + case ConfigItem::recache: + m_recache = parse_bool(value, env_var_key, negate); + break; + + case ConfigItem::run_second_cpp: + m_run_second_cpp = parse_bool(value, env_var_key, negate); + break; + + case ConfigItem::sloppiness: + m_sloppiness = parse_sloppiness(value); + break; + + case ConfigItem::stats: + m_stats = parse_bool(value, env_var_key, negate); + break; + + case ConfigItem::temporary_dir: + m_temporary_dir = Util::expand_environment_variables(value); + m_temporary_dir_configured_explicitly = true; + break; + + case ConfigItem::umask: + m_umask = parse_umask(value); + break; + } + + m_origins.emplace(key, origin); +} + +void +Config::check_key_tables_consistency() +{ + for (const auto& item : k_env_variable_table) { + if (k_config_key_table.find(item.second) == k_config_key_table.end()) { + throw Error( + "env var {} mapped to {} which is missing from k_config_key_table", + item.first, + item.second); + } + } +} + +std::string +Config::default_temporary_dir(const std::string& cache_dir) +{ +#ifdef HAVE_GETEUID + std::string user_tmp_dir = fmt::format("/run/user/{}", geteuid()); + if (Stat::stat(user_tmp_dir).is_directory()) { + return user_tmp_dir + "/ccache-tmp"; + } +#endif + return cache_dir + "/tmp"; +} diff --git a/src/Config.hpp b/src/Config.hpp new file mode 100644 index 0000000..bc8bb4f --- /dev/null +++ b/src/Config.hpp @@ -0,0 +1,477 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "NonCopyable.hpp" +#include "Util.hpp" + +#include "third_party/nonstd/optional.hpp" + +#include +#include +#include +#include + +class Config; + +class Config +{ +public: + Config() = default; + Config(Config&) = default; + Config& operator=(const Config&) = default; + + bool absolute_paths_in_stderr() const; + const std::string& base_dir() const; + const std::string& cache_dir() const; + const std::string& compiler() const; + const std::string& compiler_check() const; + bool compression() const; + int8_t compression_level() const; + const std::string& cpp_extension() const; + bool debug() const; + bool depend_mode() const; + bool direct_mode() const; + bool disable() const; + const std::string& extra_files_to_hash() const; + bool file_clone() const; + bool hard_link() const; + bool hash_dir() const; + const std::string& ignore_headers_in_manifest() const; + const std::string& ignore_options() const; + bool inode_cache() const; + bool keep_comments_cpp() const; + double limit_multiple() const; + const std::string& log_file() const; + uint64_t max_files() const; + uint64_t max_size() const; + const std::string& path() const; + bool pch_external_checksum() const; + const std::string& prefix_command() const; + const std::string& prefix_command_cpp() const; + bool read_only() const; + bool read_only_direct() const; + bool recache() const; + bool run_second_cpp() const; + uint32_t sloppiness() const; + bool stats() const; + const std::string& temporary_dir() const; + uint32_t umask() const; + + void set_base_dir(const std::string& value); + void set_cache_dir(const std::string& value); + void set_cpp_extension(const std::string& value); + void set_compiler(const std::string& value); + void set_depend_mode(bool value); + void set_debug(bool value); + void set_direct_mode(bool value); + void set_ignore_options(const std::string& value); + void set_inode_cache(bool value); + void set_limit_multiple(double value); + void set_max_files(uint64_t value); + void set_max_size(uint64_t value); + void set_run_second_cpp(bool value); + + // Where to write configuration changes. + const std::string& primary_config_path() const; + // Secondary, read-only configuration file (if any). + const std::string& secondary_config_path() const; + + void set_primary_config_path(std::string path); + void set_secondary_config_path(std::string path); + + using ItemVisitor = std::function; + + // Set config values from a configuration file. + // + // Returns false if the file can't be opened, otherwise true. Throws Error on + // invalid configuration values. + bool update_from_file(const std::string& path); + + // Set config values from environment variables. + // + // Throws Error on invalid configuration values. + void update_from_environment(); + + // Get a config value in string form given a key. + std::string get_string_value(const std::string& key) const; + + void visit_items(const ItemVisitor& item_visitor) const; + + static void set_value_in_file(const std::string& path, + const std::string& key, + const std::string& value); + + // Called from unit tests. + static void check_key_tables_consistency(); + +private: + std::string m_primary_config_path; + std::string m_secondary_config_path; + + bool m_absolute_paths_in_stderr = false; + std::string m_base_dir = ""; + std::string m_cache_dir; + std::string m_compiler = ""; + std::string m_compiler_check = "mtime"; + bool m_compression = true; + int8_t m_compression_level = 0; // Use default level + std::string m_cpp_extension = ""; + bool m_debug = false; + bool m_depend_mode = false; + bool m_direct_mode = true; + bool m_disable = false; + std::string m_extra_files_to_hash = ""; + bool m_file_clone = false; + bool m_hard_link = false; + bool m_hash_dir = true; + std::string m_ignore_headers_in_manifest = ""; + std::string m_ignore_options = ""; + bool m_inode_cache = false; + bool m_keep_comments_cpp = false; + double m_limit_multiple = 0.8; + std::string m_log_file = ""; + uint64_t m_max_files = 0; + uint64_t m_max_size = 5ULL * 1000 * 1000 * 1000; + std::string m_path = ""; + bool m_pch_external_checksum = false; + std::string m_prefix_command = ""; + std::string m_prefix_command_cpp = ""; + bool m_read_only = false; + bool m_read_only_direct = false; + bool m_recache = false; + bool m_run_second_cpp = true; + uint32_t m_sloppiness = 0; + bool m_stats = true; + std::string m_temporary_dir; + uint32_t m_umask = std::numeric_limits::max(); // Don't set umask + + bool m_temporary_dir_configured_explicitly = false; + + std::unordered_map m_origins; + + void set_item(const std::string& key, + const std::string& value, + const nonstd::optional& env_var_key, + bool negate, + const std::string& origin); + + static std::string default_temporary_dir(const std::string& cache_dir); +}; + +inline bool +Config::absolute_paths_in_stderr() const +{ + return m_absolute_paths_in_stderr; +} + +inline const std::string& +Config::base_dir() const +{ + return m_base_dir; +} + +inline const std::string& +Config::cache_dir() const +{ + return m_cache_dir; +} + +inline const std::string& +Config::compiler() const +{ + return m_compiler; +} + +inline const std::string& +Config::compiler_check() const +{ + return m_compiler_check; +} + +inline bool +Config::compression() const +{ + return m_compression; +} + +inline int8_t +Config::compression_level() const +{ + return m_compression_level; +} + +inline const std::string& +Config::cpp_extension() const +{ + return m_cpp_extension; +} + +inline bool +Config::debug() const +{ + return m_debug; +} + +inline bool +Config::depend_mode() const +{ + return m_depend_mode; +} + +inline bool +Config::direct_mode() const +{ + return m_direct_mode; +} + +inline bool +Config::disable() const +{ + return m_disable; +} + +inline const std::string& +Config::extra_files_to_hash() const +{ + return m_extra_files_to_hash; +} + +inline bool +Config::file_clone() const +{ + return m_file_clone; +} + +inline bool +Config::hard_link() const +{ + return m_hard_link; +} + +inline bool +Config::hash_dir() const +{ + return m_hash_dir; +} + +inline const std::string& +Config::ignore_headers_in_manifest() const +{ + return m_ignore_headers_in_manifest; +} + +inline const std::string& +Config::ignore_options() const +{ + return m_ignore_options; +} + +inline bool +Config::inode_cache() const +{ + return m_inode_cache; +} + +inline bool +Config::keep_comments_cpp() const +{ + return m_keep_comments_cpp; +} + +inline double +Config::limit_multiple() const +{ + return m_limit_multiple; +} + +inline const std::string& +Config::log_file() const +{ + return m_log_file; +} + +inline uint64_t +Config::max_files() const +{ + return m_max_files; +} + +inline uint64_t +Config::max_size() const +{ + return m_max_size; +} + +inline const std::string& +Config::path() const +{ + return m_path; +} + +inline bool +Config::pch_external_checksum() const +{ + return m_pch_external_checksum; +} + +inline const std::string& +Config::prefix_command() const +{ + return m_prefix_command; +} + +inline const std::string& +Config::prefix_command_cpp() const +{ + return m_prefix_command_cpp; +} + +inline bool +Config::read_only() const +{ + return m_read_only; +} + +inline bool +Config::read_only_direct() const +{ + return m_read_only_direct; +} + +inline bool +Config::recache() const +{ + return m_recache; +} + +inline bool +Config::run_second_cpp() const +{ + return m_run_second_cpp; +} + +inline uint32_t +Config::sloppiness() const +{ + return m_sloppiness; +} + +inline bool +Config::stats() const +{ + return m_stats; +} + +inline const std::string& +Config::temporary_dir() const +{ + return m_temporary_dir; +} + +inline uint32_t +Config::umask() const +{ + return m_umask; +} + +inline void +Config::set_base_dir(const std::string& value) +{ + m_base_dir = value; +} + +inline void +Config::set_cache_dir(const std::string& value) +{ + m_cache_dir = value; + if (!m_temporary_dir_configured_explicitly) { + m_temporary_dir = default_temporary_dir(m_cache_dir); + } +} + +inline void +Config::set_cpp_extension(const std::string& value) +{ + m_cpp_extension = value; +} + +inline void +Config::set_compiler(const std::string& value) +{ + m_compiler = value; +} + +inline void +Config::set_depend_mode(bool value) +{ + m_depend_mode = value; +} + +inline void +Config::set_debug(bool value) +{ + m_debug = value; +} + +inline void +Config::set_direct_mode(bool value) +{ + m_direct_mode = value; +} + +inline void +Config::set_ignore_options(const std::string& value) +{ + m_ignore_options = value; +} + +inline void +Config::set_inode_cache(bool value) +{ + m_inode_cache = value; +} + +inline void +Config::set_limit_multiple(double value) +{ + m_limit_multiple = value; +} + +inline void +Config::set_max_files(uint64_t value) +{ + m_max_files = value; +} + +inline void +Config::set_max_size(uint64_t value) +{ + m_max_size = value; +} + +inline void +Config::set_run_second_cpp(bool value) +{ + m_run_second_cpp = value; +} diff --git a/src/Context.cpp b/src/Context.cpp new file mode 100644 index 0000000..bb63946 --- /dev/null +++ b/src/Context.cpp @@ -0,0 +1,90 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "Context.hpp" + +#include "Counters.hpp" +#include "Logging.hpp" +#include "SignalHandler.hpp" +#include "Util.hpp" +#include "hashutil.hpp" + +#include +#include +#include + +using Logging::log; +using nonstd::string_view; + +Context::Context() + : actual_cwd(Util::get_actual_cwd()), + apparent_cwd(Util::get_apparent_cwd(actual_cwd)) +#ifdef INODE_CACHE_SUPPORTED + , + inode_cache(config) +#endif +{ +} + +Context::~Context() +{ + unlink_pending_tmp_files(); +} + +void +Context::register_pending_tmp_file(const std::string& path) +{ + SignalHandlerBlocker signal_handler_blocker; + + m_pending_tmp_files.push_back(path); +} + +void +Context::unlink_pending_tmp_files_signal_safe() +{ + for (const std::string& path : m_pending_tmp_files) { + // Don't call Util::unlink_tmp since its log calls aren't signal safe. + unlink(path.c_str()); + } + // Don't clear m_pending_tmp_files since this method must be signal safe. +} + +void +Context::unlink_pending_tmp_files() +{ + SignalHandlerBlocker signal_handler_blocker; + + for (const std::string& path : m_pending_tmp_files) { + Util::unlink_tmp(path, Util::UnlinkLog::ignore_failure); + } + m_pending_tmp_files.clear(); +} + +void +Context::set_ignore_options(const std::vector& options) +{ + for (const std::string& option : options) { + size_t n_wildcards = std::count(option.cbegin(), option.cend(), '*'); + if (n_wildcards == 0 || (n_wildcards == 1 && option.back() == '*')) { + m_ignore_options.push_back(option); + } else { + log("Skipping malformed ignore_options item: {}", option); + continue; + } + } +} diff --git a/src/Context.hpp b/src/Context.hpp new file mode 100644 index 0000000..239081f --- /dev/null +++ b/src/Context.hpp @@ -0,0 +1,221 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Args.hpp" +#include "ArgsInfo.hpp" +#include "Config.hpp" +#include "Digest.hpp" +#include "File.hpp" +#include "MiniTrace.hpp" +#include "NonCopyable.hpp" +#include "ccache.hpp" + +#ifdef INODE_CACHE_SUPPORTED +# include "InodeCache.hpp" +#endif + +#include "third_party/nonstd/optional.hpp" +#include "third_party/nonstd/string_view.hpp" + +#include +#include +#include + +class SignalHandler; + +class Context : NonCopyable +{ +public: + Context(); + ~Context(); + + ArgsInfo args_info; + Config config; + + // Current working directory as returned by getcwd(3). + std::string actual_cwd; + + // Current working directory according to $PWD (falling back to getcwd(3)). + std::string apparent_cwd; + + // The original argument list. + Args orig_args; + + // Name (represented as a hash) of the file containing the manifest for the + // cached result. + const nonstd::optional& manifest_name() const; + + // Full path to the file containing the manifest (cachedir/a/b/cdef[...]M), if + // any. + const nonstd::optional& manifest_path() const; + + // Name (represented as a hash) of the file containing the cached result. + const nonstd::optional& result_name() const; + + // Full path to the file containing the result (cachedir/a/b/cdef[...]R). + const nonstd::optional& result_path() const; + + // Time of compilation. Used to see if include files have changed after + // compilation. + time_t time_of_compilation = 0; + + // Files included by the preprocessor and their hashes. + std::unordered_map included_files; + + // Uses absolute path for some include files. + bool has_absolute_include_headers = false; + + // Have we tried and failed to get colored diagnostics? + bool diagnostics_color_failed = false; + + // The name of the temporary preprocessed file. + std::string i_tmpfile; + + // The name of the cpp stderr file. + std::string cpp_stderr; + + // Compiler guessing is currently only based on the compiler name, so nothing + // should hard-depend on it if possible. + GuessedCompiler guessed_compiler = GuessedCompiler::unknown; + + // The .gch/.pch/.pth file used for compilation. + std::string included_pch_file; + + // Headers (or directories with headers) to ignore in manifest mode. + std::vector ignore_header_paths; + +#ifdef INODE_CACHE_SUPPORTED + // InodeCache that caches source file hashes when enabled. + mutable InodeCache inode_cache; +#endif + + // Statistics updates which get written into the statistics file belonging to + // the result. + Counters counter_updates; + + // Statistics updates which get written into the statistics file belonging to + // the manifest. + Counters manifest_counter_updates; + + // PID of currently executing compiler that we have started, if any. 0 means + // no ongoing compilation. + pid_t compiler_pid = 0; + + // Files used by the hash debugging functionality. + std::vector hash_debug_files; + + // Options to ignore for the hash. + const std::vector& ignore_options() const; + void set_ignore_options(const std::vector& options); + + // Original umask before applying the `umask`/`CCACHE_UMASK` configuration, or + // `nullopt` if there is no such configuration. + nonstd::optional original_umask; + +#ifdef MTR_ENABLED + // Internal tracing. + std::unique_ptr mini_trace; +#endif + + void set_manifest_name(const Digest& name); + void set_manifest_path(const std::string& path); + void set_result_name(const Digest& name); + void set_result_path(const std::string& path); + + // Register a temporary file to remove at program exit. + void register_pending_tmp_file(const std::string& path); + +private: + nonstd::optional m_manifest_name; + nonstd::optional m_manifest_path; + + nonstd::optional m_result_name; + nonstd::optional m_result_path; + + // Options to ignore for the hash. + std::vector m_ignore_options; + + // [Start of variables touched by the signal handler] + + // Temporary files to remove at program exit. + std::vector m_pending_tmp_files; + + // [End of variables touched by the signal handler] + + friend SignalHandler; + void unlink_pending_tmp_files(); + void unlink_pending_tmp_files_signal_safe(); // called from signal handler +}; + +inline const nonstd::optional& +Context::manifest_name() const +{ + return m_manifest_name; +} + +inline const nonstd::optional& +Context::manifest_path() const +{ + return m_manifest_path; +} + +inline const nonstd::optional& +Context::result_name() const +{ + return m_result_name; +} + +inline const nonstd::optional& +Context::result_path() const +{ + return m_result_path; +} + +inline const std::vector& +Context::ignore_options() const +{ + return m_ignore_options; +} + +inline void +Context::set_manifest_name(const Digest& name) +{ + m_manifest_name = name; +} + +inline void +Context::set_manifest_path(const std::string& path) +{ + m_manifest_path = path; +} + +inline void +Context::set_result_name(const Digest& name) +{ + m_result_name = name; +} + +inline void +Context::set_result_path(const std::string& path) +{ + m_result_path = path; +} diff --git a/src/Counters.cpp b/src/Counters.cpp new file mode 100644 index 0000000..2e1b0e2 --- /dev/null +++ b/src/Counters.cpp @@ -0,0 +1,96 @@ +// Copyright (C) 2010-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "Counters.hpp" + +#include "Statistics.hpp" +#include "assertions.hpp" + +#include + +Counters::Counters() : m_counters(static_cast(Statistic::END)) +{ +} + +uint64_t +Counters::get(Statistic statistic) const +{ + const auto index = static_cast(statistic); + ASSERT(index < static_cast(Statistic::END)); + return index < m_counters.size() ? m_counters[index] : 0; +} + +void +Counters::set(Statistic statistic, uint64_t value) +{ + const auto index = static_cast(statistic); + ASSERT(index < static_cast(Statistic::END)); + m_counters[index] = value; +} + +uint64_t +Counters::get_raw(size_t index) const +{ + ASSERT(index < size()); + return m_counters[index]; +} + +void +Counters::set_raw(size_t index, uint64_t value) +{ + if (index >= m_counters.size()) { + m_counters.resize(index + 1); + } + m_counters[index] = value; +} + +void +Counters::increment(Statistic statistic, int64_t value) +{ + const auto i = static_cast(statistic); + if (i >= m_counters.size()) { + m_counters.resize(i + 1); + } + auto& counter = m_counters[i]; + counter = + std::max(static_cast(0), static_cast(counter + value)); +} + +void +Counters::increment(const Counters& other) +{ + m_counters.resize(std::max(size(), other.size())); + for (size_t i = 0; i < other.size(); ++i) { + auto& counter = m_counters[i]; + counter = std::max(static_cast(0), + static_cast(counter + other.m_counters[i])); + } +} + +size_t +Counters::size() const +{ + return m_counters.size(); +} + +bool +Counters::all_zero() const +{ + return !std::any_of( + m_counters.begin(), m_counters.end(), [](unsigned v) { return v != 0; }); +} diff --git a/src/Counters.hpp b/src/Counters.hpp new file mode 100644 index 0000000..d19227b --- /dev/null +++ b/src/Counters.hpp @@ -0,0 +1,50 @@ +// Copyright (C) 2010-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include + +enum class Statistic; + +// A simple wrapper around a vector of integers used for the statistics +// counters. +class Counters +{ +public: + Counters(); + + uint64_t get(Statistic statistic) const; + void set(Statistic statistic, uint64_t value); + + uint64_t get_raw(size_t index) const; + void set_raw(size_t index, uint64_t value); + + void increment(Statistic statistic, int64_t value = 1); + void increment(const Counters& other); + + size_t size() const; + + // Return true if all counters are zero, false otherwise. + bool all_zero() const; + +private: + std::vector m_counters; +}; diff --git a/src/Decompressor.cpp b/src/Decompressor.cpp new file mode 100644 index 0000000..b53cd95 --- /dev/null +++ b/src/Decompressor.cpp @@ -0,0 +1,38 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "Decompressor.hpp" + +#include "NullDecompressor.hpp" +#include "StdMakeUnique.hpp" +#include "ZstdDecompressor.hpp" +#include "assertions.hpp" + +std::unique_ptr +Decompressor::create_from_type(Compression::Type type, FILE* stream) +{ + switch (type) { + case Compression::Type::none: + return std::make_unique(stream); + + case Compression::Type::zstd: + return std::make_unique(stream); + } + + ASSERT(false); +} diff --git a/src/Decompressor.hpp b/src/Decompressor.hpp new file mode 100644 index 0000000..5955889 --- /dev/null +++ b/src/Decompressor.hpp @@ -0,0 +1,54 @@ +// Copyright (C) 2019 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Compression.hpp" + +#include + +class Decompressor +{ +public: + virtual ~Decompressor() = default; + + // Create a decompressor for the specified type. + // + // Parameters: + // - type: The type. + // - stream: The stream to read from. + static std::unique_ptr create_from_type(Compression::Type type, + FILE* stream); + + // Read data into a buffer from the compressed stream. + // + // Parameters: + // - data: Buffer to write decompressed data to. + // - count: How many bytes to write. + // + // Throws Error on failure. + virtual void read(void* data, size_t count) = 0; + + // Finalize decompression. + // + // This method checks that the end state of the compressed stream is correct + // and throws Error if not. + virtual void finalize() = 0; +}; diff --git a/src/Digest.hpp b/src/Digest.hpp new file mode 100644 index 0000000..6a7b53e --- /dev/null +++ b/src/Digest.hpp @@ -0,0 +1,95 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Util.hpp" + +#include "third_party/fmt/core.h" + +#include + +// Digest represents the binary form of the final digest (AKA hash or checksum) +// produced by the hash algorithm. +class Digest +{ +public: + Digest() = default; + + // Access the raw byte buffer, which is `size()` bytes long. + const uint8_t* bytes() const; + uint8_t* bytes(); + + // Size of the digest in bytes. + constexpr static size_t size(); + + // Format the digest as hex string. + std::string to_string() const; + + bool operator==(const Digest& other) const; + bool operator!=(const Digest& other) const; + +private: + constexpr static size_t k_digest_size = 20; + uint8_t m_bytes[k_digest_size]; +}; + +inline const uint8_t* +Digest::bytes() const +{ + return m_bytes; +} + +inline uint8_t* +Digest::bytes() +{ + return m_bytes; +} + +inline constexpr size_t +Digest::size() +{ + return k_digest_size; +} + +inline std::string +Digest::to_string() const +{ + // The first two bytes are encoded as four lowercase base16 digits to maintain + // compatibility with the cleanup algorithm in older ccache versions and to + // allow for up to four uniform cache levels. The rest are encoded as + // lowercase base32hex digits without padding characters. + const size_t base16_bytes = 2; + return Util::format_base16(m_bytes, base16_bytes) + + Util::format_base32hex(m_bytes + base16_bytes, + size() - base16_bytes); +} + +inline bool +Digest::operator==(const Digest& other) const +{ + return memcmp(bytes(), other.bytes(), size()) == 0; +} + +inline bool +Digest::operator!=(const Digest& other) const +{ + return !(*this == other); +} diff --git a/src/Fd.hpp b/src/Fd.hpp new file mode 100644 index 0000000..80bc77e --- /dev/null +++ b/src/Fd.hpp @@ -0,0 +1,104 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "NonCopyable.hpp" +#include "assertions.hpp" + +class Fd : NonCopyable +{ +public: + Fd() = default; + explicit Fd(int fd); + Fd(Fd&& other_fd) noexcept; + ~Fd(); + + explicit operator bool() const; + + int get() const; + int operator*() const; + + Fd& operator=(Fd&& other_fd) noexcept; + + // Close wrapped fd before the lifetime of Fd has ended. + bool close(); + + // Release ownership of wrapped fd. + int release(); + +private: + int m_fd = -1; +}; + +inline Fd::Fd(int fd) : m_fd(fd) +{ +} + +inline Fd::Fd(Fd&& other_fd) noexcept : m_fd(other_fd.release()) +{ +} + +inline Fd::~Fd() +{ + close(); +} + +inline Fd::operator bool() const +{ + return m_fd != -1; +} + +inline int +Fd::get() const +{ + return m_fd; +} + +// clang-format off +inline int +Fd::operator*() const +// clang-format on +{ + ASSERT(m_fd != -1); + return m_fd; +} + +inline Fd& +Fd::operator=(Fd&& other_fd) noexcept +{ + close(); + m_fd = other_fd.release(); + return *this; +} + +inline bool +Fd::close() +{ + return m_fd != -1 && ::close(release()) == 0; +} + +inline int +Fd::release() +{ + int fd = m_fd; + m_fd = -1; + return fd; +} diff --git a/src/File.hpp b/src/File.hpp new file mode 100644 index 0000000..6f0dd21 --- /dev/null +++ b/src/File.hpp @@ -0,0 +1,104 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "NonCopyable.hpp" + +#include + +class File : public NonCopyable +{ +public: + File() = default; + File(const std::string& path, const char* mode); + File(File&& other) noexcept; + ~File(); + + File& operator=(File&& other) noexcept; + + void open(const std::string& path, const char* mode); + void close(); + + operator bool() const; + FILE* operator*() const; + FILE* get(); + +private: + FILE* m_file = nullptr; +}; + +inline File::File(const std::string& path, const char* mode) +{ + open(path, mode); +} + +inline File::File(File&& other) noexcept : m_file(other.m_file) +{ + other.m_file = nullptr; +} + +inline File::~File() +{ + close(); +} + +inline File& +File::operator=(File&& other) noexcept +{ + m_file = other.m_file; + other.m_file = nullptr; + return *this; +} + +inline void +File::open(const std::string& path, const char* mode) +{ + close(); + m_file = fopen(path.c_str(), mode); +} + +inline void +File::close() +{ + if (m_file) { + fclose(m_file); + m_file = nullptr; + } +} + +inline File::operator bool() const +{ + return m_file; +} + +// clang-format off +inline FILE* +File::operator*() const +// clang-format on +{ + return m_file; +} + +inline FILE* +File::get() +{ + return m_file; +} diff --git a/src/Finalizer.hpp b/src/Finalizer.hpp new file mode 100644 index 0000000..74f6b78 --- /dev/null +++ b/src/Finalizer.hpp @@ -0,0 +1,43 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include + +class Finalizer +{ +public: + Finalizer(std::function finalizer); + ~Finalizer(); + +private: + std::function m_finalizer; +}; + +inline Finalizer::Finalizer(std::function finalizer) + : m_finalizer(finalizer) +{ +} + +inline Finalizer::~Finalizer() +{ + m_finalizer(); +} diff --git a/src/FormatNonstdStringView.hpp b/src/FormatNonstdStringView.hpp new file mode 100644 index 0000000..0770a90 --- /dev/null +++ b/src/FormatNonstdStringView.hpp @@ -0,0 +1,47 @@ +// Copyright (C) 2019 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "third_party/fmt/core.h" +#include "third_party/nonstd/string_view.hpp" + +// Specialization of fmt::formatter for nonstd::string_view. +namespace fmt { + +template<> struct formatter +{ + template + constexpr auto + parse(ParseContext& ctx) const -> decltype(ctx.begin()) + { + return ctx.begin(); + } + + template + auto + format(const nonstd::string_view& sv, FormatContext& ctx) + -> decltype(ctx.out()) + { + return format_to(ctx.out(), "{}", fmt::string_view(sv.data(), sv.size())); + } +}; + +} // namespace fmt diff --git a/src/Hash.cpp b/src/Hash.cpp new file mode 100644 index 0000000..bb186b7 --- /dev/null +++ b/src/Hash.cpp @@ -0,0 +1,140 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "Hash.hpp" + +#include "Fd.hpp" +#include "Logging.hpp" + +using Logging::log; +using nonstd::string_view; + +const string_view HASH_DELIMITER("\000cCaChE\000", 8); + +Hash::Hash() +{ + blake3_hasher_init(&m_hasher); +} + +void +Hash::enable_debug(string_view section_name, + FILE* debug_binary, + FILE* debug_text) +{ + m_debug_binary = debug_binary; + m_debug_text = debug_text; + + add_debug_text("=== "); + add_debug_text(section_name); + add_debug_text(" ===\n"); +} + +Digest +Hash::digest() const +{ + // Note that blake3_hasher_finalize doesn't modify the hasher itself, thus it + // is possible to finalize again after more data has been added. + Digest digest; + blake3_hasher_finalize(&m_hasher, digest.bytes(), digest.size()); + return digest; +} + +Hash& +Hash::hash_delimiter(string_view type) +{ + hash_buffer(HASH_DELIMITER); + hash_buffer(type); + hash_buffer(string_view("", 1)); // NUL + add_debug_text("### "); + add_debug_text(type); + add_debug_text("\n"); + return *this; +} + +Hash& +Hash::hash(const void* data, size_t size, HashType hash_type) +{ + string_view buffer(static_cast(data), size); + hash_buffer(buffer); + + switch (hash_type) { + case HashType::binary: + add_debug_text( + Util::format_base16(static_cast(data), size)); + break; + + case HashType::text: + add_debug_text(buffer); + break; + } + + add_debug_text("\n"); + return *this; +} + +Hash& +Hash::hash(string_view data) +{ + hash(data.data(), data.length()); + return *this; +} + +Hash& +Hash::hash(int64_t x) +{ + hash_buffer(string_view(reinterpret_cast(&x), sizeof(x))); + add_debug_text(fmt::format("{}\n", x)); + return *this; +} + +bool +Hash::hash_fd(int fd) +{ + return Util::read_fd( + fd, [=](const void* data, size_t size) { hash(data, size); }); +} + +bool +Hash::hash_file(const std::string& path) +{ + Fd fd(open(path.c_str(), O_RDONLY | O_BINARY)); + if (!fd) { + log("Failed to open {}: {}", path, strerror(errno)); + return false; + } + + bool ret = hash_fd(*fd); + return ret; +} + +void +Hash::hash_buffer(string_view buffer) +{ + blake3_hasher_update(&m_hasher, buffer.data(), buffer.size()); + if (!buffer.empty() && m_debug_binary) { + (void)fwrite(buffer.data(), 1, buffer.size(), m_debug_binary); + } +} + +void +Hash::add_debug_text(string_view text) +{ + if (!text.empty() && m_debug_text) { + (void)fwrite(text.data(), 1, text.length(), m_debug_text); + } +} diff --git a/src/Hash.hpp b/src/Hash.hpp new file mode 100644 index 0000000..d2686af --- /dev/null +++ b/src/Hash.hpp @@ -0,0 +1,105 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Digest.hpp" + +#include "third_party/blake3/blake3.h" +#include "third_party/nonstd/string_view.hpp" + +// This class represents a hash state. +class Hash +{ +public: + enum class HashType { binary, text }; + + Hash(); + Hash(const Hash& other) = default; + + Hash& operator=(const Hash& other) = default; + + // Enable debug logging of the hashed input to a binary and a text file. + void enable_debug(nonstd::string_view section_name, + FILE* debug_binary, + FILE* debug_text); + + // Retrieve the digest. + Digest digest() const; + + // Hash some data that is unlikely to occur in the input. The idea is twofold: + // + // - Delimit things like arguments from each other (e.g., so that -I -O2 and + // -I-O2 hash differently). + // - Tag different types of hashed information so that it's possible to do + // conditional hashing of information in a safe way (e.g., if we want to + // hash information X if CCACHE_A is set and information Y if CCACHE_B is + // set, there should never be a hash collision risk). + Hash& hash_delimiter(nonstd::string_view type); + + // Add bytes to the hash. + // + // If hash debugging is enabled: + // + // - If `hash_type` is `HashType::binary`, the buffer content is written in + // hex format to the text input file. + // - If `hash_type` is `HashType::text`, the buffer content is written + // verbatim to the text input file. + // + // In both cases a newline character is added as well. + Hash& + hash(const void* data, size_t size, HashType hash_type = HashType::text); + + // Add a string to the hash. + // + // If hash debugging is enabled, the string is written to the text input file + // followed by a newline. + Hash& hash(nonstd::string_view data); + + // Add an integer to the hash. + // + // If hash debugging is enabled, the integer is written in text form to the + // text input file followed by a newline. + Hash& hash(int64_t x); + + // Add contents read from an open file descriptor to the hash. + // + // If hash debugging is enabled, the data is written verbatim to the text + // input file. + // + // Returns true on success, otherwise false. + bool hash_fd(int fd); + + // Add file contents to the hash. + // + // If hash debugging is enabled, the data is written verbatim to the text + // input file. + // + // Returns true on success, otherwise false. + bool hash_file(const std::string& path); + +private: + blake3_hasher m_hasher; + FILE* m_debug_binary = nullptr; + FILE* m_debug_text = nullptr; + + void hash_buffer(nonstd::string_view buffer); + void add_debug_text(nonstd::string_view text); +}; diff --git a/src/InodeCache.cpp b/src/InodeCache.cpp new file mode 100644 index 0000000..eeef9ac --- /dev/null +++ b/src/InodeCache.cpp @@ -0,0 +1,488 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "InodeCache.hpp" + +#include "Config.hpp" +#include "Fd.hpp" +#include "Finalizer.hpp" +#include "Hash.hpp" +#include "Logging.hpp" +#include "Stat.hpp" +#include "TemporaryFile.hpp" +#include "Util.hpp" + +#include +#include +#include +#include + +using Logging::log; + +// The inode cache resides on a file that is mapped into shared memory by +// running processes. It is implemented as a two level structure, where the top +// level is a hash table consisting of buckets. Each bucket contains entries +// that are sorted in LRU order. Entries map from keys representing files to +// cached hash results. +// +// Concurrent access is guarded by a mutex in each bucket. +// +// Current cache size is fixed and the given constants are considered large +// enough for most projects. The size could be made configurable if there is a +// demand for it. + +namespace { + +// The version number corresponds to the format of the cache entries and to +// semantics of the key fields. +// +// Note: The key is hashed using the main hash algorithm, so the version number +// does not need to be incremented if said algorithm is changed (except if the +// digest size changes since that affects the entry format). +const uint32_t k_version = 1; + +// Note: Increment the version number if constants affecting storage size are +// changed. +const uint32_t k_num_buckets = 32 * 1024; +const uint32_t k_num_entries = 4; + +static_assert(Digest::size() == 20, + "Increment version number if size of digest is changed."); +static_assert(IS_TRIVIALLY_COPYABLE(Digest), + "Digest is expected to be trivially copyable."); + +static_assert( + static_cast(InodeCache::ContentType::binary) == 0, + "Numeric value is part of key, increment version number if changed."); +static_assert( + static_cast(InodeCache::ContentType::code) == 1, + "Numeric value is part of key, increment version number if changed."); +static_assert( + static_cast(InodeCache::ContentType::code_with_sloppy_time_macros) == 2, + "Numeric value is part of key, increment version number if changed."); +static_assert( + static_cast(InodeCache::ContentType::precompiled_header) == 3, + "Numeric value is part of key, increment version number if changed."); + +} // namespace + +struct InodeCache::Key +{ + ContentType type; + dev_t st_dev; + ino_t st_ino; + mode_t st_mode; +#ifdef HAVE_STRUCT_STAT_ST_MTIM + timespec st_mtim; +#else + time_t st_mtim; +#endif +#ifdef HAVE_STRUCT_STAT_ST_CTIM + timespec st_ctim; // Included for sanity checking. +#else + time_t st_ctim; // Included for sanity checking. +#endif + off_t st_size; // Included for sanity checking. + bool sloppy_time_macros; +}; + +struct InodeCache::Entry +{ + Digest key_digest; // Hashed key + Digest file_digest; // Cached file hash + int return_value; // Cached return value +}; + +struct InodeCache::Bucket +{ + pthread_mutex_t mt; + Entry entries[k_num_entries]; +}; + +struct InodeCache::SharedRegion +{ + uint32_t version; + std::atomic hits; + std::atomic misses; + std::atomic errors; + Bucket buckets[k_num_buckets]; +}; + +bool +InodeCache::mmap_file(const std::string& inode_cache_file) +{ + if (m_sr) { + munmap(m_sr, sizeof(SharedRegion)); + m_sr = nullptr; + } + Fd fd(open(inode_cache_file.c_str(), O_RDWR)); + if (!fd) { + log("Failed to open inode cache {}: {}", inode_cache_file, strerror(errno)); + return false; + } + bool is_nfs; + if (Util::is_nfs_fd(*fd, &is_nfs) == 0 && is_nfs) { + log( + "Inode cache not supported because the cache file is located on nfs: {}", + inode_cache_file); + return false; + } + SharedRegion* sr = reinterpret_cast(mmap( + nullptr, sizeof(SharedRegion), PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0)); + fd.close(); + if (sr == reinterpret_cast(-1)) { + log("Failed to mmap {}: {}", inode_cache_file, strerror(errno)); + return false; + } + // Drop the file from disk if the found version is not matching. This will + // allow a new file to be generated. + if (sr->version != k_version) { + log( + "Dropping inode cache because found version {} does not match expected" + " version {}", + sr->version, + k_version); + munmap(sr, sizeof(SharedRegion)); + unlink(inode_cache_file.c_str()); + return false; + } + m_sr = sr; + if (m_config.debug()) { + log("inode cache file loaded: {}", inode_cache_file); + } + return true; +} + +bool +InodeCache::hash_inode(const std::string& path, + ContentType type, + Digest& digest) +{ + Stat stat = Stat::stat(path); + if (!stat) { + log("Could not stat {}: {}", path, strerror(stat.error_number())); + return false; + } + + Key key; + memset(&key, 0, sizeof(Key)); + key.type = type; + key.st_dev = stat.device(); + key.st_ino = stat.inode(); + key.st_mode = stat.mode(); +#ifdef HAVE_STRUCT_STAT_ST_MTIM + key.st_mtim = stat.mtim(); +#else + key.st_mtim = stat.mtime(); +#endif +#ifdef HAVE_STRUCT_STAT_ST_CTIM + key.st_ctim = stat.ctim(); +#else + key.st_ctim = stat.ctime(); +#endif + key.st_size = stat.size(); + + Hash hash; + hash.hash(&key, sizeof(Key)); + digest = hash.digest(); + return true; +} + +InodeCache::Bucket* +InodeCache::acquire_bucket(uint32_t index) +{ + Bucket* bucket = &m_sr->buckets[index]; + int err = pthread_mutex_lock(&bucket->mt); +#ifdef PTHREAD_MUTEX_ROBUST + if (err == EOWNERDEAD) { + if (m_config.debug()) { + ++m_sr->errors; + } + err = pthread_mutex_consistent(&bucket->mt); + if (err) { + log( + "Can't consolidate stale mutex at index {}: {}", index, strerror(err)); + log("Consider removing the inode cache file if the problem persists"); + return nullptr; + } + log("Wiping bucket at index {} because of stale mutex", index); + memset(bucket->entries, 0, sizeof(Bucket::entries)); + } else { +#endif + if (err) { + log("Failed to lock mutex at index {}: {}", index, strerror(err)); + log("Consider removing the inode cache file if problem persists"); + ++m_sr->errors; + return nullptr; + } +#ifdef PTHREAD_MUTEX_ROBUST + } +#endif + return bucket; +} + +InodeCache::Bucket* +InodeCache::acquire_bucket(const Digest& key_digest) +{ + uint32_t hash; + Util::big_endian_to_int(key_digest.bytes(), hash); + return acquire_bucket(hash % k_num_buckets); +} + +void +InodeCache::release_bucket(Bucket* bucket) +{ + pthread_mutex_unlock(&bucket->mt); +} + +bool +InodeCache::create_new_file(const std::string& filename) +{ + log("Creating a new inode cache"); + + // Create the new file to a temporary name to prevent other processes from + // mapping it before it is fully initialized. + TemporaryFile tmp_file(filename); + + Finalizer temp_file_remover([&] { unlink(tmp_file.path.c_str()); }); + + bool is_nfs; + if (Util::is_nfs_fd(*tmp_file.fd, &is_nfs) == 0 && is_nfs) { + log( + "Inode cache not supported because the cache file would be located on" + " nfs: {}", + filename); + return false; + } + int err = Util::fallocate(*tmp_file.fd, sizeof(SharedRegion)); + if (err) { + log("Failed to allocate file space for inode cache: {}", strerror(err)); + return false; + } + SharedRegion* sr = + reinterpret_cast(mmap(nullptr, + sizeof(SharedRegion), + PROT_READ | PROT_WRITE, + MAP_SHARED, + *tmp_file.fd, + 0)); + if (sr == reinterpret_cast(-1)) { + log("Failed to mmap new inode cache: {}", strerror(errno)); + return false; + } + + // Initialize new shared region. + sr->version = k_version; + pthread_mutexattr_t mattr; + pthread_mutexattr_init(&mattr); + pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED); +#ifdef PTHREAD_MUTEX_ROBUST + pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST); +#endif + for (auto& bucket : sr->buckets) { + pthread_mutex_init(&bucket.mt, &mattr); + } + + munmap(sr, sizeof(SharedRegion)); + tmp_file.fd.close(); + + // link() will fail silently if a file with the same name already exists. + // This will be the case if two processes try to create a new file + // simultaneously. Thus close the current file handle and reopen a new one, + // which will make us use the first created file even if we didn't win the + // race. + if (link(tmp_file.path.c_str(), filename.c_str()) != 0) { + log("Failed to link new inode cache: {}", strerror(errno)); + return false; + } + + return true; +} + +bool +InodeCache::initialize() +{ + if (m_failed || !m_config.inode_cache()) { + return false; + } + + if (m_sr) { + return true; + } + + std::string filename = get_file(); + if (m_sr || mmap_file(filename)) { + return true; + } + + // Try to create a new cache if we failed to map an existing file. + create_new_file(filename); + + // Concurrent processes could try to create new files simultaneously and the + // file that actually landed on disk will be from the process that won the + // race. Thus we try to open the file from disk instead of reusing the file + // handle to the file we just created. + if (mmap_file(filename)) { + return true; + } + + m_failed = true; + return false; +} + +InodeCache::InodeCache(const Config& config) : m_config(config) +{ +} + +InodeCache::~InodeCache() +{ + if (m_sr) { + munmap(m_sr, sizeof(SharedRegion)); + } +} + +bool +InodeCache::get(const std::string& path, + ContentType type, + Digest& file_digest, + int* return_value) +{ + if (!initialize()) { + return false; + } + + Digest key_digest; + if (!hash_inode(path, type, key_digest)) { + return false; + } + + Bucket* bucket = acquire_bucket(key_digest); + + if (!bucket) { + return false; + } + + bool found = false; + + for (uint32_t i = 0; i < k_num_entries; ++i) { + if (bucket->entries[i].key_digest == key_digest) { + if (i > 0) { + Entry tmp = bucket->entries[i]; + memmove(&bucket->entries[1], &bucket->entries[0], sizeof(Entry) * i); + bucket->entries[0] = tmp; + } + + file_digest = bucket->entries[0].file_digest; + if (return_value) { + *return_value = bucket->entries[0].return_value; + } + found = true; + break; + } + } + release_bucket(bucket); + + log("inode cache {}: {}", found ? "hit" : "miss", path); + + if (m_config.debug()) { + if (found) { + ++m_sr->hits; + } else { + ++m_sr->misses; + } + log("accumulated stats for inode cache: hits={}, misses={}, errors={}", + m_sr->hits.load(), + m_sr->misses.load(), + m_sr->errors.load()); + } + return found; +} + +bool +InodeCache::put(const std::string& path, + ContentType type, + const Digest& file_digest, + int return_value) +{ + if (!initialize()) { + return false; + } + + Digest key_digest; + if (!hash_inode(path, type, key_digest)) { + return false; + } + + Bucket* bucket = acquire_bucket(key_digest); + + if (!bucket) { + return false; + } + + memmove(&bucket->entries[1], + &bucket->entries[0], + sizeof(Entry) * (k_num_entries - 1)); + + bucket->entries[0].key_digest = key_digest; + bucket->entries[0].file_digest = file_digest; + bucket->entries[0].return_value = return_value; + + release_bucket(bucket); + + log("inode cache insert: {}", path); + + return true; +} + +bool +InodeCache::drop() +{ + std::string file = get_file(); + if (unlink(file.c_str()) != 0) { + return false; + } + if (m_sr) { + munmap(m_sr, sizeof(SharedRegion)); + m_sr = nullptr; + } + return true; +} + +std::string +InodeCache::get_file() +{ + return fmt::format("{}/inode-cache.v{}", m_config.temporary_dir(), k_version); +} + +int64_t +InodeCache::get_hits() +{ + return initialize() ? m_sr->hits.load() : -1; +} + +int64_t +InodeCache::get_misses() +{ + return initialize() ? m_sr->misses.load() : -1; +} + +int64_t +InodeCache::get_errors() +{ + return initialize() ? m_sr->errors.load() : -1; +} diff --git a/src/InodeCache.hpp b/src/InodeCache.hpp new file mode 100644 index 0000000..68d42ae --- /dev/null +++ b/src/InodeCache.hpp @@ -0,0 +1,110 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "config.h" + +#include + +class Config; +class Context; +class Digest; + +class InodeCache +{ +public: + // Specifies in which role a file was hashed, since the hash result does not + // only depend on the actual content but also what we used it for. Source code + // files are scanned for macros while binary files are not as one example. + enum class ContentType { + binary = 0, + code = 1, + code_with_sloppy_time_macros = 2, + precompiled_header = 3, + }; + + InodeCache(const Config& config); + ~InodeCache(); + + // Get saved hash digest and return value from a previous call to + // hash_source_code_file(). + // + // Returns true if saved values could be retrieved from the cache, false + // otherwise. + bool get(const std::string& path, + ContentType type, + Digest& file_digest, + int* return_value = nullptr); + + // Put hash digest and return value from a successful call to + // hash_source_code_file(). + // + // Returns true if values could be stored in the cache, false otherwise. + bool put(const std::string& path, + ContentType type, + const Digest& file_digest, + int return_value = 0); + + // Unmaps the current cache and removes the mapped file from disk. + // + // Returns true on success, false otherwise. + bool drop(); + + // Returns name of the persistent file. + std::string get_file(); + + // Returns total number of cache hits. + // + // Counters are incremented in debug mode only. + int64_t get_hits(); + + // Returns total number of cache misses. + // + // Counters are incremented in debug mode only. + int64_t get_misses(); + + // Returns total number of errors. + // + // Currently only lock errors will be counted, since the counter is not + // accessible before the file has been successfully mapped into memory. + // + // Counters are incremented in debug mode only. + int64_t get_errors(); + +private: + struct Bucket; + struct Entry; + struct Key; + struct SharedRegion; + + bool mmap_file(const std::string& inode_cache_file); + static bool + hash_inode(const std::string& path, ContentType type, Digest& digest); + Bucket* acquire_bucket(uint32_t index); + Bucket* acquire_bucket(const Digest& key_digest); + static void release_bucket(Bucket* bucket); + static bool create_new_file(const std::string& filename); + bool initialize(); + + const Config& m_config; + struct SharedRegion* m_sr = nullptr; + bool m_failed = false; +}; diff --git a/src/Lockfile.cpp b/src/Lockfile.cpp new file mode 100644 index 0000000..cd72900 --- /dev/null +++ b/src/Lockfile.cpp @@ -0,0 +1,224 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "Lockfile.hpp" + +#include "Logging.hpp" +#include "Util.hpp" + +#ifdef _WIN32 +# include "Win32Util.hpp" +#endif + +#include "third_party/fmt/core.h" + +#include +#include +#include + +using Logging::log; + +namespace { + +#ifndef _WIN32 + +bool +do_acquire_posix(const std::string& lockfile, uint32_t staleness_limit) +{ + const uint32_t max_to_sleep = 10000; // Microseconds. + uint32_t to_sleep = 1000; // Microseconds. + uint32_t slept = 0; // Microseconds. + std::string initial_content; + + std::stringstream ss; + ss << Util::get_hostname() << ':' << getpid() << ':' + << std::this_thread::get_id(); + const auto content_prefix = ss.str(); + + while (true) { + auto my_content = fmt::format("{}:{}", content_prefix, time(nullptr)); + + if (symlink(my_content.c_str(), lockfile.c_str()) == 0) { + // We got the lock. + return true; + } + + int saved_errno = errno; + log("lockfile_acquire: symlink {}: {}", lockfile, strerror(saved_errno)); + if (saved_errno == ENOENT) { + // Directory doesn't exist? + if (Util::create_dir(Util::dir_name(lockfile))) { + // OK. Retry. + continue; + } + } + + if (saved_errno == EPERM) { + // The file system does not support symbolic links. We have no choice but + // to grant the lock anyway. + return true; + } + + if (saved_errno != EEXIST) { + // Directory doesn't exist or isn't writable? + return false; + } + + std::string content = Util::read_link(lockfile); + if (content.empty()) { + if (errno == ENOENT) { + // The symlink was removed after the symlink() call above, so retry + // acquiring it. + continue; + } else { + log("lockfile_acquire: readlink {}: {}", lockfile, strerror(errno)); + return false; + } + } + + if (content == my_content) { + // Lost NFS reply? + log("lockfile_acquire: symlink {} failed but we got the lock anyway", + lockfile); + return true; + } + + // A possible improvement here would be to check if the process holding the + // lock is still alive and break the lock early if it isn't. + log("lockfile_acquire: lock info for {}: {}", lockfile, content); + + if (initial_content.empty()) { + initial_content = content; + } + + if (slept <= staleness_limit) { + log("lockfile_acquire: failed to acquire {}; sleeping {} microseconds", + lockfile, + to_sleep); + usleep(to_sleep); + slept += to_sleep; + to_sleep = std::min(max_to_sleep, 2 * to_sleep); + } else if (content != initial_content) { + log("lockfile_acquire: gave up acquiring {}", lockfile); + return false; + } else { + // The lock seems to be stale -- break it and try again. + log("lockfile_acquire: breaking {}", lockfile); + if (!Util::unlink_tmp(lockfile)) { + log("Failed to unlink {}: {}", lockfile, strerror(errno)); + return false; + } + to_sleep = 1000; + slept = 0; + initial_content.clear(); + } + } +} + +#else // !_WIN32 + +HANDLE +do_acquire_win32(const std::string& lockfile, uint32_t staleness_limit) +{ + unsigned to_sleep = 1000; // Microseconds. + unsigned max_to_sleep = 10000; // Microseconds. + unsigned slept = 0; // Microseconds. + HANDLE handle; + + while (true) { + DWORD flags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE; + handle = CreateFile(lockfile.c_str(), + GENERIC_WRITE, // desired access + 0, // shared mode (0 = not shared) + nullptr, // security attributes + CREATE_ALWAYS, // creation disposition + flags, // flags and attributes + nullptr // template file + ); + if (handle != INVALID_HANDLE_VALUE) { + break; + } + + DWORD error = GetLastError(); + log("lockfile_acquire: CreateFile {}: {} ({})", + lockfile, + Win32Util::error_message(error), + error); + if (error == ERROR_PATH_NOT_FOUND) { + // Directory doesn't exist? + if (Util::create_dir(Util::dir_name(lockfile)) == 0) { + // OK. Retry. + continue; + } + } + + // ERROR_SHARING_VIOLATION: lock already held. + // ERROR_ACCESS_DENIED: maybe pending delete. + if (error != ERROR_SHARING_VIOLATION && error != ERROR_ACCESS_DENIED) { + // Fatal error, give up. + break; + } + + if (slept > staleness_limit) { + log("lockfile_acquire: gave up acquiring {}", lockfile); + break; + } + + log("lockfile_acquire: failed to acquire {}; sleeping {} microseconds", + lockfile, + to_sleep); + usleep(to_sleep); + slept += to_sleep; + to_sleep = std::min(max_to_sleep, 2 * to_sleep); + } + + return handle; +} + +#endif // !_WIN32 + +} // namespace + +Lockfile::Lockfile(const std::string& path, uint32_t staleness_limit) + : m_lockfile(path + ".lock") +{ +#ifndef _WIN32 + m_acquired = do_acquire_posix(m_lockfile, staleness_limit); +#else + m_handle = do_acquire_win32(m_lockfile, staleness_limit); +#endif + if (acquired()) { + log("Acquired lock {}", m_lockfile); + } else { + log("Failed to acquire lock {}", m_lockfile); + } +} + +Lockfile::~Lockfile() +{ + if (acquired()) { + log("Releasing lock {}", m_lockfile); +#ifndef _WIN32 + if (!Util::unlink_tmp(m_lockfile)) { + log("Failed to unlink {}: {}", m_lockfile, strerror(errno)); + } +#else + CloseHandle(m_handle); +#endif + } +} diff --git a/src/Lockfile.hpp b/src/Lockfile.hpp new file mode 100644 index 0000000..78baed8 --- /dev/null +++ b/src/Lockfile.hpp @@ -0,0 +1,55 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include + +class Lockfile +{ +public: + // Acquire a lock on `path`. Break the lock (or give up, depending on + // implementation) after `staleness_limit` Microseconds. + Lockfile(const std::string& path, uint32_t staleness_limit = 2000000); + + // Release the lock if acquired. + ~Lockfile(); + + // Return whether the lockfile was acquired successfully. + bool acquired() const; + +private: + std::string m_lockfile; +#ifndef _WIN32 + bool m_acquired = false; +#else + HANDLE m_handle = nullptr; +#endif +}; + +inline bool +Lockfile::acquired() const +{ +#ifndef _WIN32 + return m_acquired; +#else + return m_handle != INVALID_HANDLE_VALUE; +#endif +} diff --git a/src/Logging.cpp b/src/Logging.cpp new file mode 100644 index 0000000..e2057b2 --- /dev/null +++ b/src/Logging.cpp @@ -0,0 +1,189 @@ +// Copyright (C) 2002 Andrew Tridgell +// Copyright (C) 2009-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "Logging.hpp" + +#include "Config.hpp" +#include "File.hpp" +#include "Util.hpp" +#include "exceptions.hpp" +#include "execute.hpp" + +#ifdef HAVE_SYSLOG_H +# include +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#ifdef __linux__ +# ifdef HAVE_SYS_IOCTL_H +# include +# endif +#endif + +#ifdef _WIN32 +# include +# include +# include +#endif + +using nonstd::string_view; + +namespace { + +// Logfile path and file handle, read from Config::log_file(). +std::string logfile_path; +File logfile; + +// Whether to use syslog() instead. +bool use_syslog; + +// Buffer used for logs in debug mode. +std::string debug_log_buffer; + +// Whether debug logging is enabled via configuration or environment variable. +bool debug_log_enabled = false; + +// Print error message to stderr about failure writing to the log file and exit +// with failure. +[[noreturn]] void +print_fatal_error_and_exit() +{ + // Note: Can't throw Fatal since that would lead to recursion. + fmt::print(stderr, + "ccache: error: Failed to write to {}: {}\n", + logfile_path, + strerror(errno)); + exit(EXIT_FAILURE); +} + +void +do_log(string_view message, bool bulk) +{ + static char prefix[200]; + + if (!bulk) { + char timestamp[100]; + struct timeval tv; + gettimeofday(&tv, nullptr); + auto tm = Util::localtime(tv.tv_sec); + if (tm) { + strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S", &*tm); + } else { + snprintf(timestamp, sizeof(timestamp), "%lu", tv.tv_sec); + } + snprintf(prefix, + sizeof(prefix), + "[%s.%06d %-5d] ", + timestamp, + static_cast(tv.tv_usec), + static_cast(getpid())); + } + + if (logfile + && (fputs(prefix, *logfile) == EOF + || fwrite(message.data(), message.length(), 1, *logfile) != 1 + || fputc('\n', *logfile) == EOF + || (!bulk && fflush(*logfile) == EOF))) { + print_fatal_error_and_exit(); + } +#ifdef HAVE_SYSLOG + if (use_syslog) { + // Note: No log prefix since syslog will add a prefix of its own. + syslog( + LOG_DEBUG, "%.*s", static_cast(message.length()), message.data()); + // Note: No trailing newline. + } +#endif + if (debug_log_enabled) { + debug_log_buffer += prefix; + debug_log_buffer.append(message.data(), message.length()); + debug_log_buffer += '\n'; + } +} + +} // namespace + +namespace Logging { + +// Initialize logging. Call only once. +void +init(const Config& config) +{ + debug_log_enabled = config.debug(); + +#ifdef HAVE_SYSLOG + if (config.log_file() == "syslog") { + use_syslog = true; + openlog("ccache", LOG_PID, LOG_USER); + return; // Don't open logfile + } +#endif + + if (!config.log_file().empty()) { + logfile_path = config.log_file(); + logfile.open(logfile_path, "a"); + if (logfile) { + Util::set_cloexec_flag(fileno(*logfile)); + } else { + print_fatal_error_and_exit(); + } + } +} + +bool +enabled() +{ + return debug_log_enabled || logfile || use_syslog; +} + +void +log(string_view message) +{ + if (!enabled()) { + return; + } + do_log(message, false); +} + +void +bulk_log(string_view message) +{ + if (!enabled()) { + return; + } + do_log(message, true); +} + +void +dump_log(const std::string& path) +{ + if (!enabled()) { + return; + } + File file(path, "w"); + if (file) { + (void)fwrite(debug_log_buffer.data(), debug_log_buffer.length(), 1, *file); + } else { + log("Failed to open {}: {}", path, strerror(errno)); + } +} + +} // namespace Logging diff --git a/src/Logging.hpp b/src/Logging.hpp new file mode 100644 index 0000000..278812b --- /dev/null +++ b/src/Logging.hpp @@ -0,0 +1,77 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "FormatNonstdStringView.hpp" + +#include "third_party/fmt/core.h" +#include "third_party/nonstd/optional.hpp" +#include "third_party/nonstd/string_view.hpp" + +#include +#include + +class Config; + +namespace Logging { + +// Initialize global logging state. Must be called once before using the other +// logging functions. +void init(const Config& config); + +// Return whether logging is enabled to at least one destination. +bool enabled(); + +// Log `message` (plus a newline character). +void log(nonstd::string_view message); + +// Log `message` (plus a newline character) without flushing and with a reused +// timestamp. +void bulk_log(nonstd::string_view message); + +// Write the current log memory buffer `path`. +void dump_log(const std::string& path); + +// Log a message (plus a newline character). `args` are forwarded to +// `fmt::format`. +template +inline void +log(T&&... args) +{ + if (!enabled()) { + return; + } + log(nonstd::string_view(fmt::format(std::forward(args)...))); +} + +// Log a message (plus a newline character) without flushing and with a reused +// timestamp. `args` are forwarded to `fmt::format`. +template +inline void +bulk_log(T&&... args) +{ + if (!enabled()) { + return; + } + bulk_log(nonstd::string_view(fmt::format(std::forward(args)...))); +} + +} // namespace Logging diff --git a/src/Manifest.cpp b/src/Manifest.cpp new file mode 100644 index 0000000..64ceae1 --- /dev/null +++ b/src/Manifest.cpp @@ -0,0 +1,623 @@ +// Copyright (C) 2009-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "Manifest.hpp" + +#include "AtomicFile.hpp" +#include "CacheEntryReader.hpp" +#include "CacheEntryWriter.hpp" +#include "Checksum.hpp" +#include "Config.hpp" +#include "Context.hpp" +#include "Digest.hpp" +#include "File.hpp" +#include "Hash.hpp" +#include "Logging.hpp" +#include "StdMakeUnique.hpp" +#include "ccache.hpp" +#include "hashutil.hpp" + +// Manifest data format +// ==================== +// +// Integers are big-endian. +// +// ::=
::= +// +// ::= 4 bytes ("cCrS") +// ::= uint8_t +// ::= | +// ::= 0 (uint8_t) +// ::= 1 (uint8_t) +// ::= int8_t +// ::= uint64_t ; size of file if stored uncompressed +// ::= ; body is potentially +// ; compressed +// ::= * +// ::= uint32_t +// ::= +// ::= uint16_t +// ::= path_len bytes +// ::= * +// ::= uint32_t +// ::= +// ::= uint32_t +// ::= Digest::size() bytes +// ::= uint64_t ; file size +// ::= int64_t ; modification time +// ::= int64_t ; status change time +// ::= * +// ::= uint32_t +// ::= * +// ::= uint32_t +// ::= uint32_t +// ::= Digest::size() bytes +// ::= +// ::= uint64_t ; XXH3 of content bytes +// +// Sketch of concrete layout: + +// 4 bytes +// 1 byte +// 1 byte +// 1 byte +// 8 bytes +// --- [potentially compressed from here] ------------------------------------- +// 4 bytes +// 2 bytes +// path_len bytes +// ... +// ---------------------------------------------------------------------------- +// 4 bytes +// 4 bytes +// Digest::size() bytes +// 8 bytes +// 8 bytes +// 8 bytes +// ... +// ---------------------------------------------------------------------------- +// 4 bytes +// 4 bytes +// 4 bytes +// ... +// Digest::size() bytes +// ... +// checksum 8 bytes +// +// +// Version history +// =============== +// +// 1: Introduced in ccache 3.0. (Files are always compressed with gzip.) +// 2: Introduced in ccache 4.0. + +using Logging::log; +using nonstd::nullopt; +using nonstd::optional; + +const uint32_t k_max_manifest_entries = 100; +const uint32_t k_max_manifest_file_info_entries = 10000; + +namespace { + +struct FileInfo +{ + // Index to n_files. + uint32_t index; + // Digest of referenced file. + Digest digest; + // Size of referenced file. + uint64_t fsize; + // mtime of referenced file. + int64_t mtime; + // ctime of referenced file. + int64_t ctime; +}; + +bool +operator==(const FileInfo& lhs, const FileInfo& rhs) +{ + return lhs.index == rhs.index && lhs.digest == rhs.digest + && lhs.fsize == rhs.fsize && lhs.mtime == rhs.mtime + && lhs.ctime == rhs.ctime; +} + +} // namespace + +namespace std { + +template<> struct hash +{ + size_t + operator()(const FileInfo& file_info) const + { + static_assert(sizeof(FileInfo) == 48, "unexpected size"); // No padding. + Checksum checksum; + checksum.update(&file_info, sizeof(file_info)); + return checksum.digest(); + } +}; + +} // namespace std + +namespace { + +struct ResultEntry +{ + // Indexes to file_infos. + std::vector file_info_indexes; + + // Name of the result. + Digest name; +}; + +struct ManifestData +{ + // Referenced include files. + std::vector files; + + // Information about referenced include files. + std::vector file_infos; + + // Result names plus references to include file infos. + std::vector results; + + void + add_result_entry( + const Digest& result_digest, + const std::unordered_map& included_files, + time_t time_of_compilation, + bool save_timestamp) + { + std::unordered_map mf_files; + for (uint32_t i = 0; i < files.size(); ++i) { + mf_files.emplace(files[i], i); + } + + std::unordered_map mf_file_infos; + for (uint32_t i = 0; i < file_infos.size(); ++i) { + mf_file_infos.emplace(file_infos[i], i); + } + + std::vector file_info_indexes; + file_info_indexes.reserve(included_files.size()); + + for (const auto& item : included_files) { + file_info_indexes.push_back(get_file_info_index(item.first, + item.second, + mf_files, + mf_file_infos, + time_of_compilation, + save_timestamp)); + } + + results.push_back(ResultEntry{std::move(file_info_indexes), result_digest}); + } + +private: + uint32_t + get_file_info_index( + const std::string& path, + const Digest& digest, + const std::unordered_map& mf_files, + const std::unordered_map& mf_file_infos, + time_t time_of_compilation, + bool save_timestamp) + { + struct FileInfo fi; + + auto f_it = mf_files.find(path); + if (f_it != mf_files.end()) { + fi.index = f_it->second; + } else { + files.push_back(path); + fi.index = files.size() - 1; + } + + fi.digest = digest; + + // file_stat.{m,c}time() have a resolution of 1 second, so we can cache the + // file's mtime and ctime only if they're at least one second older than + // time_of_compilation. + // + // file_stat.ctime() may be 0, so we have to check time_of_compilation + // against MAX(mtime, ctime). + // + // ccache only reads mtime/ctime if file_stat_match sloppiness is enabled, + // so mtimes/ctimes are stored as a dummy value (-1) if not enabled. This + // reduces the number of file_info entries for the common case. + + auto file_stat = Stat::stat(path, Stat::OnError::log); + if (file_stat) { + if (save_timestamp + && time_of_compilation + > std::max(file_stat.mtime(), file_stat.ctime())) { + fi.mtime = file_stat.mtime(); + fi.ctime = file_stat.ctime(); + } else { + fi.mtime = -1; + fi.ctime = -1; + } + fi.fsize = file_stat.size(); + } else { + fi.mtime = -1; + fi.ctime = -1; + fi.fsize = 0; + } + + auto fi_it = mf_file_infos.find(fi); + if (fi_it != mf_file_infos.end()) { + return fi_it->second; + } else { + file_infos.push_back(fi); + return file_infos.size() - 1; + } + } +}; + +struct FileStats +{ + uint64_t size; + int64_t mtime; + int64_t ctime; +}; + +std::unique_ptr +read_manifest(const std::string& path, FILE* dump_stream = nullptr) +{ + File file(path, "rb"); + if (!file) { + return {}; + } + + CacheEntryReader reader(file.get(), Manifest::k_magic, Manifest::k_version); + + if (dump_stream) { + reader.dump_header(dump_stream); + } + + auto mf = std::make_unique(); + + uint32_t entry_count; + reader.read(entry_count); + for (uint32_t i = 0; i < entry_count; ++i) { + mf->files.emplace_back(); + auto& entry = mf->files.back(); + + uint16_t length; + reader.read(length); + entry.assign(length, 0); + reader.read(&entry[0], length); + } + + reader.read(entry_count); + for (uint32_t i = 0; i < entry_count; ++i) { + mf->file_infos.emplace_back(); + auto& entry = mf->file_infos.back(); + + reader.read(entry.index); + reader.read(entry.digest.bytes(), Digest::size()); + reader.read(entry.fsize); + reader.read(entry.mtime); + reader.read(entry.ctime); + } + + reader.read(entry_count); + for (uint32_t i = 0; i < entry_count; ++i) { + mf->results.emplace_back(); + auto& entry = mf->results.back(); + + uint32_t file_info_count; + reader.read(file_info_count); + for (uint32_t j = 0; j < file_info_count; ++j) { + uint32_t file_info_index; + reader.read(file_info_index); + entry.file_info_indexes.push_back(file_info_index); + } + reader.read(entry.name.bytes(), Digest::size()); + } + + reader.finalize(); + return mf; +} + +bool +write_manifest(const Config& config, + const std::string& path, + const ManifestData& mf) +{ + uint64_t payload_size = 0; + payload_size += 4; // n_files + for (const auto& file : mf.files) { + payload_size += 2 + file.length(); + } + payload_size += 4; // n_file_infos + payload_size += mf.file_infos.size() * (4 + Digest::size() + 8 + 8 + 8); + payload_size += 4; // n_results + for (const auto& result : mf.results) { + payload_size += 4; // n_file_info_indexes + payload_size += result.file_info_indexes.size() * 4; + payload_size += Digest::size(); + } + + AtomicFile atomic_manifest_file(path, AtomicFile::Mode::binary); + CacheEntryWriter writer(atomic_manifest_file.stream(), + Manifest::k_magic, + Manifest::k_version, + Compression::type_from_config(config), + Compression::level_from_config(config), + payload_size); + writer.write(mf.files.size()); + for (const auto& file : mf.files) { + writer.write(file.length()); + writer.write(file.data(), file.length()); + } + + writer.write(mf.file_infos.size()); + for (const auto& file_info : mf.file_infos) { + writer.write(file_info.index); + writer.write(file_info.digest.bytes(), Digest::size()); + writer.write(file_info.fsize); + writer.write(file_info.mtime); + writer.write(file_info.ctime); + } + + writer.write(mf.results.size()); + for (const auto& result : mf.results) { + writer.write(result.file_info_indexes.size()); + for (auto index : result.file_info_indexes) { + writer.write(index); + } + writer.write(result.name.bytes(), Digest::size()); + } + + writer.finalize(); + atomic_manifest_file.commit(); + return true; +} + +bool +verify_result(const Context& ctx, + const ManifestData& mf, + const ResultEntry& result, + std::unordered_map& stated_files, + std::unordered_map& hashed_files) +{ + for (uint32_t file_info_index : result.file_info_indexes) { + const auto& fi = mf.file_infos[file_info_index]; + const auto& path = mf.files[fi.index]; + + auto stated_files_iter = stated_files.find(path); + if (stated_files_iter == stated_files.end()) { + auto file_stat = Stat::stat(path, Stat::OnError::log); + if (!file_stat) { + return false; + } + FileStats st; + st.size = file_stat.size(); + st.mtime = file_stat.mtime(); + st.ctime = file_stat.ctime(); + stated_files_iter = stated_files.emplace(path, st).first; + } + const FileStats& fs = stated_files_iter->second; + + if (fi.fsize != fs.size) { + return false; + } + + // Clang stores the mtime of the included files in the precompiled header, + // and will error out if that header is later used without rebuilding. + if ((ctx.guessed_compiler == GuessedCompiler::clang + || ctx.guessed_compiler == GuessedCompiler::unknown) + && ctx.args_info.output_is_precompiled_header + && !ctx.args_info.fno_pch_timestamp && fi.mtime != fs.mtime) { + log("Precompiled header includes {}, which has a new mtime", path); + return false; + } + + if (ctx.config.sloppiness() & SLOPPY_FILE_STAT_MATCHES) { + if (!(ctx.config.sloppiness() & SLOPPY_FILE_STAT_MATCHES_CTIME)) { + if (fi.mtime == fs.mtime && fi.ctime == fs.ctime) { + log("mtime/ctime hit for {}", path); + continue; + } else { + log("mtime/ctime miss for {}", path); + } + } else { + if (fi.mtime == fs.mtime) { + log("mtime hit for {}", path); + continue; + } else { + log("mtime miss for {}", path); + } + } + } + + auto hashed_files_iter = hashed_files.find(path); + if (hashed_files_iter == hashed_files.end()) { + Hash hash; + int ret = hash_source_code_file(ctx, hash, path, fs.size); + if (ret & HASH_SOURCE_CODE_ERROR) { + log("Failed hashing {}", path); + return false; + } + if (ret & HASH_SOURCE_CODE_FOUND_TIME) { + return false; + } + + Digest actual = hash.digest(); + hashed_files_iter = hashed_files.emplace(path, actual).first; + } + + if (fi.digest != hashed_files_iter->second) { + return false; + } + } + + return true; +} + +} // namespace + +namespace Manifest { + +const std::string k_file_suffix = "M"; +const uint8_t k_magic[4] = {'c', 'C', 'm', 'F'}; +const uint8_t k_version = 2; + +// Try to get the result name from a manifest file. Returns nullopt on failure. +optional +get(const Context& ctx, const std::string& path) +{ + std::unique_ptr mf; + try { + mf = read_manifest(path); + if (mf) { + // Update modification timestamp to save files from LRU cleanup. + Util::update_mtime(path); + } else { + log("No such manifest file"); + return nullopt; + } + } catch (const Error& e) { + log("Error: {}", e.what()); + return nullopt; + } + + std::unordered_map stated_files; + std::unordered_map hashed_files; + + // Check newest result first since it's a bit more likely to match. + for (uint32_t i = mf->results.size(); i > 0; i--) { + if (verify_result( + ctx, *mf, mf->results[i - 1], stated_files, hashed_files)) { + return mf->results[i - 1].name; + } + } + + return nullopt; +} + +// Put the result name into a manifest file given a set of included files. +// Returns true on success, otherwise false. +bool +put(const Config& config, + const std::string& path, + const Digest& result_name, + const std::unordered_map& included_files, + + time_t time_of_compilation, + bool save_timestamp) +{ + // We don't bother to acquire a lock when writing the manifest to disk. A + // race between two processes will only result in one lost entry, which is + // not a big deal, and it's also very unlikely. + + std::unique_ptr mf; + try { + mf = read_manifest(path); + if (!mf) { + // Manifest file didn't exist. + mf = std::make_unique(); + } + } catch (const Error& e) { + log("Error: {}", e.what()); + // Manifest file was corrupt, ignore. + mf = std::make_unique(); + } + + if (mf->results.size() > k_max_manifest_entries) { + // Normally, there shouldn't be many result entries in the manifest since + // new entries are added only if an include file has changed but not the + // source file, and you typically change source files more often than + // header files. However, it's certainly possible to imagine cases where + // the manifest will grow large (for instance, a generated header file that + // changes for every build), and this must be taken care of since + // processing an ever growing manifest eventually will take too much time. + // A good way of solving this would be to maintain the result entries in + // LRU order and discarding the old ones. An easy way is to throw away all + // entries when there are too many. Let's do that for now. + log("More than {} entries in manifest file; discarding", + k_max_manifest_entries); + mf = std::make_unique(); + } else if (mf->file_infos.size() > k_max_manifest_file_info_entries) { + // Rarely, FileInfo entries can grow large in pathological cases where + // many included files change, but the main file does not. This also puts + // an upper bound on the number of FileInfo entries. + log("More than {} FileInfo entries in manifest file; discarding", + k_max_manifest_file_info_entries); + mf = std::make_unique(); + } + + mf->add_result_entry( + result_name, included_files, time_of_compilation, save_timestamp); + + try { + write_manifest(config, path, *mf); + return true; + } catch (const Error& e) { + log("Error: {}", e.what()); + return false; + } +} + +bool +dump(const std::string& path, FILE* stream) +{ + std::unique_ptr mf; + try { + mf = read_manifest(path, stream); + } catch (const Error& e) { + fmt::print(stream, "Error: {}\n", e.what()); + return false; + } + + if (!mf) { + fmt::print(stream, "Error: No such file: {}\n", path); + return false; + } + + fmt::print(stream, "File paths ({}):\n", mf->files.size()); + for (size_t i = 0; i < mf->files.size(); ++i) { + fmt::print(stream, " {}: {}\n", i, mf->files[i]); + } + fmt::print(stream, "File infos ({}):\n", mf->file_infos.size()); + for (size_t i = 0; i < mf->file_infos.size(); ++i) { + fmt::print(stream, " {}:\n", i); + fmt::print(stream, " Path index: {}\n", mf->file_infos[i].index); + fmt::print(stream, " Hash: {}\n", mf->file_infos[i].digest.to_string()); + fmt::print(stream, " File size: {}\n", mf->file_infos[i].fsize); + fmt::print(stream, " Mtime: {}\n", mf->file_infos[i].mtime); + fmt::print(stream, " Ctime: {}\n", mf->file_infos[i].ctime); + } + fmt::print(stream, "Results ({}):\n", mf->results.size()); + for (size_t i = 0; i < mf->results.size(); ++i) { + fmt::print(stream, " {}:\n", i); + fmt::print(stream, " File info indexes:"); + for (uint32_t file_info_index : mf->results[i].file_info_indexes) { + fmt::print(stream, " {}", file_info_index); + } + fmt::print(stream, "\n"); + fmt::print(stream, " Name: {}\n", mf->results[i].name.to_string()); + } + + return true; +} + +} // namespace Manifest diff --git a/src/Manifest.hpp b/src/Manifest.hpp new file mode 100644 index 0000000..fda7758 --- /dev/null +++ b/src/Manifest.hpp @@ -0,0 +1,47 @@ +// Copyright (C) 2009-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "third_party/nonstd/optional.hpp" + +#include +#include + +class Config; +class Context; +class Digest; + +namespace Manifest { + +extern const std::string k_file_suffix; +extern const uint8_t k_magic[4]; +extern const uint8_t k_version; + +nonstd::optional get(const Context& ctx, const std::string& path); +bool put(const Config& config, + const std::string& path, + const Digest& result_name, + const std::unordered_map& included_files, + time_t time_of_compilation, + bool save_timestamp); +bool dump(const std::string& path, FILE* stream); + +} // namespace Manifest diff --git a/src/MiniTrace.cpp b/src/MiniTrace.cpp new file mode 100644 index 0000000..788d316 --- /dev/null +++ b/src/MiniTrace.cpp @@ -0,0 +1,90 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "system.hpp" + +#ifdef MTR_ENABLED + +# include "ArgsInfo.hpp" +# include "MiniTrace.hpp" +# include "TemporaryFile.hpp" +# include "Util.hpp" + +# ifdef HAVE_SYS_TIME_H +# include +# endif + +namespace { + +std::string +get_system_tmp_dir() +{ +# ifndef _WIN32 + const char* tmpdir = getenv("TMPDIR"); + if (tmpdir) { + return tmpdir; + } +# else + static char dirbuf[PATH_MAX]; + DWORD retval = GetTempPath(PATH_MAX, dirbuf); + if (retval > 0 && retval < PATH_MAX) { + return dirbuf; + } +# endif + return "/tmp"; +} + +double +time_seconds() +{ +# ifdef HAVE_GETTIMEOFDAY + struct timeval tv; + gettimeofday(&tv, nullptr); + return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; +# else + return (double)time(nullptr); +# endif +} + +} // namespace + +MiniTrace::MiniTrace(const ArgsInfo& args_info) + : m_args_info(args_info), m_trace_id(reinterpret_cast(getpid())) +{ + TemporaryFile tmp_file(get_system_tmp_dir() + "/ccache-trace"); + m_tmp_trace_file = tmp_file.path; + + mtr_init(m_tmp_trace_file.c_str()); + MTR_INSTANT_C("", "", "time", fmt::format("{:f}", time_seconds()).c_str()); + MTR_META_PROCESS_NAME("ccache"); + MTR_START("program", "ccache", m_trace_id); +} + +MiniTrace::~MiniTrace() +{ + MTR_FINISH("program", "ccache", m_trace_id); + mtr_flush(); + mtr_shutdown(); + + if (!m_args_info.output_obj.empty()) { + Util::copy_file(m_tmp_trace_file, m_args_info.output_obj + ".ccache-trace"); + } + Util::unlink_tmp(m_tmp_trace_file); +} + +#endif diff --git a/unittest/util.c b/src/MiniTrace.hpp similarity index 62% rename from unittest/util.c rename to src/MiniTrace.hpp index e06761a..3b44360 100644 --- a/unittest/util.c +++ b/src/MiniTrace.hpp @@ -1,4 +1,6 @@ -// Copyright (C) 2010-2020 Joel Rosdahl +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the Free @@ -14,28 +16,28 @@ // this program; if not, write to the Free Software Foundation, Inc., 51 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -#include "../src/system.h" -#include "util.h" +#pragma once -#ifdef _WIN32 -# define lstat(a, b) stat(a, b) -#endif +#include "system.hpp" -bool -path_exists(const char *path) -{ - struct stat st; - return lstat(path, &st) == 0; -} +#include "third_party/minitrace.h" + +#ifdef MTR_ENABLED + +# include -void -create_file(const char *path, const char *content) +struct ArgsInfo; + +class MiniTrace { - FILE *f = fopen(path, "wb"); - if (!f || fputs(content, f) < 0) { - fprintf(stderr, "create_file: %s: %s\n", path, strerror(errno)); - } - if (f) { - fclose(f); - } -} +public: + MiniTrace(const ArgsInfo& args_info); + ~MiniTrace(); + +private: + const ArgsInfo& m_args_info; + const void* const m_trace_id; + std::string m_tmp_trace_file; +}; + +#endif diff --git a/src/NonCopyable.hpp b/src/NonCopyable.hpp new file mode 100644 index 0000000..86004a9 --- /dev/null +++ b/src/NonCopyable.hpp @@ -0,0 +1,29 @@ +// Copyright (C) 2019 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +class NonCopyable +{ +protected: + NonCopyable() = default; + +private: + NonCopyable(const NonCopyable&) = delete; + NonCopyable& operator=(const NonCopyable&) = delete; +}; diff --git a/src/NullCompressor.cpp b/src/NullCompressor.cpp new file mode 100644 index 0000000..13494fc --- /dev/null +++ b/src/NullCompressor.cpp @@ -0,0 +1,47 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "NullCompressor.hpp" + +#include "exceptions.hpp" + +NullCompressor::NullCompressor(FILE* stream) : m_stream(stream) +{ +} + +int8_t +NullCompressor::actual_compression_level() const +{ + return 0; +} + +void +NullCompressor::write(const void* data, size_t count) +{ + if (fwrite(data, 1, count, m_stream) != count) { + throw Error("failed to write to uncompressed stream"); + } +} + +void +NullCompressor::finalize() +{ + if (fflush(m_stream) != 0) { + throw Error("failed to finalize uncompressed stream"); + } +} diff --git a/src/NullCompressor.hpp b/src/NullCompressor.hpp new file mode 100644 index 0000000..9a4fcd1 --- /dev/null +++ b/src/NullCompressor.hpp @@ -0,0 +1,40 @@ +// Copyright (C) 2019 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Compressor.hpp" +#include "NonCopyable.hpp" + +// A compressor of an uncompressed stream. +class NullCompressor : public Compressor, NonCopyable +{ +public: + // Parameters: + // - stream: The file to write data to. + explicit NullCompressor(FILE* stream); + + int8_t actual_compression_level() const override; + void write(const void* data, size_t count) override; + void finalize() override; + +private: + FILE* m_stream; +}; diff --git a/src/NullDecompressor.cpp b/src/NullDecompressor.cpp new file mode 100644 index 0000000..090bdf5 --- /dev/null +++ b/src/NullDecompressor.cpp @@ -0,0 +1,41 @@ +// Copyright (C) 2019 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "NullDecompressor.hpp" + +#include "exceptions.hpp" + +NullDecompressor::NullDecompressor(FILE* stream) : m_stream(stream) +{ +} + +void +NullDecompressor::read(void* data, size_t count) +{ + if (fread(data, count, 1, m_stream) != 1) { + throw Error("failed to read from uncompressed stream"); + } +} + +void +NullDecompressor::finalize() +{ + if (fgetc(m_stream) != EOF) { + throw Error("garbage data at end of uncompressed stream"); + } +} diff --git a/src/NullDecompressor.hpp b/src/NullDecompressor.hpp new file mode 100644 index 0000000..514ed7d --- /dev/null +++ b/src/NullDecompressor.hpp @@ -0,0 +1,39 @@ +// Copyright (C) 2019 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Decompressor.hpp" +#include "NonCopyable.hpp" + +// A decompressor of an uncompressed stream. +class NullDecompressor : public Decompressor, NonCopyable +{ +public: + // Parameters: + // - stream: The file to read data from. + explicit NullDecompressor(FILE* stream); + + void read(void* data, size_t count) override; + void finalize() override; + +private: + FILE* m_stream; +}; diff --git a/src/ProgressBar.cpp b/src/ProgressBar.cpp new file mode 100644 index 0000000..cb0811b --- /dev/null +++ b/src/ProgressBar.cpp @@ -0,0 +1,93 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "ProgressBar.hpp" + +#include "third_party/fmt/core.h" + +#ifndef _WIN32 +# include +#endif + +#ifdef __sun +# include +#endif + +#include + +namespace { + +const size_t k_max_width = 120; + +size_t +get_terminal_width() +{ +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO info; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info); + return info.srWindow.Right - info.srWindow.Left; +#else + struct winsize winsize; + ioctl(0, TIOCGWINSZ, &winsize); + return winsize.ws_col; +#endif +} + +} // namespace + +ProgressBar::ProgressBar(const std::string& header) + : m_header(header), + m_width(std::min(k_max_width, get_terminal_width())), + m_stdout_is_a_terminal(isatty(STDOUT_FILENO)) +{ + update(0.0); +} + +void +ProgressBar::update(double value) +{ + if (!m_stdout_is_a_terminal) { + return; + } + + int16_t new_value = static_cast(1000 * value); + if (new_value == m_current_value) { + return; + } + m_current_value = new_value; + + size_t first_part_width = m_header.size() + 10; + if (first_part_width + 10 > m_width) { + // The progress bar would be less than 10 characters, so just print the + // percentage. + fmt::print("\r{} {:5.1f}%", m_header, 100 * value); + } else { + size_t total_bar_width = m_width - first_part_width; + size_t filled_bar_width = value * total_bar_width; + size_t unfilled_bar_width = total_bar_width - filled_bar_width; + fmt::print("\r{} {:5.1f}% [{:=<{}}{: <{}}]", + m_header, + 100 * value, + "", + filled_bar_width, + "", + unfilled_bar_width); + } + + fflush(stdout); +} diff --git a/src/ProgressBar.hpp b/src/ProgressBar.hpp new file mode 100644 index 0000000..8aefa89 --- /dev/null +++ b/src/ProgressBar.hpp @@ -0,0 +1,41 @@ +// Copyright (C) 2019 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include + +class ProgressBar +{ +public: + explicit ProgressBar(const std::string& header); + + // Update progress bar. + // + // Parameters: + // - value: A value between 0.0 (nothing completed) to 1.0 (all completed). + void update(double value); + +private: + const std::string m_header; + const size_t m_width; + const bool m_stdout_is_a_terminal; + int16_t m_current_value = -1; // trunc(1000 * value) +}; diff --git a/src/Result.cpp b/src/Result.cpp new file mode 100644 index 0000000..73a2680 --- /dev/null +++ b/src/Result.cpp @@ -0,0 +1,432 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "Result.hpp" + +#include "AtomicFile.hpp" +#include "CacheEntryReader.hpp" +#include "CacheEntryWriter.hpp" +#include "Config.hpp" +#include "Context.hpp" +#include "Fd.hpp" +#include "File.hpp" +#include "Logging.hpp" +#include "Stat.hpp" +#include "Statistics.hpp" +#include "Util.hpp" +#include "exceptions.hpp" + +#include + +// Result data format +// ================== +// +// Integers are big-endian. +// +// ::=
+//
::= +// +// ::= 4 bytes ("cCrS") +// ::= uint8_t +// ::= | +// ::= 0 (uint8_t) +// ::= 1 (uint8_t) +// ::= int8_t +// ::= uint64_t ; size of file if stored uncompressed +// ::= * ; potentially compressed +// ::= uint8_t +// ::= | +// ::= +// +// ::= 0 (uint8_t) +// ::= uint8_t +// ::= uint64_t +// ::= data_len bytes +// ::= +// ::= 1 (uint8_t) +// ::= uint64_t +// ::= +// ::= uint64_t ; XXH3 of content bytes +// +// Sketch of concrete layout: +// +// 4 bytes +// 1 byte +// 1 byte +// 1 byte +// 8 bytes +// --- [potentially compressed from here] ------------------------------------- +// 1 byte +// 1 byte +// 1 byte +// 8 bytes +// data_len bytes +// ... +// 1 byte +// 1 byte +// key_len bytes +// ... +// checksum 8 bytes +// +// +// Version history +// =============== +// +// 1: Introduced in ccache 4.0. + +using Logging::log; +using nonstd::nullopt; +using nonstd::optional; +using nonstd::string_view; + +namespace { + +// File data stored inside the result file. +const uint8_t k_embedded_file_marker = 0; + +// File stored as-is in the file system. +const uint8_t k_raw_file_marker = 1; + +std::string +get_raw_file_path(string_view result_path, uint32_t entry_number) +{ + const auto prefix = result_path.substr( + 0, result_path.length() - Result::k_file_suffix.length()); + return fmt::format("{}{}W", prefix, entry_number); +} + +bool +should_store_raw_file(const Config& config, Result::FileType type) +{ + if (!config.file_clone() && !config.hard_link()) { + return false; + } + + // Only store object files as raw files since there are several problems with + // storing other file types: + // + // 1. The compiler unlinks object files before writing to them but it doesn't + // unlink .d files, so just it's possible to corrupt .d files just by + // running the compiler (see ccache issue 599). + // 2. .d files cause trouble for automake if hard-linked (see ccache issue + // 378). + // 3. It's unknown how the compiler treats other file types, so better safe + // than sorry. + // + // It would be possible to store all files in raw form for the file_clone case + // and only hard link object files. However, most likely it's only object + // files that become large enough that it's of interest to clone or hard link + // them, so we keep things simple for now. This will also save i-nodes in the + // cache. + return type == Result::FileType::object; +} + +} // namespace + +namespace Result { + +const std::string k_file_suffix = "R"; +const uint8_t k_magic[4] = {'c', 'C', 'r', 'S'}; +const uint8_t k_version = 1; +const char* const k_unknown_file_type = ""; + +const char* +file_type_to_string(FileType type) +{ + switch (type) { + case FileType::object: + return ".o"; + + case FileType::dependency: + return ".d"; + + case FileType::stderr_output: + return ""; + + case FileType::coverage_unmangled: + return ".gcno-unmangled"; + + case FileType::stackusage: + return ".su"; + + case FileType::diagnostic: + return ".dia"; + + case FileType::dwarf_object: + return ".dwo"; + + case FileType::coverage_mangled: + return ".gcno-mangled"; + } + + return k_unknown_file_type; +} + +std::string +gcno_file_in_mangled_form(const Context& ctx) +{ + const auto& output_obj = ctx.args_info.output_obj; + const std::string abs_output_obj = + Util::is_absolute_path(output_obj) + ? output_obj + : fmt::format("{}/{}", ctx.apparent_cwd, output_obj); + std::string hashified_obj = abs_output_obj; + std::replace(hashified_obj.begin(), hashified_obj.end(), '/', '#'); + return Util::change_extension(hashified_obj, ".gcno"); +} + +std::string +gcno_file_in_unmangled_form(const Context& ctx) +{ + return Util::change_extension(ctx.args_info.output_obj, ".gcno"); +} + +Result::Reader::Reader(const std::string& result_path) + : m_result_path(result_path) +{ +} + +optional +Result::Reader::read(Consumer& consumer) +{ + log("Reading result {}", m_result_path); + + try { + if (read_result(consumer)) { + return nullopt; + } else { + return "No such result file"; + } + } catch (const Error& e) { + return e.what(); + } +} + +bool +Reader::read_result(Consumer& consumer) +{ + File file(m_result_path, "rb"); + if (!file) { + // Cache miss. + return false; + } + + CacheEntryReader cache_entry_reader(file.get(), k_magic, k_version); + + consumer.on_header(cache_entry_reader); + + uint8_t n_entries; + cache_entry_reader.read(n_entries); + + uint32_t i; + for (i = 0; i < n_entries; ++i) { + read_entry(cache_entry_reader, i, consumer); + } + + if (i != n_entries) { + throw Error("Too few entries (read {}, expected {})", i, n_entries); + } + + cache_entry_reader.finalize(); + return true; +} + +void +Reader::read_entry(CacheEntryReader& cache_entry_reader, + uint32_t entry_number, + Reader::Consumer& consumer) +{ + uint8_t marker; + cache_entry_reader.read(marker); + + switch (marker) { + case k_embedded_file_marker: + case k_raw_file_marker: + break; + + default: + throw Error("Unknown entry type: {}", marker); + } + + UnderlyingFileTypeInt type; + cache_entry_reader.read(type); + FileType file_type = FileType(type); + + uint64_t file_len; + cache_entry_reader.read(file_len); + + if (marker == k_embedded_file_marker) { + consumer.on_entry_start(entry_number, file_type, file_len, nullopt); + + uint8_t buf[READ_BUFFER_SIZE]; + size_t remain = file_len; + while (remain > 0) { + size_t n = std::min(remain, sizeof(buf)); + cache_entry_reader.read(buf, n); + consumer.on_entry_data(buf, n); + remain -= n; + } + } else { + ASSERT(marker == k_raw_file_marker); + + auto raw_path = get_raw_file_path(m_result_path, entry_number); + auto st = Stat::stat(raw_path, Stat::OnError::throw_error); + if (st.size() != file_len) { + throw Error("Bad file size of {} (actual {} bytes, expected {} bytes)", + raw_path, + st.size(), + file_len); + } + + consumer.on_entry_start(entry_number, file_type, file_len, raw_path); + } + + consumer.on_entry_end(); +} + +Writer::Writer(Context& ctx, const std::string& result_path) + : m_ctx(ctx), m_result_path(result_path) +{ +} + +void +Writer::write(FileType file_type, const std::string& file_path) +{ + m_entries_to_write.emplace_back(file_type, file_path); +} + +optional +Writer::finalize() +{ + try { + do_finalize(); + return nullopt; + } catch (const Error& e) { + return e.what(); + } +} + +void +Writer::do_finalize() +{ + uint64_t payload_size = 0; + payload_size += 1; // n_entries + for (const auto& pair : m_entries_to_write) { + const auto& path = pair.second; + auto st = Stat::stat(path, Stat::OnError::throw_error); + + payload_size += 1; // embedded_file_marker + payload_size += 1; // embedded_file_type + payload_size += 8; // data_len + payload_size += st.size(); // data + } + + AtomicFile atomic_result_file(m_result_path, AtomicFile::Mode::binary); + CacheEntryWriter writer(atomic_result_file.stream(), + k_magic, + k_version, + Compression::type_from_config(m_ctx.config), + Compression::level_from_config(m_ctx.config), + payload_size); + + writer.write(m_entries_to_write.size()); + + uint32_t entry_number = 0; + for (const auto& pair : m_entries_to_write) { + const auto file_type = pair.first; + const auto& path = pair.second; + log("Storing result {}", path); + + const bool store_raw = should_store_raw_file(m_ctx.config, file_type); + uint64_t file_size = Stat::stat(path, Stat::OnError::throw_error).size(); + + log("Storing {} file #{} {} ({} bytes) from {}", + store_raw ? "raw" : "embedded", + entry_number, + file_type_to_string(file_type), + file_size, + path); + + writer.write(store_raw ? k_raw_file_marker + : k_embedded_file_marker); + writer.write(UnderlyingFileTypeInt(file_type)); + writer.write(file_size); + + if (store_raw) { + write_raw_file_entry(path, entry_number); + } else { + write_embedded_file_entry(writer, path, file_size); + } + + ++entry_number; + } + + writer.finalize(); + atomic_result_file.commit(); +} + +void +Result::Writer::write_embedded_file_entry(CacheEntryWriter& writer, + const std::string& path, + uint64_t file_size) +{ + Fd file(open(path.c_str(), O_RDONLY | O_BINARY)); + if (!file) { + throw Error("Failed to open {} for reading", path); + } + + uint64_t remain = file_size; + while (remain > 0) { + uint8_t buf[READ_BUFFER_SIZE]; + size_t n = std::min(remain, static_cast(sizeof(buf))); + ssize_t bytes_read = read(*file, buf, n); + if (bytes_read == -1) { + if (errno == EINTR) { + continue; + } + throw Error("Error reading from {}: {}", path, strerror(errno)); + } + if (bytes_read == 0) { + throw Error("Error reading from {}: end of file", path); + } + writer.write(buf, bytes_read); + remain -= bytes_read; + } +} + +void +Result::Writer::write_raw_file_entry(const std::string& path, + uint32_t entry_number) +{ + const auto raw_file = get_raw_file_path(m_result_path, entry_number); + const auto old_stat = Stat::stat(raw_file); + try { + Util::clone_hard_link_or_copy_file(m_ctx, path, raw_file, true); + } catch (Error& e) { + throw Error( + "Failed to store {} as raw file {}: {}", path, raw_file, e.what()); + } + const auto new_stat = Stat::stat(raw_file); + m_ctx.counter_updates.increment( + Statistic::cache_size_kibibyte, + Util::size_change_kibibyte(old_stat, new_stat)); + m_ctx.counter_updates.increment(Statistic::files_in_cache, + (new_stat ? 1 : 0) - (old_stat ? 1 : 0)); +} + +} // namespace Result diff --git a/src/Result.hpp b/src/Result.hpp new file mode 100644 index 0000000..01068c9 --- /dev/null +++ b/src/Result.hpp @@ -0,0 +1,138 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "third_party/nonstd/optional.hpp" + +#include +#include +#include + +class CacheEntryReader; +class CacheEntryWriter; +class Context; + +namespace Result { + +extern const std::string k_file_suffix; +extern const uint8_t k_magic[4]; +extern const uint8_t k_version; + +extern const char* const k_unknown_file_type; + +using UnderlyingFileTypeInt = uint8_t; +enum class FileType : UnderlyingFileTypeInt { + // These values are written into the cache result file. This means they must + // never be changed or removed unless the result file version is incremented. + // Adding new values is OK. + + // The main output specified with -o or implicitly from the input filename. + object = 0, + + // Dependency file specified with -MF or implicitly from the output filename. + dependency = 1, + + // Text sent to standard output. + stderr_output = 2, + + // Coverage notes file generated by -ftest-coverage with filename in unmangled + // form, i.e. output file but with a .gcno extension. + coverage_unmangled = 3, + + // Stack usage file generated by -fstack-usage, i.e. output file but with a + // .su extension. + stackusage = 4, + + // Diagnostics output file specified by --serialize-diagnostics. + diagnostic = 5, + + // DWARF object file geenrated by -gsplit-dwarf, i.e. output file but with a + // .dwo extension. + dwarf_object = 6, + + // Coverage notes file generated by -ftest-coverage with filename in mangled + // form, i.e. full output file path but with a .gcno extension and with + // slashes replaced with hashes. + coverage_mangled = 7, +}; + +const char* file_type_to_string(FileType type); + +std::string gcno_file_in_mangled_form(const Context& ctx); +std::string gcno_file_in_unmangled_form(const Context& ctx); + +// This class knows how to read a result cache entry. +class Reader +{ +public: + Reader(const std::string& result_path); + + class Consumer + { + public: + virtual ~Consumer() = default; + + virtual void on_header(CacheEntryReader& cache_entry_reader) = 0; + virtual void on_entry_start(uint32_t entry_number, + FileType file_type, + uint64_t file_len, + nonstd::optional raw_file) = 0; + virtual void on_entry_data(const uint8_t* data, size_t size) = 0; + virtual void on_entry_end() = 0; + }; + + // Returns error message on error, otherwise nonstd::nullopt. + nonstd::optional read(Consumer& consumer); + +private: + const std::string m_result_path; + + bool read_result(Consumer& consumer); + void read_entry(CacheEntryReader& cache_entry_reader, + uint32_t entry_number, + Reader::Consumer& consumer); +}; + +// This class knows how to write a result cache entry. +class Writer +{ +public: + Writer(Context& ctx, const std::string& result_path); + + // Register a file to include in the result. Does not throw. + void write(FileType file_type, const std::string& file_path); + + // Write registered files to the result. Returns an error message on error. + nonstd::optional finalize(); + +private: + Context& m_ctx; + const std::string m_result_path; + std::vector> m_entries_to_write; + + void do_finalize(); + static void write_embedded_file_entry(CacheEntryWriter& writer, + const std::string& path, + uint64_t file_size); + void write_raw_file_entry(const std::string& path, uint32_t entry_number); +}; + +} // namespace Result diff --git a/src/ResultDumper.cpp b/src/ResultDumper.cpp new file mode 100644 index 0000000..7c740af --- /dev/null +++ b/src/ResultDumper.cpp @@ -0,0 +1,59 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "ResultDumper.hpp" + +#include "CacheEntryReader.hpp" +#include "Context.hpp" +#include "Logging.hpp" + +using nonstd::optional; + +ResultDumper::ResultDumper(FILE* stream) : m_stream(stream) +{ +} + +void +ResultDumper::on_header(CacheEntryReader& cache_entry_reader) +{ + cache_entry_reader.dump_header(m_stream); +} + +void +ResultDumper::on_entry_start(uint32_t entry_number, + Result::FileType file_type, + uint64_t file_len, + optional raw_file) +{ + fmt::print(m_stream, + "{} file #{}: {} ({} bytes)\n", + raw_file ? "Raw" : "Embedded", + entry_number, + Result::file_type_to_string(file_type), + file_len); +} + +void +ResultDumper::on_entry_data(const uint8_t* /*data*/, size_t /*size*/) +{ +} + +void +ResultDumper::on_entry_end() +{ +} diff --git a/src/ResultDumper.hpp b/src/ResultDumper.hpp new file mode 100644 index 0000000..85602d6 --- /dev/null +++ b/src/ResultDumper.hpp @@ -0,0 +1,41 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Result.hpp" + +// This class dumps information about the result entry to `stream`. +class ResultDumper : public Result::Reader::Consumer +{ +public: + ResultDumper(FILE* stream); + + void on_header(CacheEntryReader& cache_entry_reader) override; + void on_entry_start(uint32_t entry_number, + Result::FileType file_type, + uint64_t file_len, + nonstd::optional raw_file) override; + void on_entry_data(const uint8_t* data, size_t size) override; + void on_entry_end() override; + +private: + FILE* m_stream; +}; diff --git a/src/ResultExtractor.cpp b/src/ResultExtractor.cpp new file mode 100644 index 0000000..10b8189 --- /dev/null +++ b/src/ResultExtractor.cpp @@ -0,0 +1,84 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "ResultExtractor.hpp" + +#include "Util.hpp" + +ResultExtractor::ResultExtractor(const std::string& directory) + : m_directory(directory) +{ +} + +void +ResultExtractor::on_header(CacheEntryReader& /*cache_entry_reader*/) +{ +} + +void +ResultExtractor::on_entry_start(uint32_t /*entry_number*/, + Result::FileType file_type, + uint64_t /*file_len*/, + nonstd::optional raw_file) +{ + std::string suffix = Result::file_type_to_string(file_type); + if (suffix == Result::k_unknown_file_type) { + suffix = fmt::format(".type_{}", file_type); + } else if (suffix[0] == '<') { + suffix[0] = '.'; + suffix.resize(suffix.length() - 1); + } + + m_dest_path = fmt::format("{}/ccache-result{}", m_directory, suffix); + + if (!raw_file) { + m_dest_fd = Fd( + open(m_dest_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666)); + if (!m_dest_fd) { + throw Error( + "Failed to open {} for writing: {}", m_dest_path, strerror(errno)); + } + } else { + try { + Util::copy_file(*raw_file, m_dest_path, false); + } catch (Error& e) { + throw Error( + "Failed to copy {} to {}: {}", *raw_file, m_dest_path, e.what()); + } + } +} + +void +ResultExtractor::on_entry_data(const uint8_t* data, size_t size) +{ + ASSERT(m_dest_fd); + + try { + Util::write_fd(*m_dest_fd, data, size); + } catch (Error& e) { + throw Error("Failed to write to {}: {}", m_dest_path, e.what()); + } +} + +void +ResultExtractor::on_entry_end() +{ + if (m_dest_fd) { + m_dest_fd.close(); + } +} diff --git a/src/ResultExtractor.hpp b/src/ResultExtractor.hpp new file mode 100644 index 0000000..f4b8f3b --- /dev/null +++ b/src/ResultExtractor.hpp @@ -0,0 +1,46 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Fd.hpp" +#include "Result.hpp" + +class Context; + +// This class extracts the parts of a result entry to a directory. +class ResultExtractor : public Result::Reader::Consumer +{ +public: + ResultExtractor(const std::string& directory); + + void on_header(CacheEntryReader& cache_entry_reader) override; + void on_entry_start(uint32_t entry_number, + Result::FileType file_type, + uint64_t file_len, + nonstd::optional raw_file) override; + void on_entry_data(const uint8_t* data, size_t size) override; + void on_entry_end() override; + +private: + const std::string m_directory; + Fd m_dest_fd; + std::string m_dest_path; +}; diff --git a/src/ResultRetriever.cpp b/src/ResultRetriever.cpp new file mode 100644 index 0000000..263f494 --- /dev/null +++ b/src/ResultRetriever.cpp @@ -0,0 +1,180 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "ResultRetriever.hpp" + +#include "Context.hpp" +#include "Logging.hpp" + +using Logging::log; +using Result::FileType; + +ResultRetriever::ResultRetriever(Context& ctx, bool rewrite_dependency_target) + : m_ctx(ctx), m_rewrite_dependency_target(rewrite_dependency_target) +{ +} + +void +ResultRetriever::on_header(CacheEntryReader& /*cache_entry_reader*/) +{ +} + +void +ResultRetriever::on_entry_start(uint32_t entry_number, + FileType file_type, + uint64_t file_len, + nonstd::optional raw_file) +{ + std::string dest_path; + + m_dest_file_type = file_type; + + switch (file_type) { + case FileType::object: + dest_path = m_ctx.args_info.output_obj; + break; + + case FileType::dependency: + if (m_ctx.args_info.generating_dependencies) { + dest_path = m_ctx.args_info.output_dep; + m_dest_data.reserve(file_len); + } + break; + + case FileType::stderr_output: + m_dest_data.reserve(file_len); + return; + + case FileType::coverage_unmangled: + if (m_ctx.args_info.generating_coverage) { + dest_path = Util::change_extension(m_ctx.args_info.output_obj, ".gcno"); + } + break; + + case FileType::stackusage: + if (m_ctx.args_info.generating_stackusage) { + dest_path = m_ctx.args_info.output_su; + } + break; + + case FileType::diagnostic: + if (m_ctx.args_info.generating_diagnostics) { + dest_path = m_ctx.args_info.output_dia; + } + break; + + case FileType::dwarf_object: + if (m_ctx.args_info.seen_split_dwarf + && m_ctx.args_info.output_obj != "/dev/null") { + dest_path = m_ctx.args_info.output_dwo; + } + break; + + case FileType::coverage_mangled: + if (m_ctx.args_info.generating_coverage) { + dest_path = Result::gcno_file_in_mangled_form(m_ctx); + } + break; + } + + if (dest_path.empty()) { + log("Not copying"); + } else if (dest_path == "/dev/null") { + log("Not copying to /dev/null"); + } else { + log("Retrieving {} file #{} {} ({} bytes)", + raw_file ? "raw" : "embedded", + entry_number, + Result::file_type_to_string(file_type), + file_len); + + if (raw_file) { + Util::clone_hard_link_or_copy_file(m_ctx, *raw_file, dest_path, false); + + // Update modification timestamp to save the file from LRU cleanup (and, + // if hard-linked, to make the object file newer than the source file). + Util::update_mtime(*raw_file); + } else { + log("Copying to {}", dest_path); + m_dest_fd = Fd( + open(dest_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666)); + if (!m_dest_fd) { + throw Error( + "Failed to open {} for writing: {}", dest_path, strerror(errno)); + } + m_dest_path = dest_path; + } + } +} + +void +ResultRetriever::on_entry_data(const uint8_t* data, size_t size) +{ + ASSERT((m_dest_file_type == FileType::stderr_output && !m_dest_fd) + || (m_dest_file_type != FileType::stderr_output && m_dest_fd)); + + if (m_dest_file_type == FileType::stderr_output + || (m_dest_file_type == FileType::dependency && !m_dest_path.empty())) { + m_dest_data.append(reinterpret_cast(data), size); + } else { + try { + Util::write_fd(*m_dest_fd, data, size); + } catch (Error& e) { + throw Error("Failed to write to {}: {}", m_dest_path, e.what()); + } + } +} + +void +ResultRetriever::on_entry_end() +{ + if (m_dest_file_type == FileType::stderr_output) { + Util::send_to_stderr(m_ctx, m_dest_data); + } else if (m_dest_file_type == FileType::dependency && !m_dest_path.empty()) { + write_dependency_file(); + } + + if (m_dest_fd) { + m_dest_fd.close(); + } + m_dest_path.clear(); + m_dest_data.clear(); +} + +void +ResultRetriever::write_dependency_file() +{ + try { + size_t start_pos = 0; + if (m_rewrite_dependency_target) { + size_t colon_pos = m_dest_data.find(':'); + if (colon_pos != std::string::npos) { + Util::write_fd(*m_dest_fd, + m_ctx.args_info.output_obj.data(), + m_ctx.args_info.output_obj.length()); + start_pos = colon_pos; + } + } + + Util::write_fd(*m_dest_fd, + m_dest_data.data() + start_pos, + m_dest_data.length() - start_pos); + } catch (Error& e) { + throw Error("Failed to write to {}: {}", m_dest_path, e.what()); + } +} diff --git a/src/ResultRetriever.hpp b/src/ResultRetriever.hpp new file mode 100644 index 0000000..609f15f --- /dev/null +++ b/src/ResultRetriever.hpp @@ -0,0 +1,59 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Fd.hpp" +#include "Result.hpp" + +class Context; + +// This class retrieves a result entry to the local file system. +class ResultRetriever : public Result::Reader::Consumer +{ +public: + ResultRetriever(Context& ctx, bool rewrite_dependency_target); + + void on_header(CacheEntryReader& cache_entry_reader) override; + void on_entry_start(uint32_t entry_number, + Result::FileType file_type, + uint64_t file_len, + nonstd::optional raw_file) override; + void on_entry_data(const uint8_t* data, size_t size) override; + void on_entry_end() override; + +private: + Context& m_ctx; + Result::FileType m_dest_file_type; + Fd m_dest_fd; + std::string m_dest_path; + + // Collects the full data of stderr output (since we want to potentially strip + // color codes which could span chunk boundaries) or dependency data (since we + // potentially want to rewrite the dependency target which in theory can span + // a chunk boundary). + std::string m_dest_data; + + // Whether to rewrite the first part of the dependency file data to the + // destination object file. + const bool m_rewrite_dependency_target; + + void write_dependency_file(); +}; diff --git a/src/SignalHandler.cpp b/src/SignalHandler.cpp new file mode 100644 index 0000000..37604c2 --- /dev/null +++ b/src/SignalHandler.cpp @@ -0,0 +1,145 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "SignalHandler.hpp" + +#include "assertions.hpp" + +#ifndef _WIN32 + +# include "Context.hpp" + +namespace { + +SignalHandler* g_the_signal_handler = nullptr; +sigset_t g_fatal_signal_set; + +void +register_signal_handler(int signum) +{ + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = SignalHandler::on_signal; + act.sa_mask = g_fatal_signal_set; +# ifdef SA_RESTART + act.sa_flags = SA_RESTART; +# endif + sigaction(signum, &act, nullptr); +} + +} // namespace + +SignalHandler::SignalHandler(Context& ctx) : m_ctx(ctx) +{ + ASSERT(!g_the_signal_handler); + g_the_signal_handler = this; + + sigemptyset(&g_fatal_signal_set); + sigaddset(&g_fatal_signal_set, SIGINT); + sigaddset(&g_fatal_signal_set, SIGTERM); +# ifdef SIGHUP + sigaddset(&g_fatal_signal_set, SIGHUP); +# endif +# ifdef SIGQUIT + sigaddset(&g_fatal_signal_set, SIGQUIT); +# endif + + register_signal_handler(SIGINT); + register_signal_handler(SIGTERM); +# ifdef SIGHUP + register_signal_handler(SIGHUP); +# endif +# ifdef SIGQUIT + register_signal_handler(SIGQUIT); +# endif +} + +SignalHandler::~SignalHandler() +{ + ASSERT(g_the_signal_handler); + g_the_signal_handler = nullptr; +} + +void +SignalHandler::on_signal(int signum) +{ + ASSERT(g_the_signal_handler); + Context& ctx = g_the_signal_handler->m_ctx; + + // Unregister handler for this signal so that we can send the signal to + // ourselves at the end of the handler. + signal(signum, SIG_DFL); + + // If ccache was killed explicitly, then bring the compiler subprocess (if + // any) with us as well. + if (signum == SIGTERM && ctx.compiler_pid != 0 + && waitpid(ctx.compiler_pid, nullptr, WNOHANG) == 0) { + kill(ctx.compiler_pid, signum); + } + + ctx.unlink_pending_tmp_files_signal_safe(); + + if (ctx.compiler_pid != 0) { + // Wait for compiler subprocess to exit before we snuff it. + waitpid(ctx.compiler_pid, nullptr, 0); + } + + // Resend signal to ourselves to exit properly after returning from the + // handler. + kill(getpid(), signum); +} + +#else // !_WIN32 + +SignalHandler::SignalHandler(Context& ctx) : m_ctx(ctx) +{ +} + +SignalHandler::~SignalHandler() +{ +} + +#endif // !_WIN32 + +void +SignalHandler::block_signals() +{ +#ifndef _WIN32 + sigprocmask(SIG_BLOCK, &g_fatal_signal_set, nullptr); +#endif +} + +void +SignalHandler::unblock_signals() +{ +#ifndef _WIN32 + sigset_t empty; + sigemptyset(&empty); + sigprocmask(SIG_SETMASK, &empty, nullptr); +#endif +} + +SignalHandlerBlocker::SignalHandlerBlocker() +{ + SignalHandler::block_signals(); +} + +SignalHandlerBlocker::~SignalHandlerBlocker() +{ + SignalHandler::unblock_signals(); +} diff --git a/src/SignalHandler.hpp b/src/SignalHandler.hpp new file mode 100644 index 0000000..50e7b0e --- /dev/null +++ b/src/SignalHandler.hpp @@ -0,0 +1,46 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "signal.h" + +class Context; + +class SignalHandler +{ +public: + SignalHandler(Context& ctx); + ~SignalHandler(); + + static void on_signal(int signum); + static void block_signals(); + static void unblock_signals(); + +private: + Context& m_ctx; +}; + +class SignalHandlerBlocker +{ +public: + SignalHandlerBlocker(); + ~SignalHandlerBlocker(); +}; diff --git a/src/Stat.cpp b/src/Stat.cpp new file mode 100644 index 0000000..80805bb --- /dev/null +++ b/src/Stat.cpp @@ -0,0 +1,45 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "Stat.hpp" + +#include "Logging.hpp" + +using Logging::log; + +Stat::Stat(StatFunction stat_function, + const std::string& path, + Stat::OnError on_error) +{ + int result = stat_function(path.c_str(), &m_stat); + if (result == 0) { + m_errno = 0; + } else { + m_errno = errno; + if (on_error == OnError::throw_error) { + throw Error("failed to stat {}: {}", path, strerror(errno)); + } + if (on_error == OnError::log) { + log("Failed to stat {}: {}", path, strerror(errno)); + } + + // The file is missing, so just zero fill the stat structure. This will + // make e.g. the is_*() methods return false and mtime() will be 0, etc. + memset(&m_stat, '\0', sizeof(m_stat)); + } +} diff --git a/src/Stat.hpp b/src/Stat.hpp new file mode 100644 index 0000000..721b824 --- /dev/null +++ b/src/Stat.hpp @@ -0,0 +1,227 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "exceptions.hpp" + +#include + +class Stat +{ +public: + enum class OnError { + // Ignore any error (including missing file) from the underlying stat call. + // On error, error_number() will return the error number (AKA errno) and + // the query functions will return 0 or false. + ignore, + // Like above but log an error message as well. + log, + // Throw Error on errors (including missing file). + throw_error, + }; + + // Create an empty stat result. operator bool() will return false, + // error_number() will return -1 and other accessors will return false or 0. + Stat(); + + // Run stat(2). + // + // Arguments: + // - path: Path to stat. + // - on_error: What to do on errors (including missing file). + static Stat stat(const std::string& path, OnError on_error = OnError::ignore); + + // Run lstat(2) if available, otherwise stat(2). + // + // Arguments: + // - path: Path to (l)stat. + // - on_error: What to do on errors (including missing file). + static Stat lstat(const std::string& path, + OnError on_error = OnError::ignore); + + // Return true if the file could be (l)stat-ed (i.e., the file exists), + // otherwise false. + operator bool() const; + + // Return whether this object refers to the same device and i-node as `other` + // does. + bool same_inode_as(const Stat& other) const; + + // Return errno from the (l)stat call (0 if successful). + int error_number() const; + + dev_t device() const; + ino_t inode() const; + mode_t mode() const; + time_t ctime() const; + time_t mtime() const; + uint64_t size() const; + + uint64_t size_on_disk() const; + + bool is_directory() const; + bool is_regular() const; + bool is_symlink() const; + +#ifdef HAVE_STRUCT_STAT_ST_CTIM + timespec ctim() const; +#endif + +#ifdef HAVE_STRUCT_STAT_ST_MTIM + timespec mtim() const; +#endif + +protected: + using StatFunction = int (*)(const char*, struct stat*); + + Stat(StatFunction stat_function, const std::string& path, OnError on_error); + +private: + struct stat m_stat; + int m_errno; + + bool operator==(const Stat&) const; + bool operator!=(const Stat&) const; +}; + +inline Stat::Stat() : m_stat{}, m_errno(-1) +{ +} + +inline Stat +Stat::stat(const std::string& path, OnError on_error) +{ + return Stat(::stat, path, on_error); +} + +inline Stat +Stat::lstat(const std::string& path, OnError on_error) +{ + return Stat( +#ifdef _WIN32 + ::stat, +#else + ::lstat, +#endif + path, + on_error); +} + +inline Stat::operator bool() const +{ + return m_errno == 0; +} + +inline bool +Stat::same_inode_as(const Stat& other) const +{ + return device() == other.device() && inode() == other.inode(); +} + +inline int +Stat::error_number() const +{ + return m_errno; +} + +inline dev_t +Stat::device() const +{ + return m_stat.st_dev; +} + +inline ino_t +Stat::inode() const +{ + return m_stat.st_ino; +} + +inline mode_t +Stat::mode() const +{ + return m_stat.st_mode; +} + +inline time_t +Stat::ctime() const +{ + return m_stat.st_ctime; +} + +inline time_t +Stat::mtime() const +{ + return m_stat.st_mtime; +} + +inline uint64_t +Stat::size() const +{ + return m_stat.st_size; +} + +inline uint64_t +Stat::size_on_disk() const +{ +#ifdef _WIN32 + return (size() + 1023) & ~1023; +#else + return m_stat.st_blocks * 512; +#endif +} + +inline bool +Stat::is_directory() const +{ + return S_ISDIR(mode()); +} + +inline bool +Stat::is_symlink() const +{ +#ifndef _WIN32 + return S_ISLNK(mode()); +#else + return false; +#endif +} + +inline bool +Stat::is_regular() const +{ + return S_ISREG(mode()); +} + +#ifdef HAVE_STRUCT_STAT_ST_CTIM +inline timespec +Stat::ctim() const +{ + return m_stat.st_ctim; +} +#endif + +#ifdef HAVE_STRUCT_STAT_ST_MTIM +inline timespec +Stat::mtim() const +{ + return m_stat.st_mtim; +} +#endif diff --git a/src/Statistics.cpp b/src/Statistics.cpp new file mode 100644 index 0000000..0861de6 --- /dev/null +++ b/src/Statistics.cpp @@ -0,0 +1,360 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "Statistics.hpp" + +#include "AtomicFile.hpp" +#include "Config.hpp" +#include "Lockfile.hpp" +#include "Logging.hpp" +#include "Util.hpp" +#include "exceptions.hpp" + +using Logging::log; +using nonstd::nullopt; +using nonstd::optional; + +const unsigned FLAG_NOZERO = 1; // don't zero with the -z option +const unsigned FLAG_ALWAYS = 2; // always show, even if zero +const unsigned FLAG_NEVER = 4; // never show + +using Logging::log; +using nonstd::nullopt; +using nonstd::optional; + +// Returns a formatted version of a statistics value, or the empty string if the +// statistics line shouldn't be printed. +using FormatFunction = std::string (*)(uint64_t value); + +static std::string +format_size(uint64_t size) +{ + return fmt::format("{:>11}", Util::format_human_readable_size(size)); +} + +static std::string +format_size_times_1024(uint64_t size) +{ + return format_size(size * 1024); +} + +static std::string +format_timestamp(uint64_t timestamp) +{ + if (timestamp > 0) { + const auto tm = Util::localtime(timestamp); + char buffer[100] = "?"; + if (tm) { + strftime(buffer, sizeof(buffer), "%c", &*tm); + } + return std::string(" ") + buffer; + } else { + return {}; + } +} + +static double +hit_rate(const Counters& counters) +{ + const uint64_t direct = counters.get(Statistic::direct_cache_hit); + const uint64_t preprocessed = counters.get(Statistic::preprocessed_cache_hit); + const uint64_t hit = direct + preprocessed; + const uint64_t miss = counters.get(Statistic::cache_miss); + const uint64_t total = hit + miss; + return total > 0 ? (100.0 * hit) / total : 0.0; +} + +static void +for_each_level_1_and_2_stats_file( + const std::string& cache_dir, + const std::function function) +{ + for (size_t level_1 = 0; level_1 <= 0xF; ++level_1) { + function(fmt::format("{}/{:x}/stats", cache_dir, level_1)); + for (size_t level_2 = 0; level_2 <= 0xF; ++level_2) { + function(fmt::format("{}/{:x}/{:x}/stats", cache_dir, level_1, level_2)); + } + } +} + +static std::pair +collect_counters(const Config& config) +{ + Counters counters; + uint64_t zero_timestamp = 0; + time_t last_updated = 0; + + // Add up the stats in each directory. + for_each_level_1_and_2_stats_file( + config.cache_dir(), [&](const std::string& path) { + counters.set(Statistic::stats_zeroed_timestamp, 0); // Don't add + counters.increment(Statistics::read(path)); + zero_timestamp = std::max(counters.get(Statistic::stats_zeroed_timestamp), + zero_timestamp); + last_updated = std::max(last_updated, Stat::stat(path).mtime()); + }); + + counters.set(Statistic::stats_zeroed_timestamp, zero_timestamp); + return std::make_pair(counters, last_updated); +} + +namespace { + +struct StatisticsField +{ + StatisticsField(Statistic statistic_, + const char* id_, + const char* message_, + unsigned flags_ = 0, + FormatFunction format_ = nullptr) + : statistic(statistic_), + id(id_), + message(message_), + flags(flags_), + format(format_) + { + } + + const Statistic statistic; + const char* const id; // for --print-stats + const char* const message; // for --show-stats + const unsigned flags; // bitmask of FLAG_* values + const FormatFunction format; // nullptr -> use plain integer format +}; + +} // namespace + +#define STATISTICS_FIELD(id, ...) \ + { \ + Statistic::id, #id, __VA_ARGS__ \ + } + +// Statistics fields in display order. +const StatisticsField k_statistics_fields[] = { + STATISTICS_FIELD( + stats_zeroed_timestamp, "stats zeroed", FLAG_ALWAYS, format_timestamp), + STATISTICS_FIELD(direct_cache_hit, "cache hit (direct)", FLAG_ALWAYS), + STATISTICS_FIELD( + preprocessed_cache_hit, "cache hit (preprocessed)", FLAG_ALWAYS), + STATISTICS_FIELD(cache_miss, "cache miss", FLAG_ALWAYS), + STATISTICS_FIELD(called_for_link, "called for link"), + STATISTICS_FIELD(called_for_preprocessing, "called for preprocessing"), + STATISTICS_FIELD(multiple_source_files, "multiple source files"), + STATISTICS_FIELD(compiler_produced_stdout, "compiler produced stdout"), + STATISTICS_FIELD(compiler_produced_no_output, "compiler produced no output"), + STATISTICS_FIELD(compiler_produced_empty_output, + "compiler produced empty output"), + STATISTICS_FIELD(compile_failed, "compile failed"), + STATISTICS_FIELD(internal_error, "ccache internal error"), + STATISTICS_FIELD(preprocessor_error, "preprocessor error"), + STATISTICS_FIELD(could_not_use_precompiled_header, + "can't use precompiled header"), + STATISTICS_FIELD(could_not_use_modules, "can't use modules"), + STATISTICS_FIELD(could_not_find_compiler, "couldn't find the compiler"), + STATISTICS_FIELD(missing_cache_file, "cache file missing"), + STATISTICS_FIELD(bad_compiler_arguments, "bad compiler arguments"), + STATISTICS_FIELD(unsupported_source_language, "unsupported source language"), + STATISTICS_FIELD(compiler_check_failed, "compiler check failed"), + STATISTICS_FIELD(autoconf_test, "autoconf compile/link"), + STATISTICS_FIELD(unsupported_compiler_option, "unsupported compiler option"), + STATISTICS_FIELD(unsupported_code_directive, "unsupported code directive"), + STATISTICS_FIELD(output_to_stdout, "output to stdout"), + STATISTICS_FIELD(bad_output_file, "could not write to output file"), + STATISTICS_FIELD(no_input_file, "no input file"), + STATISTICS_FIELD(error_hashing_extra_file, "error hashing extra file"), + STATISTICS_FIELD(cleanups_performed, "cleanups performed", FLAG_ALWAYS), + STATISTICS_FIELD(files_in_cache, "files in cache", FLAG_NOZERO | FLAG_ALWAYS), + STATISTICS_FIELD(cache_size_kibibyte, + "cache size", + FLAG_NOZERO | FLAG_ALWAYS, + format_size_times_1024), + STATISTICS_FIELD(obsolete_max_files, "OBSOLETE", FLAG_NOZERO | FLAG_NEVER), + STATISTICS_FIELD(obsolete_max_size, "OBSOLETE", FLAG_NOZERO | FLAG_NEVER), + STATISTICS_FIELD(none, nullptr), +}; + +namespace Statistics { + +Counters +read(const std::string& path) +{ + Counters counters; + + std::string data; + try { + data = Util::read_file(path); + } catch (const Error&) { + // Ignore. + return counters; + } + + size_t i = 0; + const char* str = data.c_str(); + while (true) { + char* end; + const uint64_t value = std::strtoull(str, &end, 10); + if (end == str) { + break; + } + counters.set_raw(i, value); + ++i; + str = end; + } + + return counters; +} + +optional +update(const std::string& path, + std::function function) +{ + Lockfile lock(path); + if (!lock.acquired()) { + log("failed to acquire lock for {}", path); + return nullopt; + } + + auto counters = Statistics::read(path); + function(counters); + + AtomicFile file(path, AtomicFile::Mode::text); + for (size_t i = 0; i < counters.size(); ++i) { + file.write(fmt::format("{}\n", counters.get_raw(i))); + } + try { + file.commit(); + } catch (const Error& e) { + // Make failure to write a stats file a soft error since it's not + // important enough to fail whole the process and also because it is + // called in the Context destructor. + log("Error: {}", e.what()); + } + + return counters; +} + +optional +get_result(const Counters& counters) +{ + for (const auto& field : k_statistics_fields) { + if (counters.get(field.statistic) != 0 && !(field.flags & FLAG_NOZERO)) { + return field.message; + } + } + return nullopt; +} + +void +zero_all_counters(const Config& config) +{ + const time_t timestamp = time(nullptr); + + for_each_level_1_and_2_stats_file( + config.cache_dir(), [=](const std::string& path) { + Statistics::update(path, [=](Counters& cs) { + for (size_t i = 0; k_statistics_fields[i].message; ++i) { + if (!(k_statistics_fields[i].flags & FLAG_NOZERO)) { + cs.set(k_statistics_fields[i].statistic, 0); + } + } + cs.set(Statistic::stats_zeroed_timestamp, timestamp); + }); + }); +} + +std::string +format_human_readable(const Config& config) +{ + Counters counters; + time_t last_updated; + std::tie(counters, last_updated) = collect_counters(config); + std::string result; + + result += fmt::format("{:36}{}\n", "cache directory", config.cache_dir()); + result += + fmt::format("{:36}{}\n", "primary config", config.primary_config_path()); + result += fmt::format( + "{:36}{}\n", "secondary config (readonly)", config.secondary_config_path()); + if (last_updated > 0) { + const auto tm = Util::localtime(last_updated); + char timestamp[100] = "?"; + if (tm) { + strftime(timestamp, sizeof(timestamp), "%c", &*tm); + } + result += fmt::format("{:36}{}\n", "stats updated", timestamp); + } + + // ...and display them. + for (size_t i = 0; k_statistics_fields[i].message; i++) { + const Statistic statistic = k_statistics_fields[i].statistic; + + if (k_statistics_fields[i].flags & FLAG_NEVER) { + continue; + } + if (counters.get(statistic) == 0 + && !(k_statistics_fields[i].flags & FLAG_ALWAYS)) { + continue; + } + + const std::string value = + k_statistics_fields[i].format + ? k_statistics_fields[i].format(counters.get(statistic)) + : fmt::format("{:8}", counters.get(statistic)); + if (!value.empty()) { + result += fmt::format("{:32}{}\n", k_statistics_fields[i].message, value); + } + + if (statistic == Statistic::cache_miss) { + double percent = hit_rate(counters); + result += fmt::format("{:34}{:6.2f} %\n", "cache hit rate", percent); + } + } + + if (config.max_files() != 0) { + result += fmt::format("{:32}{:8}\n", "max files", config.max_files()); + } + if (config.max_size() != 0) { + result += fmt::format( + "{:32}{}\n", "max cache size", format_size(config.max_size())); + } + + return result; +} + +std::string +format_machine_readable(const Config& config) +{ + Counters counters; + time_t last_updated; + std::tie(counters, last_updated) = collect_counters(config); + std::string result; + + result += fmt::format("stats_updated_timestamp\t{}\n", last_updated); + + for (size_t i = 0; k_statistics_fields[i].message; i++) { + if (!(k_statistics_fields[i].flags & FLAG_NEVER)) { + result += fmt::format("{}\t{}\n", + k_statistics_fields[i].id, + counters.get(k_statistics_fields[i].statistic)); + } + } + + return result; +} + +} // namespace Statistics diff --git a/src/Statistics.hpp b/src/Statistics.hpp new file mode 100644 index 0000000..61721b4 --- /dev/null +++ b/src/Statistics.hpp @@ -0,0 +1,96 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Counters.hpp" + +#include "third_party/nonstd/optional.hpp" + +#include +#include + +class Config; + +// Statistics fields in storage order. +enum class Statistic { + none = 0, + compiler_produced_stdout = 1, + compile_failed = 2, + internal_error = 3, + cache_miss = 4, + preprocessor_error = 5, + could_not_find_compiler = 6, + missing_cache_file = 7, + preprocessed_cache_hit = 8, + bad_compiler_arguments = 9, + called_for_link = 10, + files_in_cache = 11, + cache_size_kibibyte = 12, + obsolete_max_files = 13, + obsolete_max_size = 14, + unsupported_source_language = 15, + bad_output_file = 16, + no_input_file = 17, + multiple_source_files = 18, + autoconf_test = 19, + unsupported_compiler_option = 20, + output_to_stdout = 21, + direct_cache_hit = 22, + compiler_produced_no_output = 23, + compiler_produced_empty_output = 24, + error_hashing_extra_file = 25, + compiler_check_failed = 26, + could_not_use_precompiled_header = 27, + called_for_preprocessing = 28, + cleanups_performed = 29, + unsupported_code_directive = 30, + stats_zeroed_timestamp = 31, + could_not_use_modules = 32, + + END +}; + +namespace Statistics { + +// Read counters from `path`. No lock is acquired. +Counters read(const std::string& path); + +// Acquire a lock, read counters from `path`, call `function` with the counters, +// write the counters to `path` and release the lock. Returns the resulting +// counters or nullopt on error (e.g. if the lock could not be acquired). +nonstd::optional update(const std::string& path, + std::function); + +// Return a human-readable string representing the final ccache result, or +// nullopt if there was no result. +nonstd::optional get_result(const Counters& counters); + +// Zero all statistics counters except those tracking cache size and number of +// files in the cache. +void zero_all_counters(const Config& config); + +// Format cache statistics in human-readable format. +std::string format_human_readable(const Config& config); + +// Format cache statistics in machine-readable format. +std::string format_machine_readable(const Config& config); + +} // namespace Statistics diff --git a/src/counters.h b/src/StdMakeUnique.hpp similarity index 65% rename from src/counters.h rename to src/StdMakeUnique.hpp index 91dd75e..829d2d5 100644 --- a/src/counters.h +++ b/src/StdMakeUnique.hpp @@ -1,4 +1,6 @@ -// Copyright (C) 2010-2016 Joel Rosdahl +// Copyright (C) 2019 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the Free @@ -14,19 +16,17 @@ // this program; if not, write to the Free Software Foundation, Inc., 51 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -#ifndef COUNTERS_H -#define COUNTERS_H - -#include +#pragma once -struct counters { - unsigned *data; // counter value - size_t size; // logical array size - size_t allocated; // allocated size -}; - -struct counters *counters_init(size_t initial_size); -void counters_resize(struct counters *c, size_t new_size); -void counters_free(struct counters *c); +namespace std { +#if __cplusplus < 201402L +template +inline unique_ptr +make_unique(TArgs&&... args) +{ + return unique_ptr(new T(std::forward(args)...)); +} #endif + +} // namespace std diff --git a/src/TemporaryFile.cpp b/src/TemporaryFile.cpp new file mode 100644 index 0000000..0c64f5e --- /dev/null +++ b/src/TemporaryFile.cpp @@ -0,0 +1,75 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "TemporaryFile.hpp" + +#include "Util.hpp" + +using nonstd::string_view; + +namespace { + +#ifndef _WIN32 +mode_t +get_umask() +{ + static bool mask_retrieved = false; + static mode_t mask; + if (!mask_retrieved) { + mask = umask(0); + umask(mask); + mask_retrieved = true; + } + return mask; +} +#endif + +#ifndef HAVE_MKSTEMP +// Cheap and nasty mkstemp replacement. +int +mkstemp(char* name_template) +{ +# ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# endif + mktemp(name_template); +# ifdef __GNUC__ +# pragma GCC diagnostic pop +# endif + return open(name_template, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600); +} +#endif + +} // namespace + +TemporaryFile::TemporaryFile(string_view path_prefix) + : path(std::string(path_prefix) + ".XXXXXX") +{ + Util::ensure_dir_exists(Util::dir_name(path)); + fd = Fd(mkstemp(&path[0])); + if (!fd) { + throw Fatal( + "Failed to create temporary file for {}: {}", path, strerror(errno)); + } + + Util::set_cloexec_flag(*fd); +#ifndef _WIN32 + fchmod(*fd, 0666 & ~get_umask()); +#endif +} diff --git a/src/TemporaryFile.hpp b/src/TemporaryFile.hpp new file mode 100644 index 0000000..0538d9a --- /dev/null +++ b/src/TemporaryFile.hpp @@ -0,0 +1,48 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "Fd.hpp" + +#include "third_party/nonstd/string_view.hpp" + +#include + +// This class represents a unique temporary file created by mkstemp. The file is +// not deleted by the destructor. +class TemporaryFile +{ +public: + // `path_prefix` is the base path. The resulting filename will be this path + // plus a unique suffix. If `path_prefix` refers to a nonexistent directory + // the directory will be created if possible.` + TemporaryFile(nonstd::string_view path_prefix); + + TemporaryFile(TemporaryFile&& other) noexcept = default; + + // Note: Should be declared noexcept, but since GCC 4.8 trips on it, don't do + // that for now. + TemporaryFile& operator=(TemporaryFile&& other) = default; + + // The resulting open file descriptor in read/write mode. Unset on error. + Fd fd; + + // The actual filename. Empty on error. + std::string path; +}; diff --git a/src/ThreadPool.cpp b/src/ThreadPool.cpp new file mode 100644 index 0000000..a1d931a --- /dev/null +++ b/src/ThreadPool.cpp @@ -0,0 +1,84 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "ThreadPool.hpp" + +ThreadPool::ThreadPool(size_t number_of_threads, size_t task_queue_max_size) + : m_task_queue_max_size(task_queue_max_size) +{ + m_worker_threads.reserve(number_of_threads); + for (size_t i = 0; i < number_of_threads; ++i) { + m_worker_threads.emplace_back(&ThreadPool::worker_thread_main, this); + } +} + +ThreadPool::~ThreadPool() +{ + shut_down(); +} + +void +ThreadPool::enqueue(std::function function) +{ + { + std::unique_lock lock(m_mutex); + if (m_task_queue.size() >= m_task_queue_max_size) { + m_task_popped_condition.wait( + lock, [this] { return m_task_queue.size() < m_task_queue_max_size; }); + } + m_task_queue.emplace(function); + } + m_task_enqueued_or_shutting_down_condition.notify_one(); +} + +void +ThreadPool::shut_down() +{ + { + std::unique_lock lock(m_mutex); + m_shutting_down = true; + } + m_task_enqueued_or_shutting_down_condition.notify_all(); + for (auto& thread : m_worker_threads) { + if (thread.joinable()) { + thread.join(); + } + } +} + +void +ThreadPool::worker_thread_main() +{ + while (true) { + std::function task; + + { + std::unique_lock lock(m_mutex); + m_task_enqueued_or_shutting_down_condition.wait( + lock, [this] { return m_shutting_down || !m_task_queue.empty(); }); + if (m_shutting_down && m_task_queue.empty()) { + return; + } + task = std::move(m_task_queue.front()); + m_task_queue.pop(); + } + + m_task_popped_condition.notify_all(); + task(); + } +} diff --git a/src/ThreadPool.hpp b/src/ThreadPool.hpp new file mode 100644 index 0000000..5eee381 --- /dev/null +++ b/src/ThreadPool.hpp @@ -0,0 +1,51 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include +#include +#include +#include +#include +#include + +class ThreadPool +{ +public: + explicit ThreadPool( + size_t number_of_threads, + size_t task_queue_max_size = std::numeric_limits::max()); + ~ThreadPool(); + + void enqueue(std::function function); + void shut_down(); + +private: + std::vector m_worker_threads; + std::queue> m_task_queue; + size_t m_task_queue_max_size; + bool m_shutting_down = false; + std::mutex m_mutex; + std::condition_variable m_task_enqueued_or_shutting_down_condition; + std::condition_variable m_task_popped_condition; + + void worker_thread_main(); +}; diff --git a/src/UmaskScope.hpp b/src/UmaskScope.hpp new file mode 100644 index 0000000..d96f448 --- /dev/null +++ b/src/UmaskScope.hpp @@ -0,0 +1,55 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "third_party/nonstd/optional.hpp" + +// This class sets a new (process-global) umask and restores the previous umask +// when destructed. +class UmaskScope +{ +public: + UmaskScope(nonstd::optional new_umask); + ~UmaskScope(); + +private: + nonstd::optional m_saved_umask; +}; + +UmaskScope::UmaskScope(nonstd::optional new_umask) +{ +#ifndef _WIN32 + if (new_umask) { + m_saved_umask = umask(*new_umask); + } +#else + (void)new_umask; +#endif +} + +UmaskScope::~UmaskScope() +{ +#ifndef _WIN32 + if (m_saved_umask) { + umask(*m_saved_umask); + } +#endif +} diff --git a/src/Util.cpp b/src/Util.cpp new file mode 100644 index 0000000..54519f7 --- /dev/null +++ b/src/Util.cpp @@ -0,0 +1,1560 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "Util.hpp" + +#include "Config.hpp" +#include "Context.hpp" +#include "Fd.hpp" +#include "FormatNonstdStringView.hpp" +#include "Logging.hpp" +#include "TemporaryFile.hpp" + +extern "C" { +#include "third_party/base32hex.h" +} + +#include +#include + +#ifndef HAVE_DIRENT_H +# include +#endif + +#ifdef HAVE_PWD_H +# include +#endif + +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#ifdef HAVE_LINUX_FS_H +# include +# include +#elif defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) +# include +# include +#endif + +#ifdef _WIN32 +# include "Win32Util.hpp" +#endif + +#ifdef __linux__ +# ifdef HAVE_SYS_IOCTL_H +# include +# endif +# ifdef HAVE_LINUX_FS_H +# include +# ifndef FICLONE +# define FICLONE _IOW(0x94, 9, int) +# endif +# define FILE_CLONING_SUPPORTED 1 +# endif +#endif + +#ifdef __APPLE__ +# ifdef HAVE_SYS_CLONEFILE_H +# include +# define FILE_CLONING_SUPPORTED 1 +# endif +#endif + +using Logging::log; +using nonstd::nullopt; +using nonstd::optional; +using nonstd::string_view; + +namespace { + +// Search for the first match of the following regular expression: +// +// \x1b\[[\x30-\x3f]*[\x20-\x2f]*[Km] +// +// The primary reason for not using std::regex is that it's not available for +// GCC 4.8. It's also a bit bloated. The reason for not using POSIX regex +// functionality is that it's are not available in MinGW. +string_view +find_first_ansi_csi_seq(string_view string) +{ + size_t pos = 0; + while (pos < string.length() && string[pos] != 0x1b) { + ++pos; + } + if (pos + 1 >= string.length() || string[pos + 1] != '[') { + return {}; + } + size_t start = pos; + pos += 2; + while (pos < string.length() + && (string[pos] >= 0x30 && string[pos] <= 0x3f)) { + ++pos; + } + while (pos < string.length() + && (string[pos] >= 0x20 && string[pos] <= 0x2f)) { + ++pos; + } + if (pos < string.length() && (string[pos] == 'K' || string[pos] == 'm')) { + return string.substr(start, pos + 1 - start); + } else { + return {}; + } +} + +size_t +path_max(const std::string& path) +{ +#ifdef PATH_MAX + (void)path; + return PATH_MAX; +#elif defined(MAXPATHLEN) + (void)path; + return MAXPATHLEN; +#elif defined(_PC_PATH_MAX) + long maxlen = pathconf(path.c_str(), _PC_PATH_MAX); + return maxlen >= 4096 ? maxlen : 4096; +#endif +} + +template +std::vector +split_at(string_view input, const char* separators) +{ + ASSERT(separators != nullptr && separators[0] != '\0'); + + std::vector result; + + size_t start = 0; + while (start < input.size()) { + size_t end = input.find_first_of(separators, start); + + if (end == string_view::npos) { + result.emplace_back(input.data() + start, input.size() - start); + break; + } else if (start != end) { + result.emplace_back(input.data() + start, end - start); + } + + start = end + 1; + } + + return result; +} + +std::string +rewrite_stderr_to_absolute_paths(string_view text) +{ + static const std::string in_file_included_from = "In file included from "; + + std::string result; + for (auto line : Util::split_into_views(text, "\n")) { + // Rewrite to in the following two cases, where X may + // be optional ANSI CSI sequences: + // + // In file included from XX:1: + // XX:1:2: ... + + if (Util::starts_with(line, in_file_included_from)) { + result += in_file_included_from; + line = line.substr(in_file_included_from.length()); + } + while (!line.empty() && line[0] == 0x1b) { + auto csi_seq = find_first_ansi_csi_seq(line); + result.append(csi_seq.data(), csi_seq.length()); + line = line.substr(csi_seq.length()); + } + size_t path_end = line.find(':'); + if (path_end == string_view::npos) { + result.append(line.data(), line.length()); + } else { + std::string path(line.substr(0, path_end)); + if (Stat::stat(path)) { + result += Util::real_path(path); + auto tail = line.substr(path_end); + result.append(tail.data(), tail.length()); + } else { + result.append(line.data(), line.length()); + } + } + result += '\n'; + } + return result; +} + +} // namespace + +namespace Util { + +string_view +base_name(string_view path) +{ +#ifdef _WIN32 + const char delim[] = "/\\"; +#else + const char delim[] = "/"; +#endif + size_t n = path.find_last_of(delim); + return n == std::string::npos ? path : path.substr(n + 1); +} + +std::string +change_extension(string_view path, string_view new_ext) +{ + string_view without_ext = Util::remove_extension(path); + return std::string(without_ext).append(new_ext.data(), new_ext.length()); +} + +#ifdef FILE_CLONING_SUPPORTED +void +clone_file(const std::string& src, const std::string& dest, bool via_tmp_file) +{ +# if defined(__linux__) + Fd src_fd(open(src.c_str(), O_RDONLY)); + if (!src_fd) { + throw Error("{}: {}", src, strerror(errno)); + } + + Fd dest_fd; + std::string tmp_file; + if (via_tmp_file) { + TemporaryFile temp_file(dest); + dest_fd = std::move(temp_file.fd); + tmp_file = temp_file.path; + } else { + dest_fd = + Fd(open(dest.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666)); + if (!dest_fd) { + throw Error("{}: {}", src, strerror(errno)); + } + } + + if (ioctl(*dest_fd, FICLONE, *src_fd) != 0) { + throw Error(strerror(errno)); + } + + dest_fd.close(); + src_fd.close(); + + if (via_tmp_file) { + Util::rename(tmp_file, dest); + } +# elif defined(__APPLE__) + (void)via_tmp_file; + if (clonefile(src.c_str(), dest.c_str(), CLONE_NOOWNERCOPY) != 0) { + throw Error(strerror(errno)); + } +# else + (void)src; + (void)dest; + (void)via_tmp_file; + throw Error(strerror(EOPNOTSUPP)); +# endif +} +#endif // FILE_CLONING_SUPPORTED + +void +clone_hard_link_or_copy_file(const Context& ctx, + const std::string& source, + const std::string& dest, + bool via_tmp_file) +{ + if (ctx.config.file_clone()) { +#ifdef FILE_CLONING_SUPPORTED + log("Cloning {} to {}", source, dest); + try { + clone_file(source, dest, via_tmp_file); + return; + } catch (Error& e) { + log("Failed to clone: {}", e.what()); + } +#else + log("Not cloning {} to {} since it's unsupported"); +#endif + } + if (ctx.config.hard_link()) { + unlink(dest.c_str()); + log("Hard linking {} to {}", source, dest); + int ret = link(source.c_str(), dest.c_str()); + if (ret == 0) { + if (chmod(dest.c_str(), 0444) != 0) { + log("Failed to chmod: {}", strerror(errno)); + } + return; + } + log("Failed to hard link: {}", strerror(errno)); + } + + log("Copying {} to {}", source, dest); + copy_file(source, dest, via_tmp_file); +} + +size_t +common_dir_prefix_length(string_view dir, string_view path) +{ + if (dir.empty() || path.empty() || dir == "/" || path == "/") { + return 0; + } + + ASSERT(dir[0] == '/'); + ASSERT(path[0] == '/'); + + const size_t limit = std::min(dir.length(), path.length()); + size_t i = 0; + + while (i < limit && dir[i] == path[i]) { + ++i; + } + + if ((i == dir.length() && i == path.length()) + || (i == dir.length() && path[i] == '/') + || (i == path.length() && dir[i] == '/')) { + return i; + } + + do { + --i; + } while (i > 0 && dir[i] != '/' && path[i] != '/'); + + return i; +} + +void +copy_fd(int fd_in, int fd_out) +{ + read_fd(fd_in, + [=](const void* data, size_t size) { write_fd(fd_out, data, size); }); +} + +void +copy_file(const std::string& src, const std::string& dest, bool via_tmp_file) +{ + Fd src_fd(open(src.c_str(), O_RDONLY)); + if (!src_fd) { + throw Error("{}: {}", src, strerror(errno)); + } + + Fd dest_fd; + std::string tmp_file; + if (via_tmp_file) { + TemporaryFile temp_file(dest); + dest_fd = std::move(temp_file.fd); + tmp_file = temp_file.path; + } else { + dest_fd = + Fd(open(dest.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666)); + if (!dest_fd) { + throw Error("{}: {}", dest, strerror(errno)); + } + } + + copy_fd(*src_fd, *dest_fd); + dest_fd.close(); + src_fd.close(); + + if (via_tmp_file) { + Util::rename(tmp_file, dest); + } +} + +bool +create_dir(string_view dir) +{ + std::string dir_str(dir); + auto st = Stat::stat(dir_str); + if (st) { + if (st.is_directory()) { + return true; + } else { + errno = ENOTDIR; + return false; + } + } else { + if (!create_dir(Util::dir_name(dir))) { + return false; + } + int result = mkdir(dir_str.c_str(), 0777); + // Treat an already existing directory as OK since the file system could + // have changed in between calling stat and actually creating the + // directory. This can happen when there are multiple instances of ccache + // running and trying to create the same directory chain, which usually is + // the case when the cache root does not initially exist. As long as one of + // the processes creates the directories then our condition is satisfied + // and we avoid a race condition. + return result == 0 || errno == EEXIST; + } +} + +string_view +dir_name(string_view path) +{ +#ifdef _WIN32 + const char delim[] = "/\\"; +#else + const char delim[] = "/"; +#endif + size_t n = path.find_last_of(delim); + if (n == std::string::npos) { + return "."; + } else { + return n == 0 ? "/" : path.substr(0, n); + } +} + +std::string +expand_environment_variables(const std::string& str) +{ + std::string result; + const char* left = str.c_str(); + for (const char* right = left; *right; ++right) { + if (*right == '$') { + result.append(left, right - left); + + left = right + 1; + bool curly = *left == '{'; + if (curly) { + ++left; + } + right = left; + while (isalnum(*right) || *right == '_') { + ++right; + } + if (curly && *right != '}') { + throw Error("syntax error: missing '}}' after \"{}\"", left); + } + if (right == left) { + // Special case: don't consider a single $ the left of a variable. + result += '$'; + --right; + } else { + std::string name(left, right - left); + const char* value = getenv(name.c_str()); + if (!value) { + throw Error("environment variable \"{}\" not set", name); + } + result += value; + if (!curly) { + --right; + } + left = right + 1; + } + } + } + result += left; + return result; +} + +int +fallocate(int fd, long new_size) +{ +#ifdef HAVE_POSIX_FALLOCATE + return posix_fallocate(fd, 0, new_size); +#else + off_t saved_pos = lseek(fd, 0, SEEK_END); + off_t old_size = lseek(fd, 0, SEEK_END); + if (old_size == -1) { + int err = errno; + lseek(fd, saved_pos, SEEK_SET); + return err; + } + if (old_size >= new_size) { + lseek(fd, saved_pos, SEEK_SET); + return 0; + } + long bytes_to_write = new_size - old_size; + void* buf = calloc(bytes_to_write, 1); + if (!buf) { + lseek(fd, saved_pos, SEEK_SET); + return ENOMEM; + } + int err = 0; + try { + write_fd(fd, buf, bytes_to_write); + } catch (Error&) { + err = errno; + } + lseek(fd, saved_pos, SEEK_SET); + free(buf); + return err; +#endif +} + +void +for_each_level_1_subdir(const std::string& cache_dir, + const SubdirVisitor& visitor, + const ProgressReceiver& progress_receiver) +{ + for (int i = 0; i <= 0xF; i++) { + double progress = 1.0 * i / 16; + progress_receiver(progress); + std::string subdir_path = fmt::format("{}/{:x}", cache_dir, i); + visitor(subdir_path, [&](double inner_progress) { + progress_receiver(progress + inner_progress / 16); + }); + } + progress_receiver(1.0); +} + +std::string +format_argv_for_logging(const char* const* argv) +{ + std::string result; + for (size_t i = 0; argv[i]; ++i) { + if (i != 0) { + result += ' '; + } + for (const char* arg = argv[i]; *arg; ++arg) { + result += *arg; + } + } + return result; +} + +std::string +format_base16(const uint8_t* data, size_t size) +{ + static const char digits[] = "0123456789abcdef"; + std::string result; + result.resize(2 * size); + for (size_t i = 0; i < size; ++i) { + result[i * 2] = digits[data[i] >> 4]; + result[i * 2 + 1] = digits[data[i] & 0xF]; + } + return result; +} + +std::string +format_base32hex(const uint8_t* data, size_t size) +{ + const size_t bytes_to_reserve = size * 8 / 5 + 1; + std::string result(bytes_to_reserve, 0); + const size_t actual_size = base32hex(&result[0], data, size); + result.resize(actual_size); + return result; +} + +std::string +format_human_readable_size(uint64_t size) +{ + if (size >= 1000 * 1000 * 1000) { + return fmt::format("{:.1f} GB", size / ((double)(1000 * 1000 * 1000))); + } else if (size >= 1000 * 1000) { + return fmt::format("{:.1f} MB", size / ((double)(1000 * 1000))); + } else { + return fmt::format("{:.1f} kB", size / 1000.0); + } +} + +std::string +format_parsable_size_with_suffix(uint64_t size) +{ + if (size >= 1000 * 1000 * 1000) { + return fmt::format("{:.1f}G", size / ((double)(1000 * 1000 * 1000))); + } else if (size >= 1000 * 1000) { + return fmt::format("{:.1f}M", size / ((double)(1000 * 1000))); + } else { + return fmt::format("{}", size); + } +} + +void +ensure_dir_exists(nonstd::string_view dir) +{ + if (!create_dir(dir)) { + throw Fatal("Failed to create directory {}: {}", dir, strerror(errno)); + } +} + +std::string +get_actual_cwd() +{ + char buffer[PATH_MAX]; + if (getcwd(buffer, sizeof(buffer))) { +#ifndef _WIN32 + return buffer; +#else + std::string cwd = buffer; + std::replace(cwd.begin(), cwd.end(), '\\', '/'); + return cwd; +#endif + } else { + return {}; + } +} + +std::string +get_apparent_cwd(const std::string& actual_cwd) +{ +#ifdef _WIN32 + return actual_cwd; +#else + auto pwd = getenv("PWD"); + if (!pwd) { + return actual_cwd; + } + + auto pwd_stat = Stat::stat(pwd); + auto cwd_stat = Stat::stat(actual_cwd); + if (!pwd_stat || !cwd_stat || !pwd_stat.same_inode_as(cwd_stat)) { + return actual_cwd; + } + std::string normalized_pwd = normalize_absolute_path(pwd); + return normalized_pwd == pwd + || Stat::stat(normalized_pwd).same_inode_as(pwd_stat) + ? normalized_pwd + : pwd; +#endif +} + +string_view +get_extension(string_view path) +{ +#ifndef _WIN32 + const char stop_at_chars[] = "./"; +#else + const char stop_at_chars[] = "./\\"; +#endif + size_t pos = path.find_last_of(stop_at_chars); + if (pos == string_view::npos || path.at(pos) == '/') { + return {}; +#ifdef _WIN32 + } else if (path.at(pos) == '\\') { + return {}; +#endif + } else { + return path.substr(pos); + } +} + +void +get_level_1_files(const std::string& dir, + const ProgressReceiver& progress_receiver, + std::vector>& files) +{ + if (!Stat::stat(dir)) { + return; + } + + size_t level_2_directories = 0; + + Util::traverse(dir, [&](const std::string& path, bool is_dir) { + auto name = Util::base_name(path); + if (name == "CACHEDIR.TAG" || name == "stats" || name.starts_with(".nfs")) { + return; + } + + if (!is_dir) { + files.push_back(std::make_shared(path)); + } else if (path != dir + && path.find('/', dir.size() + 1) == std::string::npos) { + ++level_2_directories; + progress_receiver(level_2_directories / 16.0); + } + }); + + progress_receiver(1.0); +} + +std::string +get_home_directory() +{ + const char* p = getenv("HOME"); + if (p) { + return p; + } +#ifdef _WIN32 + p = getenv("APPDATA"); + if (p) { + return p; + } +#endif +#ifdef HAVE_GETPWUID + { + struct passwd* pwd = getpwuid(getuid()); + if (pwd) { + return pwd->pw_dir; + } + } +#endif + throw Fatal("Could not determine home directory from $HOME or getpwuid(3)"); +} + +const char* +get_hostname() +{ + static char hostname[260] = ""; + + if (hostname[0]) { + return hostname; + } + + if (gethostname(hostname, sizeof(hostname)) != 0) { + strcpy(hostname, "unknown"); + } + hostname[sizeof(hostname) - 1] = 0; + return hostname; +} + +std::string +get_relative_path(string_view dir, string_view path) +{ + ASSERT(Util::is_absolute_path(dir)); + ASSERT(Util::is_absolute_path(path)); + +#ifdef _WIN32 + // Paths can be escaped by a slash for use with e.g. -isystem. + if (dir.length() >= 3 && dir[0] == '/' && dir[2] == ':') { + dir = dir.substr(1); + } + if (path.length() >= 3 && path[0] == '/' && path[2] == ':') { + path = path.substr(1); + } + if (dir[0] != path[0]) { + // Drive letters differ. + return std::string(path); + } + dir = dir.substr(2); + path = path.substr(2); +#endif + + std::string result; + size_t common_prefix_len = Util::common_dir_prefix_length(dir, path); + if (common_prefix_len > 0 || dir != "/") { + for (size_t i = common_prefix_len; i < dir.length(); ++i) { + if (dir[i] == '/') { + if (!result.empty()) { + result += '/'; + } + result += ".."; + } + } + } + if (path.length() > common_prefix_len) { + if (!result.empty()) { + result += '/'; + } + result += std::string(path.substr(common_prefix_len + 1)); + } + result.erase(result.find_last_not_of('/') + 1); + return result.empty() ? "." : result; +} + +std::string +get_path_in_cache(string_view cache_dir, uint8_t level, string_view name) +{ + ASSERT(level >= 1 && level <= 8); + ASSERT(name.length() >= level); + + std::string path(cache_dir); + path.reserve(path.size() + level * 2 + 1 + name.length() - level); + + for (uint8_t i = 0; i < level; ++i) { + path.push_back('/'); + path.push_back(name.at(i)); + } + + path.push_back('/'); + string_view name_remaining = name.substr(level); + path.append(name_remaining.data(), name_remaining.length()); + + return path; +} + +bool +is_absolute_path(string_view path) +{ +#ifdef _WIN32 + if (path.length() >= 2 && path[1] == ':' + && (path[2] == '/' || path[2] == '\\')) { + return true; + } +#endif + return !path.empty() && path[0] == '/'; +} + +#if defined(HAVE_LINUX_FS_H) || defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) +int +is_nfs_fd(int fd, bool* is_nfs) +{ + struct statfs buf; + if (fstatfs(fd, &buf) != 0) { + return errno; + } +# ifdef HAVE_LINUX_FS_H + *is_nfs = buf.f_type == NFS_SUPER_MAGIC; +# else // Mac OS X and some other BSD flavors + *is_nfs = strcmp(buf.f_fstypename, "nfs") == 0; +# endif + return 0; +} +#else +int +is_nfs_fd(int /*fd*/, bool* /*is_nfs*/) +{ + return -1; +} +#endif + +bool +is_precompiled_header(string_view path) +{ + string_view ext = get_extension(path); + return ext == ".gch" || ext == ".pch" || ext == ".pth" + || get_extension(dir_name(path)) == ".gch"; +} + +optional +localtime(optional time) +{ + time_t timestamp = time ? *time : ::time(nullptr); + tm result; + if (localtime_r(×tamp, &result)) { + return result; + } else { + return nullopt; + } +} + +std::string +make_relative_path(const Context& ctx, string_view path) +{ + if (ctx.config.base_dir().empty() + || !Util::starts_with(path, ctx.config.base_dir())) { + return std::string(path); + } + +#ifdef _WIN32 + std::string winpath; + if (path.length() >= 3 && path[0] == '/') { + if (isalpha(path[1]) && path[2] == '/') { + // Transform /c/path... to c:/path... + winpath = fmt::format("{}:/{}", path[1], path.substr(3)); + path = winpath; + } else if (path[2] == ':') { + // Transform /c:/path to c:/path + winpath = std::string(path.substr(1)); + path = winpath; + } + } +#endif + + // The algorithm for computing relative paths below only works for existing + // paths. If the path doesn't exist, find the first ancestor directory that + // does exist and assemble the path again afterwards. + string_view original_path = path; + std::string path_suffix; + Stat path_stat; + while (!(path_stat = Stat::stat(std::string(path)))) { + path = Util::dir_name(path); + } + path_suffix = std::string(original_path.substr(path.length())); + + std::string path_str(path); + std::string normalized_path = Util::normalize_absolute_path(path_str); + std::vector relpath_candidates = { + Util::get_relative_path(ctx.actual_cwd, normalized_path), + }; + if (ctx.apparent_cwd != ctx.actual_cwd) { + relpath_candidates.emplace_back( + Util::get_relative_path(ctx.apparent_cwd, normalized_path)); + // Move best (= shortest) match first: + if (relpath_candidates[0].length() > relpath_candidates[1].length()) { + std::swap(relpath_candidates[0], relpath_candidates[1]); + } + } + + for (const auto& relpath : relpath_candidates) { + if (Stat::stat(relpath).same_inode_as(path_stat)) { + return relpath + path_suffix; + } + } + + // No match so nothing else to do than to return the unmodified path. + return std::string(original_path); +} + +bool +matches_dir_prefix_or_file(string_view dir_prefix_or_file, string_view path) +{ + return !dir_prefix_or_file.empty() && !path.empty() + && dir_prefix_or_file.length() <= path.length() + && path.starts_with(dir_prefix_or_file) + && (dir_prefix_or_file.length() == path.length() + || is_dir_separator(path[dir_prefix_or_file.length()]) + || is_dir_separator(dir_prefix_or_file.back())); +} + +std::string +normalize_absolute_path(string_view path) +{ + if (!is_absolute_path(path)) { + return std::string(path); + } + +#ifdef _WIN32 + if (path.find("\\") != string_view::npos) { + std::string new_path(path); + std::replace(new_path.begin(), new_path.end(), '\\', '/'); + return normalize_absolute_path(new_path); + } + + std::string drive(path.substr(0, 2)); + path = path.substr(2); +#endif + + std::string result = "/"; + const size_t npos = string_view::npos; + size_t left = 1; + + while (true) { + if (left >= path.length()) { + break; + } + const auto right = path.find('/', left); + string_view part = path.substr(left, right == npos ? npos : right - left); + if (part == "..") { + if (result.length() > 1) { + // "/x/../part" -> "/part" + result.erase(result.rfind('/', result.length() - 2) + 1); + } else { + // "/../part" -> "/part" + } + } else if (part == ".") { + // "/x/." -> "/x" + } else { + result.append(part.begin(), part.end()); + if (result[result.length() - 1] != '/') { + result += '/'; + } + } + if (right == npos) { + break; + } + left = right + 1; + } + if (result.length() > 1) { + result.erase(result.find_last_not_of('/') + 1); + } + +#ifdef _WIN32 + return drive + result; +#else + return result; +#endif +} + +uint64_t +parse_duration(const std::string& duration) +{ + uint64_t factor = 0; + char last_ch = duration.empty() ? '\0' : duration[duration.length() - 1]; + + switch (last_ch) { + case 'd': + factor = 24 * 60 * 60; + break; + case 's': + factor = 1; + break; + default: + throw Error("invalid suffix (supported: d (day) and s (second)): \"{}\"", + duration); + } + + return factor * parse_unsigned(duration.substr(0, duration.length() - 1)); +} + +int64_t +parse_signed(const std::string& value, + optional min_value, + optional max_value, + string_view description) +{ + std::string stripped_value = strip_whitespace(value); + + size_t end = 0; + long long result = 0; + bool failed = false; + try { + // Note: sizeof(long long) is guaranteed to be >= sizeof(int64_t) + result = std::stoll(stripped_value, &end, 10); + } catch (std::exception&) { + failed = true; + } + if (failed || end != stripped_value.size()) { + throw Error("invalid integer: \"{}\"", stripped_value); + } + + int64_t min = min_value ? *min_value : INT64_MIN; + int64_t max = max_value ? *max_value : INT64_MAX; + if (result < min || result > max) { + throw Error("{} must be between {} and {}", description, min, max); + } + return result; +} + +uint64_t +parse_size(const std::string& value) +{ + errno = 0; + + char* p; + double result = strtod(value.c_str(), &p); + if (errno != 0 || result < 0 || p == value.c_str() || value.empty()) { + throw Error("invalid size: \"{}\"", value); + } + + while (isspace(*p)) { + ++p; + } + + if (*p != '\0') { + unsigned multiplier = *(p + 1) == 'i' ? 1024 : 1000; + switch (*p) { + case 'T': + result *= multiplier; + // Fallthrough. + case 'G': + result *= multiplier; + // Fallthrough. + case 'M': + result *= multiplier; + // Fallthrough. + case 'K': + case 'k': + result *= multiplier; + break; + default: + throw Error("invalid size: \"{}\"", value); + } + } else { + // Default suffix: G. + result *= 1000 * 1000 * 1000; + } + return static_cast(result); +} + +uint64_t +parse_unsigned(const std::string& value, + optional min_value, + optional max_value, + string_view description) +{ + std::string stripped_value = strip_whitespace(value); + + size_t end = 0; + unsigned long long result = 0; + bool failed = false; + if (Util::starts_with(stripped_value, "-")) { + failed = true; + } else { + try { + // Note: sizeof(unsigned long long) is guaranteed to be >= + // sizeof(uint64_t) + result = std::stoull(stripped_value, &end, 10); + } catch (std::exception&) { + failed = true; + } + } + if (failed || end != stripped_value.size()) { + throw Error("invalid unsigned integer: \"{}\"", stripped_value); + } + + uint64_t min = min_value ? *min_value : 0; + uint64_t max = max_value ? *max_value : UINT64_MAX; + if (result < min || result > max) { + throw Error("{} must be between {} and {}", description, min, max); + } + return result; +} + +bool +read_fd(int fd, DataReceiver data_receiver) +{ + ssize_t n; + char buffer[READ_BUFFER_SIZE]; + while ((n = read(fd, buffer, sizeof(buffer))) != 0) { + if (n == -1 && errno != EINTR) { + break; + } + if (n > 0) { + data_receiver(buffer, n); + } + } + return n >= 0; +} + +std::string +read_file(const std::string& path, size_t size_hint) +{ + if (size_hint == 0) { + auto stat = Stat::stat(path); + if (!stat) { + throw Error(strerror(errno)); + } + size_hint = stat.size(); + } + + // +1 to be able to detect EOF in the first read call + size_hint = (size_hint < 1024) ? 1024 : size_hint + 1; + + Fd fd(open(path.c_str(), O_RDONLY | O_BINARY)); + if (!fd) { + throw Error(strerror(errno)); + } + + ssize_t ret = 0; + size_t pos = 0; + std::string result; + result.resize(size_hint); + + while (true) { + if (pos > result.size()) { + result.resize(2 * result.size()); + } + const size_t max_read = result.size() - pos; + ret = read(*fd, &result[pos], max_read); + if (ret == 0 || (ret == -1 && errno != EINTR)) { + break; + } + if (ret > 0) { + pos += ret; + if (static_cast(ret) < max_read) { + break; + } + } + } + + if (ret == -1) { + log("Failed reading {}", path); + throw Error(strerror(errno)); + } + + result.resize(pos); + return result; +} + +#ifndef _WIN32 +std::string +read_link(const std::string& path) +{ + size_t buffer_size = path_max(path); + std::unique_ptr buffer(new char[buffer_size]); + ssize_t len = readlink(path.c_str(), buffer.get(), buffer_size - 1); + if (len == -1) { + return ""; + } + buffer[len] = 0; + return buffer.get(); +} +#endif + +std::string +real_path(const std::string& path, bool return_empty_on_error) +{ + size_t buffer_size = path_max(path); + std::unique_ptr managed_buffer(new char[buffer_size]); + char* buffer = managed_buffer.get(); + char* resolved = nullptr; + +#ifdef HAVE_REALPATH + resolved = realpath(path.c_str(), buffer); +#elif defined(_WIN32) + const char* c_path = path.c_str(); + if (c_path[0] == '/') { + c_path++; // Skip leading slash. + } + HANDLE path_handle = CreateFile(c_path, + GENERIC_READ, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + if (INVALID_HANDLE_VALUE != path_handle) { + bool ok = GetFinalPathNameByHandle( + path_handle, buffer, buffer_size, FILE_NAME_NORMALIZED); + CloseHandle(path_handle); + if (!ok) { + return path; + } + resolved = buffer + 4; // Strip \\?\ from the file name. + } else { + snprintf(buffer, buffer_size, "%s", c_path); + resolved = buffer; + } +#else + // Yes, there are such systems. This replacement relies on the fact that when + // we call x_realpath we only care about symlinks. + { + ssize_t len = readlink(path.c_str(), buffer, buffer_size - 1); + if (len != -1) { + buffer[len] = 0; + resolved = buffer; + } + } +#endif + + return resolved ? resolved : (return_empty_on_error ? "" : path); +} + +string_view +remove_extension(string_view path) +{ + return path.substr(0, path.length() - get_extension(path).length()); +} + +void +rename(const std::string& oldpath, const std::string& newpath) +{ +#ifndef _WIN32 + if (::rename(oldpath.c_str(), newpath.c_str()) != 0) { + throw Error( + "failed to rename {} to {}: {}", oldpath, newpath, strerror(errno)); + } +#else + // Windows' rename() won't overwrite an existing file, so need to use + // MoveFileEx instead. + if (!MoveFileExA( + oldpath.c_str(), newpath.c_str(), MOVEFILE_REPLACE_EXISTING)) { + DWORD error = GetLastError(); + throw Error("failed to rename {} to {}: {}", + oldpath, + newpath, + Win32Util::error_message(error)); + } +#endif +} + +bool +same_program_name(nonstd::string_view program_name, + nonstd::string_view canonical_program_name) +{ +#ifdef _WIN32 + std::string lowercase_program_name = Util::to_lowercase(program_name); + return lowercase_program_name == canonical_program_name + || lowercase_program_name + == fmt::format("{}.exe", canonical_program_name); +#else + return program_name == canonical_program_name; +#endif +} + +void +send_to_stderr(const Context& ctx, const std::string& text) +{ + const std::string* text_to_send = &text; + std::string modified_text; + + if (ctx.args_info.strip_diagnostics_colors) { + try { + modified_text = strip_ansi_csi_seqs(text); + text_to_send = &modified_text; + } catch (const Error&) { + // Fall through + } + } + + if (ctx.config.absolute_paths_in_stderr()) { + modified_text = rewrite_stderr_to_absolute_paths(*text_to_send); + text_to_send = &modified_text; + } + + try { + write_fd(STDERR_FILENO, text_to_send->data(), text_to_send->length()); + } catch (Error& e) { + throw Error("Failed to write to stderr: {}", e.what()); + } +} + +void +set_cloexec_flag(int fd) +{ +#ifndef _WIN32 + int flags = fcntl(fd, F_GETFD, 0); + if (flags >= 0) { + fcntl(fd, F_SETFD, flags | FD_CLOEXEC); + } +#else + (void)fd; +#endif +} + +void +setenv(const std::string& name, const std::string& value) +{ +#ifdef HAVE_SETENV + ::setenv(name.c_str(), value.c_str(), true); +#else + char* string; + asprintf(&string, "%s=%s", name.c_str(), value.c_str()); + putenv(string); // Leak to environment. +#endif +} + +std::vector +split_into_views(string_view input, const char* separators) +{ + return split_at(input, separators); +} + +std::vector +split_into_strings(string_view input, const char* separators) +{ + return split_at(input, separators); +} + +std::string +strip_ansi_csi_seqs(string_view string) +{ + size_t pos = 0; + std::string result; + + while (true) { + auto seq_span = find_first_ansi_csi_seq(string.substr(pos)); + auto data_start = string.data() + pos; + auto data_length = + seq_span.empty() ? string.length() - pos : seq_span.data() - data_start; + result.append(data_start, data_length); + if (seq_span.empty()) { + // Reached tail. + break; + } + pos += data_length + seq_span.length(); + } + + return result; +} + +std::string +strip_whitespace(string_view string) +{ + auto is_space = [](int ch) { return std::isspace(ch); }; + auto start = std::find_if_not(string.begin(), string.end(), is_space); + auto end = std::find_if_not(string.rbegin(), string.rend(), is_space).base(); + return start < end ? std::string(start, end) : std::string(); +} + +std::string +to_lowercase(string_view string) +{ + std::string result; + result.resize(string.length()); + std::transform(string.begin(), string.end(), result.begin(), tolower); + return result; +} + +#ifdef HAVE_DIRENT_H + +void +traverse(const std::string& path, const TraverseVisitor& visitor) +{ + DIR* dir = opendir(path.c_str()); + if (dir) { + struct dirent* entry; + while ((entry = readdir(dir))) { + if (strcmp(entry->d_name, "") == 0 || strcmp(entry->d_name, ".") == 0 + || strcmp(entry->d_name, "..") == 0) { + continue; + } + + std::string entry_path = path + "/" + entry->d_name; + bool is_dir; +# ifdef _DIRENT_HAVE_D_TYPE + if (entry->d_type != DT_UNKNOWN) { + is_dir = entry->d_type == DT_DIR; + } else +# endif + { + auto stat = Stat::lstat(entry_path); + if (!stat) { + if (stat.error_number() == ENOENT || stat.error_number() == ESTALE) { + continue; + } + throw Error("failed to lstat {}: {}", + entry_path, + strerror(stat.error_number())); + } + is_dir = stat.is_directory(); + } + if (is_dir) { + traverse(entry_path, visitor); + } else { + visitor(entry_path, false); + } + } + closedir(dir); + visitor(path, true); + } else if (errno == ENOTDIR) { + visitor(path, false); + } else { + throw Error("failed to open directory {}: {}", path, strerror(errno)); + } +} + +#else // If not available, use the C++17 std::filesystem implementation. + +void +traverse(const std::string& path, const TraverseVisitor& visitor) +{ + if (std::filesystem::is_directory(path)) { + for (auto&& p : std::filesystem::directory_iterator(path)) { + std::string entry = p.path().string(); + + if (p.is_directory()) { + traverse(entry, visitor); + } else { + visitor(entry, false); + } + } + visitor(path, true); + } else if (std::filesystem::exists(path)) { + visitor(path, false); + } else { + throw Error("failed to open directory {}: {}", path, strerror(errno)); + } +} + +#endif + +bool +unlink_safe(const std::string& path, UnlinkLog unlink_log) +{ + int saved_errno = 0; + + // If path is on an NFS share, unlink isn't atomic, so we rename to a temp + // file. We don't care if the temp file is trashed, so it's always safe to + // unlink it first. + std::string tmp_name = path + ".ccache.rm.tmp"; + + bool success = true; + try { + Util::rename(path, tmp_name); + } catch (Error&) { + success = false; + saved_errno = errno; + } + if (success && unlink(tmp_name.c_str()) != 0) { + // It's OK if it was unlinked in a race. + if (errno != ENOENT && errno != ESTALE) { + success = false; + saved_errno = errno; + } + } + if (success || unlink_log == UnlinkLog::log_failure) { + log("Unlink {} via {}", path, tmp_name); + if (!success) { + log("Unlink failed: {}", strerror(saved_errno)); + } + } + + errno = saved_errno; + return success; +} + +bool +unlink_tmp(const std::string& path, UnlinkLog unlink_log) +{ + int saved_errno = 0; + + bool success = + unlink(path.c_str()) == 0 || (errno == ENOENT || errno == ESTALE); + saved_errno = errno; + if (success || unlink_log == UnlinkLog::log_failure) { + log("Unlink {}", path); + if (!success) { + log("Unlink failed: {}", strerror(saved_errno)); + } + } + + errno = saved_errno; + return success; +} + +void +unsetenv(const std::string& name) +{ +#ifdef HAVE_UNSETENV + ::unsetenv(name.c_str()); +#else + putenv(strdup(name.c_str())); // Leak to environment. +#endif +} + +void +update_mtime(const std::string& path) +{ +#ifdef HAVE_UTIMES + utimes(path.c_str(), nullptr); +#else + utime(path.c_str(), nullptr); +#endif +} + +void +wipe_path(const std::string& path) +{ + if (!Stat::lstat(path)) { + return; + } + traverse(path, [](const std::string& p, bool is_dir) { + if (is_dir) { + if (rmdir(p.c_str()) != 0 && errno != ENOENT && errno != ESTALE) { + throw Error("failed to rmdir {}: {}", p, strerror(errno)); + } + } else if (unlink(p.c_str()) != 0 && errno != ENOENT && errno != ESTALE) { + throw Error("failed to unlink {}: {}", p, strerror(errno)); + } + }); +} + +void +write_fd(int fd, const void* data, size_t size) +{ + ssize_t written = 0; + do { + ssize_t count = + write(fd, static_cast(data) + written, size - written); + if (count == -1) { + if (errno != EAGAIN && errno != EINTR) { + throw Error(strerror(errno)); + } + } else { + written += count; + } + } while (static_cast(written) < size); +} + +void +write_file(const std::string& path, + const std::string& data, + std::ios_base::openmode open_mode) +{ + if (path.empty()) { + throw Error("No such file or directory"); + } + + open_mode |= std::ios::out; + std::ofstream file(path, open_mode); + if (!file) { + throw Error(strerror(errno)); + } + file << data; +} + +} // namespace Util diff --git a/src/Util.hpp b/src/Util.hpp new file mode 100644 index 0000000..b3ab9e9 --- /dev/null +++ b/src/Util.hpp @@ -0,0 +1,496 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "CacheFile.hpp" + +#include "third_party/nonstd/optional.hpp" +#include "third_party/nonstd/string_view.hpp" + +#include +#include +#include +#include +#include +#include +#include + +class Context; + +namespace Util { + +using DataReceiver = std::function; +using ProgressReceiver = std::function; +using SubdirVisitor = std::function; +using TraverseVisitor = + std::function; + +enum class UnlinkLog { log_failure, ignore_failure }; + +// Get base name of path. +nonstd::string_view base_name(nonstd::string_view path); + +// Get an integer value from bytes in big endian order. +// +// Parameters: +// - buffer: Bytes to read. +// - count: Number of bytes to read. +template +void +big_endian_to_int(const uint8_t* buffer, T& value) +{ + value = 0; + for (size_t i = 0; i < sizeof(T); ++i) { + value <<= 8; + value |= buffer[i]; + } +} + +template<> +inline void +big_endian_to_int(const uint8_t* buffer, int8_t& value) +{ + value = buffer[0]; +} + +template<> +inline void +big_endian_to_int(const uint8_t* buffer, uint8_t& value) +{ + value = buffer[0]; +} + +// Remove the extension via `remove_extension()`, then add `new_ext`. `new_ext` +// should start with a dot, no extra dot is inserted. +std::string change_extension(nonstd::string_view path, + nonstd::string_view new_ext); + +// Return `value` adjusted to not be less than `min` and not more than `max`. +template +T +clamp(T value, T min, T max) +{ + return std::min(max, std::max(min, value)); +} + +// Clone a file from `src` to `dest`. If `via_tmp_file` is true, `src` is cloned +// to a temporary file and then renamed to `dest`. Throws `Error` on error. +void clone_file(const std::string& src, + const std::string& dest, + bool via_tmp_file = false); + +// Clone, hard link or copy a file from `source` to `dest` depending on settings +// in `ctx`. If cloning or hard linking cannot and should not be done the file +// will be copied instead. Throws `Error` on error. +void clone_hard_link_or_copy_file(const Context& ctx, + const std::string& source, + const std::string& dest, + bool via_tmp_file = false); + +// Compute the length of the longest directory path that is common to paths +// `dir` (a directory) and `path` (any path). +size_t common_dir_prefix_length(nonstd::string_view dir, + nonstd::string_view path); + +// Copy all data from `fd_in` to `fd_out`. Throws `Error` on error. +void copy_fd(int fd_in, int fd_out); + +// Copy a file from `src` to `dest`. If via_tmp_file is true, `src` is copied to +// a temporary file and then renamed to dest. Throws `Error` on error. +void copy_file(const std::string& src, + const std::string& dest, + bool via_tmp_file = false); + +// Create a directory if needed, including its parents if needed. +// +// Returns true if the directory exists or could be created, otherwise false. +bool create_dir(nonstd::string_view dir); + +// Get directory name of path. +nonstd::string_view dir_name(nonstd::string_view path); + +// Return true if `suffix` is a suffix of `string`. +inline bool +ends_with(nonstd::string_view string, nonstd::string_view suffix) +{ + return string.ends_with(suffix); +} + +// Like create_dir but throws Fatal on error. +void ensure_dir_exists(nonstd::string_view dir); + +// Expand all instances of $VAR or ${VAR}, where VAR is an environment variable, +// in `str`. Throws `Error` if one of the environment variables. +[[nodiscard]] std::string expand_environment_variables(const std::string& str); + +// Extends file size to at least new_size by calling posix_fallocate() if +// supported, otherwise by writing zeros last to the file. +// +// Note that existing holes are not filled in case posix_fallocate() is not +// supported. +// +// Returns 0 on success, an error number otherwise. +int fallocate(int fd, long new_size); + +// Call a function for each subdir (0-9a-f) in the cache. +// +// Parameters: +// - cache_dir: Path to the cache directory. +// - visitor: Function to call with directory path and progress_receiver as +// arguments. +// - progress_receiver: Function that will be called for progress updates. +void for_each_level_1_subdir(const std::string& cache_dir, + const SubdirVisitor& visitor, + const ProgressReceiver& progress_receiver); + +// Format `argv` as a simple string for logging purposes. That is, the result is +// not intended to be machine parsable. `argv` must be terminated by a nullptr. +std::string format_argv_for_logging(const char* const* argv); + +// Format a hexadecimal string representing `size` bytes of `data`. The returned +// string will be `2 * size` long. +std::string format_base16(const uint8_t* data, size_t size); + +// Format a lowercase base32hex string representing `size` bytes of `data`. No +// padding characters will be added. +std::string format_base32hex(const uint8_t* data, size_t size); + +// Format `size` as a human-readable string. +std::string format_human_readable_size(uint64_t size); + +// Format `size` as a parsable string. +std::string format_parsable_size_with_suffix(uint64_t size); + +// Return current working directory (CWD) as returned from getcwd(3) (i.e., +// normalized path without symlink parts). Returns the empty string on error. +std::string get_actual_cwd(); + +// Return current working directory (CWD) by reading the environment variable +// PWD (thus keeping any symlink parts in the path and potentially ".." or "//" +// parts). If PWD does not resolve to the same i-node as `actual_cwd` then +// `actual_cwd` is returned instead. +std::string get_apparent_cwd(const std::string& actual_cwd); + +// Return the file extension (including the dot) as a view into `path`. If +// `path` has no file extension, an empty string_view is returned. +nonstd::string_view get_extension(nonstd::string_view path); + +// Get a list of files in a level 1 subdirectory of the cache. +// +// The function works under the assumption that directory entries with one +// character names (except ".") are subdirectories and that there are no other +// subdirectories. +// +// Files ignored: +// - CACHEDIR.TAG +// - stats +// - .nfs* (temporary NFS files that may be left for open but deleted files). +// +// Parameters: +// - dir: The directory to traverse recursively. +// - progress_receiver: Function that will be called for progress updates. +// - files: Found files. +void get_level_1_files(const std::string& dir, + const ProgressReceiver& progress_receiver, + std::vector>& files); + +// Return the current user's home directory, or throw `Fatal` if it can't +// be determined. +std::string get_home_directory(); + +// Return a static string with the current hostname. +const char* get_hostname(); + +// Compute a relative path from `dir` (an absolute path to a directory) to +// `path` (an absolute path). Assumes that both `dir` and `path` are normalized. +// The algorithm does *not* follow symlinks, so the result may not actually +// resolve to the same file as `path`. +std::string get_relative_path(nonstd::string_view dir, + nonstd::string_view path); + +// Join `cache_dir`, a '/' and `name` into a single path and return it. +// Additionally, `level` single-character, '/'-separated subpaths are split from +// the beginning of `name` before joining them all. +std::string get_path_in_cache(nonstd::string_view cache_dir, + uint8_t level, + nonstd::string_view name); + +// Write bytes in big endian order from an integer value. +// +// Parameters: +// - value: Integer value to read. +// - buffer: Buffer to write bytes to. +template +void +int_to_big_endian(T value, uint8_t* buffer) +{ + for (size_t i = 0; i < sizeof(T); ++i) { + buffer[sizeof(T) - i - 1] = value & 0xFF; + value >>= 8; + } +} + +template<> +inline void +int_to_big_endian(uint8_t value, uint8_t* buffer) +{ + buffer[0] = value; +} + +template<> +inline void +int_to_big_endian(int8_t value, uint8_t* buffer) +{ + buffer[0] = value; +} + +// Return whether `path` is absolute. +bool is_absolute_path(nonstd::string_view path); + +// Test if a file is on nfs. +// +// Sets is_nfs to the result if fstatfs is available and no error occurred. +// +// Returns 0 if is_nfs was set, -1 if fstatfs is not available or errno if an +// error occurred. +int is_nfs_fd(int fd, bool* is_nfs); + +// Return whether `ch` is a directory separator, i.e. '/' on POSIX systems and +// '/' or '\\' on Windows systems. +inline bool +is_dir_separator(char ch) +{ + return ch == '/' +#ifdef _WIN32 + || ch == '\\' +#endif + ; +} + +// Return whether `path` is a full path. +inline bool +is_full_path(nonstd::string_view path) +{ +#ifdef _WIN32 + if (path.find('\\') != nonstd::string_view::npos) { + return true; + } +#endif + return path.find('/') != nonstd::string_view::npos; +} + +// Return whether `path` represents a precompiled header (see "Precompiled +// Headers" in GCC docs). +bool is_precompiled_header(nonstd::string_view path); + +// Thread-safe version of `localtime(3)`. If `time` is not specified the current +// time of day is used. +nonstd::optional localtime(nonstd::optional time = {}); + +// Make a relative path from current working directory to `path` if `path` is +// under the base directory. +std::string make_relative_path(const Context& ctx, nonstd::string_view path); + +// Return whether `path` is equal to `dir_prefix_or_file` or if +// `dir_prefix_or_file` is a directory prefix of `path`. +bool matches_dir_prefix_or_file(nonstd::string_view dir_prefix_or_file, + nonstd::string_view path); + +// Normalize absolute path `path`, not taking symlinks into account. +// +// Normalization here means syntactically removing redundant slashes and +// resolving "." and ".." parts. The algorithm does however *not* follow +// symlinks, so the result may not actually resolve to `path`. +// +// On Windows: Backslashes are replaced with forward slashes. +std::string normalize_absolute_path(nonstd::string_view path); + +// Parse `duration`, an unsigned integer with d (days) or s (seconds) suffix, +// into seconds. Throws `Error` on error. +uint64_t parse_duration(const std::string& duration); + +// Parse a string into a signed integer. +// +// Throws `Error` if `value` cannot be parsed as an int64_t or if the value +// falls out of the range [`min_value`, `max_value`]. `min_value` and +// `max_value` default to min and max values of int64_t. `description` is +// included in the error message for range violations. +int64_t parse_signed(const std::string& value, + nonstd::optional min_value = nonstd::nullopt, + nonstd::optional max_value = nonstd::nullopt, + nonstd::string_view description = "integer"); + +// Parse a "size value", i.e. a string that can end in k, M, G, T (10-based +// suffixes) or Ki, Mi, Gi, Ti (2-based suffixes). For backward compatibility, K +// is also recognized as a synonym of k. Throws `Error` on parse error. +uint64_t parse_size(const std::string& value); + +// Parse a string into an unsigned integer. +// +// Throws `Error` if `value` cannot be parsed as an uint64_t or if the value +// falls out of the range [`min_value`, `max_value`]. `min_value` and +// `max_value` default to min and max values of uint64_t. `description` is +// included in the error message for range violations. +uint64_t parse_unsigned(const std::string& value, + nonstd::optional min_value = nonstd::nullopt, + nonstd::optional max_value = nonstd::nullopt, + nonstd::string_view description = "integer"); + +// Read data from `fd` until end of file and call `data_receiver` with the read +// data. Returns whether reading was successful, i.e. whether the read(2) call +// did not return -1. +bool read_fd(int fd, DataReceiver data_receiver); + +// Return `path`'s content as a string. If `size_hint` is not 0 then assume that +// `path` has this size (this saves system calls). +// +// Throws `Error` on error. The description contains the error message without +// the path. +std::string read_file(const std::string& path, size_t size_hint = 0); + +#ifndef _WIN32 +// Like readlink(2) but returns the string (or the empty string on failure). +std::string read_link(const std::string& path); +#endif + +// Return a normalized absolute path of `path`. On error (e.g. if the `path` +// doesn't exist) the empty string is returned if return_empty_on_error is true, +// otherwise `path` unmodified. +std::string real_path(const std::string& path, + bool return_empty_on_error = false); + +// Return a view into `path` containing the given path without the filename +// extension as determined by `get_extension()`. +nonstd::string_view remove_extension(nonstd::string_view path); + +// Rename `oldpath` to `newpath` (deleting `newpath`). Throws `Error` on error. +void rename(const std::string& oldpath, const std::string& newpath); + +// Detmine if `program_name` is equal to `canonical_program_name`. On Windows, +// this means performing a case insensitive equality check with and without a +// ".exe" suffix. On non-Windows, it is a simple equality check. +bool same_program_name(nonstd::string_view program_name, + nonstd::string_view canonical_program_name); + +// Send `text` to STDERR_FILENO, optionally stripping ANSI color sequences if +// `ctx.args_info.strip_diagnostics_colors` is true and rewriting paths to +// absolute if `ctx.config.absolute_paths_in_stderr` is true. Throws `Error` on +// error. +void send_to_stderr(const Context& ctx, const std::string& text); + +// Set the FD_CLOEXEC on file descriptor `fd`. This is a NOP on Windows. +void set_cloexec_flag(int fd); + +// Set environment variable `name` to `value`. +void setenv(const std::string& name, const std::string& value); + +// Return size change in KiB between `old_stat` and `new_stat`. +inline int64_t +size_change_kibibyte(const Stat& old_stat, const Stat& new_stat) +{ + return (static_cast(new_stat.size_on_disk()) + - static_cast(old_stat.size_on_disk())) + / 1024; +} + +// Split `input` into words at any of the characters listed in `separators`. +// These words are a view into `input`; empty words are omitted. `separators` +// must neither be the empty string nor a nullptr. +std::vector split_into_views(nonstd::string_view input, + const char* separators); + +// Same as `split_into_views` but the words are copied from `input`. +std::vector split_into_strings(nonstd::string_view input, + const char* separators); + +// Return true if `prefix` is a prefix of `string`. +inline bool +starts_with(const char* string, nonstd::string_view prefix) +{ + // Optimized version of starts_with(string_view, string_view): avoid computing + // the length of the string argument. + return strncmp(string, prefix.data(), prefix.length()) == 0; +} + +// Return true if `prefix` is a prefix of `string`. +inline bool +starts_with(nonstd::string_view string, nonstd::string_view prefix) +{ + return string.starts_with(prefix); +} + +// Returns a copy of string with the specified ANSI CSI sequences removed. +[[nodiscard]] std::string strip_ansi_csi_seqs(nonstd::string_view string); + +// Strip whitespace from left and right side of a string. +[[nodiscard]] std::string strip_whitespace(nonstd::string_view string); + +// Convert a string to lowercase. +[[nodiscard]] std::string to_lowercase(nonstd::string_view string); + +// Traverse `path` recursively (postorder, i.e. files are visited before their +// parent directory). +// +// Throws Error on error. +void traverse(const std::string& path, const TraverseVisitor& visitor); + +// Remove `path` (non-directory), NFS safe. Logs according to `unlink_log`. +// +// Returns whether removal was successful. A non-existing `path` is considered +// successful. +bool unlink_safe(const std::string& path, + UnlinkLog unlink_log = UnlinkLog::log_failure); + +// Remove `path` (non-directory), NFS hazardous. Use only for files that will +// not exist on other systems. Logs according to `unlink_log`. +// +// Returns whether removal was successful. A non-existing `path` is considered +// successful. +bool unlink_tmp(const std::string& path, + UnlinkLog unlink_log = UnlinkLog::log_failure); + +// Unset environment variable `name`. +void unsetenv(const std::string& name); + +// Set mtime of `path` to the current timestamp. +void update_mtime(const std::string& path); + +// Remove `path` (and its contents if it's a directory). A non-existing path is +// not considered an error. +// +// Throws Error on error. +void wipe_path(const std::string& path); + +// Write `size` bytes from `data` to `fd`. Throws `Error` on error. +void write_fd(int fd, const void* data, size_t size); + +// Write `data` to `path`. The file will be opened according to `open_mode`, +// which always will include `std::ios::out` even if not specified at the call +// site. +// +// Throws `Error` on error. The description contains the error message without +// the path. +void write_file(const std::string& path, + const std::string& data, + std::ios_base::openmode open_mode = std::ios::binary); + +} // namespace Util diff --git a/src/Win32Util.cpp b/src/Win32Util.cpp new file mode 100644 index 0000000..354c9ae --- /dev/null +++ b/src/Win32Util.cpp @@ -0,0 +1,168 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "Win32Util.hpp" + +#include "Util.hpp" + +#include +#include + +namespace Win32Util { + +std::string +add_exe_suffix(const std::string& path) +{ + auto ext = Util::to_lowercase(Util::get_extension(path)); + if (ext == ".exe" || ext == ".bat" || ext == ".sh") { + return path; + } else { + return path + ".exe"; + } +} + +std::string +error_message(DWORD error_code) +{ + LPSTR buffer; + size_t size = + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + error_code, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&buffer), + 0, + nullptr); + std::string message(buffer, size); + LocalFree(buffer); + return message; +} + +std::string +argv_to_string(const char* const* argv, const std::string& prefix) +{ + std::string result; + size_t i = 0; + const char* arg = prefix.empty() ? argv[i++] : prefix.c_str(); + + do { + int bs = 0; + result += '"'; + for (size_t j = 0; arg[j]; ++j) { + switch (arg[j]) { + case '\\': + ++bs; + break; + // Fallthrough. + case '"': + bs = (bs << 1) + 1; + // Fallthrough. + default: + while (bs > 0) { + result += '\\'; + --bs; + } + result += arg[j]; + } + } + bs <<= 1; + while (bs > 0) { + result += '\\'; + --bs; + } + result += "\" "; + } while ((arg = argv[i++])); + + result.resize(result.length() - 1); + return result; +} + +} // namespace Win32Util + +// From: https://stackoverflow.com/a/58162122/262458 +#ifdef _MSC_VER +int +gettimeofday(struct timeval* tp, struct timezone* /*tzp*/) +{ + namespace sc = std::chrono; + sc::system_clock::duration d = sc::system_clock::now().time_since_epoch(); + sc::seconds s = sc::duration_cast(d); + tp->tv_sec = static_cast(s.count()); + tp->tv_usec = + static_cast(sc::duration_cast(d - s).count()); + + return 0; +} +#endif + +void +usleep(int64_t usec) +{ + std::this_thread::sleep_for(std::chrono::microseconds(usec)); +} + +struct tm* +localtime_r(time_t* _clock, struct tm* _result) +{ + struct tm* p = localtime(_clock); + + if (p) + *(_result) = *p; + + return p; +} + +// From: https://stackoverflow.com/a/40160038/262458 +#ifdef _MSC_VER +int +vasprintf(char** strp, const char* fmt, va_list ap) +{ + // _vscprintf tells you how big the buffer needs to be + int len = _vscprintf(fmt, ap); + if (len == -1) { + return -1; + } + size_t size = (size_t)len + 1; + char* str = static_cast(malloc(size)); + if (!str) { + return -1; + } + // vsprintf_s is the "secure" version of vsprintf + int r = vsprintf_s(str, len + 1, fmt, ap); + if (r == -1) { + free(str); + return -1; + } + *strp = str; + return r; +} +#endif + +// Also from: https://stackoverflow.com/a/40160038/262458 +#ifdef _MSC_VER +int +asprintf(char** strp, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + int r = vasprintf(strp, fmt, ap); + va_end(ap); + return r; +} +#endif diff --git a/src/Win32Util.hpp b/src/Win32Util.hpp new file mode 100644 index 0000000..a56387d --- /dev/null +++ b/src/Win32Util.hpp @@ -0,0 +1,38 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include + +namespace Win32Util { + +// Add ".exe" suffix to `program` if it doesn't already end with ".exe", ".bat" +// or ".sh". +std::string add_exe_suffix(const std::string& program); + +// Recreate a Windows command line string based on `argv`. If `prefix` is +// non-empty, add it as the first argument. +std::string argv_to_string(const char* const* argv, const std::string& prefix); + +// Return the error message corresponding to `error_code`. +std::string error_message(DWORD error_code); + +} // namespace Win32Util diff --git a/src/ZstdCompressor.cpp b/src/ZstdCompressor.cpp new file mode 100644 index 0000000..8b2c518 --- /dev/null +++ b/src/ZstdCompressor.cpp @@ -0,0 +1,115 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "ZstdCompressor.hpp" + +#include "Logging.hpp" +#include "assertions.hpp" +#include "exceptions.hpp" + +#include + +using Logging::log; + +ZstdCompressor::ZstdCompressor(FILE* stream, int8_t compression_level) + : m_stream(stream), m_zstd_stream(ZSTD_createCStream()) +{ + if (compression_level == 0) { + compression_level = default_compression_level; + log("Using default compression level {}", compression_level); + } + + // libzstd 1.3.4 and newer support negative levels. However, the query + // function ZSTD_minCLevel did not appear until 1.3.6, so perform detection + // based on version instead. + if (ZSTD_versionNumber() < 10304 && compression_level < 1) { + log( + "Using compression level 1 (minimum level supported by libzstd) instead" + " of {}", + compression_level); + compression_level = 1; + } + + m_compression_level = std::min(compression_level, ZSTD_maxCLevel()); + if (m_compression_level != compression_level) { + log("Using compression level {} (max libzstd level) instead of {}", + m_compression_level, + compression_level); + } + + size_t ret = ZSTD_initCStream(m_zstd_stream, m_compression_level); + if (ZSTD_isError(ret)) { + ZSTD_freeCStream(m_zstd_stream); + throw Error("error initializing zstd compression stream"); + } +} + +ZstdCompressor::~ZstdCompressor() +{ + ZSTD_freeCStream(m_zstd_stream); +} + +int8_t +ZstdCompressor::actual_compression_level() const +{ + return m_compression_level; +} + +void +ZstdCompressor::write(const void* data, size_t count) +{ + m_zstd_in.src = data; + m_zstd_in.size = count; + m_zstd_in.pos = 0; + + int flush = data ? 0 : 1; + + size_t ret; + while (m_zstd_in.pos < m_zstd_in.size) { + uint8_t buffer[READ_BUFFER_SIZE]; + m_zstd_out.dst = buffer; + m_zstd_out.size = sizeof(buffer); + m_zstd_out.pos = 0; + ret = ZSTD_compressStream(m_zstd_stream, &m_zstd_out, &m_zstd_in); + ASSERT(!(ZSTD_isError(ret))); + size_t compressed_bytes = m_zstd_out.pos; + if (fwrite(buffer, 1, compressed_bytes, m_stream) != compressed_bytes + || ferror(m_stream)) { + throw Error("failed to write to zstd output stream "); + } + } + ret = flush; + while (ret > 0) { + uint8_t buffer[READ_BUFFER_SIZE]; + m_zstd_out.dst = buffer; + m_zstd_out.size = sizeof(buffer); + m_zstd_out.pos = 0; + ret = ZSTD_endStream(m_zstd_stream, &m_zstd_out); + size_t compressed_bytes = m_zstd_out.pos; + if (fwrite(buffer, 1, compressed_bytes, m_stream) != compressed_bytes + || ferror(m_stream)) { + throw Error("failed to write to zstd output stream"); + } + } +} + +void +ZstdCompressor::finalize() +{ + write(nullptr, 0); +} diff --git a/src/ZstdCompressor.hpp b/src/ZstdCompressor.hpp new file mode 100644 index 0000000..58ca453 --- /dev/null +++ b/src/ZstdCompressor.hpp @@ -0,0 +1,51 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Compressor.hpp" +#include "NonCopyable.hpp" + +#include + +// A compressor of a Zstandard stream. +class ZstdCompressor : public Compressor, NonCopyable +{ +public: + // Parameters: + // - stream: The file to write data to. + // - compression_level: Desired compression level. + ZstdCompressor(FILE* stream, int8_t compression_level); + + ~ZstdCompressor() override; + + int8_t actual_compression_level() const override; + void write(const void* data, size_t count) override; + void finalize() override; + + constexpr static uint8_t default_compression_level = 1; + +private: + FILE* m_stream; + ZSTD_CStream* m_zstd_stream; + ZSTD_inBuffer m_zstd_in; + ZSTD_outBuffer m_zstd_out; + int8_t m_compression_level; +}; diff --git a/src/ZstdDecompressor.cpp b/src/ZstdDecompressor.cpp new file mode 100644 index 0000000..c08d0f9 --- /dev/null +++ b/src/ZstdDecompressor.cpp @@ -0,0 +1,83 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "ZstdDecompressor.hpp" + +#include "assertions.hpp" +#include "exceptions.hpp" + +ZstdDecompressor::ZstdDecompressor(FILE* stream) + : m_stream(stream), + m_input_size(0), + m_input_consumed(0), + m_zstd_stream(ZSTD_createDStream()), + m_reached_stream_end(false) +{ + size_t ret = ZSTD_initDStream(m_zstd_stream); + if (ZSTD_isError(ret)) { + ZSTD_freeDStream(m_zstd_stream); + throw Error("failed to initialize zstd decompression stream"); + } +} + +ZstdDecompressor::~ZstdDecompressor() +{ + ZSTD_freeDStream(m_zstd_stream); +} + +void +ZstdDecompressor::read(void* data, size_t count) +{ + size_t bytes_read = 0; + while (bytes_read < count) { + ASSERT(m_input_size >= m_input_consumed); + if (m_input_size == m_input_consumed) { + m_input_size = fread(m_input_buffer, 1, sizeof(m_input_buffer), m_stream); + if (m_input_size == 0) { + throw Error("failed to read from zstd input stream"); + } + m_input_consumed = 0; + } + + m_zstd_in.src = (m_input_buffer + m_input_consumed); + m_zstd_in.size = m_input_size - m_input_consumed; + m_zstd_in.pos = 0; + + m_zstd_out.dst = static_cast(data) + bytes_read; + m_zstd_out.size = count - bytes_read; + m_zstd_out.pos = 0; + size_t ret = ZSTD_decompressStream(m_zstd_stream, &m_zstd_out, &m_zstd_in); + if (ZSTD_isError(ret)) { + throw Error("failed to read from zstd input stream"); + } + if (ret == 0) { + m_reached_stream_end = true; + break; + } + bytes_read += m_zstd_out.pos; + m_input_consumed += m_zstd_in.pos; + } +} + +void +ZstdDecompressor::finalize() +{ + if (!m_reached_stream_end) { + throw Error("garbage data at end of zstd input stream"); + } +} diff --git a/src/ZstdDecompressor.hpp b/src/ZstdDecompressor.hpp new file mode 100644 index 0000000..8f85ce6 --- /dev/null +++ b/src/ZstdDecompressor.hpp @@ -0,0 +1,50 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Decompressor.hpp" + +#include +#include + +// A decompressor of a Zstandard stream. +class ZstdDecompressor : public Decompressor +{ +public: + // Parameters: + // - stream: The file to read data from. + explicit ZstdDecompressor(FILE* stream); + + ~ZstdDecompressor() override; + + void read(void* data, size_t count) override; + void finalize() override; + +private: + FILE* m_stream; + char m_input_buffer[READ_BUFFER_SIZE]; + size_t m_input_size; + size_t m_input_consumed; + ZSTD_DStream* m_zstd_stream; + ZSTD_inBuffer m_zstd_in; + ZSTD_outBuffer m_zstd_out; + bool m_reached_stream_end; +}; diff --git a/src/argprocessing.cpp b/src/argprocessing.cpp new file mode 100644 index 0000000..bda801d --- /dev/null +++ b/src/argprocessing.cpp @@ -0,0 +1,1216 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "argprocessing.hpp" + +#include "Context.hpp" +#include "FormatNonstdStringView.hpp" +#include "Logging.hpp" +#include "assertions.hpp" +#include "compopt.hpp" +#include "language.hpp" + +#include + +using Logging::log; +using nonstd::nullopt; +using nonstd::optional; +using nonstd::string_view; + +namespace { + +enum class ColorDiagnostics : int8_t { never, automatic, always }; + +struct ArgumentProcessingState +{ + bool found_c_opt = false; + bool found_dc_opt = false; + bool found_S_opt = false; + bool found_pch = false; + bool found_fpch_preprocess = false; + ColorDiagnostics color_diagnostics = ColorDiagnostics::automatic; + bool found_directives_only = false; + bool found_rewrite_includes = false; + + std::string explicit_language; // As specified with -x. + std::string file_language; // As deduced from file extension. + std::string input_charset_option; // -finput-charset=... + + // Is the dependency makefile name overridden with -MF? + bool dependency_filename_specified = false; + + // Is the dependency target name implicitly specified using + // DEPENDENCIES_OUTPUT or SUNPRO_DEPENDENCIES? + bool dependency_implicit_target_specified = false; + + // Is the compiler being asked to output debug info on level 3? + bool generating_debuginfo_level_3 = false; + + // common_args contains all original arguments except: + // * those that never should be passed to the preprocessor, + // * those that only should be passed to the preprocessor (if run_second_cpp + // is false), and + // * dependency options (like -MD and friends). + Args common_args; + + // cpp_args contains arguments that were not added to common_args, i.e. those + // that should only be passed to the preprocessor if run_second_cpp is false. + // If run_second_cpp is true, they will be passed to the compiler as well. + Args cpp_args; + + // dep_args contains dependency options like -MD. They are only passed to the + // preprocessor, never to the compiler. + Args dep_args; + + // compiler_only_args contains arguments that should only be passed to the + // compiler, not the preprocessor. + Args compiler_only_args; +}; + +bool +color_output_possible() +{ + const char* term_env = getenv("TERM"); + return isatty(STDERR_FILENO) && term_env && strcasecmp(term_env, "DUMB") != 0; +} + +bool +detect_pch(Context& ctx, + const std::string& option, + const std::string& arg, + bool is_cc1_option, + bool* found_pch) +{ + ASSERT(found_pch); + + // Try to be smart about detecting precompiled headers. + // If the option is an option for Clang (is_cc1_option), don't accept + // anything just because it has a corresponding precompiled header, + // because Clang doesn't behave that way either. + std::string pch_file; + if (option == "-include-pch" || option == "-include-pth") { + if (Stat::stat(arg)) { + log("Detected use of precompiled header: {}", arg); + pch_file = arg; + } + } else if (!is_cc1_option) { + for (const auto& extension : {".gch", ".pch", ".pth"}) { + std::string path = arg + extension; + if (Stat::stat(path)) { + log("Detected use of precompiled header: {}", path); + pch_file = path; + } + } + } + + if (!pch_file.empty()) { + if (!ctx.included_pch_file.empty()) { + log("Multiple precompiled headers used: {} and {}", + ctx.included_pch_file, + pch_file); + return false; + } + ctx.included_pch_file = pch_file; + *found_pch = true; + } + return true; +} + +bool +process_profiling_option(Context& ctx, const std::string& arg) +{ + static const std::vector known_simple_options = { + "-fprofile-correction", + "-fprofile-reorder-functions", + "-fprofile-sample-accurate", + "-fprofile-values", + }; + + if (std::find(known_simple_options.begin(), known_simple_options.end(), arg) + != known_simple_options.end()) { + return true; + } + + std::string new_profile_path; + bool new_profile_use = false; + + if (Util::starts_with(arg, "-fprofile-dir=")) { + new_profile_path = arg.substr(arg.find('=') + 1); + } else if (arg == "-fprofile-generate" || arg == "-fprofile-instr-generate") { + ctx.args_info.profile_generate = true; + if (ctx.guessed_compiler == GuessedCompiler::clang) { + new_profile_path = "."; + } else { + // GCC uses $PWD/$(basename $obj). + new_profile_path = ctx.apparent_cwd; + } + } else if (Util::starts_with(arg, "-fprofile-generate=") + || Util::starts_with(arg, "-fprofile-instr-generate=")) { + ctx.args_info.profile_generate = true; + new_profile_path = arg.substr(arg.find('=') + 1); + } else if (arg == "-fprofile-use" || arg == "-fprofile-instr-use" + || arg == "-fprofile-sample-use" || arg == "-fbranch-probabilities" + || arg == "-fauto-profile") { + new_profile_use = true; + if (ctx.args_info.profile_path.empty()) { + new_profile_path = "."; + } + } else if (Util::starts_with(arg, "-fprofile-use=") + || Util::starts_with(arg, "-fprofile-instr-use=") + || Util::starts_with(arg, "-fprofile-sample-use=") + || Util::starts_with(arg, "-fauto-profile=")) { + new_profile_use = true; + new_profile_path = arg.substr(arg.find('=') + 1); + } else { + log("Unknown profiling option: {}", arg); + return false; + } + + if (new_profile_use) { + if (ctx.args_info.profile_use) { + log("Multiple profiling options not supported"); + return false; + } + ctx.args_info.profile_use = true; + } + + if (!new_profile_path.empty()) { + ctx.args_info.profile_path = new_profile_path; + log("Set profile directory to {}", ctx.args_info.profile_path); + } + + if (ctx.args_info.profile_generate && ctx.args_info.profile_use) { + // Too hard to figure out what the compiler will do. + log("Both generating and using profile info, giving up"); + return false; + } + + return true; +} + +// The compiler is invoked with the original arguments in the depend mode. +// Collect extra arguments that should be added. +void +add_depend_mode_extra_original_args(Context& ctx, const std::string& arg) +{ + if (ctx.config.depend_mode()) { + ctx.args_info.depend_extra_args.push_back(arg); + } +} + +optional +process_arg(Context& ctx, + Args& args, + size_t& args_index, + ArgumentProcessingState& state) +{ + ArgsInfo& args_info = ctx.args_info; + Config& config = ctx.config; + + size_t& i = args_index; + + // The user knows best: just swallow the next arg. + if (args[i] == "--ccache-skip") { + i++; + if (i == args.size()) { + log("--ccache-skip lacks an argument"); + return Statistic::bad_compiler_arguments; + } + state.common_args.push_back(args[i]); + return nullopt; + } + + // Special case for -E. + if (args[i] == "-E") { + return Statistic::called_for_preprocessing; + } + + // Handle "@file" argument. + if (Util::starts_with(args[i], "@") || Util::starts_with(args[i], "-@")) { + const char* argpath = args[i].c_str() + 1; + + if (argpath[-1] == '-') { + ++argpath; + } + auto file_args = Args::from_gcc_atfile(argpath); + if (!file_args) { + log("Couldn't read arg file {}", argpath); + return Statistic::bad_compiler_arguments; + } + + args.replace(i, *file_args); + i--; + return nullopt; + } + + // Handle cuda "-optf" and "--options-file" argument. + if (ctx.guessed_compiler == GuessedCompiler::nvcc + && (args[i] == "-optf" || args[i] == "--options-file")) { + if (i == args.size() - 1) { + log("Expected argument after {}", args[i]); + return Statistic::bad_compiler_arguments; + } + ++i; + + // Argument is a comma-separated list of files. + auto paths = Util::split_into_strings(args[i], ","); + for (auto it = paths.rbegin(); it != paths.rend(); ++it) { + auto file_args = Args::from_gcc_atfile(*it); + if (!file_args) { + log("Couldn't read CUDA options file {}", *it); + return Statistic::bad_compiler_arguments; + } + + args.insert(i + 1, *file_args); + } + + return nullopt; + } + + // These are always too hard. + if (compopt_too_hard(args[i]) || Util::starts_with(args[i], "-fdump-") + || Util::starts_with(args[i], "-MJ")) { + log("Compiler option {} is unsupported", args[i]); + return Statistic::unsupported_compiler_option; + } + + // These are too hard in direct mode. + if (config.direct_mode() && compopt_too_hard_for_direct_mode(args[i])) { + log("Unsupported compiler option for direct mode: {}", args[i]); + config.set_direct_mode(false); + } + + // -Xarch_* options are too hard. + if (Util::starts_with(args[i], "-Xarch_")) { + log("Unsupported compiler option: {}", args[i]); + return Statistic::unsupported_compiler_option; + } + + // Handle -arch options. + if (args[i] == "-arch") { + ++i; + args_info.arch_args.emplace_back(args[i]); + if (args_info.arch_args.size() == 2) { + config.set_run_second_cpp(true); + } + return nullopt; + } + + // Some arguments that clang passes directly to cc1 (related to precompiled + // headers) need the usual ccache handling. In those cases, the -Xclang + // prefix is skipped and the cc1 argument is handled instead. + if (args[i] == "-Xclang" && i < args.size() - 1 + && (args[i + 1] == "-emit-pch" || args[i + 1] == "-emit-pth" + || args[i + 1] == "-include-pch" || args[i + 1] == "-include-pth" + || args[i + 1] == "-fno-pch-timestamp")) { + if (compopt_affects_compiler_output(args[i + 1])) { + state.compiler_only_args.push_back(args[i]); + } else if (compopt_affects_cpp_output(args[i + 1])) { + state.cpp_args.push_back(args[i]); + } else { + state.common_args.push_back(args[i]); + } + ++i; + } + + // Handle options that should not be passed to the preprocessor. + if (compopt_affects_compiler_output(args[i])) { + state.compiler_only_args.push_back(args[i]); + if (compopt_takes_arg(args[i]) + || (ctx.guessed_compiler == GuessedCompiler::nvcc + && args[i] == "-Werror")) { + if (i == args.size() - 1) { + log("Missing argument to {}", args[i]); + return Statistic::bad_compiler_arguments; + } + state.compiler_only_args.push_back(args[i + 1]); + ++i; + } + return nullopt; + } + if (compopt_prefix_affects_compiler_output(args[i])) { + state.compiler_only_args.push_back(args[i]); + return nullopt; + } + + // Modules are handled on demand as necessary in the background, so there is + // no need to cache them, they can in practice be ignored. All that is needed + // is to correctly depend also on module.modulemap files, and those are + // included only in depend mode (preprocessed output does not list them). + // Still, not including the modules themselves in the hash could possibly + // result in an object file that would be different from the actual + // compilation (even though it should be compatible), so require a sloppiness + // flag. + if (args[i] == "-fmodules") { + if (!config.depend_mode() || !config.direct_mode()) { + log("Compiler option {} is unsupported without direct depend mode", + args[i]); + return Statistic::could_not_use_modules; + } else if (!(config.sloppiness() & SLOPPY_MODULES)) { + log( + "You have to specify \"modules\" sloppiness when using" + " -fmodules to get hits"); + return Statistic::could_not_use_modules; + } + } + + // We must have -c. + if (args[i] == "-c") { + state.found_c_opt = true; + return nullopt; + } + + // when using nvcc with separable compilation, -dc implies -c + if ((args[i] == "-dc" || args[i] == "--device-c") + && ctx.guessed_compiler == GuessedCompiler::nvcc) { + state.found_dc_opt = true; + return nullopt; + } + + // -S changes the default extension. + if (args[i] == "-S") { + state.common_args.push_back(args[i]); + state.found_S_opt = true; + return nullopt; + } + + if (Util::starts_with(args[i], "-x")) { + if (args[i].length() >= 3 && !islower(args[i][2])) { + // -xCODE (where CODE can be e.g. Host or CORE-AVX2, always starting with + // an uppercase letter) is an ordinary Intel compiler option, not a + // language specification. (GCC's "-x" language argument is always + // lowercase.) + state.common_args.push_back(args[i]); + return nullopt; + } + + // Special handling for -x: remember the last specified language before the + // input file and strip all -x options from the arguments. + if (args[i].length() == 2) { + if (i == args.size() - 1) { + log("Missing argument to {}", args[i]); + return Statistic::bad_compiler_arguments; + } + if (args_info.input_file.empty()) { + state.explicit_language = args[i + 1]; + } + i++; + return nullopt; + } + + DEBUG_ASSERT(args[i].length() >= 3); + if (args_info.input_file.empty()) { + state.explicit_language = args[i].substr(2); + } + return nullopt; + } + + // We need to work out where the output was meant to go. + if (args[i] == "-o") { + if (i == args.size() - 1) { + log("Missing argument to {}", args[i]); + return Statistic::bad_compiler_arguments; + } + args_info.output_obj = Util::make_relative_path(ctx, args[i + 1]); + i++; + return nullopt; + } + + // Alternate form of -o with no space. Nvcc does not support this. + if (Util::starts_with(args[i], "-o") + && ctx.guessed_compiler != GuessedCompiler::nvcc) { + args_info.output_obj = + Util::make_relative_path(ctx, string_view(args[i]).substr(2)); + return nullopt; + } + + if (Util::starts_with(args[i], "-fdebug-prefix-map=") + || Util::starts_with(args[i], "-ffile-prefix-map=")) { + std::string map = args[i].substr(args[i].find('=') + 1); + args_info.debug_prefix_maps.push_back(map); + state.common_args.push_back(args[i]); + return nullopt; + } + + // Debugging is handled specially, so that we know if we can strip line + // number info. + if (Util::starts_with(args[i], "-g")) { + state.common_args.push_back(args[i]); + + if (Util::starts_with(args[i], "-gdwarf")) { + // Selection of DWARF format (-gdwarf or -gdwarf-) enables + // debug info on level 2. + args_info.generating_debuginfo = true; + return nullopt; + } + + if (Util::starts_with(args[i], "-gz")) { + // -gz[=type] neither disables nor enables debug info. + return nullopt; + } + + char last_char = args[i].back(); + if (last_char == '0') { + // "-g0", "-ggdb0" or similar: All debug information disabled. + args_info.generating_debuginfo = false; + state.generating_debuginfo_level_3 = false; + } else { + args_info.generating_debuginfo = true; + if (last_char == '3') { + state.generating_debuginfo_level_3 = true; + } + if (args[i] == "-gsplit-dwarf") { + args_info.seen_split_dwarf = true; + } + } + return nullopt; + } + + // These options require special handling, because they behave differently + // with gcc -E, when the output file is not specified. + if (args[i] == "-MD" || args[i] == "-MMD") { + args_info.generating_dependencies = true; + args_info.seen_MD_MMD = true; + state.dep_args.push_back(args[i]); + return nullopt; + } + + if (Util::starts_with(args[i], "-MF")) { + state.dependency_filename_specified = true; + + std::string dep_file; + bool separate_argument = (args[i].size() == 3); + if (separate_argument) { + // -MF arg + if (i == args.size() - 1) { + log("Missing argument to {}", args[i]); + return Statistic::bad_compiler_arguments; + } + dep_file = args[i + 1]; + i++; + } else { + // -MFarg or -MF=arg (EDG-based compilers) + dep_file = args[i].substr(args[i][3] == '=' ? 4 : 3); + } + args_info.output_dep = Util::make_relative_path(ctx, dep_file); + // Keep the format of the args the same. + if (separate_argument) { + state.dep_args.push_back("-MF"); + state.dep_args.push_back(args_info.output_dep); + } else { + state.dep_args.push_back("-MF" + args_info.output_dep); + } + return nullopt; + } + + if (Util::starts_with(args[i], "-MQ") || Util::starts_with(args[i], "-MT")) { + ctx.args_info.dependency_target_specified = true; + + if (args[i].size() == 3) { + // -MQ arg or -MT arg + if (i == args.size() - 1) { + log("Missing argument to {}", args[i]); + return Statistic::bad_compiler_arguments; + } + state.dep_args.push_back(args[i]); + std::string relpath = Util::make_relative_path(ctx, args[i + 1]); + state.dep_args.push_back(relpath); + i++; + } else { + auto arg_opt = string_view(args[i]).substr(0, 3); + auto option = string_view(args[i]).substr(3); + auto relpath = Util::make_relative_path(ctx, option); + state.dep_args.push_back(fmt::format("{}{}", arg_opt, relpath)); + } + return nullopt; + } + + if (args[i] == "-fprofile-arcs") { + args_info.profile_arcs = true; + state.common_args.push_back(args[i]); + return nullopt; + } + + if (args[i] == "-ftest-coverage") { + args_info.generating_coverage = true; + state.common_args.push_back(args[i]); + return nullopt; + } + + if (args[i] == "-fstack-usage") { + args_info.generating_stackusage = true; + state.common_args.push_back(args[i]); + return nullopt; + } + + if (args[i] == "--coverage" // = -fprofile-arcs -ftest-coverage + || args[i] == "-coverage") { // Undocumented but still works. + args_info.profile_arcs = true; + args_info.generating_coverage = true; + state.common_args.push_back(args[i]); + return nullopt; + } + + if (Util::starts_with(args[i], "-fprofile-") + || Util::starts_with(args[i], "-fauto-profile") + || args[i] == "-fbranch-probabilities") { + if (!process_profiling_option(ctx, args[i])) { + // The failure is logged by process_profiling_option. + return Statistic::unsupported_compiler_option; + } + state.common_args.push_back(args[i]); + return nullopt; + } + + if (Util::starts_with(args[i], "-fsanitize-blacklist=")) { + args_info.sanitize_blacklists.emplace_back(args[i].substr(21)); + state.common_args.push_back(args[i]); + return nullopt; + } + + if (Util::starts_with(args[i], "--sysroot=")) { + auto path = string_view(args[i]).substr(10); + auto relpath = Util::make_relative_path(ctx, path); + state.common_args.push_back("--sysroot=" + relpath); + return nullopt; + } + + // Alternate form of specifying sysroot without = + if (args[i] == "--sysroot") { + if (i == args.size() - 1) { + log("Missing argument to {}", args[i]); + return Statistic::bad_compiler_arguments; + } + state.common_args.push_back(args[i]); + auto relpath = Util::make_relative_path(ctx, args[i + 1]); + state.common_args.push_back(relpath); + i++; + return nullopt; + } + + // Alternate form of specifying target without = + if (args[i] == "-target") { + if (i == args.size() - 1) { + log("Missing argument to {}", args[i]); + return Statistic::bad_compiler_arguments; + } + state.common_args.push_back(args[i]); + state.common_args.push_back(args[i + 1]); + i++; + return nullopt; + } + + if (Util::starts_with(args[i], "-Wp,")) { + if (args[i] == "-Wp,-P" || args[i].find(",-P,") != std::string::npos + || Util::ends_with(args[i], ",-P")) { + // -P removes preprocessor information in such a way that the object file + // from compiling the preprocessed file will not be equal to the object + // file produced when compiling without ccache. + log("Too hard option -Wp,-P detected"); + return Statistic::unsupported_compiler_option; + } else if (Util::starts_with(args[i], "-Wp,-MD,") + && args[i].find(',', 8) == std::string::npos) { + args_info.generating_dependencies = true; + state.dependency_filename_specified = true; + args_info.output_dep = + Util::make_relative_path(ctx, string_view(args[i]).substr(8)); + state.dep_args.push_back(args[i]); + return nullopt; + } else if (Util::starts_with(args[i], "-Wp,-MMD,") + && args[i].find(',', 9) == std::string::npos) { + args_info.generating_dependencies = true; + state.dependency_filename_specified = true; + args_info.output_dep = + Util::make_relative_path(ctx, string_view(args[i]).substr(9)); + state.dep_args.push_back(args[i]); + return nullopt; + } else if (Util::starts_with(args[i], "-Wp,-D") + && args[i].find(',', 6) == std::string::npos) { + // Treat it like -D. + state.cpp_args.push_back(args[i].substr(4)); + return nullopt; + } else if (args[i] == "-Wp,-MP" + || (args[i].size() > 8 && Util::starts_with(args[i], "-Wp,-M") + && args[i][7] == ',' + && (args[i][6] == 'F' || args[i][6] == 'Q' + || args[i][6] == 'T') + && args[i].find(',', 8) == std::string::npos)) { + // TODO: Make argument to MF/MQ/MT relative. + state.dep_args.push_back(args[i]); + return nullopt; + } else if (config.direct_mode()) { + // -Wp, can be used to pass too hard options to the preprocessor. + // Hence, disable direct mode. + log("Unsupported compiler option for direct mode: {}", args[i]); + config.set_direct_mode(false); + } + + // Any other -Wp,* arguments are only relevant for the preprocessor. + state.cpp_args.push_back(args[i]); + return nullopt; + } + + if (args[i] == "-MP") { + state.dep_args.push_back(args[i]); + return nullopt; + } + + // Input charset needs to be handled specially. + if (Util::starts_with(args[i], "-finput-charset=")) { + state.input_charset_option = args[i]; + return nullopt; + } + + if (args[i] == "--serialize-diagnostics") { + if (i == args.size() - 1) { + log("Missing argument to {}", args[i]); + return Statistic::bad_compiler_arguments; + } + args_info.generating_diagnostics = true; + args_info.output_dia = Util::make_relative_path(ctx, args[i + 1]); + i++; + return nullopt; + } + + if (args[i] == "-fcolor-diagnostics" || args[i] == "-fdiagnostics-color" + || args[i] == "-fdiagnostics-color=always") { + state.color_diagnostics = ColorDiagnostics::always; + return nullopt; + } + if (args[i] == "-fno-color-diagnostics" || args[i] == "-fno-diagnostics-color" + || args[i] == "-fdiagnostics-color=never") { + state.color_diagnostics = ColorDiagnostics::never; + return nullopt; + } + if (args[i] == "-fdiagnostics-color=auto") { + state.color_diagnostics = ColorDiagnostics::automatic; + return nullopt; + } + + // GCC + if (args[i] == "-fdirectives-only") { + state.found_directives_only = true; + return nullopt; + } + + // Clang + if (args[i] == "-frewrite-includes") { + state.found_rewrite_includes = true; + return nullopt; + } + + if (args[i] == "-fno-pch-timestamp") { + args_info.fno_pch_timestamp = true; + state.common_args.push_back(args[i]); + return nullopt; + } + + if (args[i] == "-fpch-preprocess") { + state.found_fpch_preprocess = true; + state.common_args.push_back(args[i]); + return nullopt; + } + + if (config.sloppiness() & SLOPPY_CLANG_INDEX_STORE + && args[i] == "-index-store-path") { + // Xcode 9 or later calls Clang with this option. The given path includes a + // UUID that might lead to cache misses, especially when cache is shared + // among multiple users. + i++; + if (i <= args.size() - 1) { + log("Skipping argument -index-store-path {}", args[i]); + } + return nullopt; + } + + // Options taking an argument that we may want to rewrite to relative paths to + // get better hit rate. A secondary effect is that paths in the standard error + // output produced by the compiler will be normalized. + if (compopt_takes_path(args[i])) { + if (i == args.size() - 1) { + log("Missing argument to {}", args[i]); + return Statistic::bad_compiler_arguments; + } + + // In the -Xclang -include-(pch/pth) -Xclang case, the path is one + // index further behind. + int next = 1; + if (args[i + 1] == "-Xclang" && i + 2 < args.size()) { + next = 2; + } + + if (!detect_pch( + ctx, args[i], args[i + next], next == 2, &state.found_pch)) { + return Statistic::bad_compiler_arguments; + } + + std::string relpath = Util::make_relative_path(ctx, args[i + next]); + auto& dest_args = + compopt_affects_cpp_output(args[i]) ? state.cpp_args : state.common_args; + dest_args.push_back(args[i]); + if (next == 2) { + dest_args.push_back(args[i + 1]); + } + dest_args.push_back(relpath); + + i += next; + return nullopt; + } + + // Same as above but options with concatenated argument beginning with a + // slash. + if (args[i][0] == '-') { + size_t slash_pos = args[i].find('/'); + if (slash_pos != std::string::npos) { + std::string option = args[i].substr(0, slash_pos); + if (compopt_takes_concat_arg(option) && compopt_takes_path(option)) { + auto relpath = + Util::make_relative_path(ctx, string_view(args[i]).substr(slash_pos)); + std::string new_option = option + relpath; + if (compopt_affects_cpp_output(option)) { + state.cpp_args.push_back(new_option); + } else { + state.common_args.push_back(new_option); + } + return nullopt; + } + } + } + + // Options that take an argument. + if (compopt_takes_arg(args[i])) { + if (i == args.size() - 1) { + log("Missing argument to {}", args[i]); + return Statistic::bad_compiler_arguments; + } + + if (compopt_affects_cpp_output(args[i])) { + state.cpp_args.push_back(args[i]); + state.cpp_args.push_back(args[i + 1]); + } else { + state.common_args.push_back(args[i]); + state.common_args.push_back(args[i + 1]); + } + + i++; + return nullopt; + } + + // Other options. + if (args[i][0] == '-') { + if (compopt_affects_cpp_output(args[i]) + || compopt_prefix_affects_cpp_output(args[i])) { + state.cpp_args.push_back(args[i]); + } else { + state.common_args.push_back(args[i]); + } + return nullopt; + } + + // If an argument isn't a plain file then assume its an option, not an input + // file. This allows us to cope better with unusual compiler options. + // + // Note that "/dev/null" is an exception that is sometimes used as an input + // file when code is testing compiler flags. + if (args[i] != "/dev/null") { + auto st = Stat::stat(args[i]); + if (!st || !st.is_regular()) { + log("{} is not a regular file, not considering as input file", args[i]); + state.common_args.push_back(args[i]); + return nullopt; + } + } + + if (!args_info.input_file.empty()) { + if (!language_for_file(args[i]).empty()) { + log("Multiple input files: {} and {}", args_info.input_file, args[i]); + return Statistic::multiple_source_files; + } else if (!state.found_c_opt && !state.found_dc_opt) { + log("Called for link with {}", args[i]); + if (args[i].find("conftest.") != std::string::npos) { + return Statistic::autoconf_test; + } else { + return Statistic::called_for_link; + } + } else { + log("Unsupported source extension: {}", args[i]); + return Statistic::unsupported_source_language; + } + } + + // The source code file path gets put into the notes. + if (args_info.generating_coverage) { + args_info.input_file = args[i]; + return nullopt; + } + + // Rewrite to relative to increase hit rate. + args_info.input_file = Util::make_relative_path(ctx, args[i]); + + return nullopt; +} + +void +handle_dependency_environment_variables(Context& ctx, + ArgumentProcessingState& state) +{ + ArgsInfo& args_info = ctx.args_info; + + // See . + // Contrary to what the documentation seems to imply the compiler still + // creates object files with these defined (confirmed with GCC 8.2.1), i.e. + // they work as -MMD/-MD, not -MM/-M. These environment variables do nothing + // on Clang. + const char* dependencies_env = getenv("DEPENDENCIES_OUTPUT"); + bool using_sunpro_dependencies = false; + if (!dependencies_env) { + dependencies_env = getenv("SUNPRO_DEPENDENCIES"); + using_sunpro_dependencies = true; + } + if (!dependencies_env) { + return; + } + + args_info.generating_dependencies = true; + state.dependency_filename_specified = true; + + auto dependencies = Util::split_into_views(dependencies_env, " "); + + if (!dependencies.empty()) { + auto abspath_file = dependencies[0]; + args_info.output_dep = Util::make_relative_path(ctx, abspath_file); + } + + // Specifying target object is optional. + if (dependencies.size() > 1) { + // It's the "file target" form. + ctx.args_info.dependency_target_specified = true; + string_view abspath_obj = dependencies[1]; + std::string relpath_obj = Util::make_relative_path(ctx, abspath_obj); + // Ensure that the compiler gets a relative path. + std::string relpath_both = + fmt::format("{} {}", args_info.output_dep, relpath_obj); + if (using_sunpro_dependencies) { + Util::setenv("SUNPRO_DEPENDENCIES", relpath_both); + } else { + Util::setenv("DEPENDENCIES_OUTPUT", relpath_both); + } + } else { + // It's the "file" form. + state.dependency_implicit_target_specified = true; + // Ensure that the compiler gets a relative path. + if (using_sunpro_dependencies) { + Util::setenv("SUNPRO_DEPENDENCIES", args_info.output_dep); + } else { + Util::setenv("DEPENDENCIES_OUTPUT", args_info.output_dep); + } + } +} + +} // namespace + +ProcessArgsResult +process_args(Context& ctx) +{ + ASSERT(!ctx.orig_args.empty()); + + ArgsInfo& args_info = ctx.args_info; + Config& config = ctx.config; + + // args is a copy of the original arguments given to the compiler but with + // arguments from @file and similar constructs expanded. It's only used as a + // temporary data structure to loop over. + Args args = ctx.orig_args; + ArgumentProcessingState state; + + state.common_args.push_back(args[0]); // Compiler + + for (size_t i = 1; i < args.size(); i++) { + auto error = process_arg(ctx, args, i, state); + if (error) { + return *error; + } + } + + if (state.generating_debuginfo_level_3 && !config.run_second_cpp()) { + log("Generating debug info level 3; not compiling preprocessed code"); + config.set_run_second_cpp(true); + } + + handle_dependency_environment_variables(ctx, state); + + if (args_info.input_file.empty()) { + log("No input file found"); + return Statistic::no_input_file; + } + + if (state.found_pch || state.found_fpch_preprocess) { + args_info.using_precompiled_header = true; + if (!(config.sloppiness() & SLOPPY_TIME_MACROS)) { + log( + "You have to specify \"time_macros\" sloppiness when using" + " precompiled headers to get direct hits"); + log("Disabling direct mode"); + return Statistic::could_not_use_precompiled_header; + } + } + + if (args_info.profile_path.empty()) { + args_info.profile_path = ctx.apparent_cwd; + } + + if (!state.explicit_language.empty() && state.explicit_language == "none") { + state.explicit_language.clear(); + } + state.file_language = language_for_file(args_info.input_file); + if (!state.explicit_language.empty()) { + if (!language_is_supported(state.explicit_language)) { + log("Unsupported language: {}", state.explicit_language); + return Statistic::unsupported_source_language; + } + args_info.actual_language = state.explicit_language; + } else { + args_info.actual_language = state.file_language; + } + + args_info.output_is_precompiled_header = + args_info.actual_language.find("-header") != std::string::npos + || Util::is_precompiled_header(args_info.output_obj); + + if (args_info.output_is_precompiled_header + && !(config.sloppiness() & SLOPPY_PCH_DEFINES)) { + log( + "You have to specify \"pch_defines,time_macros\" sloppiness when" + " creating precompiled headers"); + return Statistic::could_not_use_precompiled_header; + } + + if (!state.found_c_opt && !state.found_dc_opt && !state.found_S_opt) { + if (args_info.output_is_precompiled_header) { + state.common_args.push_back("-c"); + } else { + log("No -c option found"); + // Having a separate statistic for autoconf tests is useful, as they are + // the dominant form of "called for link" in many cases. + return args_info.input_file.find("conftest.") != std::string::npos + ? Statistic::autoconf_test + : Statistic::called_for_link; + } + } + + if (args_info.actual_language.empty()) { + log("Unsupported source extension: {}", args_info.input_file); + return Statistic::unsupported_source_language; + } + + if (!config.run_second_cpp() && args_info.actual_language == "cu") { + log("Using CUDA compiler; not compiling preprocessed code"); + config.set_run_second_cpp(true); + } + + args_info.direct_i_file = language_is_preprocessed(args_info.actual_language); + + if (args_info.output_is_precompiled_header && !config.run_second_cpp()) { + // It doesn't work to create the .gch from preprocessed source. + log("Creating precompiled header; not compiling preprocessed code"); + config.set_run_second_cpp(true); + } + + if (config.cpp_extension().empty()) { + std::string p_language = p_language_for_language(args_info.actual_language); + config.set_cpp_extension(extension_for_language(p_language).substr(1)); + } + + // Don't try to second guess the compilers heuristics for stdout handling. + if (args_info.output_obj == "-") { + log("Output file is -"); + return Statistic::output_to_stdout; + } + + if (args_info.output_obj.empty()) { + if (args_info.output_is_precompiled_header) { + args_info.output_obj = args_info.input_file + ".gch"; + } else { + string_view extension = state.found_S_opt ? ".s" : ".o"; + args_info.output_obj = Util::change_extension( + Util::base_name(args_info.input_file), extension); + } + } + + if (args_info.seen_split_dwarf) { + size_t pos = args_info.output_obj.rfind('.'); + if (pos == std::string::npos || pos == args_info.output_obj.size() - 1) { + log("Badly formed object filename"); + return Statistic::bad_compiler_arguments; + } + + args_info.output_dwo = Util::change_extension(args_info.output_obj, ".dwo"); + } + + // Cope with -o /dev/null. + if (args_info.output_obj != "/dev/null") { + auto st = Stat::stat(args_info.output_obj); + if (st && !st.is_regular()) { + log("Not a regular file: {}", args_info.output_obj); + return Statistic::bad_output_file; + } + } + + auto output_dir = std::string(Util::dir_name(args_info.output_obj)); + auto st = Stat::stat(output_dir); + if (!st || !st.is_directory()) { + log("Directory does not exist: {}", output_dir); + return Statistic::bad_output_file; + } + + // Some options shouldn't be passed to the real compiler when it compiles + // preprocessed code: + // + // -finput-charset=XXX (otherwise conversion happens twice) + // -x XXX (otherwise the wrong language is selected) + if (!state.input_charset_option.empty()) { + state.cpp_args.push_back(state.input_charset_option); + } + if (state.found_pch) { + state.cpp_args.push_back("-fpch-preprocess"); + } + if (!state.explicit_language.empty()) { + state.cpp_args.push_back("-x"); + state.cpp_args.push_back(state.explicit_language); + } + + args_info.strip_diagnostics_colors = + state.color_diagnostics != ColorDiagnostics::automatic + ? state.color_diagnostics == ColorDiagnostics::never + : !color_output_possible(); + // Since output is redirected, compilers will not color their output by + // default, so force it explicitly. + if (ctx.guessed_compiler == GuessedCompiler::clang) { + if (args_info.actual_language != "assembler") { + if (!config.run_second_cpp()) { + state.cpp_args.push_back("-fcolor-diagnostics"); + } + state.compiler_only_args.push_back("-fcolor-diagnostics"); + add_depend_mode_extra_original_args(ctx, "-fcolor-diagnostics"); + } + } else if (ctx.guessed_compiler == GuessedCompiler::gcc) { + if (!config.run_second_cpp()) { + state.cpp_args.push_back("-fdiagnostics-color"); + } + state.compiler_only_args.push_back("-fdiagnostics-color"); + add_depend_mode_extra_original_args(ctx, "-fdiagnostics-color"); + } else { + // Other compilers shouldn't output color, so no need to strip it. + args_info.strip_diagnostics_colors = false; + } + + if (args_info.generating_dependencies) { + if (!state.dependency_filename_specified) { + auto default_depfile_name = + Util::change_extension(args_info.output_obj, ".d"); + args_info.output_dep = + Util::make_relative_path(ctx, default_depfile_name); + if (!config.run_second_cpp()) { + // If we're compiling preprocessed code we're sending dep_args to the + // preprocessor so we need to use -MF to write to the correct .d file + // location since the preprocessor doesn't know the final object path. + state.dep_args.push_back("-MF"); + state.dep_args.push_back(default_depfile_name); + } + } + + if (!ctx.args_info.dependency_target_specified + && !state.dependency_implicit_target_specified + && !config.run_second_cpp()) { + // If we're compiling preprocessed code we're sending dep_args to the + // preprocessor so we need to use -MQ to get the correct target object + // file in the .d file. + state.dep_args.push_back("-MQ"); + state.dep_args.push_back(args_info.output_obj); + } + } + + if (args_info.generating_stackusage) { + auto default_sufile_name = + Util::change_extension(args_info.output_obj, ".su"); + args_info.output_su = Util::make_relative_path(ctx, default_sufile_name); + } + + Args compiler_args = state.common_args; + compiler_args.push_back(state.compiler_only_args); + + if (config.run_second_cpp()) { + compiler_args.push_back(state.cpp_args); + } else if (state.found_directives_only || state.found_rewrite_includes) { + // Need to pass the macros and any other preprocessor directives again. + compiler_args.push_back(state.cpp_args); + if (state.found_directives_only) { + state.cpp_args.push_back("-fdirectives-only"); + // The preprocessed source code still needs some more preprocessing. + compiler_args.push_back("-fpreprocessed"); + compiler_args.push_back("-fdirectives-only"); + } + if (state.found_rewrite_includes) { + state.cpp_args.push_back("-frewrite-includes"); + // The preprocessed source code still needs some more preprocessing. + compiler_args.push_back("-x"); + compiler_args.push_back(args_info.actual_language); + } + } else if (!state.explicit_language.empty()) { + // Workaround for a bug in Apple's patched distcc -- it doesn't properly + // reset the language specified with -x, so if -x is given, we have to + // specify the preprocessed language explicitly. + compiler_args.push_back("-x"); + compiler_args.push_back(p_language_for_language(state.explicit_language)); + } + + if (state.found_c_opt) { + compiler_args.push_back("-c"); + } + + if (state.found_dc_opt) { + compiler_args.push_back("-dc"); + } + + for (const auto& arch : args_info.arch_args) { + compiler_args.push_back("-arch"); + compiler_args.push_back(arch); + } + + Args preprocessor_args = state.common_args; + preprocessor_args.push_back(state.cpp_args); + + if (config.run_second_cpp()) { + // When not compiling the preprocessed source code, only pass dependency + // arguments to the compiler to avoid having to add -MQ, supporting e.g. + // EDG-based compilers which don't support -MQ. + compiler_args.push_back(state.dep_args); + } else { + // When compiling the preprocessed source code, pass dependency arguments to + // the preprocessor since the compiler doesn't produce a .d file when + // compiling preprocessed source code. + preprocessor_args.push_back(state.dep_args); + } + + Args extra_args_to_hash = state.compiler_only_args; + if (config.run_second_cpp()) { + extra_args_to_hash.push_back(state.dep_args); + } + + return {preprocessor_args, extra_args_to_hash, compiler_args}; +} diff --git a/src/argprocessing.hpp b/src/argprocessing.hpp new file mode 100644 index 0000000..c040c44 --- /dev/null +++ b/src/argprocessing.hpp @@ -0,0 +1,62 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "Args.hpp" +#include "Statistics.hpp" + +#include "third_party/nonstd/optional.hpp" + +class Context; + +struct ProcessArgsResult +{ + ProcessArgsResult(Statistic error); + ProcessArgsResult(const Args& preprocessor_args, + const Args& extra_args_to_hash, + const Args& compiler_args); + + // nullopt on success, otherwise the statistics counter that should be + // incremented. + nonstd::optional error; + + // Arguments (except -E) to send to the preprocessor. + Args preprocessor_args; + + // Arguments not sent to the preprocessor but that should be part of the hash. + Args extra_args_to_hash; + + // Arguments to send to the real compiler. + Args compiler_args; +}; + +inline ProcessArgsResult::ProcessArgsResult(Statistic error_) : error(error_) +{ +} + +inline ProcessArgsResult::ProcessArgsResult(const Args& preprocessor_args_, + const Args& extra_args_to_hash_, + const Args& compiler_args_) + : preprocessor_args(preprocessor_args_), + extra_args_to_hash(extra_args_to_hash_), + compiler_args(compiler_args_) +{ +} + +ProcessArgsResult process_args(Context& ctx); diff --git a/src/args.c b/src/args.c deleted file mode 100644 index e240025..0000000 --- a/src/args.c +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright (C) 2002 Andrew Tridgell -// Copyright (C) 2009-2018 Joel Rosdahl -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -#include "ccache.h" - -struct args * -args_init(int init_argc, char **init_args) -{ - struct args *args = (struct args *)x_malloc(sizeof(struct args)); - args->argc = 0; - args->argv = (char **)x_malloc(sizeof(char *)); - args->argv[0] = NULL; - for (int i = 0; i < init_argc; i++) { - args_add(args, init_args[i]); - } - return args; -} - -struct args * -args_init_from_string(const char *command) -{ - char *p = x_strdup(command); - char *q = p; - char *word, *saveptr = NULL; - struct args *args = args_init(0, NULL); - while ((word = strtok_r(q, " \t\r\n", &saveptr))) { - args_add(args, word); - q = NULL; - } - - free(p); - return args; -} - -struct args * -args_init_from_gcc_atfile(const char *filename) -{ - char *argtext; - if (!(argtext = read_text_file(filename, 0))) { - return NULL; - } - - struct args *args = args_init(0, NULL); - char *pos = argtext; - char *argbuf = x_malloc(strlen(argtext) + 1); - char *argpos = argbuf; - - // Used to track quoting state; if \0, we are not inside quotes. Otherwise - // stores the quoting character that started it, for matching the end quote. - char quoting = '\0'; - - while (1) { - switch (*pos) { - case '\\': - pos++; - if (*pos == '\0') { - continue; - } - break; - - case '\"': - case '\'': - if (quoting != '\0') { - if (quoting == *pos) { - quoting = '\0'; - pos++; - continue; - } else { - break; - } - } else { - quoting = *pos; - pos++; - continue; - } - - case '\n': - case '\r': - case '\t': - case ' ': - if (quoting) { - break; - } - // Fall through. - - case '\0': - // End of token - *argpos = '\0'; - if (argbuf[0] != '\0') { - args_add(args, argbuf); - } - argpos = argbuf; - if (*pos == '\0') { - goto out; - } else { - pos++; - continue; - } - } - - *argpos = *pos; - pos++; - argpos++; - } - -out: - free(argbuf); - free(argtext); - return args; -} - -struct args * -args_copy(struct args *args) -{ - return args_init(args->argc, args->argv); -} - -// Insert all arguments in src into dest at position index. If replace is true, -// the element at dest->argv[index] is replaced with the contents of src and -// everything past it is shifted. Otherwise, dest->argv[index] is also shifted. -// -// src is consumed by this operation and should not be freed or used again by -// the caller. -void -args_insert(struct args *dest, int index, struct args *src, bool replace) -{ - // Adjustments made if we are replacing or shifting the element currently at - // dest->argv[index]. - int offset = replace ? 1 : 0; - - if (replace) { - free(dest->argv[index]); - } - - if (src->argc == 0) { - if (replace) { - // Have to shift everything down by 1 since we replaced with an empty - // list. - for (int i = index; i < dest->argc; i++) { - dest->argv[i] = dest->argv[i + 1]; - } - dest->argc--; - } - args_free(src); - return; - } - - if (src->argc == 1 && replace) { - // Trivial case; replace with 1 element. - dest->argv[index] = src->argv[0]; - src->argc = 0; - args_free(src); - return; - } - - dest->argv = (char **)x_realloc( - dest->argv, - (src->argc + dest->argc + 1 - offset) * sizeof(char *)); - - // Shift arguments over. - for (int i = dest->argc; i >= index + offset; i--) { - dest->argv[i + src->argc - offset] = dest->argv[i]; - } - - // Copy the new arguments into place. - for (int i = 0; i < src->argc; i++) { - dest->argv[i + index] = src->argv[i]; - } - - dest->argc += src->argc - offset; - src->argc = 0; - args_free(src); -} - -void -args_free(struct args *args) -{ - if (!args) { - return; - } - for (int i = 0; i < args->argc; ++i) { - if (args->argv[i]) { - free(args->argv[i]); - } - } - free(args->argv); - free(args); -} - -void -args_add(struct args *args, const char *s) -{ - args->argv = (char **)x_realloc(args->argv, - (args->argc + 2) * sizeof(char *)); - args->argv[args->argc] = x_strdup(s); - args->argc++; - args->argv[args->argc] = NULL; -} - -// Add all arguments in to_append to args. -void -args_extend(struct args *args, struct args *to_append) -{ - for (int i = 0; i < to_append->argc; i++) { - args_add(args, to_append->argv[i]); - } -} - -// Pop the last element off the args list. -void -args_pop(struct args *args, int n) -{ - while (n--) { - args->argc--; - free(args->argv[args->argc]); - args->argv[args->argc] = NULL; - } -} - -// Set argument at given index. -void -args_set(struct args *args, int index, const char *value) -{ - assert(index < args->argc); - free(args->argv[index]); - args->argv[index] = x_strdup(value); -} - -// Remove the first element of the argument list. -void -args_remove_first(struct args *args) -{ - free(args->argv[0]); - memmove(&args->argv[0], &args->argv[1], args->argc * sizeof(args->argv[0])); - args->argc--; -} - -// Add an argument into the front of the argument list. -void -args_add_prefix(struct args *args, const char *s) -{ - args->argv = (char **)x_realloc(args->argv, - (args->argc + 2) * sizeof(char *)); - memmove(&args->argv[1], &args->argv[0], - (args->argc+1) * sizeof(args->argv[0])); - args->argv[0] = x_strdup(s); - args->argc++; -} - -// Strip any arguments beginning with the specified prefix. -void -args_strip(struct args *args, const char *prefix) -{ - for (int i = 0; i < args->argc;) { - if (str_startswith(args->argv[i], prefix)) { - free(args->argv[i]); - memmove(&args->argv[i], - &args->argv[i+1], - (args->argc - i) * sizeof(args->argv[i])); - args->argc--; - } else { - i++; - } - } -} - -// Format args to a space-separated string. Does not quote spaces. Caller -// frees. -char * -args_to_string(struct args *args) -{ - unsigned size = 0; - for (char **p = args->argv; *p; p++) { - size += strlen(*p) + 1; - } - - char *result = x_malloc(size + 1); - int pos = 0; - for (char **p = args->argv; *p; p++) { - pos += sprintf(&result[pos], "%s ", *p); - } - result[pos - 1] = '\0'; - return result; -} - -// Returns true if args1 equals args2, else false. -bool -args_equal(struct args *args1, struct args *args2) -{ - if (args1->argc != args2->argc) { - return false; - } - for (int i = 0; i < args1->argc; i++) { - if (!str_eq(args1->argv[i], args2->argv[i])) { - return false; - } - } - return true; -} diff --git a/src/assertions.cpp b/src/assertions.cpp new file mode 100644 index 0000000..3c5a563 --- /dev/null +++ b/src/assertions.cpp @@ -0,0 +1,38 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "assertions.hpp" + +#include "Util.hpp" + +#include "third_party/fmt/core.h" + +void +handle_failed_assertion(const char* file, + size_t line, + const char* function, + const char* condition) +{ + fmt::print(stderr, + "ccache: {}:{}: {}: failed assertion: {}\n", + Util::base_name(file), + line, + function, + condition); + abort(); +} diff --git a/src/assertions.hpp b/src/assertions.hpp new file mode 100644 index 0000000..040c3a5 --- /dev/null +++ b/src/assertions.hpp @@ -0,0 +1,50 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#ifdef _MSC_VER +# define CCACHE_FUNCTION __func__ +#else +# define CCACHE_FUNCTION __PRETTY_FUNCTION__ +#endif + +// ASSERT is like the standard C `assert` macro but enabled in both debug and +// release builds. +#define ASSERT(condition) \ + do { \ + if (!(condition)) { \ + handle_failed_assertion( \ + __FILE__, __LINE__, CCACHE_FUNCTION, #condition); \ + } \ + } while (false) + +// DEBUG_ASSERT is like the standard C `assert` macro, i.e. only enabled in +// debug builds. +#ifdef NDEBUG +# define DEBUG_ASSERT(condition) ((void)0) +#else +# define DEBUG_ASSERT(condition) ASSERT(condition) +#endif + +[[noreturn]] void handle_failed_assertion(const char* file, + size_t line, + const char* function, + const char* condition); diff --git a/src/ccache.c b/src/ccache.c deleted file mode 100644 index c0d2a4f..0000000 --- a/src/ccache.c +++ /dev/null @@ -1,4447 +0,0 @@ -// ccache -- a fast C/C++ compiler cache -// -// Copyright (C) 2002-2007 Andrew Tridgell -// Copyright (C) 2009-2020 Joel Rosdahl -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -#include "ccache.h" -#include "compopt.h" -#ifdef HAVE_GETOPT_LONG -#include -#else -#include "getopt_long.h" -#endif -#include "hash.h" -#include "hashtable.h" -#include "hashtable_itr.h" -#include "hashutil.h" -#include "language.h" -#include "manifest.h" - -#define STRINGIFY(x) #x -#define TO_STRING(x) STRINGIFY(x) - -// Global variables used by other compilation units. -extern struct conf *conf; -extern char *primary_config_path; -extern char *secondary_config_path; -extern char *current_working_dir; -extern char *stats_file; -extern unsigned lock_staleness_limit; - -static const char VERSION_TEXT[] = - MYNAME " version %s\n" - "\n" - "Copyright (C) 2002-2007 Andrew Tridgell\n" - "Copyright (C) 2009-2020 Joel Rosdahl\n" - "\n" - "This program is free software; you can redistribute it and/or modify it under\n" - "the terms of the GNU General Public License as published by the Free Software\n" - "Foundation; either version 3 of the License, or (at your option) any later\n" - "version.\n"; - -static const char USAGE_TEXT[] = - "Usage:\n" - " " MYNAME " [options]\n" - " " MYNAME " compiler [compiler options]\n" - " compiler [compiler options] (via symbolic link)\n" - "\n" - "Common options:\n" - " -c, --cleanup delete old files and recalculate size counters\n" - " (normally not needed as this is done\n" - " automatically)\n" - " -C, --clear clear the cache completely (except configuration)\n" - " -F, --max-files=N set maximum number of files in cache to N (use 0\n" - " for no limit)\n" - " -M, --max-size=SIZE set maximum size of cache to SIZE (use 0 for no\n" - " limit); available suffixes: k, M, G, T (decimal)\n" - " and Ki, Mi, Gi, Ti (binary); default suffix: G\n" - " -p, --show-config show current configuration options in\n" - " human-readable format\n" - " -s, --show-stats show summary of configuration and statistics\n" - " counters in human-readable format\n" - " -z, --zero-stats zero statistics counters\n" - "\n" - " -h, --help print this help text\n" - " -V, --version print version and copyright information\n" - "\n" - "Options for scripting or debugging:\n" - " --dump-manifest=PATH dump manifest file at PATH in text format\n" - " -k, --get-config=K print the value of configuration key K\n" - " --hash-file=PATH print the hash (-) of the file at PATH\n" - " --print-stats print statistics counter IDs and corresponding\n" - " values in machine-parsable format\n" - " -o, --set-config=K=V set configuration item K to value V\n" - "\n" - "See also .\n"; - -// Global configuration data. -struct conf *conf = NULL; - -// Where to write configuration changes. -char *primary_config_path = NULL; - -// Secondary, read-only configuration file (if any). -char *secondary_config_path = NULL; - -// Current working directory taken from $PWD, or getcwd() if $PWD is bad. -char *current_working_dir = NULL; - -// The original argument list. -static struct args *orig_args; - -// Argument list to add to compiler invocation in depend mode. -static struct args *depend_extra_args; - -// The source file. -static char *input_file; - -// The output file being compiled to. -static char *output_obj; - -// The path to the dependency file (implicit or specified with -MF). -static char *output_dep; - -// The path to the coverage file (implicit when using -ftest-coverage). -static char *output_cov; - -// The path to the stack usage (implicit when using -fstack-usage). -static char *output_su; - -// Diagnostic generation information (clang). Contains pathname if not NULL. -static char *output_dia; - -// Split dwarf information (GCC 4.8 and up). Contains pathname if not NULL. -static char *output_dwo; - -// Language to use for the compilation target (see language.c). -static const char *actual_language; - -// Array for storing -arch options. -#define MAX_ARCH_ARGS 10 -static size_t arch_args_size = 0; -static char *arch_args[MAX_ARCH_ARGS] = {NULL}; - -// Name (represented as a struct file_hash) of the file containing the cached -// object code. -static struct file_hash *cached_obj_hash; - -// Full path to the file containing the cached object code -// (cachedir/a/b/cdef[...]-size.o). -static char *cached_obj; - -// Full path to the file containing the standard error output -// (cachedir/a/b/cdef[...]-size.stderr). -static char *cached_stderr; - -// Full path to the file containing the dependency information -// (cachedir/a/b/cdef[...]-size.d). -static char *cached_dep; - -// Full path to the file containing the coverage information -// (cachedir/a/b/cdef[...]-size.gcno). -static char *cached_cov; - -// Full path to the file containing the stack usage -// (cachedir/a/b/cdef[...]-size.su). -static char *cached_su; - -// Full path to the file containing the diagnostic information (for clang) -// (cachedir/a/b/cdef[...]-size.dia). -static char *cached_dia; - -// Full path to the file containing the split dwarf (for GCC 4.8 and above) -// (cachedir/a/b/cdef[...]-size.dwo). -// -// Contains NULL if -gsplit-dwarf is not given. -static char *cached_dwo; - -// Full path to the file containing the manifest -// (cachedir/a/b/cdef[...]-size.manifest). -static char *manifest_path; - -// Time of compilation. Used to see if include files have changed after -// compilation. -time_t time_of_compilation; - -// Files included by the preprocessor and their hashes/sizes. Key: file path. -// Value: struct file_hash. -static struct hashtable *included_files = NULL; - -// Uses absolute path for some include files. -static bool has_absolute_include_headers = false; - -// List of headers to ignore. -static char **ignore_headers; - -// Size of headers to ignore list. -static size_t ignore_headers_len; - -// Is the compiler being asked to output debug info? -static bool generating_debuginfo; - -// Is the compiler being asked to output debug info on level 3? -static bool generating_debuginfo_level_3; - -// Is the compiler being asked to output dependencies? -static bool generating_dependencies; - -// Is the compiler being asked to output coverage? -static bool generating_coverage; - -// Is the compiler being asked to output stack usage? -static bool generating_stackusage; - -// Us the compiler being asked to generate diagnostics -// (--serialize-diagnostics)? -static bool generating_diagnostics; - -// Is the compiler being asked to separate dwarf debug info into a separate -// file (-gsplit-dwarf)"? -static bool using_split_dwarf; - -// Relocating debuginfo in the format old=new. -static char **debug_prefix_maps = NULL; - -// Size of debug_prefix_maps list. -static size_t debug_prefix_maps_len = 0; - -// Is the compiler being asked to output coverage data (.gcda) at runtime? -static bool profile_arcs; - -// The name of the temporary preprocessed file. -static char *i_tmpfile; - -// Are we compiling a .i or .ii file directly? -static bool direct_i_file; - -// The name of the cpp stderr file. -static char *cpp_stderr; - -// Full path to the statistics file in the subdirectory where the cached result -// belongs (//stats). -char *stats_file = NULL; - -// The stats file to use for the manifest. -static char *manifest_stats_file; - -// Whether the output is a precompiled header. -bool output_is_precompiled_header = false; - -// Compiler guessing is currently only based on the compiler name, so nothing -// should hard-depend on it if possible. -enum guessed_compiler guessed_compiler = GUESSED_UNKNOWN; - -// Profile generation / usage information. -static char *profile_path = NULL; // directory (GCC/Clang) or file (Clang) -static bool profile_use = false; -static bool profile_generate = false; - -// Sanitize blacklist -static char **sanitize_blacklists = NULL; - -// Size of sanitize_blacklists -static size_t sanitize_blacklists_len = 0; - -// Whether we are using a precompiled header (either via -include, #include or -// clang's -include-pch or -include-pth). -static bool using_precompiled_header = false; - -// The .gch/.pch/.pth file used for compilation. -static char *included_pch_file = NULL; - -// How long (in microseconds) to wait before breaking a stale lock. -unsigned lock_staleness_limit = 2000000; - -enum fromcache_call_mode { - FROMCACHE_DIRECT_MODE, - FROMCACHE_CPP_MODE -}; - -struct pending_tmp_file { - char *path; - struct pending_tmp_file *next; -}; - -// Temporary files to remove at program exit. -static struct pending_tmp_file *pending_tmp_files = NULL; - -// How often (in seconds) to scan $CCACHE_DIR/tmp for left-over temporary -// files. -static const int k_tempdir_cleanup_interval = 2 * 24 * 60 * 60; // 2 days - -#ifndef _WIN32 -static sigset_t fatal_signal_set; - -// PID of currently executing compiler that we have started, if any. 0 means no -// ongoing compilation. -static pid_t compiler_pid = 0; -#endif - -// This is a string that identifies the current "version" of the hash sum -// computed by ccache. If, for any reason, we want to force the hash sum to be -// different for the same input in a new ccache version, we can just change -// this string. A typical example would be if the format of one of the files -// stored in the cache changes in a backwards-incompatible way. -static const char HASH_PREFIX[] = "3"; - -static void -add_prefix(struct args *args, char *prefix_command) -{ - if (str_eq(prefix_command, "")) { - return; - } - - struct args *prefix = args_init(0, NULL); - char *e = x_strdup(prefix_command); - char *saveptr = NULL; - for (char *tok = strtok_r(e, " ", &saveptr); - tok; - tok = strtok_r(NULL, " ", &saveptr)) { - char *p; - - p = find_executable(tok, MYNAME); - if (!p) { - fatal("%s: %s", tok, strerror(errno)); - } - - args_add(prefix, p); - free(p); - } - free(e); - - cc_log("Using command-line prefix %s", prefix_command); - for (int i = prefix->argc; i != 0; i--) { - args_add_prefix(args, prefix->argv[i-1]); - } - args_free(prefix); -} - -// Compiler in depend mode is invoked with the original arguments. -// Collect extra arguments that should be added. -static void -add_extra_arg(const char *arg) -{ - if (conf->depend_mode) { - if (depend_extra_args == NULL) { - depend_extra_args = args_init(0, NULL); - } - args_add(depend_extra_args, arg); - } -} - -static void failed(void) ATTR_NORETURN; - -// Something went badly wrong - just execute the real compiler. -static void -failed(void) -{ - assert(orig_args); - - args_strip(orig_args, "--ccache-"); - add_prefix(orig_args, conf->prefix_command); - - cc_log("Failed; falling back to running the real compiler"); - cc_log_argv("Executing ", orig_args->argv); - exitfn_call(); - execv(orig_args->argv[0], orig_args->argv); - fatal("execv of %s failed: %s", orig_args->argv[0], strerror(errno)); -} - -static const char * -temp_dir() -{ - static char *path = NULL; - if (path) { - return path; // Memoize - } - path = conf->temporary_dir; - if (str_eq(path, "")) { - path = format("%s/tmp", conf->cache_dir); - } - return path; -} - -void -block_signals(void) -{ -#ifndef _WIN32 - sigprocmask(SIG_BLOCK, &fatal_signal_set, NULL); -#endif -} - -void -unblock_signals(void) -{ -#ifndef _WIN32 - sigset_t empty; - sigemptyset(&empty); - sigprocmask(SIG_SETMASK, &empty, NULL); -#endif -} - -static void -add_pending_tmp_file(const char *path) -{ - block_signals(); - struct pending_tmp_file *e = x_malloc(sizeof(*e)); - e->path = x_strdup(path); - e->next = pending_tmp_files; - pending_tmp_files = e; - unblock_signals(); -} - -static void -do_clean_up_pending_tmp_files(void) -{ - struct pending_tmp_file *p = pending_tmp_files; - while (p) { - // Can't call tmp_unlink here since its cc_log calls aren't signal safe. - unlink(p->path); - p = p->next; - // Leak p->path and p here because clean_up_pending_tmp_files needs to be - // signal safe. - } -} - -static void -clean_up_pending_tmp_files(void) -{ - block_signals(); - do_clean_up_pending_tmp_files(); - unblock_signals(); -} - -#ifndef _WIN32 -static void -signal_handler(int signum) -{ - // Unregister handler for this signal so that we can send the signal to - // ourselves at the end of the handler. - signal(signum, SIG_DFL); - - // If ccache was killed explicitly, then bring the compiler subprocess (if - // any) with us as well. - if (signum == SIGTERM - && compiler_pid != 0 - && waitpid(compiler_pid, NULL, WNOHANG) == 0) { - kill(compiler_pid, signum); - } - - do_clean_up_pending_tmp_files(); - - if (compiler_pid != 0) { - // Wait for compiler subprocess to exit before we snuff it. - waitpid(compiler_pid, NULL, 0); - } - - // Resend signal to ourselves to exit properly after returning from the - // handler. - kill(getpid(), signum); -} - -static void -register_signal_handler(int signum) -{ - struct sigaction act; - memset(&act, 0, sizeof(act)); - act.sa_handler = signal_handler; - act.sa_mask = fatal_signal_set; -#ifdef SA_RESTART - act.sa_flags = SA_RESTART; -#endif - sigaction(signum, &act, NULL); -} - -static void -set_up_signal_handlers(void) -{ - sigemptyset(&fatal_signal_set); - sigaddset(&fatal_signal_set, SIGINT); - sigaddset(&fatal_signal_set, SIGTERM); -#ifdef SIGHUP - sigaddset(&fatal_signal_set, SIGHUP); -#endif -#ifdef SIGQUIT - sigaddset(&fatal_signal_set, SIGQUIT); -#endif - - register_signal_handler(SIGINT); - register_signal_handler(SIGTERM); -#ifdef SIGHUP - register_signal_handler(SIGHUP); -#endif -#ifdef SIGQUIT - register_signal_handler(SIGQUIT); -#endif -} -#endif // _WIN32 - -static void -clean_up_internal_tempdir(void) -{ - time_t now = time(NULL); - struct stat st; - if (x_stat(conf->cache_dir, &st) != 0 - || st.st_mtime + k_tempdir_cleanup_interval >= now) { - // No cleanup needed. - return; - } - - update_mtime(conf->cache_dir); - - DIR *dir = opendir(temp_dir()); - if (!dir) { - return; - } - - struct dirent *entry; - while ((entry = readdir(dir))) { - if (str_eq(entry->d_name, ".") || str_eq(entry->d_name, "..")) { - continue; - } - - char *path = format("%s/%s", temp_dir(), entry->d_name); - if (x_lstat(path, &st) == 0 - && st.st_mtime + k_tempdir_cleanup_interval < now) { - tmp_unlink(path); - } - free(path); - } - - closedir(dir); -} - -static void -fclose_exitfn(void *context) -{ - fclose((FILE *)context); -} - -static void -dump_debug_log_buffer_exitfn(void *context) -{ - if (!conf->debug) { - return; - } - - char *path = format("%s.ccache-log", (const char *)context); - cc_dump_debug_log_buffer(path); - free(path); -} - -static void -init_hash_debug(struct hash *hash, const char *obj_path, char type, - const char *section_name, FILE *debug_text_file) -{ - if (!conf->debug) { - return; - } - - char *path = format("%s.ccache-input-%c", obj_path, type); - FILE *debug_binary_file = fopen(path, "wb"); - if (debug_binary_file) { - hash_enable_debug(hash, section_name, debug_binary_file, debug_text_file); - exitfn_add(fclose_exitfn, debug_binary_file); - } else { - cc_log("Failed to open %s: %s", path, strerror(errno)); - } - free(path); -} - -static enum guessed_compiler -guess_compiler(const char *path) -{ - char *name = basename(path); - enum guessed_compiler result = GUESSED_UNKNOWN; - if (strstr(name, "clang")) { - result = GUESSED_CLANG; - } else if (strstr(name, "gcc") || strstr(name, "g++")) { - result = GUESSED_GCC; - } else if (strstr(name, "nvcc")) { - result = GUESSED_NVCC; - } else if (str_eq(name, "pump") || str_eq(name, "distcc-pump")) { - result = GUESSED_PUMP; - } - free(name); - return result; -} - -static char * -get_current_working_dir(void) -{ - if (!current_working_dir) { - char *cwd = get_cwd(); - if (cwd) { - current_working_dir = x_realpath(cwd); - free(cwd); - } - if (!current_working_dir) { - cc_log("Unable to determine current working directory: %s", - strerror(errno)); - stats_update(STATS_ERROR); - failed(); - } - } - return current_working_dir; -} - -// Transform a name to a full path into the cache directory, creating needed -// sublevels if needed. Caller frees. -static char * -get_path_in_cache(const char *name, const char *suffix) -{ - char *path = x_strdup(conf->cache_dir); - for (unsigned i = 0; i < conf->cache_dir_levels; ++i) { - char *p = format("%s/%c", path, name[i]); - free(path); - path = p; - } - - char *result = - format("%s/%s%s", path, name + conf->cache_dir_levels, suffix); - free(path); - return result; -} - -// This function hashes an include file and stores the path and hash in the -// global included_files variable. If the include file is a PCH, cpp_hash is -// also updated. Takes over ownership of path. -static void -remember_include_file(char *path, struct hash *cpp_hash, bool system, - struct hash *depend_mode_hash) -{ - struct hash *fhash = NULL; - - size_t path_len = strlen(path); - if (path_len >= 2 && (path[0] == '<' && path[path_len - 1] == '>')) { - // Typically or . - goto out; - } - - if (str_eq(path, input_file)) { - // Don't remember the input file. - goto out; - } - - if (system && (conf->sloppiness & SLOPPY_SYSTEM_HEADERS)) { - // Don't remember this system header. - goto out; - } - - if (hashtable_search(included_files, path)) { - // Already known include file. - goto out; - } - -#ifdef _WIN32 - // stat fails on directories on win32. - DWORD attributes = GetFileAttributes(path); - if (attributes != INVALID_FILE_ATTRIBUTES && - attributes & FILE_ATTRIBUTE_DIRECTORY) { - goto out; - } -#endif - - struct stat st; - if (x_stat(path, &st) != 0) { - goto failure; - } - if (S_ISDIR(st.st_mode)) { - // Ignore directory, typically $PWD. - goto out; - } - if (!S_ISREG(st.st_mode)) { - // Device, pipe, socket or other strange creature. - cc_log("Non-regular include file %s", path); - goto failure; - } - - // Canonicalize path for comparison; clang uses ./header.h. - char *canonical = path; - size_t canonical_len = path_len; - if (canonical[0] == '.' && canonical[1] == '/') { - canonical += 2; - canonical_len -= 2; - } - - for (size_t i = 0; i < ignore_headers_len; i++) { - char *ignore = ignore_headers[i]; - size_t ignore_len = strlen(ignore); - if (ignore_len > canonical_len) { - continue; - } - if (strncmp(canonical, ignore, ignore_len) == 0 - && (ignore[ignore_len-1] == DIR_DELIM_CH - || canonical[ignore_len] == DIR_DELIM_CH - || canonical[ignore_len] == '\0')) { - goto out; - } - } - - // The comparison using >= is intentional, due to a possible race between - // starting compilation and writing the include file. See also the notes - // under "Performance" in doc/MANUAL.adoc. - if (!(conf->sloppiness & SLOPPY_INCLUDE_FILE_MTIME) - && st.st_mtime >= time_of_compilation) { - cc_log("Include file %s too new", path); - goto failure; - } - - // The same >= logic as above applies to the change time of the file. - if (!(conf->sloppiness & SLOPPY_INCLUDE_FILE_CTIME) - && st.st_ctime >= time_of_compilation) { - cc_log("Include file %s ctime too new", path); - goto failure; - } - - // Let's hash the include file content. - fhash = hash_init(); - - bool is_pch = is_precompiled_header(path); - if (is_pch) { - if (!included_pch_file) { - cc_log("Detected use of precompiled header: %s", path); - } - bool using_pch_sum = false; - if (conf->pch_external_checksum) { - // hash pch.sum instead of pch when it exists - // to prevent hashing a very large .pch file every time - char *pch_sum_path = format("%s.sum", path); - if (x_stat(pch_sum_path, &st) == 0) { - char *old_path = path; - path = pch_sum_path; - pch_sum_path = old_path; - using_pch_sum = true; - cc_log("Using pch.sum file %s", path); - } - free(pch_sum_path); - } - - if (!hash_file(fhash, path)) { - goto failure; - } - hash_delimiter(cpp_hash, using_pch_sum ? "pch_sum_hash" : "pch_hash"); - char *pch_hash_result = hash_result(fhash); - hash_string(cpp_hash, pch_hash_result); - free(pch_hash_result); - } - - if (conf->direct_mode) { - if (!is_pch) { // else: the file has already been hashed. - char *source = NULL; - size_t size; - if (st.st_size > 0) { - if (!read_file(path, st.st_size, &source, &size)) { - goto failure; - } - } else { - source = x_strdup(""); - size = 0; - } - - int result = hash_source_code_string(conf, fhash, source, size, path); - free(source); - if (result & HASH_SOURCE_CODE_ERROR - || result & HASH_SOURCE_CODE_FOUND_TIME) { - goto failure; - } - } - - struct file_hash *h = x_malloc(sizeof(*h)); - hash_result_as_bytes(fhash, h->hash); - h->size = hash_input_size(fhash); - hashtable_insert(included_files, path, h); - path = NULL; // Ownership transferred to included_files. - - if (depend_mode_hash) { - hash_delimiter(depend_mode_hash, "include"); - char *result = format_hash_as_string(h->hash, h->size); - hash_string(depend_mode_hash, result); - free(result); - } - } - - goto out; - -failure: - if (conf->direct_mode) { - cc_log("Disabling direct mode"); - conf->direct_mode = false; - } - // Fall through. -out: - hash_free(fhash); - free(path); -} - -static void -print_included_files(FILE *fp) -{ - struct hashtable_itr *iter = hashtable_iterator(included_files); - do { - char *path = hashtable_iterator_key(iter); - fprintf(fp, "%s\n", path); - } while (hashtable_iterator_advance(iter)); -} - -// Make a relative path from current working directory to path if path is under -// the base directory. Takes over ownership of path. Caller frees. -static char * -make_relative_path(char *path) -{ - if (str_eq(conf->base_dir, "") || !str_startswith(path, conf->base_dir)) { - return path; - } - -#ifdef _WIN32 - if (path[0] == '/') { - char *p = NULL; - if (isalpha(path[1]) && path[2] == '/') { - // Transform /c/path... to c:/path... - p = format("%c:/%s", path[1], &path[3]); - } else { - p = x_strdup(path+1); // Skip leading slash. - } - free(path); - path = p; - } -#endif - - // x_realpath only works for existing paths, so if path doesn't exist, try - // dirname(path) and assemble the path afterwards. We only bother to try - // canonicalizing one of these two paths since a compiler path argument - // typically only makes sense if path or dirname(path) exists. - char *path_suffix = NULL; - struct stat st; - if (stat(path, &st) != 0) { - // path doesn't exist. - char *dir = dirname(path); - // find the nearest existing directory in path - while (stat(dir, &st) != 0) { - char *parent_dir = dirname(dir); - free(dir); - dir = parent_dir; - } - - // suffix is the remaining of the path, skip the first delimiter - size_t dir_len = strlen(dir); - if (path[dir_len] == '/' || path[dir_len] == '\\') { - dir_len++; - } - path_suffix = x_strdup(&path[dir_len]); - char *p = path; - path = dir; - free(p); - } - - char *canon_path = x_realpath(path); - if (canon_path) { - free(path); - char *relpath = get_relative_path(get_current_working_dir(), canon_path); - free(canon_path); - if (path_suffix) { - path = format("%s/%s", relpath, path_suffix); - free(relpath); - free(path_suffix); - return path; - } else { - return relpath; - } - } else { - // path doesn't exist, so leave it as it is. - free(path_suffix); - return path; - } -} - -static void -init_included_files_table(void) -{ - // (This function may be called multiple times if several -arch options are - // used.) - if (!included_files) { - included_files = create_hashtable(1000, hash_from_string, strings_equal); - } -} - -// This function reads and hashes a file. While doing this, it also does these -// things: -// -// - Makes include file paths for which the base directory is a prefix relative -// when computing the hash sum. -// - Stores the paths and hashes of included files in the global variable -// included_files. -static bool -process_preprocessed_file(struct hash *hash, const char *path, bool pump) -{ - char *data; - size_t size; - if (!read_file(path, 0, &data, &size)) { - return false; - } - - ignore_headers = NULL; - ignore_headers_len = 0; - if (!str_eq(conf->ignore_headers_in_manifest, "")) { - char *header, *p, *q, *saveptr = NULL; - p = x_strdup(conf->ignore_headers_in_manifest); - q = p; - while ((header = strtok_r(q, PATH_DELIM, &saveptr))) { - ignore_headers = x_realloc(ignore_headers, - (ignore_headers_len+1) * sizeof(char *)); - ignore_headers[ignore_headers_len++] = x_strdup(header); - q = NULL; - } - free(p); - } - - init_included_files_table(); - - char *cwd = get_cwd(); - - // Bytes between p and q are pending to be hashed. - char *p = data; - char *q = data; - char *end = data + size; - - // There must be at least 7 characters (# 1 "x") left to potentially find an - // include file path. - while (q < end - 7) { - // Check if we look at a line containing the file name of an included file. - // At least the following formats exist (where N is a positive integer): - // - // GCC: - // - // # N "file" - // # N "file" N - // #pragma GCC pch_preprocess "file" - // - // HP's compiler: - // - // #line N "file" - // - // AIX's compiler: - // - // #line N "file" - // #line N - // - // Note that there may be other lines starting with '#' left after - // preprocessing as well, for instance "# pragma". - if (q[0] == '#' - // GCC: - && ((q[1] == ' ' && q[2] >= '0' && q[2] <= '9') - // GCC precompiled header: - || (q[1] == 'p' - && str_startswith(&q[2], "ragma GCC pch_preprocess ")) - // HP/AIX: - || (q[1] == 'l' && q[2] == 'i' && q[3] == 'n' && q[4] == 'e' - && q[5] == ' ')) - && (q == data || q[-1] == '\n')) { - // Workarounds for preprocessor linemarker bugs in GCC version 6. - if (q[2] == '3') { - if (str_startswith(q, "# 31 \"\"\n")) { - // Bogus extra line with #31, after the regular #1: Ignore the whole - // line, and continue parsing. - hash_string_buffer(hash, p, q - p); - while (q < end && *q != '\n') { - q++; - } - q++; - p = q; - continue; - } else if (str_startswith(q, "# 32 \"\" 2\n")) { - // Bogus wrong line with #32, instead of regular #1: Replace the line - // number with the usual one. - hash_string_buffer(hash, p, q - p); - q += 1; - q[0] = '#'; - q[1] = ' '; - q[2] = '1'; - p = q; - } - } - - while (q < end && *q != '"' && *q != '\n') { - q++; - } - if (q < end && *q == '\n') { - // A newline before the quotation mark -> no match. - continue; - } - q++; - if (q >= end) { - cc_log("Failed to parse included file path"); - free(data); - free(cwd); - return false; - } - // q points to the beginning of an include file path - hash_string_buffer(hash, p, q - p); - p = q; - while (q < end && *q != '"') { - q++; - } - // Look for preprocessor flags, after the "filename". - bool system = false; - char *r = q + 1; - while (r < end && *r != '\n') { - if (*r == '3') { // System header. - system = true; - } - r++; - } - // p and q span the include file path. - char *inc_path = x_strndup(p, q - p); - if (!has_absolute_include_headers) { - has_absolute_include_headers = is_absolute_path(inc_path); - } - inc_path = make_relative_path(inc_path); - - bool should_hash_inc_path = true; - if (!conf->hash_dir) { - if (str_startswith(inc_path, cwd) && str_endswith(inc_path, "//")) { - // When compiling with -g or similar, GCC adds the absolute path to - // CWD like this: - // - // # 1 "CWD//" - // - // If the user has opted out of including the CWD in the hash, don't - // hash it. See also how debug_prefix_map is handled. - should_hash_inc_path = false; - } - } - if (should_hash_inc_path) { - hash_string_buffer(hash, inc_path, strlen(inc_path)); - } - - remember_include_file(inc_path, hash, system, NULL); - p = q; // Everything of interest between p and q has been hashed now. - } else if (q[0] == '.' && q[1] == 'i' && q[2] == 'n' && q[3] == 'c' - && q[4] == 'b' && q[5] == 'i' && q[6] == 'n') { - // An assembler .inc bin (without the space) statement, which could be - // part of inline assembly, refers to an external file. If the file - // changes, the hash should change as well, but finding out what file to - // hash is too hard for ccache, so just bail out. - cc_log("Found unsupported .inc" "bin directive in source code"); - stats_update(STATS_UNSUPPORTED_DIRECTIVE); - failed(); - } else if (pump && strncmp(q, "_________", 9) == 0) { - // Unfortunately the distcc-pump wrapper outputs standard output lines: - // __________Using distcc-pump from /usr/bin - // __________Using # distcc servers in pump mode - // __________Shutting down distcc-pump include server - while (q < end && *q != '\n') { - q++; - } - if (*q == '\n') { - q++; - } - p = q; - continue; - } else { - q++; - } - } - - hash_string_buffer(hash, p, (end - p)); - free(data); - free(cwd); - - // Explicitly check the .gch/.pch/.pth file as Clang does not include any - // mention of it in the preprocessed output. - if (included_pch_file) { - char *pch_path = x_strdup(included_pch_file); - pch_path = make_relative_path(pch_path); - hash_string(hash, pch_path); - remember_include_file(pch_path, hash, false, NULL); - } - - bool debug_included = getenv("CCACHE_DEBUG_INCLUDED"); - if (debug_included) { - print_included_files(stdout); - } - - return true; -} - -// Replace absolute paths with relative paths in the provided dependency file. -static void -use_relative_paths_in_depfile(const char *depfile) -{ - if (str_eq(conf->base_dir, "")) { - cc_log("Base dir not set, skip using relative paths"); - return; // nothing to do - } - if (!has_absolute_include_headers) { - cc_log("No absolute path for included files found, skip using relative" - " paths"); - return; // nothing to do - } - - FILE *f; - f = fopen(depfile, "r"); - if (!f) { - cc_log("Cannot open dependency file: %s (%s)", depfile, strerror(errno)); - return; - } - - char *tmp_file = format("%s.tmp", depfile); - FILE *tmpf = create_tmp_file(&tmp_file, "w"); - - bool result = false; - char buf[10000]; - while (fgets(buf, sizeof(buf), f) && !ferror(tmpf)) { - char *saveptr; - char *token = strtok_r(buf, " \t", &saveptr); - while (token) { - char *relpath; - if (is_absolute_path(token) && str_startswith(token, conf->base_dir)) { - relpath = make_relative_path(x_strdup(token)); - result = true; - } else { - relpath = token; - } - if (token != buf) { // This is a dependency file. - fputc(' ', tmpf); - } - fputs(relpath, tmpf); - if (relpath != token) { - free(relpath); - } - token = strtok_r(NULL, " \t", &saveptr); - } - } - - if (ferror(f)) { - cc_log("Error reading dependency file: %s, skip relative path usage", - depfile); - result = false; - goto out; - } - if (ferror(tmpf)) { - cc_log("Error writing temporary dependency file: %s, skip relative path" - " usage", tmp_file); - result = false; - goto out; - } - -out: - fclose(tmpf); - fclose(f); - if (result) { - if (x_rename(tmp_file, depfile) != 0) { - cc_log("Error renaming dependency file: %s -> %s (%s), skip relative" - " path usage", tmp_file, depfile, strerror(errno)); - result = false; - } else { - cc_log("Renamed dependency file: %s -> %s", tmp_file, depfile); - } - } - if (!result) { - cc_log("Removing temporary dependency file: %s", tmp_file); - tmp_unlink(tmp_file); - } - free(tmp_file); -} - -// Extract the used includes from the dependency file. Note that we cannot -// distinguish system headers from other includes here. -static struct file_hash * -object_hash_from_depfile(const char *depfile, struct hash *hash) -{ - FILE *f = fopen(depfile, "r"); - if (!f) { - cc_log("Cannot open dependency file %s: %s", depfile, strerror(errno)); - return NULL; - } - - init_included_files_table(); - - char buf[10000]; - while (fgets(buf, sizeof(buf), f) && !ferror(f)) { - char *saveptr; - char *token; - for (token = strtok_r(buf, " \t\n", &saveptr); - token; - token = strtok_r(NULL, " \t\n", &saveptr)) { - if (str_endswith(token, ":") || str_eq(token, "\\")) { - continue; - } - if (!has_absolute_include_headers) { - has_absolute_include_headers = is_absolute_path(token); - } - char *path = make_relative_path(x_strdup(token)); - remember_include_file(path, hash, false, hash); - } - } - - fclose(f); - - // Explicitly check the .gch/.pch/.pth file as it may not be mentioned in the - // dependencies output. - if (included_pch_file) { - char *pch_path = x_strdup(included_pch_file); - pch_path = make_relative_path(pch_path); - hash_string(hash, pch_path); - remember_include_file(pch_path, hash, false, NULL); - } - - bool debug_included = getenv("CCACHE_DEBUG_INCLUDED"); - if (debug_included) { - print_included_files(stdout); - } - - struct file_hash *result = x_malloc(sizeof(*result)); - hash_result_as_bytes(hash, result->hash); - result->size = hash_input_size(hash); - return result; -} - -// Helper function for put_file_in_cache and move_file_to_cache_same_fs. -static void -do_copy_or_move_file_to_cache(const char *source, const char *dest, bool copy, - bool attempt_link) -{ - assert(!conf->read_only); - assert(!conf->read_only_direct); - - struct stat orig_dest_st; - bool orig_dest_existed = stat(dest, &orig_dest_st) == 0; - int compression_level = conf->compression ? conf->compression_level : 0; - bool do_move = !copy && !conf->compression; - bool do_link = attempt_link && copy && conf->hard_link && !conf->compression; - - if (do_move) { - move_uncompressed_file(source, dest, compression_level); - } else { - if (do_link) { - x_unlink(dest); - int ret = link(source, dest); - if (ret != 0 && errno == ENOENT) { - create_parent_dirs(dest); - ret = link(source, dest); - } - if (ret != 0) { - cc_log("Failed to link %s to %s: %s", source, dest, strerror(errno)); - cc_log("Falling back to copying"); - do_link = false; - } - } - if (!do_link) { - int ret = copy_file(source, dest, compression_level, true); - if (ret != 0) { - cc_log("Failed to copy %s to %s: %s", source, dest, strerror(errno)); - stats_update(STATS_ERROR); - failed(); - } - } - } - - if (!copy && conf->compression) { - // We fell back to copying since dest should be compressed, so clean up. - x_unlink(source); - } - - cc_log("Stored in cache: %s -> %s (%s)", - source, - dest, - do_move ? "moved" : (do_link ? "linked" : "copied")); - - struct stat st; - if (x_stat(dest, &st) != 0) { - stats_update(STATS_ERROR); - failed(); - } - stats_update_size( - stats_file, - file_size(&st) - (orig_dest_existed ? file_size(&orig_dest_st) : 0), - orig_dest_existed ? 0 : 1); -} - -// Copy or link a file into the cache. -// -// dest must be a path in the cache (see get_path_in_cache). source does not -// have to be on the same file system as dest. -// -// An attempt will be made to hard link source to dest if conf->hard_link is -// true and conf->compression is false, otherwise copy. dest will be compressed -// if conf->compression is true. -static void -put_file_in_cache(const char *source, const char *dest) -{ - do_copy_or_move_file_to_cache(source, dest, true, true); -} - -// Copy a file to the cache. -// -// dest must be a path in the cache (see get_path_in_cache). source does not -// have to be on the same file system as dest. -static void -copy_file_to_cache(const char *source, const char *dest) -{ - do_copy_or_move_file_to_cache(source, dest, true, false); -} - -// Move a file into the cache. -// -// dest must be a path in the cache (see get_path_in_cache). source must be on -// the same file system as dest. dest will be compressed if conf->compression -// is true. -static void -move_file_to_cache_same_fs(const char *source, const char *dest) -{ - do_copy_or_move_file_to_cache(source, dest, false, true); -} - -// Helper function for get_file_from_cache and copy_file_from_cache. -static void -do_copy_or_link_file_from_cache(const char *source, const char *dest, bool copy) -{ - if (str_eq(dest, "/dev/null")) { - cc_log("Skipping copy from %s to %s", source, dest); - return; - } - - int ret; - bool do_link = !copy && conf->hard_link && !file_is_compressed(source); - if (do_link) { - x_unlink(dest); - ret = link(source, dest); - } else { - ret = copy_file(source, dest, 0, false); - } - - if (ret == -1) { - if (errno == ENOENT || errno == ESTALE) { - cc_log("File missing in cache: %s", source); - stats_update(STATS_MISSING); - } else { - cc_log("Failed to %s %s to %s: %s", - do_link ? "link" : "copy", - source, - dest, - strerror(errno)); - stats_update(STATS_ERROR); - } - - // If there was trouble getting a file from the cached result, wipe the - // whole cached result for consistency. - x_unlink(cached_stderr); - x_unlink(cached_obj); - x_unlink(cached_dep); - x_unlink(cached_cov); - x_unlink(cached_su); - x_unlink(cached_dia); - x_unlink(cached_dwo); - - failed(); - } - - cc_log("Created from cache: %s -> %s", source, dest); -} - -// Copy or link a file from the cache. -// -// source must be a path in the cache (see get_path_in_cache). dest does not -// have to be on the same file system as source. -// -// An attempt will be made to hard link source to dest if conf->hard_link is -// true and conf->compression is false, otherwise copy. dest will be compressed -// if conf->compression is true. -static void -get_file_from_cache(const char *source, const char *dest) -{ - do_copy_or_link_file_from_cache(source, dest, false); -} - -// Copy a file from the cache. -static void -copy_file_from_cache(const char *source, const char *dest) -{ - do_copy_or_link_file_from_cache(source, dest, true); -} - -// Send cached stderr, if any, to stderr. -static void -send_cached_stderr(void) -{ - int fd_stderr = open(cached_stderr, O_RDONLY | O_BINARY); - if (fd_stderr != -1) { - copy_fd(fd_stderr, 2); - close(fd_stderr); - } -} - -// Create or update the manifest file. -static void -update_manifest_file(void) -{ - if (!conf->direct_mode - || !included_files - || conf->read_only - || conf->read_only_direct) { - return; - } - - struct stat st; - size_t old_size = 0; // in bytes - if (stat(manifest_path, &st) == 0) { - old_size = file_size(&st); - } - - // See comment in get_file_hash_index for why saving of timestamps is forced - // for precompiled headers. - bool save_timestamp = - (conf->sloppiness & SLOPPY_FILE_STAT_MATCHES) - || output_is_precompiled_header; - - MTR_BEGIN("manifest", "manifest_put"); - if (manifest_put(manifest_path, cached_obj_hash, included_files, - save_timestamp)) { - cc_log("Added object file hash to %s", manifest_path); - if (x_stat(manifest_path, &st) == 0) { - stats_update_size( - manifest_stats_file, - file_size(&st) - old_size, - old_size == 0 ? 1 : 0); - } - } else { - cc_log("Failed to add object file hash to %s", manifest_path); - } - MTR_END("manifest", "manifest_put"); -} - -static void -update_cached_result_globals(struct file_hash *hash) -{ - char *object_name = format_hash_as_string(hash->hash, hash->size); - cached_obj_hash = hash; - cached_obj = get_path_in_cache(object_name, ".o"); - cached_stderr = get_path_in_cache(object_name, ".stderr"); - cached_dep = get_path_in_cache(object_name, ".d"); - cached_cov = get_path_in_cache(object_name, ".gcno"); - cached_su = get_path_in_cache(object_name, ".su"); - cached_dia = get_path_in_cache(object_name, ".dia"); - cached_dwo = get_path_in_cache(object_name, ".dwo"); - - stats_file = format("%s/%c/stats", conf->cache_dir, object_name[0]); - free(object_name); -} - -// Run the real compiler and put the result in cache. -static void -to_cache(struct args *args, struct hash *depend_mode_hash) -{ - args_add(args, "-o"); - args_add(args, output_obj); - - if (conf->hard_link && !str_eq(output_obj, "/dev/null")) { - // This is a workaround for https://bugs.llvm.org/show_bug.cgi?id=39782. - x_unlink(output_obj); - } - - if (generating_diagnostics) { - args_add(args, "--serialize-diagnostics"); - args_add(args, output_dia); - } - - // Turn off DEPENDENCIES_OUTPUT when running cc1, because otherwise it will - // emit a line like this: - // - // tmp.stdout.vexed.732.o: /home/mbp/.ccache/tmp.stdout.vexed.732.i - x_unsetenv("DEPENDENCIES_OUTPUT"); - x_unsetenv("SUNPRO_DEPENDENCIES"); - - if (conf->run_second_cpp) { - args_add(args, input_file); - } else { - args_add(args, i_tmpfile); - } - - cc_log("Running real compiler"); - MTR_BEGIN("execute", "compiler"); - char *tmp_stdout; - int tmp_stdout_fd; - char *tmp_stderr; - int tmp_stderr_fd; - int status; - if (!conf->depend_mode) { - tmp_stdout = format("%s.tmp.stdout", cached_obj); - tmp_stdout_fd = create_tmp_fd(&tmp_stdout); - add_pending_tmp_file(tmp_stdout); - - tmp_stderr = format("%s.tmp.stderr", cached_obj); - tmp_stderr_fd = create_tmp_fd(&tmp_stderr); - add_pending_tmp_file(tmp_stderr); - - status = execute(args->argv, tmp_stdout_fd, tmp_stderr_fd, &compiler_pid); - args_pop(args, 3); - } else { - // The cached object path is not known yet, use temporary files. - tmp_stdout = format("%s/tmp.stdout", temp_dir()); - tmp_stdout_fd = create_tmp_fd(&tmp_stdout); - add_pending_tmp_file(tmp_stdout); - - tmp_stderr = format("%s/tmp.stderr", temp_dir()); - tmp_stderr_fd = create_tmp_fd(&tmp_stderr); - add_pending_tmp_file(tmp_stderr); - - // Use the original arguments (including dependency options) in depend - // mode. - assert(orig_args); - struct args *depend_mode_args = args_copy(orig_args); - args_strip(depend_mode_args, "--ccache-"); - if (depend_extra_args) { - args_extend(depend_mode_args, depend_extra_args); - } - add_prefix(depend_mode_args, conf->prefix_command); - - time_of_compilation = time(NULL); - status = execute( - depend_mode_args->argv, tmp_stdout_fd, tmp_stderr_fd, &compiler_pid); - args_free(depend_mode_args); - } - MTR_END("execute", "compiler"); - - struct stat st; - if (x_stat(tmp_stdout, &st) != 0) { - // The stdout file was removed - cleanup in progress? Better bail out. - stats_update(STATS_MISSING); - tmp_unlink(tmp_stdout); - tmp_unlink(tmp_stderr); - failed(); - } - - // distcc-pump outputs lines like this: - // __________Using # distcc servers in pump mode - if (st.st_size != 0 && guessed_compiler != GUESSED_PUMP) { - cc_log("Compiler produced stdout"); - stats_update(STATS_STDOUT); - tmp_unlink(tmp_stdout); - tmp_unlink(tmp_stderr); - failed(); - } - tmp_unlink(tmp_stdout); - - // Merge stderr from the preprocessor (if any) and stderr from the real - // compiler into tmp_stderr. - if (cpp_stderr) { - char *tmp_stderr2 = format("%s.2", tmp_stderr); - if (x_rename(tmp_stderr, tmp_stderr2)) { - cc_log("Failed to rename %s to %s: %s", tmp_stderr, tmp_stderr2, - strerror(errno)); - stats_update(STATS_ERROR); - failed(); - } - - int fd_cpp_stderr = open(cpp_stderr, O_RDONLY | O_BINARY); - if (fd_cpp_stderr == -1) { - cc_log("Failed opening %s: %s", cpp_stderr, strerror(errno)); - stats_update(STATS_ERROR); - failed(); - } - - int fd_real_stderr = open(tmp_stderr2, O_RDONLY | O_BINARY); - if (fd_real_stderr == -1) { - cc_log("Failed opening %s: %s", tmp_stderr2, strerror(errno)); - stats_update(STATS_ERROR); - failed(); - } - - int fd_result = - open(tmp_stderr, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); - if (fd_result == -1) { - cc_log("Failed opening %s: %s", tmp_stderr, strerror(errno)); - stats_update(STATS_ERROR); - failed(); - } - - copy_fd(fd_cpp_stderr, fd_result); - copy_fd(fd_real_stderr, fd_result); - close(fd_cpp_stderr); - close(fd_real_stderr); - close(fd_result); - tmp_unlink(tmp_stderr2); - free(tmp_stderr2); - } - - if (status != 0) { - cc_log("Compiler gave exit status %d", status); - stats_update(STATS_STATUS); - - int fd = open(tmp_stderr, O_RDONLY | O_BINARY); - if (fd != -1) { - // We can output stderr immediately instead of rerunning the compiler. - copy_fd(fd, 2); - close(fd); - tmp_unlink(tmp_stderr); - - x_exit(status); - } - - tmp_unlink(tmp_stderr); - failed(); - } - - if (conf->depend_mode) { - struct file_hash *object_hash = - object_hash_from_depfile(output_dep, depend_mode_hash); - if (!object_hash) { - stats_update(STATS_ERROR); - failed(); - } - update_cached_result_globals(object_hash); - } - - bool produce_dep_file = - generating_dependencies && !str_eq(output_dep, "/dev/null"); - - if (produce_dep_file) { - use_relative_paths_in_depfile(output_dep); - } - - if (stat(output_obj, &st) != 0) { - cc_log("Compiler didn't produce an object file"); - stats_update(STATS_NOOUTPUT); - failed(); - } - if (st.st_size == 0) { - cc_log("Compiler produced an empty object file"); - stats_update(STATS_EMPTYOUTPUT); - failed(); - } - - if (x_stat(tmp_stderr, &st) != 0) { - stats_update(STATS_ERROR); - failed(); - } - if (st.st_size > 0) { - if (!conf->depend_mode) { - move_file_to_cache_same_fs(tmp_stderr, cached_stderr); - } else { - put_file_in_cache(tmp_stderr, cached_stderr); - } - } else if (conf->recache) { - // If recaching, we need to remove any previous .stderr. - x_unlink(cached_stderr); - } - if (st.st_size == 0 || conf->depend_mode) { - tmp_unlink(tmp_stderr); - } - - MTR_BEGIN("file", "file_put"); - - if (produce_dep_file) { - copy_file_to_cache(output_dep, cached_dep); - } - if (generating_coverage) { - if (stat(output_cov, &st) == 0) { - copy_file_to_cache(output_cov, cached_cov); - } else { - // The .gcno file is missing. This is likely due to compiling with GCC 9+, - // which uses another name for the .gcno file when using -ftest-coverage - // or --coverage when -fprofile-dir=dir is given. The .gcno file is still - // placed next to the object file, not in the specified profile directory, - // though. - cc_log("%s is missing", output_cov); - stats_update(STATS_UNSUPPORTED_OPTION); - failed(); - } - } - if (generating_stackusage) { - copy_file_to_cache(output_su, cached_su); - } - if (generating_diagnostics) { - copy_file_to_cache(output_dia, cached_dia); - } - if (using_split_dwarf) { - copy_file_to_cache(output_dwo, cached_dwo); - } - put_file_in_cache(output_obj, cached_obj); - - MTR_END("file", "file_put"); - - stats_update(STATS_CACHEMISS); - - // Make sure we have a CACHEDIR.TAG in the cache part of cache_dir. This can - // be done almost anywhere, but we might as well do it near the end as we - // save the stat call if we exit early. - { - char *first_level_dir = dirname(stats_file); - if (create_cachedirtag(first_level_dir) != 0) { - cc_log("Failed to create %s/CACHEDIR.TAG (%s)\n", - first_level_dir, strerror(errno)); - stats_update(STATS_ERROR); - failed(); - } - free(first_level_dir); - - // Remove any CACHEDIR.TAG on the cache_dir level where it was located in - // previous ccache versions. - if (getpid() % 1000 == 0) { - char *path = format("%s/CACHEDIR.TAG", conf->cache_dir); - x_unlink(path); - free(path); - } - } - - // Everything OK. - send_cached_stderr(); - update_manifest_file(); - - free(tmp_stderr); - free(tmp_stdout); -} - -// Find the object file name by running the compiler in preprocessor mode. -// Returns the hash as a heap-allocated hex string. -static struct file_hash * -get_object_name_from_cpp(struct args *args, struct hash *hash) -{ - time_of_compilation = time(NULL); - - char *path_stderr = NULL; - char *path_stdout; - int status; - if (direct_i_file) { - // We are compiling a .i or .ii file - that means we can skip the cpp stage - // and directly form the correct i_tmpfile. - path_stdout = input_file; - status = 0; - } else { - // Run cpp on the input file to obtain the .i. - - // Limit the basename to 10 characters in order to cope with filesystem with - // small maximum filename length limits. - char *input_base = basename(input_file); - char *tmp = strchr(input_base, '.'); - if (tmp) { - *tmp = 0; - } - if (strlen(input_base) > 10) { - input_base[10] = 0; - } - - path_stdout = format("%s/%s.stdout", temp_dir(), input_base); - free(input_base); - int path_stdout_fd = create_tmp_fd(&path_stdout); - add_pending_tmp_file(path_stdout); - - path_stderr = format("%s/tmp.cpp_stderr", temp_dir()); - int path_stderr_fd = create_tmp_fd(&path_stderr); - add_pending_tmp_file(path_stderr); - - int args_added = 2; - args_add(args, "-E"); - if (conf->keep_comments_cpp) { - args_add(args, "-C"); - args_added = 3; - } - args_add(args, input_file); - add_prefix(args, conf->prefix_command_cpp); - cc_log("Running preprocessor"); - MTR_BEGIN("execute", "preprocessor"); - status = execute(args->argv, path_stdout_fd, path_stderr_fd, &compiler_pid); - MTR_END("execute", "preprocessor"); - args_pop(args, args_added); - } - - if (status != 0) { - cc_log("Preprocessor gave exit status %d", status); - stats_update(STATS_PREPROCESSOR); - failed(); - } - - hash_delimiter(hash, "cpp"); - if (!process_preprocessed_file(hash, path_stdout, - guessed_compiler == GUESSED_PUMP)) { - stats_update(STATS_ERROR); - failed(); - } - - hash_delimiter(hash, "cppstderr"); - if (!direct_i_file && !hash_file(hash, path_stderr)) { - // Somebody removed the temporary file? - stats_update(STATS_ERROR); - cc_log("Failed to open %s: %s", path_stderr, strerror(errno)); - failed(); - } - - if (direct_i_file) { - i_tmpfile = input_file; - } else { - // i_tmpfile needs the proper cpp_extension for the compiler to do its - // thing correctly - i_tmpfile = format("%s.%s", path_stdout, conf->cpp_extension); - x_rename(path_stdout, i_tmpfile); - add_pending_tmp_file(i_tmpfile); - } - - if (conf->run_second_cpp) { - free(path_stderr); - } else { - // If we are using the CPP trick, we need to remember this stderr data and - // output it just before the main stderr from the compiler pass. - cpp_stderr = path_stderr; - hash_delimiter(hash, "runsecondcpp"); - hash_string(hash, "false"); - } - - struct file_hash *result = x_malloc(sizeof(*result)); - hash_result_as_bytes(hash, result->hash); - result->size = hash_input_size(hash); - return result; -} - -// Hash mtime or content of a file, or the output of a command, according to -// the CCACHE_COMPILERCHECK setting. -static void -hash_compiler(struct hash *hash, struct stat *st, const char *path, - bool allow_command) -{ - if (str_eq(conf->compiler_check, "none")) { - // Do nothing. - } else if (str_eq(conf->compiler_check, "mtime")) { - hash_delimiter(hash, "cc_mtime"); - hash_int(hash, st->st_size); - hash_int(hash, st->st_mtime); - } else if (str_startswith(conf->compiler_check, "string:")) { - hash_delimiter(hash, "cc_hash"); - hash_string(hash, conf->compiler_check + strlen("string:")); - } else if (str_eq(conf->compiler_check, "content") || !allow_command) { - hash_delimiter(hash, "cc_content"); - hash_file(hash, path); - } else { // command string - bool ok = hash_multicommand_output( - hash, conf->compiler_check, orig_args->argv[0]); - if (!ok) { - fatal("Failure running compiler check command: %s", conf->compiler_check); - } - } -} - -// Hash the host compiler(s) invoked by nvcc. -// -// If ccbin_st and ccbin are set, they refer to a directory or compiler set -// with -ccbin/--compiler-bindir. If they are NULL, the compilers are looked up -// in PATH instead. -static void -hash_nvcc_host_compiler(struct hash *hash, struct stat *ccbin_st, - const char *ccbin) -{ - // From : - // - // "[...] Specify the directory in which the compiler executable resides. - // The host compiler executable name can be also specified to ensure that - // the correct host compiler is selected." - // - // and - // - // "On all platforms, the default host compiler executable (gcc and g++ on - // Linux, clang and clang++ on Mac OS X, and cl.exe on Windows) found in - // the current execution search path will be used". - - if (!ccbin || S_ISDIR(ccbin_st->st_mode)) { -#if defined(__APPLE__) - const char *compilers[] = {"clang", "clang++"}; -#elif defined(_WIN32) - const char *compilers[] = {"cl.exe"}; -#else - const char *compilers[] = {"gcc", "g++"}; -#endif - for (size_t i = 0; i < ARRAY_SIZE(compilers); i++) { - if (ccbin) { - char *path = format("%s/%s", ccbin, compilers[i]); - struct stat st; - if (stat(path, &st) == 0) { - hash_compiler(hash, &st, path, false); - } - free(path); - } else { - char *path = find_executable(compilers[i], MYNAME); - if (path) { - struct stat st; - x_stat(path, &st); - hash_compiler(hash, &st, ccbin, false); - free(path); - } - } - } - } else { - hash_compiler(hash, ccbin_st, ccbin, false); - } -} - -// Update a hash sum with information common for the direct and preprocessor -// modes. -static void -calculate_common_hash(struct args *args, struct hash *hash) -{ - hash_string(hash, HASH_PREFIX); - - // We have to hash the extension, as a .i file isn't treated the same by the - // compiler as a .ii file. - hash_delimiter(hash, "ext"); - hash_string(hash, conf->cpp_extension); - -#ifdef _WIN32 - const char *ext = strrchr(args->argv[0], '.'); - char full_path_win_ext[MAX_PATH + 1] = {0}; - add_exe_ext_if_no_to_fullpath(full_path_win_ext, MAX_PATH, ext, - args->argv[0]); - const char *full_path = full_path_win_ext; -#else - const char *full_path = args->argv[0]; -#endif - - struct stat st; - if (x_stat(full_path, &st) != 0) { - stats_update(STATS_COMPILER); - failed(); - } - - // Hash information about the compiler. - hash_compiler(hash, &st, args->argv[0], true); - - // Also hash the compiler name as some compilers use hard links and behave - // differently depending on the real name. - hash_delimiter(hash, "cc_name"); - char *base = basename(args->argv[0]); - hash_string(hash, base); - free(base); - - if (!(conf->sloppiness & SLOPPY_LOCALE)) { - // Hash environment variables that may affect localization of compiler - // warning messages. - const char *envvars[] = { - "LANG", - "LC_ALL", - "LC_CTYPE", - "LC_MESSAGES", - NULL - }; - for (const char **p = envvars; *p; ++p) { - char *v = getenv(*p); - if (v) { - hash_delimiter(hash, *p); - hash_string(hash, v); - } - } - } - - // Possibly hash the current working directory. - if (generating_debuginfo && conf->hash_dir) { - char *cwd = get_cwd(); - for (size_t i = 0; i < debug_prefix_maps_len; i++) { - char *map = debug_prefix_maps[i]; - char *sep = strchr(map, '='); - if (sep) { - char *old = x_strndup(map, sep - map); - char *new = x_strdup(sep + 1); - cc_log("Relocating debuginfo CWD %s from %s to %s", cwd, old, new); - if (str_startswith(cwd, old)) { - char *dir = format("%s%s", new, cwd + strlen(old)); - free(cwd); - cwd = dir; - } - free(old); - free(new); - } - } - if (cwd) { - cc_log("Hashing CWD %s", cwd); - hash_delimiter(hash, "cwd"); - hash_string(hash, cwd); - free(cwd); - } - } - - if (using_split_dwarf) { - // When using -gsplit-dwarf, object files include a link to the - // corresponding .dwo file based on the target object filename, so we need - // to include the target filename in the hash to avoid handing out an - // object file with an incorrect .dwo link. - hash_delimiter(hash, "filename"); - hash_string(hash, basename(output_obj)); - } - - // Possibly hash the coverage data file path. - if (generating_coverage && profile_arcs) { - char *dir = dirname(output_obj); - if (profile_path) { - dir = x_strdup(profile_path); - } else { - char *real_dir = x_realpath(dir); - free(dir); - dir = real_dir; - } - if (dir) { - char *base_name = basename(output_obj); - char *p = remove_extension(base_name); - free(base_name); - char *gcda_path = format("%s/%s.gcda", dir, p); - cc_log("Hashing coverage path %s", gcda_path); - free(p); - hash_delimiter(hash, "gcda"); - hash_string(hash, gcda_path); - free(dir); - } - } - - // Possibly hash the sanitize blacklist file path. - for (size_t i = 0; i < sanitize_blacklists_len; i++) { - char *sanitize_blacklist = sanitize_blacklists[i]; - cc_log("Hashing sanitize blacklist %s", sanitize_blacklist); - hash_delimiter(hash, "sanitizeblacklist"); - if (!hash_file(hash, sanitize_blacklist)) { - stats_update(STATS_BADEXTRAFILE); - failed(); - } - } - - if (!str_eq(conf->extra_files_to_hash, "")) { - char *p = x_strdup(conf->extra_files_to_hash); - char *q = p; - char *path; - char *saveptr = NULL; - while ((path = strtok_r(q, PATH_DELIM, &saveptr))) { - cc_log("Hashing extra file %s", path); - hash_delimiter(hash, "extrafile"); - if (!hash_file(hash, path)) { - stats_update(STATS_BADEXTRAFILE); - failed(); - } - q = NULL; - } - free(p); - } - - // Possibly hash GCC_COLORS (for color diagnostics). - if (guessed_compiler == GUESSED_GCC) { - const char *gcc_colors = getenv("GCC_COLORS"); - if (gcc_colors) { - hash_delimiter(hash, "gcccolors"); - hash_string(hash, gcc_colors); - } - } -} - -static bool -hash_profile_data_file(const char *path, struct hash *hash) -{ - assert(path); - - char *base_name = remove_extension(output_obj); - char *hashified_cwd = get_cwd(); - for (char *p = hashified_cwd; *p; ++p) { - if (*p == '/') { - *p = '#'; - } - } - char *paths_to_try[] = { - // -fprofile-use[=dir]/-fbranch-probabilities (GCC <9) - format("%s/%s.gcda", path, base_name), - // -fprofile-use[=dir]/-fbranch-probabilities (GCC >=9) - format("%s/%s#%s.gcda", path, hashified_cwd, base_name), - // -fprofile(-instr|-sample)-use=file (Clang), -fauto-profile=file (GCC >=5) - x_strdup(path), - // -fprofile(-instr|-sample)-use=dir (Clang) - format("%s/default.profdata", path), - // -fauto-profile (GCC >=5) - x_strdup("fbdata.afdo"), // -fprofile-dir is not used - NULL - }; - free(hashified_cwd); - free(base_name); - - bool found = false; - for (char **p = paths_to_try; *p; ++p) { - cc_log("Checking for profile data file %s", *p); - struct stat st; - if (stat(*p, &st) == 0 && !S_ISDIR(st.st_mode)) { - cc_log("Adding profile data %s to the hash", *p); - hash_delimiter(hash, "-fprofile-use"); - if (hash_file(hash, *p)) { - found = true; - } - } - free(*p); - } - - return found; -} - -// Update a hash sum with information specific to the direct and preprocessor -// modes and calculate the object hash. Returns the object hash on success, -// otherwise NULL. Caller frees. -static struct file_hash * -calculate_object_hash(struct args *args, struct args *preprocessor_args, - struct hash *hash, int direct_mode) -{ - bool found_ccbin = false; - - if (direct_mode) { - hash_delimiter(hash, "manifest version"); - hash_int(hash, MANIFEST_VERSION); - } - - // clang will emit warnings for unused linker flags, so we shouldn't skip - // those arguments. - int is_clang = - guessed_compiler == GUESSED_CLANG || guessed_compiler == GUESSED_UNKNOWN; - - // First the arguments. - for (int i = 1; i < args->argc; i++) { - // -L doesn't affect compilation (except for clang). - if (i < args->argc-1 && str_eq(args->argv[i], "-L") && !is_clang) { - i++; - continue; - } - if (str_startswith(args->argv[i], "-L") && !is_clang) { - continue; - } - - // -Wl,... doesn't affect compilation (except for clang). - if (str_startswith(args->argv[i], "-Wl,") && !is_clang) { - continue; - } - - // The -fdebug-prefix-map option may be used in combination with - // CCACHE_BASEDIR to reuse results across different directories. Skip using - // the value of the option from hashing but still hash the existence of the - // option. - if (str_startswith(args->argv[i], "-fdebug-prefix-map=")) { - hash_delimiter(hash, "arg"); - hash_string(hash, "-fdebug-prefix-map="); - continue; - } - if (str_startswith(args->argv[i], "-ffile-prefix-map=")) { - hash_delimiter(hash, "arg"); - hash_string(hash, "-ffile-prefix-map="); - continue; - } - if (str_startswith(args->argv[i], "-fmacro-prefix-map=")) { - hash_delimiter(hash, "arg"); - hash_string(hash, "-fmacro-prefix-map="); - continue; - } - - // When using the preprocessor, some arguments don't contribute to the - // hash. The theory is that these arguments will change the output of -E if - // they are going to have any effect at all. For precompiled headers this - // might not be the case. - if (!direct_mode && !output_is_precompiled_header - && !using_precompiled_header) { - if (compopt_affects_cpp(args->argv[i])) { - if (compopt_takes_arg(args->argv[i])) { - i++; - } - continue; - } - if (compopt_short(compopt_affects_cpp, args->argv[i])) { - continue; - } - } - - // If we're generating dependencies, we make sure to skip the filename of - // the dependency file, since it doesn't impact the output. - if (generating_dependencies) { - if (str_startswith(args->argv[i], "-Wp,")) { - if (str_startswith(args->argv[i], "-Wp,-MD,") - && !strchr(args->argv[i] + 8, ',')) { - hash_string_buffer(hash, args->argv[i], 8); - continue; - } else if (str_startswith(args->argv[i], "-Wp,-MMD,") - && !strchr(args->argv[i] + 9, ',')) { - hash_string_buffer(hash, args->argv[i], 9); - continue; - } - } else if (str_startswith(args->argv[i], "-MF")) { - // In either case, hash the "-MF" part. - hash_delimiter(hash, "arg"); - hash_string_buffer(hash, args->argv[i], 3); - - if (!str_eq(output_dep, "/dev/null")) { - bool separate_argument = (strlen(args->argv[i]) == 3); - if (separate_argument) { - // Next argument is dependency name, so skip it. - i++; - } - } - continue; - } - } - - char *p = NULL; - if (str_startswith(args->argv[i], "-specs=")) { - p = args->argv[i] + 7; - } else if (str_startswith(args->argv[i], "--specs=")) { - p = args->argv[i] + 8; - } - - struct stat st; - if (p && x_stat(p, &st) == 0) { - // If given an explicit specs file, then hash that file, but don't - // include the path to it in the hash. - hash_delimiter(hash, "specs"); - hash_compiler(hash, &st, p, false); - continue; - } - - if (str_startswith(args->argv[i], "-fplugin=") - && x_stat(args->argv[i] + 9, &st) == 0) { - hash_delimiter(hash, "plugin"); - hash_compiler(hash, &st, args->argv[i] + 9, false); - continue; - } - - if (str_eq(args->argv[i], "-Xclang") - && i + 3 < args->argc - && str_eq(args->argv[i+1], "-load") - && str_eq(args->argv[i+2], "-Xclang") - && x_stat(args->argv[i+3], &st) == 0) { - hash_delimiter(hash, "plugin"); - hash_compiler(hash, &st, args->argv[i+3], false); - i += 3; - continue; - } - - if ((str_eq(args->argv[i], "-ccbin") - || str_eq(args->argv[i], "--compiler-bindir")) - && i + 1 < args->argc - && stat(args->argv[i+1], &st) == 0) { - found_ccbin = true; - hash_delimiter(hash, "ccbin"); - hash_nvcc_host_compiler(hash, &st, args->argv[i+1]); - i++; - continue; - } - - // All other arguments are included in the hash. - hash_delimiter(hash, "arg"); - hash_string(hash, args->argv[i]); - if (i + 1 < args->argc && compopt_takes_arg(args->argv[i])) { - i++; - hash_delimiter(hash, "arg"); - hash_string(hash, args->argv[i]); - } - } - - // Make results with dependency file /dev/null different from those without - // it. - if (generating_dependencies && str_eq(output_dep, "/dev/null")) { - hash_delimiter(hash, "/dev/null dependency file"); - } - - if (!found_ccbin && str_eq(actual_language, "cu")) { - hash_nvcc_host_compiler(hash, NULL, NULL); - } - - // For profile generation (-fprofile(-instr)-generate[=path]) - // - hash profile path - // - // For profile usage (-fprofile(-instr|-sample)-use, -fbranch-probabilities): - // - hash profile data - // - // The profile directory can be specified as an argument to - // -fprofile(-instr)-generate=, -fprofile(-instr|-sample)-use= or - // --fprofile-dir=. - if (profile_generate) { - assert(profile_path); - cc_log("Adding profile directory %s to our hash", profile_path); - hash_delimiter(hash, "-fprofile-dir"); - hash_string(hash, profile_path); - } - - if (profile_use && !hash_profile_data_file(profile_path, hash)) { - cc_log("No profile data file found"); - stats_update(STATS_NOINPUT); - failed(); - } - - // Adding -arch to hash since cpp output is affected. - for (size_t i = 0; i < arch_args_size; ++i) { - hash_delimiter(hash, "-arch"); - hash_string(hash, arch_args[i]); - } - - struct file_hash *object_hash = NULL; - if (direct_mode) { - // Hash environment variables that affect the preprocessor output. - const char *envvars[] = { - "CPATH", - "C_INCLUDE_PATH", - "CPLUS_INCLUDE_PATH", - "OBJC_INCLUDE_PATH", - "OBJCPLUS_INCLUDE_PATH", // clang - NULL - }; - for (const char **p = envvars; *p; ++p) { - char *v = getenv(*p); - if (v) { - hash_delimiter(hash, *p); - hash_string(hash, v); - } - } - - // Make sure that the direct mode hash is unique for the input file path. - // If this would not be the case: - // - // * An false cache hit may be produced. Scenario: - // - a/r.h exists. - // - a/x.c has #include "r.h". - // - b/x.c is identical to a/x.c. - // - Compiling a/x.c records a/r.h in the manifest. - // - Compiling b/x.c results in a false cache hit since a/x.c and b/x.c - // share manifests and a/r.h exists. - // * The expansion of __FILE__ may be incorrect. - hash_delimiter(hash, "inputfile"); - hash_string(hash, input_file); - - hash_delimiter(hash, "sourcecode"); - int result = hash_source_code_file(conf, hash, input_file); - if (result & HASH_SOURCE_CODE_ERROR) { - stats_update(STATS_ERROR); - failed(); - } - if (result & HASH_SOURCE_CODE_FOUND_TIME) { - cc_log("Disabling direct mode"); - conf->direct_mode = false; - return NULL; - } - - char *manifest_name = hash_result(hash); - manifest_path = get_path_in_cache(manifest_name, ".manifest"); - manifest_stats_file = - format("%s/%c/stats", conf->cache_dir, manifest_name[0]); - free(manifest_name); - - cc_log("Looking for object file hash in %s", manifest_path); - MTR_BEGIN("manifest", "manifest_get"); - object_hash = manifest_get(conf, manifest_path); - MTR_END("manifest", "manifest_get"); - if (object_hash) { - cc_log("Got object file hash from manifest"); - update_mtime(manifest_path); - } else { - cc_log("Did not find object file hash in manifest"); - } - } else { - assert(preprocessor_args); - if (arch_args_size == 0) { - object_hash = get_object_name_from_cpp(preprocessor_args, hash); - cc_log("Got object file hash from preprocessor"); - } else { - args_add(preprocessor_args, "-arch"); - for (size_t i = 0; i < arch_args_size; ++i) { - args_add(preprocessor_args, arch_args[i]); - object_hash = get_object_name_from_cpp(preprocessor_args, hash); - cc_log("Got object file hash from preprocessor with -arch %s", - arch_args[i]); - if (i != arch_args_size - 1) { - free(object_hash); - object_hash = NULL; - } - args_pop(preprocessor_args, 1); - } - args_pop(preprocessor_args, 1); - } - if (generating_dependencies) { - // Nothing is actually created with -MF /dev/null - if (!str_eq(output_dep, "/dev/null")) { - cc_log("Preprocessor created %s", output_dep); - } - } - } - - return object_hash; -} - -// Try to return the compile result from cache. If we can return from cache -// then this function exits with the correct status code, otherwise it returns. -static void -from_cache(enum fromcache_call_mode mode, bool put_object_in_manifest) -{ - // The user might be disabling cache hits. - if (conf->recache) { - return; - } - - // If we're using Clang, we can't trust a precompiled header object based on - // running the preprocessor since clang will produce a fatal error when the - // precompiled header is used and one of the included files has an updated - // timestamp: - // - // file 'foo.h' has been modified since the precompiled header 'foo.pch' - // was built - if ((guessed_compiler == GUESSED_CLANG || guessed_compiler == GUESSED_UNKNOWN) - && output_is_precompiled_header - && mode == FROMCACHE_CPP_MODE) { - cc_log("Not considering cached precompiled header in preprocessor mode"); - return; - } - - // Occasionally, e.g. on hard reset, our cache ends up as just filesystem - // meta-data with no content. Catch an easy case of this. - struct stat st; - if (stat(cached_obj, &st) != 0) { - cc_log("Object file %s not in cache", cached_obj); - return; - } - if (st.st_size == 0) { - cc_log("Invalid (empty) object file %s in cache", cached_obj); - x_unlink(cached_obj); - return; - } - - MTR_BEGIN("cache", "from_cache"); - - // (If mode != FROMCACHE_DIRECT_MODE, the dependency file is created by gcc.) - bool produce_dep_file = - generating_dependencies && mode == FROMCACHE_DIRECT_MODE - && !str_eq(output_dep, "/dev/null"); - - MTR_BEGIN("file", "file_get"); - - // Get result from cache. - if (!str_eq(output_obj, "/dev/null")) { - get_file_from_cache(cached_obj, output_obj); - if (using_split_dwarf) { - copy_file_from_cache(cached_dwo, output_dwo); - } - } - if (produce_dep_file) { - // Never hardlink the .d file since automake fails to move a foo.d.tmp file - // to foo.d if they have the same i-node. - copy_file_from_cache(cached_dep, output_dep); - } - if (generating_coverage) { - copy_file_from_cache(cached_cov, output_cov); - } - if (generating_stackusage) { - copy_file_from_cache(cached_su, output_su); - } - if (generating_diagnostics) { - copy_file_from_cache(cached_dia, output_dia); - } - - MTR_END("file", "file_get"); - - // Update modification timestamps to save files from LRU cleanup. Also gives - // files a sensible mtime when hard-linking. - update_mtime(cached_obj); - update_mtime(cached_stderr); - if (produce_dep_file) { - update_mtime(cached_dep); - } - if (generating_coverage) { - update_mtime(cached_cov); - } - if (generating_stackusage) { - update_mtime(cached_su); - } - if (generating_diagnostics) { - update_mtime(cached_dia); - } - if (cached_dwo) { - update_mtime(cached_dwo); - } - - send_cached_stderr(); - - if (put_object_in_manifest) { - update_manifest_file(); - } - - // Log the cache hit. - switch (mode) { - case FROMCACHE_DIRECT_MODE: - cc_log("Succeeded getting cached result"); - stats_update(STATS_CACHEHIT_DIR); - break; - - case FROMCACHE_CPP_MODE: - cc_log("Succeeded getting cached result"); - stats_update(STATS_CACHEHIT_CPP); - break; - } - - MTR_END("cache", "from_cache"); - - // And exit with the right status code. - x_exit(0); -} - -// Find the real compiler. We just search the PATH to find an executable of the -// same name that isn't a link to ourselves. -static void -find_compiler(char **argv) -{ - // We might be being invoked like "ccache gcc -c foo.c". - char *base = basename(argv[0]); - if (same_executable_name(base, MYNAME)) { - args_remove_first(orig_args); - free(base); - if (is_full_path(orig_args->argv[0])) { - // A full path was given. - return; - } - base = basename(orig_args->argv[0]); - } - - // Support user override of the compiler. - if (!str_eq(conf->compiler, "")) { - base = conf->compiler; - } - - char *compiler = find_executable(base, MYNAME); - if (!compiler) { - stats_update(STATS_COMPILER); - fatal("Could not find compiler \"%s\" in PATH", base); - } - if (str_eq(compiler, argv[0])) { - fatal("Recursive invocation (the name of the ccache binary must be \"%s\")", - MYNAME); - } - orig_args->argv[0] = compiler; -} - -bool -is_precompiled_header(const char *path) -{ - const char *ext = get_extension(path); - char *dir = dirname(path); - const char *dir_ext = get_extension(dir); - bool result = - str_eq(ext, ".gch") - || str_eq(ext, ".pch") - || str_eq(ext, ".pth") - || str_eq(dir_ext, ".gch"); // See "Precompiled Headers" in GCC docs. - free(dir); - return result; -} - -static bool -color_output_possible(void) -{ - const char *term_env = getenv("TERM"); - return isatty(STDERR_FILENO) && term_env && strcasecmp(term_env, "DUMB") != 0; -} - -static bool -detect_pch(const char *option, const char *arg, bool *found_pch) -{ - struct stat st; - - // Try to be smart about detecting precompiled headers. - char *pch_file = NULL; - if (str_eq(option, "-include-pch") || str_eq(option, "-include-pth")) { - if (stat(arg, &st) == 0) { - cc_log("Detected use of precompiled header: %s", arg); - pch_file = x_strdup(arg); - } - } else { - char *gchpath = format("%s.gch", arg); - if (stat(gchpath, &st) == 0) { - cc_log("Detected use of precompiled header: %s", gchpath); - pch_file = x_strdup(gchpath); - } else { - char *pchpath = format("%s.pch", arg); - if (stat(pchpath, &st) == 0) { - cc_log("Detected use of precompiled header: %s", pchpath); - pch_file = x_strdup(pchpath); - } else { - // clang may use pretokenized headers. - char *pthpath = format("%s.pth", arg); - if (stat(pthpath, &st) == 0) { - cc_log("Detected use of pretokenized header: %s", pthpath); - pch_file = x_strdup(pthpath); - } - free(pthpath); - } - free(pchpath); - } - free(gchpath); - } - - if (pch_file) { - if (included_pch_file) { - cc_log("Multiple precompiled headers used: %s and %s\n", - included_pch_file, pch_file); - stats_update(STATS_ARGS); - return false; - } - included_pch_file = pch_file; - *found_pch = true; - } - return true; -} - -static bool -process_profiling_option(const char *arg) -{ - char *new_profile_path = NULL; - bool new_profile_use = false; - - if (str_startswith(arg, "-fprofile-dir=")) { - new_profile_path = x_strdup(strchr(arg, '=') + 1); - } else if (str_eq(arg, "-fprofile-generate") - || str_eq(arg, "-fprofile-instr-generate")) { - profile_generate = true; - if (guessed_compiler == GUESSED_CLANG) { - new_profile_path = x_strdup("."); - } else { - // GCC uses $PWD/$(basename $obj). - new_profile_path = get_cwd(); - } - } else if (str_startswith(arg, "-fprofile-generate=") - || str_startswith(arg, "-fprofile-instr-generate=")) { - profile_generate = true; - new_profile_path = x_strdup(strchr(arg, '=') + 1); - } else if (str_eq(arg, "-fprofile-use") - || str_eq(arg, "-fprofile-instr-use") - || str_eq(arg, "-fprofile-sample-use") - || str_eq(arg, "-fbranch-probabilities") - || str_eq(arg, "-fauto-profile")) { - new_profile_use = true; - if (!profile_path) { - new_profile_path = x_strdup("."); - } - } else if (str_startswith(arg, "-fprofile-use=") - || str_startswith(arg, "-fprofile-instr-use=") - || str_startswith(arg, "-fprofile-sample-use=") - || str_startswith(arg, "-fauto-profile=")) { - new_profile_use = true; - new_profile_path = x_strdup(strchr(arg, '=') + 1); - } else if (str_eq(arg, "-fprofile-correction") - || str_eq(arg, "-fprofile-reorder-functions") - || str_eq(arg, "-fprofile-sample-accurate") - || str_eq(arg, "-fprofile-values")) { - return true; - } else { - cc_log("Unknown profiling option: %s", arg); - stats_update(STATS_UNSUPPORTED_OPTION); - return false; - } - - if (new_profile_use) { - if (profile_use) { - free(new_profile_path); - cc_log("Multiple profiling options not supported"); - stats_update(STATS_UNSUPPORTED_OPTION); - return false; - } - profile_use = true; - } - - if (new_profile_path) { - free(profile_path); - profile_path = new_profile_path; - cc_log("Set profile directory to %s", profile_path); - } - - if (profile_generate && profile_use) { - // Too hard to figure out what the compiler will do. - cc_log("Both generating and using profile info, giving up"); - stats_update(STATS_UNSUPPORTED_OPTION); - return false; - } - - return true; -} - -// Process the compiler options into options suitable for passing to the -// preprocessor and the real compiler. preprocessor_args doesn't include -E; -// this is added later. extra_args_to_hash are the arguments that are not -// included in preprocessor_args but that should be included in the hash. -// -// Returns true on success, otherwise false. -bool -cc_process_args(struct args *args, - struct args **preprocessor_args, - struct args **extra_args_to_hash, - struct args **compiler_args) -{ - bool found_c_opt = false; - bool found_dc_opt = false; - bool found_S_opt = false; - bool found_pch = false; - bool found_fpch_preprocess = false; - const char *explicit_language = NULL; // As specified with -x. - const char *file_language; // As deduced from file extension. - const char *input_charset = NULL; - - // Is the dependency makefile name overridden with -MF? - bool dependency_filename_specified = false; - - // Is the dependency makefile target name specified with -MT or -MQ? - bool dependency_target_specified = false; - - // Is the dependency target name implicitly specified using - // DEPENDENCIES_OUTPUT or SUNPRO_DEPENDENCIES? - bool dependency_implicit_target_specified = false; - - // expanded_args is a copy of the original arguments given to the compiler - // but with arguments from @file and similar constructs expanded. It's only - // used as a temporary data structure to loop over. - struct args *expanded_args = args_copy(args); - - // common_args contains all original arguments except: - // * those that never should be passed to the preprocessor, - // * those that only should be passed to the preprocessor (if run_second_cpp - // is false), and - // * dependency options (like -MD and friends). - struct args *common_args = args_init(0, NULL); - - // cpp_args contains arguments that were not added to common_args, i.e. those - // that should only be passed to the preprocessor if run_second_cpp is false. - // If run_second_cpp is true, they will be passed to the compiler as well. - struct args *cpp_args = args_init(0, NULL); - - // dep_args contains dependency options like -MD. They are only passed to the - // preprocessor, never to the compiler. - struct args *dep_args = args_init(0, NULL); - - // compiler_only_args contains arguments that should only be passed to the - // compiler, not the preprocessor. - struct args *compiler_only_args = args_init(0, NULL); - - bool found_color_diagnostics = false; - bool found_directives_only = false; - bool found_rewrite_includes = false; - - int argc = expanded_args->argc; - char **argv = expanded_args->argv; - args_add(common_args, argv[0]); - - bool result = true; - for (int i = 1; i < argc; i++) { - // The user knows best: just swallow the next arg. - if (str_eq(argv[i], "--ccache-skip")) { - i++; - if (i == argc) { - cc_log("--ccache-skip lacks an argument"); - stats_update(STATS_ARGS); - result = false; - goto out; - } - args_add(common_args, argv[i]); - continue; - } - - // Special case for -E. - if (str_eq(argv[i], "-E")) { - stats_update(STATS_PREPROCESSING); - result = false; - goto out; - } - - // Handle "@file" argument. - if (str_startswith(argv[i], "@") || str_startswith(argv[i], "-@")) { - char *argpath = argv[i] + 1; - - if (argpath[-1] == '-') { - ++argpath; - } - struct args *file_args = args_init_from_gcc_atfile(argpath); - if (!file_args) { - cc_log("Couldn't read arg file %s", argpath); - stats_update(STATS_ARGS); - result = false; - goto out; - } - - args_insert(expanded_args, i, file_args, true); - argc = expanded_args->argc; - argv = expanded_args->argv; - i--; - continue; - } - - // Handle cuda "-optf" and "--options-file" argument. - if (guessed_compiler == GUESSED_NVCC - && (str_eq(argv[i], "-optf") || str_eq(argv[i], "--options-file"))) { - if (i == argc - 1) { - cc_log("Expected argument after %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - ++i; - - // Argument is a comma-separated list of files. - char *str_start = argv[i]; - char *str_end = strchr(str_start, ','); - int index = i + 1; - - if (!str_end) { - str_end = str_start + strlen(str_start); - } - - while (str_end) { - *str_end = '\0'; - struct args *file_args = args_init_from_gcc_atfile(str_start); - if (!file_args) { - cc_log("Couldn't read cuda options file %s", str_start); - stats_update(STATS_ARGS); - result = false; - goto out; - } - - int new_index = file_args->argc + index; - args_insert(expanded_args, index, file_args, false); - index = new_index; - str_start = str_end; - str_end = strchr(str_start, ','); - } - - argc = expanded_args->argc; - argv = expanded_args->argv; - continue; - } - - // These are always too hard. - if (compopt_too_hard(argv[i]) - || str_startswith(argv[i], "-fdump-") - || str_startswith(argv[i], "-MJ")) { - cc_log("Compiler option %s is unsupported", argv[i]); - stats_update(STATS_UNSUPPORTED_OPTION); - result = false; - goto out; - } - - // These are too hard in direct mode. - if (conf->direct_mode && compopt_too_hard_for_direct_mode(argv[i])) { - cc_log("Unsupported compiler option for direct mode: %s", argv[i]); - conf->direct_mode = false; - } - - // -Xarch_* options are too hard. - if (str_startswith(argv[i], "-Xarch_")) { - cc_log("Unsupported compiler option: %s", argv[i]); - stats_update(STATS_UNSUPPORTED_OPTION); - result = false; - goto out; - } - - // Handle -arch options. - if (str_eq(argv[i], "-arch")) { - if (arch_args_size == MAX_ARCH_ARGS - 1) { - cc_log("Too many -arch compiler options; ccache supports at most %d", - MAX_ARCH_ARGS); - stats_update(STATS_UNSUPPORTED_OPTION); - result = false; - goto out; - } - - ++i; - arch_args[arch_args_size] = x_strdup(argv[i]); // It will leak. - ++arch_args_size; - if (arch_args_size == 2) { - conf->run_second_cpp = true; - } - continue; - } - - // Handle options that should not be passed to the preprocessor. - if (compopt_affects_comp(argv[i])) { - args_add(compiler_only_args, argv[i]); - if (compopt_takes_arg(argv[i]) - || (guessed_compiler == GUESSED_NVCC && str_eq(argv[i], "-Werror"))) { - if (i == argc - 1) { - cc_log("Missing argument to %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - args_add(compiler_only_args, argv[i + 1]); - ++i; - } - continue; - } - if (compopt_prefix_affects_comp(argv[i])) { - args_add(compiler_only_args, argv[i]); - continue; - } - - if (str_eq(argv[i], "-fpch-preprocess") - || str_eq(argv[i], "-emit-pch") - || str_eq(argv[i], "-emit-pth")) { - found_fpch_preprocess = true; - } - - // We must have -c. - if (str_eq(argv[i], "-c")) { - found_c_opt = true; - continue; - } - - // when using nvcc with separable compilation, -dc implies -c - if ((str_eq(argv[i], "-dc") || str_eq(argv[i], "--device-c")) - && guessed_compiler == GUESSED_NVCC) { - found_dc_opt = true; - continue; - } - - // -S changes the default extension. - if (str_eq(argv[i], "-S")) { - args_add(common_args, argv[i]); - found_S_opt = true; - continue; - } - - if (strlen(argv[i]) >= 3 - && str_startswith(argv[i], "-x") - && !islower(argv[i][2])) { - // -xCODE (where CODE can be e.g. Host or CORE-AVX2, always starting with - // an uppercase letter) is an ordinary Intel compiler option, not a - // language specification. (GCC's "-x" language argument is always - // lowercase.) - args_add(common_args, argv[i]); - continue; - } - - // Special handling for -x: remember the last specified language before the - // input file and strip all -x options from the arguments. - if (str_eq(argv[i], "-x")) { - if (i == argc - 1) { - cc_log("Missing argument to %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - if (!input_file) { - explicit_language = argv[i+1]; - } - i++; - continue; - } - if (str_startswith(argv[i], "-x")) { - if (!input_file) { - explicit_language = &argv[i][2]; - } - continue; - } - - // We need to work out where the output was meant to go. - if (str_eq(argv[i], "-o")) { - if (i == argc - 1) { - cc_log("Missing argument to %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - output_obj = make_relative_path(x_strdup(argv[i+1])); - i++; - continue; - } - - // Alternate form of -o with no space. Nvcc does not support this. - if (str_startswith(argv[i], "-o") && guessed_compiler != GUESSED_NVCC) { - output_obj = make_relative_path(x_strdup(&argv[i][2])); - continue; - } - - if (str_startswith(argv[i], "-fdebug-prefix-map=") - || str_startswith(argv[i], "-ffile-prefix-map=")) { - debug_prefix_maps = x_realloc( - debug_prefix_maps, - (debug_prefix_maps_len + 1) * sizeof(char *)); - debug_prefix_maps[debug_prefix_maps_len++] = - x_strdup(&argv[i][argv[i][2] == 'f' ? 18 : 19]); - args_add(common_args, argv[i]); - continue; - } - - // Debugging is handled specially, so that we know if we can strip line - // number info. - if (str_startswith(argv[i], "-g")) { - args_add(common_args, argv[i]); - - if (str_startswith(argv[i], "-gdwarf")) { - // Selection of DWARF format (-gdwarf or -gdwarf-) enables - // debug info on level 2. - generating_debuginfo = true; - continue; - } - - if (str_startswith(argv[i], "-gz")) { - // -gz[=type] neither disables nor enables debug info. - continue; - } - - char last_char = argv[i][strlen(argv[i]) - 1]; - if (last_char == '0') { - // "-g0", "-ggdb0" or similar: All debug information disabled. - // "-gsplit-dwarf" is still in effect if given previously, though. - generating_debuginfo = false; - generating_debuginfo_level_3 = false; - } else { - generating_debuginfo = true; - if (last_char == '3') { - generating_debuginfo_level_3 = true; - } - if (str_eq(argv[i], "-gsplit-dwarf")) { - using_split_dwarf = true; - } - } - continue; - } - - // These options require special handling, because they behave differently - // with gcc -E, when the output file is not specified. - if (str_eq(argv[i], "-MD") || str_eq(argv[i], "-MMD")) { - generating_dependencies = true; - args_add(dep_args, argv[i]); - continue; - } - if (str_startswith(argv[i], "-MF")) { - dependency_filename_specified = true; - free(output_dep); - - char *arg; - bool separate_argument = (strlen(argv[i]) == 3); - if (separate_argument) { - // -MF arg - if (i == argc - 1) { - cc_log("Missing argument to %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - arg = argv[i + 1]; - i++; - } else { - // -MFarg or -MF=arg (EDG-based compilers) - arg = &argv[i][3]; - if (arg[0] == '=') { - ++arg; - } - } - output_dep = make_relative_path(x_strdup(arg)); - // Keep the format of the args the same. - if (separate_argument) { - args_add(dep_args, "-MF"); - args_add(dep_args, output_dep); - } else { - char *option = format("-MF%s", output_dep); - args_add(dep_args, option); - free(option); - } - continue; - } - if (str_startswith(argv[i], "-MQ") || str_startswith(argv[i], "-MT")) { - dependency_target_specified = true; - - char *relpath; - if (strlen(argv[i]) == 3) { - // -MQ arg or -MT arg - if (i == argc - 1) { - cc_log("Missing argument to %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - args_add(dep_args, argv[i]); - relpath = make_relative_path(x_strdup(argv[i + 1])); - args_add(dep_args, relpath); - free(relpath); - i++; - } else { - char *arg_opt = x_strndup(argv[i], 3); - relpath = make_relative_path(x_strdup(argv[i] + 3)); - char *option = format("%s%s", arg_opt, relpath); - args_add(dep_args, option); - free(arg_opt); - free(relpath); - free(option); - } - continue; - } - if (str_eq(argv[i], "-fprofile-arcs")) { - profile_arcs = true; - args_add(common_args, argv[i]); - continue; - } - if (str_eq(argv[i], "-ftest-coverage")) { - generating_coverage = true; - args_add(common_args, argv[i]); - continue; - } - if (str_eq(argv[i], "-fstack-usage")) { - generating_stackusage = true; - args_add(common_args, argv[i]); - continue; - } - if (str_eq(argv[i], "--coverage") // = -fprofile-arcs -ftest-coverage - || str_eq(argv[i], "-coverage")) { // Undocumented but still works. - profile_arcs = true; - generating_coverage = true; - args_add(common_args, argv[i]); - continue; - } - if (str_startswith(argv[i], "-fprofile-") - || str_startswith(argv[i], "-fauto-profile") - || str_eq(argv[i], "-fbranch-probabilities")) { - if (process_profiling_option(argv[i])) { - args_add(common_args, argv[i]); - continue; - } else { - result = false; - goto out; - } - } - if (str_startswith(argv[i], "-fsanitize-blacklist=")) { - sanitize_blacklists = x_realloc( - sanitize_blacklists, - (sanitize_blacklists_len + 1) * sizeof(char *)); - sanitize_blacklists[sanitize_blacklists_len++] = x_strdup(argv[i] + 21); - args_add(common_args, argv[i]); - continue; - } - if (str_startswith(argv[i], "--sysroot=")) { - char *relpath = make_relative_path(x_strdup(argv[i] + 10)); - char *option = format("--sysroot=%s", relpath); - args_add(common_args, option); - free(relpath); - free(option); - continue; - } - // Alternate form of specifying sysroot without = - if (str_eq(argv[i], "--sysroot")) { - if (i == argc-1) { - cc_log("Missing argument to %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - args_add(common_args, argv[i]); - char *relpath = make_relative_path(x_strdup(argv[i+1])); - args_add(common_args, relpath); - i++; - free(relpath); - continue; - } - // Alternate form of specifying target without = - if (str_eq(argv[i], "-target")) { - if (i == argc-1) { - cc_log("Missing argument to %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - args_add(common_args, argv[i]); - args_add(common_args, argv[i+1]); - i++; - continue; - } - if (str_startswith(argv[i], "-Wp,")) { - if (str_eq(argv[i], "-Wp,-P") - || strstr(argv[i], ",-P,") - || str_endswith(argv[i], ",-P")) { - // -P removes preprocessor information in such a way that the object - // file from compiling the preprocessed file will not be equal to the - // object file produced when compiling without ccache. - cc_log("Too hard option -Wp,-P detected"); - stats_update(STATS_UNSUPPORTED_OPTION); - failed(); - } else if (str_startswith(argv[i], "-Wp,-MD,") - && !strchr(argv[i] + 8, ',')) { - generating_dependencies = true; - dependency_filename_specified = true; - free(output_dep); - output_dep = make_relative_path(x_strdup(argv[i] + 8)); - args_add(dep_args, argv[i]); - continue; - } else if (str_startswith(argv[i], "-Wp,-MMD,") - && !strchr(argv[i] + 9, ',')) { - generating_dependencies = true; - dependency_filename_specified = true; - free(output_dep); - output_dep = make_relative_path(x_strdup(argv[i] + 9)); - args_add(dep_args, argv[i]); - continue; - } else if (str_startswith(argv[i], "-Wp,-D") - && !strchr(argv[i] + 6, ',')) { - // Treat it like -D. - args_add(cpp_args, argv[i] + 4); - continue; - } else if (str_eq(argv[i], "-Wp,-MP") - || (strlen(argv[i]) > 8 - && str_startswith(argv[i], "-Wp,-M") - && argv[i][7] == ',' - && (argv[i][6] == 'F' - || argv[i][6] == 'Q' - || argv[i][6] == 'T') - && !strchr(argv[i] + 8, ','))) { - // TODO: Make argument to MF/MQ/MT relative. - args_add(dep_args, argv[i]); - continue; - } else if (conf->direct_mode) { - // -Wp, can be used to pass too hard options to the preprocessor. - // Hence, disable direct mode. - cc_log("Unsupported compiler option for direct mode: %s", argv[i]); - conf->direct_mode = false; - } - - // Any other -Wp,* arguments are only relevant for the preprocessor. - args_add(cpp_args, argv[i]); - continue; - } - if (str_eq(argv[i], "-MP")) { - args_add(dep_args, argv[i]); - continue; - } - - // Input charset needs to be handled specially. - if (str_startswith(argv[i], "-finput-charset=")) { - input_charset = argv[i]; - continue; - } - - if (str_eq(argv[i], "--serialize-diagnostics")) { - if (i == argc - 1) { - cc_log("Missing argument to %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - generating_diagnostics = true; - output_dia = make_relative_path(x_strdup(argv[i+1])); - i++; - continue; - } - - if (str_eq(argv[i], "-fcolor-diagnostics") - || str_eq(argv[i], "-fno-color-diagnostics") - || str_eq(argv[i], "-fdiagnostics-color") - || str_eq(argv[i], "-fdiagnostics-color=always") - || str_eq(argv[i], "-fno-diagnostics-color") - || str_eq(argv[i], "-fdiagnostics-color=never")) { - args_add(common_args, argv[i]); - found_color_diagnostics = true; - continue; - } - if (str_eq(argv[i], "-fdiagnostics-color=auto")) { - if (color_output_possible()) { - // Output is redirected, so color output must be forced. - args_add(common_args, "-fdiagnostics-color=always"); - add_extra_arg("-fdiagnostics-color=always"); - cc_log("Automatically forcing colors"); - } else { - args_add(common_args, argv[i]); - } - found_color_diagnostics = true; - continue; - } - - // GCC - if (str_eq(argv[i], "-fdirectives-only")) { - found_directives_only = true; - continue; - } - // Clang - if (str_eq(argv[i], "-frewrite-includes")) { - found_rewrite_includes = true; - continue; - } - - if (conf->sloppiness & SLOPPY_CLANG_INDEX_STORE - && str_eq(argv[i], "-index-store-path")) { - // Xcode 9 or later calls Clang with this option. The given path includes - // a UUID that might lead to cache misses, especially when cache is - // shared among multiple users. - i++; - if (i <= argc - 1) { - cc_log("Skipping argument -index-store-path %s", argv[i]); - } - continue; - } - - // Options taking an argument that we may want to rewrite to relative paths - // to get better hit rate. A secondary effect is that paths in the standard - // error output produced by the compiler will be normalized. - if (compopt_takes_path(argv[i])) { - if (i == argc - 1) { - cc_log("Missing argument to %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - - if (!detect_pch(argv[i], argv[i+1], &found_pch)) { - result = false; - goto out; - } - - char *relpath = make_relative_path(x_strdup(argv[i+1])); - if (compopt_affects_cpp(argv[i])) { - args_add(cpp_args, argv[i]); - args_add(cpp_args, relpath); - } else { - args_add(common_args, argv[i]); - args_add(common_args, relpath); - } - free(relpath); - - i++; - continue; - } - - // Same as above but options with concatenated argument beginning with a - // slash. - if (argv[i][0] == '-') { - char *slash_pos = strchr(argv[i], '/'); - if (slash_pos) { - char *option = x_strndup(argv[i], slash_pos - argv[i]); - if (compopt_takes_concat_arg(option) && compopt_takes_path(option)) { - char *relpath = make_relative_path(x_strdup(slash_pos)); - char *new_option = format("%s%s", option, relpath); - if (compopt_affects_cpp(option)) { - args_add(cpp_args, new_option); - } else { - args_add(common_args, new_option); - } - free(new_option); - free(relpath); - free(option); - continue; - } else { - free(option); - } - } - } - - // Options that take an argument. - if (compopt_takes_arg(argv[i])) { - if (i == argc - 1) { - cc_log("Missing argument to %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - - if (compopt_affects_cpp(argv[i])) { - args_add(cpp_args, argv[i]); - args_add(cpp_args, argv[i+1]); - } else { - args_add(common_args, argv[i]); - args_add(common_args, argv[i+1]); - } - - i++; - continue; - } - - // Other options. - if (argv[i][0] == '-') { - if (compopt_affects_cpp(argv[i]) - || compopt_prefix_affects_cpp(argv[i])) { - args_add(cpp_args, argv[i]); - } else { - args_add(common_args, argv[i]); - } - continue; - } - - // If an argument isn't a plain file then assume its an option, not an - // input file. This allows us to cope better with unusual compiler options. - // - // Note that "/dev/null" is an exception that is sometimes used as an input - // file when code is testing compiler flags. - struct stat st; - if (!str_eq(argv[i], "/dev/null") - && (stat(argv[i], &st) != 0 || !S_ISREG(st.st_mode))) { - cc_log("%s is not a regular file, not considering as input file", - argv[i]); - args_add(common_args, argv[i]); - continue; - } - - if (input_file) { - if (language_for_file(argv[i])) { - cc_log("Multiple input files: %s and %s", input_file, argv[i]); - stats_update(STATS_MULTIPLE); - } else if (!found_c_opt && !found_dc_opt) { - cc_log("Called for link with %s", argv[i]); - if (strstr(argv[i], "conftest.")) { - stats_update(STATS_CONFTEST); - } else { - stats_update(STATS_LINK); - } - } else { - cc_log("Unsupported source extension: %s", argv[i]); - stats_update(STATS_SOURCELANG); - } - result = false; - goto out; - } - - // The source code file path gets put into the notes. - if (generating_coverage) { - input_file = x_strdup(argv[i]); - continue; - } - - if (is_symlink(argv[i])) { - // Don't rewrite source file path if it's a symlink since - // make_relative_path resolves symlinks using realpath(3) and this leads - // to potentially choosing incorrect relative header files. See the - // "symlink to source file" test. - input_file = x_strdup(argv[i]); - } else { - // Rewrite to relative to increase hit rate. - input_file = make_relative_path(x_strdup(argv[i])); - } - } // for - - if (generating_debuginfo_level_3 && !conf->run_second_cpp) { - cc_log("Generating debug info level 3; not compiling preprocessed code"); - conf->run_second_cpp = true; - } - - // See . - // Contrary to what the documentation seems to imply the compiler still - // creates object files with these defined (confirmed with GCC 8.2.1), i.e. - // they work as -MMD/-MD, not -MM/-M. These environment variables do nothing - // on Clang. - char *dependencies_env = getenv("DEPENDENCIES_OUTPUT"); - bool using_sunpro_dependencies = false; - if (!dependencies_env) { - dependencies_env = getenv("SUNPRO_DEPENDENCIES"); - using_sunpro_dependencies = true; - } - if (dependencies_env) { - generating_dependencies = true; - dependency_filename_specified = true; - char *saveptr = NULL; - char *abspath_file = strtok_r(dependencies_env, " ", &saveptr); - - free(output_dep); - output_dep = make_relative_path(x_strdup(abspath_file)); - - // Specifying target object is optional. - char *abspath_obj = strtok_r(NULL, " ", &saveptr); - if (abspath_obj) { - // It's the "file target" form. - - dependency_target_specified = true; - char *relpath_obj = make_relative_path(x_strdup(abspath_obj)); - // Ensure compiler gets relative path. - char *relpath_both = format("%s %s", output_dep, relpath_obj); - if (using_sunpro_dependencies) { - x_setenv("SUNPRO_DEPENDENCIES", relpath_both); - } else { - x_setenv("DEPENDENCIES_OUTPUT", relpath_both); - } - free(relpath_obj); - free(relpath_both); - } else { - // It's the "file" form. - - dependency_implicit_target_specified = true; - // Ensure compiler gets relative path. - if (using_sunpro_dependencies) { - x_setenv("SUNPRO_DEPENDENCIES", output_dep); - } else { - x_setenv("DEPENDENCIES_OUTPUT", output_dep); - } - } - } - - if (found_S_opt) { - // Even if -gsplit-dwarf is given, the .dwo file is not generated when -S - // is also given. - using_split_dwarf = false; - cc_log("Disabling caching of dwarf files since -S is used"); - } - - if (!input_file) { - cc_log("No input file found"); - stats_update(STATS_NOINPUT); - result = false; - goto out; - } - - if (found_pch || found_fpch_preprocess) { - using_precompiled_header = true; - if (!(conf->sloppiness & SLOPPY_TIME_MACROS)) { - cc_log("You have to specify \"time_macros\" sloppiness when using" - " precompiled headers to get direct hits"); - cc_log("Disabling direct mode"); - stats_update(STATS_CANTUSEPCH); - result = false; - goto out; - } - } - - if (explicit_language && str_eq(explicit_language, "none")) { - explicit_language = NULL; - } - file_language = language_for_file(input_file); - if (explicit_language) { - if (!language_is_supported(explicit_language)) { - cc_log("Unsupported language: %s", explicit_language); - stats_update(STATS_SOURCELANG); - result = false; - goto out; - } - actual_language = x_strdup(explicit_language); - } else { - actual_language = file_language; - } - - output_is_precompiled_header = - actual_language && strstr(actual_language, "-header"); - - if (output_is_precompiled_header - && !(conf->sloppiness & SLOPPY_PCH_DEFINES)) { - cc_log("You have to specify \"pch_defines,time_macros\" sloppiness when" - " creating precompiled headers"); - stats_update(STATS_CANTUSEPCH); - result = false; - goto out; - } - - if (!found_c_opt && !found_dc_opt && !found_S_opt) { - if (output_is_precompiled_header) { - args_add(common_args, "-c"); - } else { - cc_log("No -c option found"); - // I find that having a separate statistic for autoconf tests is useful, - // as they are the dominant form of "called for link" in many cases. - if (strstr(input_file, "conftest.")) { - stats_update(STATS_CONFTEST); - } else { - stats_update(STATS_LINK); - } - result = false; - goto out; - } - } - - if (!actual_language) { - cc_log("Unsupported source extension: %s", input_file); - stats_update(STATS_SOURCELANG); - result = false; - goto out; - } - - if (!conf->run_second_cpp && str_eq(actual_language, "cu")) { - cc_log("Using CUDA compiler; not compiling preprocessed code"); - conf->run_second_cpp = true; - } - - direct_i_file = language_is_preprocessed(actual_language); - - if (output_is_precompiled_header && !conf->run_second_cpp) { - // It doesn't work to create the .gch from preprocessed source. - cc_log("Creating precompiled header; not compiling preprocessed code"); - conf->run_second_cpp = true; - } - - if (str_eq(conf->cpp_extension, "")) { - const char *p_language = p_language_for_language(actual_language); - free(conf->cpp_extension); - conf->cpp_extension = x_strdup(extension_for_language(p_language) + 1); - } - - // Don't try to second guess the compilers heuristics for stdout handling. - if (output_obj && str_eq(output_obj, "-")) { - stats_update(STATS_OUTSTDOUT); - cc_log("Output file is -"); - result = false; - goto out; - } - - if (!output_obj) { - if (output_is_precompiled_header) { - output_obj = format("%s.gch", input_file); - } else { - char extension = found_S_opt ? 's' : 'o'; - output_obj = basename(input_file); - char *p = strrchr(output_obj, '.'); - if (!p) { - reformat(&output_obj, "%s.%c", output_obj, extension); - } else if (!p[1]) { - reformat(&output_obj, "%s%c", output_obj, extension); - } else { - p[1] = extension; - p[2] = 0; - } - } - } - - if (using_split_dwarf) { - char *p = strrchr(output_obj, '.'); - if (!p || !p[1]) { - cc_log("Badly formed object filename"); - stats_update(STATS_ARGS); - result = false; - goto out; - } - - char *base_name = remove_extension(output_obj); - output_dwo = format("%s.dwo", base_name); - free(base_name); - } - - // Cope with -o /dev/null. - struct stat st; - if (!str_eq(output_obj, "/dev/null") - && stat(output_obj, &st) == 0 - && !S_ISREG(st.st_mode)) { - cc_log("Not a regular file: %s", output_obj); - stats_update(STATS_BADOUTPUTFILE); - result = false; - goto out; - } - - char *output_dir = dirname(output_obj); - if (stat(output_dir, &st) != 0 || !S_ISDIR(st.st_mode)) { - cc_log("Directory does not exist: %s", output_dir); - stats_update(STATS_BADOUTPUTFILE); - result = false; - free(output_dir); - goto out; - } - free(output_dir); - - // Some options shouldn't be passed to the real compiler when it compiles - // preprocessed code: - // - // -finput-charset=XXX (otherwise conversion happens twice) - // -x XXX (otherwise the wrong language is selected) - if (input_charset) { - args_add(cpp_args, input_charset); - } - if (found_pch) { - args_add(cpp_args, "-fpch-preprocess"); - } - if (explicit_language) { - args_add(cpp_args, "-x"); - args_add(cpp_args, explicit_language); - } - - // Since output is redirected, compilers will not color their output by - // default, so force it explicitly if it would be otherwise done. - if (!found_color_diagnostics && color_output_possible()) { - if (guessed_compiler == GUESSED_CLANG) { - if (!str_eq(actual_language, "assembler")) { - args_add(common_args, "-fcolor-diagnostics"); - add_extra_arg("-fcolor-diagnostics"); - cc_log("Automatically enabling colors"); - } - } else if (guessed_compiler == GUESSED_GCC) { - // GCC has it since 4.9, but that'd require detecting what GCC version is - // used for the actual compile. However it requires also GCC_COLORS to be - // set (and not empty), so use that for detecting if GCC would use - // colors. - if (getenv("GCC_COLORS") && getenv("GCC_COLORS")[0] != '\0') { - args_add(common_args, "-fdiagnostics-color"); - add_extra_arg("-fdiagnostics-color"); - cc_log("Automatically enabling colors"); - } - } - } - - // Add flags for dependency generation only to the preprocessor command line. - if (generating_dependencies) { - if (!dependency_filename_specified) { - char *base_name = remove_extension(output_obj); - char *default_depfile_name = format("%s.d", base_name); - free(base_name); - args_add(dep_args, "-MF"); - args_add(dep_args, default_depfile_name); - output_dep = make_relative_path(x_strdup(default_depfile_name)); - } - - if (!dependency_target_specified - && !dependency_implicit_target_specified) { - args_add(dep_args, "-MQ"); - args_add(dep_args, output_obj); - } - } - if (generating_coverage) { - char *base_name = remove_extension(output_obj); - char *default_covfile_name = format("%s.gcno", base_name); - free(base_name); - output_cov = make_relative_path(default_covfile_name); - } - if (generating_stackusage) { - char *base_name = remove_extension(output_obj); - char *default_sufile_name = format("%s.su", base_name); - free(base_name); - output_su = make_relative_path(default_sufile_name); - } - - *compiler_args = args_copy(common_args); - args_extend(*compiler_args, compiler_only_args); - - if (conf->run_second_cpp) { - args_extend(*compiler_args, cpp_args); - } else if (found_directives_only || found_rewrite_includes) { - // Need to pass the macros and any other preprocessor directives again. - args_extend(*compiler_args, cpp_args); - if (found_directives_only) { - args_add(cpp_args, "-fdirectives-only"); - // The preprocessed source code still needs some more preprocessing. - args_add(*compiler_args, "-fpreprocessed"); - args_add(*compiler_args, "-fdirectives-only"); - } - if (found_rewrite_includes) { - args_add(cpp_args, "-frewrite-includes"); - // The preprocessed source code still needs some more preprocessing. - args_add(*compiler_args, "-x"); - args_add(*compiler_args, actual_language); - } - } else if (explicit_language) { - // Workaround for a bug in Apple's patched distcc -- it doesn't properly - // reset the language specified with -x, so if -x is given, we have to - // specify the preprocessed language explicitly. - args_add(*compiler_args, "-x"); - args_add(*compiler_args, p_language_for_language(explicit_language)); - } - - if (found_c_opt) { - args_add(*compiler_args, "-c"); - } - - if (found_dc_opt) { - args_add(*compiler_args, "-dc"); - } - - for (size_t i = 0; i < arch_args_size; ++i) { - args_add(*compiler_args, "-arch"); - args_add(*compiler_args, arch_args[i]); - } - - // Only pass dependency arguments to the preprocessor since Intel's C++ - // compiler doesn't produce a correct .d file when compiling preprocessed - // source. - args_extend(cpp_args, dep_args); - - *preprocessor_args = args_copy(common_args); - args_extend(*preprocessor_args, cpp_args); - - if (extra_args_to_hash) { - *extra_args_to_hash = compiler_only_args; - } - -out: - args_free(expanded_args); - args_free(common_args); - args_free(dep_args); - args_free(cpp_args); - return result; -} - -static void -create_initial_config_file(const char *path) -{ - if (create_parent_dirs(path) != 0) { - return; - } - - unsigned max_files; - uint64_t max_size; - char *stats_dir = format("%s/0", conf->cache_dir); - struct stat st; - if (stat(stats_dir, &st) == 0) { - stats_get_obsolete_limits(stats_dir, &max_files, &max_size); - // STATS_MAXFILES and STATS_MAXSIZE was stored for each top directory. - max_files *= 16; - max_size *= 16; - } else { - max_files = 0; - max_size = conf->max_size; - } - free(stats_dir); - - FILE *f = fopen(path, "w"); - if (!f) { - return; - } - if (max_files != 0) { - fprintf(f, "max_files = %u\n", max_files); - conf->max_files = max_files; - } - if (max_size != 0) { - char *size = format_parsable_size_with_suffix(max_size); - fprintf(f, "max_size = %s\n", size); - free(size); - conf->max_size = max_size; - } - fclose(f); -} - -#ifdef MTR_ENABLED -static void *trace_id; -static const char *trace_file; - -static void -trace_init(const char *json) -{ - trace_file = json; - mtr_init(json); - char *s = format("%f", time_seconds()); - MTR_INSTANT_C("", "", "time", s); -} - -static void -trace_start(const char *tracefile) -{ - trace_file = tracefile; - MTR_META_PROCESS_NAME(MYNAME); - trace_id = (void *) ((long) getpid()); - MTR_START("program", "ccache", trace_id); -} - -static void -trace_stop(void) -{ - const char *json = format("%s%s", output_obj, ".ccache-trace"); - MTR_FINISH("program", "ccache", trace_id); - mtr_flush(); - mtr_shutdown(); - move_file(trace_file, json, 0); -} - -static const char * -tmpdir() -{ -#ifndef _WIN32 - const char *tmpdir = getenv("TMPDIR"); - if (tmpdir != NULL) { - return tmpdir; - } -#else - static char dirbuf[PATH_MAX]; - DWORD retval = GetTempPath(PATH_MAX, dirbuf); - if (retval > 0 && retval < PATH_MAX) { - return dirbuf; - } -#endif - return "/tmp"; -} - -#endif // MTR_ENABLED - -// Read config file(s), populate variables, create configuration file in cache -// directory if missing, etc. -static void -initialize(void) -{ - char *tracefile = getenv("CCACHE_INTERNAL_TRACE"); - if (tracefile != NULL) { -#ifdef MTR_ENABLED - // We don't have any conf yet, so we can't use temp_dir() here. - tracefile = format("%s/trace.%d.json", tmpdir(), (int)getpid()); - - trace_init(tracefile); -#endif - } - - conf_free(conf); - MTR_BEGIN("config", "conf_create"); - conf = conf_create(); - MTR_END("config", "conf_create"); - - char *errmsg; - char *p = getenv("CCACHE_CONFIGPATH"); - if (p) { - primary_config_path = x_strdup(p); - } else { - secondary_config_path = format("%s/ccache.conf", TO_STRING(SYSCONFDIR)); - MTR_BEGIN("config", "conf_read_secondary"); - if (!conf_read(conf, secondary_config_path, &errmsg)) { - if (errno == 0) { - // We could read the file but it contained errors. - fatal("%s", errmsg); - } - // A missing config file in SYSCONFDIR is OK. - free(errmsg); - } - MTR_END("config", "conf_read_secondary"); - - if (str_eq(conf->cache_dir, "")) { - fatal("configuration setting \"cache_dir\" must not be the empty string"); - } - if ((p = getenv("CCACHE_DIR"))) { - free(conf->cache_dir); - conf->cache_dir = strdup(p); - } - if (str_eq(conf->cache_dir, "")) { - fatal("CCACHE_DIR must not be the empty string"); - } - - primary_config_path = format("%s/ccache.conf", conf->cache_dir); - } - - bool should_create_initial_config = false; - MTR_BEGIN("config", "conf_read_primary"); - if (!conf_read(conf, primary_config_path, &errmsg)) { - if (errno == 0) { - // We could read the file but it contained errors. - fatal("%s", errmsg); - } - if (!conf->disable) { - should_create_initial_config = true; - } - } - MTR_END("config", "conf_read_primary"); - - MTR_BEGIN("config", "conf_update_from_environment"); - if (!conf_update_from_environment(conf, &errmsg)) { - fatal("%s", errmsg); - } - MTR_END("config", "conf_update_from_environment"); - - if (should_create_initial_config) { - create_initial_config_file(primary_config_path); - } - - exitfn_init(); - exitfn_add_nullary(stats_flush); - exitfn_add_nullary(clean_up_pending_tmp_files); - - cc_log("=== CCACHE %s STARTED =========================================", - CCACHE_VERSION); - - if (conf->umask != UINT_MAX) { - umask(conf->umask); - } - - if (tracefile != NULL) { -#ifdef MTR_ENABLED - trace_start(tracefile); - exitfn_add_nullary(trace_stop); -#else - cc_log("Error: tracing is not enabled!"); -#endif - } -} - -// Reset the global state. Used by the test suite. -void -cc_reset(void) -{ - conf_free(conf); conf = NULL; - free(primary_config_path); primary_config_path = NULL; - free(secondary_config_path); secondary_config_path = NULL; - free(current_working_dir); current_working_dir = NULL; - for (size_t i = 0; i < debug_prefix_maps_len; i++) { - free(debug_prefix_maps[i]); - debug_prefix_maps[i] = NULL; - } - free(debug_prefix_maps); debug_prefix_maps = NULL; - debug_prefix_maps_len = 0; - free(profile_path); profile_path = NULL; - profile_use = false; - profile_generate = false; - for (size_t i = 0; i < sanitize_blacklists_len; i++) { - free(sanitize_blacklists[i]); - sanitize_blacklists[i] = NULL; - } - free(sanitize_blacklists); sanitize_blacklists = NULL; - sanitize_blacklists_len = 0; - free(included_pch_file); included_pch_file = NULL; - args_free(orig_args); orig_args = NULL; - args_free(depend_extra_args); depend_extra_args = NULL; - free(input_file); input_file = NULL; - free(output_obj); output_obj = NULL; - free(output_dep); output_dep = NULL; - free(output_cov); output_cov = NULL; - free(output_su); output_su = NULL; - free(output_dia); output_dia = NULL; - free(output_dwo); output_dwo = NULL; - free(cached_obj_hash); cached_obj_hash = NULL; - free(cached_stderr); cached_stderr = NULL; - free(cached_obj); cached_obj = NULL; - free(cached_dep); cached_dep = NULL; - free(cached_cov); cached_cov = NULL; - free(cached_su); cached_su = NULL; - free(cached_dia); cached_dia = NULL; - free(cached_dwo); cached_dwo = NULL; - free(manifest_path); manifest_path = NULL; - time_of_compilation = 0; - for (size_t i = 0; i < ignore_headers_len; i++) { - free(ignore_headers[i]); - ignore_headers[i] = NULL; - } - free(ignore_headers); ignore_headers = NULL; - ignore_headers_len = 0; - if (included_files) { - hashtable_destroy(included_files, 1); included_files = NULL; - } - has_absolute_include_headers = false; - generating_debuginfo = false; - generating_debuginfo_level_3 = false; - generating_dependencies = false; - generating_coverage = false; - generating_stackusage = false; - profile_arcs = false; - i_tmpfile = NULL; - direct_i_file = false; - free(cpp_stderr); cpp_stderr = NULL; - free(stats_file); stats_file = NULL; - output_is_precompiled_header = false; - - conf = conf_create(); - using_split_dwarf = false; -} - -// Make a copy of stderr that will not be cached, so things like distcc can -// send networking errors to it. -static void -set_up_uncached_err(void) -{ - int uncached_fd = dup(2); // The file descriptor is intentionally leaked. - if (uncached_fd == -1) { - cc_log("dup(2) failed: %s", strerror(errno)); - stats_update(STATS_ERROR); - failed(); - } - - // Leak a pointer to the environment. - char *buf = format("UNCACHED_ERR_FD=%d", uncached_fd); - if (putenv(buf) == -1) { - cc_log("putenv failed: %s", strerror(errno)); - stats_update(STATS_ERROR); - failed(); - } -} - -static void -configuration_logger(const char *descr, const char *origin, void *context) -{ - (void)context; - cc_bulklog("Config: (%s) %s", origin, descr); -} - -static void ccache(int argc, char *argv[]) ATTR_NORETURN; - -// The main ccache driver function. -static void -ccache(int argc, char *argv[]) -{ -#ifndef _WIN32 - set_up_signal_handlers(); -#endif - - // Needed for portability when using localtime_r. - tzset(); - - orig_args = args_init(argc, argv); - - initialize(); - MTR_BEGIN("main", "find_compiler"); - find_compiler(argv); - MTR_END("main", "find_compiler"); - - MTR_BEGIN("main", "clean_up_internal_tempdir"); - if (str_eq(conf->temporary_dir, "")) { - clean_up_internal_tempdir(); - } - MTR_END("main", "clean_up_internal_tempdir"); - - if (!str_eq(conf->log_file, "") || conf->debug) { - conf_print_items(conf, configuration_logger, NULL); - } - - if (conf->disable) { - cc_log("ccache is disabled"); - stats_update(STATS_CACHEMISS); // Dummy to trigger stats_flush. - failed(); - } - - MTR_BEGIN("main", "set_up_uncached_err"); - set_up_uncached_err(); - MTR_END("main", "set_up_uncached_err"); - - cc_log_argv("Command line: ", argv); - cc_log("Hostname: %s", get_hostname()); - cc_log("Working directory: %s", get_current_working_dir()); - - conf->limit_multiple = MIN(MAX(conf->limit_multiple, 0.0), 1.0); - - MTR_BEGIN("main", "guess_compiler"); - guessed_compiler = guess_compiler(orig_args->argv[0]); - MTR_END("main", "guess_compiler"); - - // Arguments (except -E) to send to the preprocessor. - struct args *preprocessor_args; - // Arguments not sent to the preprocessor but that should be part of the - // hash. - struct args *extra_args_to_hash; - // Arguments to send to the real compiler. - struct args *compiler_args; - MTR_BEGIN("main", "process_args"); - if (!cc_process_args( - orig_args, &preprocessor_args, &extra_args_to_hash, &compiler_args)) { - failed(); // stats_update is called in cc_process_args. - } - MTR_END("main", "process_args"); - - if (conf->depend_mode - && (!generating_dependencies || str_eq(output_dep, "/dev/null") - || !conf->run_second_cpp)) { - cc_log("Disabling depend mode"); - conf->depend_mode = false; - } - - cc_log("Source file: %s", input_file); - if (generating_dependencies) { - cc_log("Dependency file: %s", output_dep); - } - if (generating_coverage) { - cc_log("Coverage file: %s", output_cov); - } - if (generating_stackusage) { - cc_log("Stack usage file: %s", output_su); - } - if (generating_diagnostics) { - cc_log("Diagnostics file: %s", output_dia); - } - if (output_dwo) { - cc_log("Split dwarf file: %s", output_dwo); - } - - cc_log("Object file: %s", output_obj); - MTR_META_THREAD_NAME(output_obj); - - // Need to dump log buffer as the last exit function to not lose any logs. - exitfn_add_last(dump_debug_log_buffer_exitfn, output_obj); - - FILE *debug_text_file = NULL; - if (conf->debug) { - char *path = format("%s.ccache-input-text", output_obj); - debug_text_file = fopen(path, "w"); - if (debug_text_file) { - exitfn_add(fclose_exitfn, debug_text_file); - } else { - cc_log("Failed to open %s: %s", path, strerror(errno)); - } - free(path); - } - - struct hash *common_hash = hash_init(); - init_hash_debug(common_hash, output_obj, 'c', "COMMON", debug_text_file); - - MTR_BEGIN("hash", "common_hash"); - calculate_common_hash(preprocessor_args, common_hash); - MTR_END("hash", "common_hash"); - - // Try to find the hash using the manifest. - struct hash *direct_hash = hash_copy(common_hash); - init_hash_debug( - direct_hash, output_obj, 'd', "DIRECT MODE", debug_text_file); - - struct args *args_to_hash = args_copy(preprocessor_args); - args_extend(args_to_hash, extra_args_to_hash); - - bool put_object_in_manifest = false; - struct file_hash *object_hash = NULL; - struct file_hash *object_hash_from_manifest = NULL; - if (conf->direct_mode) { - cc_log("Trying direct lookup"); - MTR_BEGIN("hash", "direct_hash"); - object_hash = calculate_object_hash(args_to_hash, NULL, direct_hash, 1); - MTR_END("hash", "direct_hash"); - if (object_hash) { - update_cached_result_globals(object_hash); - - // If we can return from cache at this point then do so. - from_cache(FROMCACHE_DIRECT_MODE, 0); - - // Wasn't able to return from cache at this point. However, the object - // was already found in manifest, so don't re-add it later. - put_object_in_manifest = false; - - object_hash_from_manifest = object_hash; - } else { - // Add object to manifest later. - put_object_in_manifest = true; - } - } - - if (conf->read_only_direct) { - cc_log("Read-only direct mode; running real compiler"); - stats_update(STATS_CACHEMISS); - failed(); - } - - if (!conf->depend_mode) { - // Find the hash using the preprocessed output. Also updates - // included_files. - struct hash *cpp_hash = hash_copy(common_hash); - init_hash_debug( - cpp_hash, output_obj, 'p', "PREPROCESSOR MODE", debug_text_file); - - MTR_BEGIN("hash", "cpp_hash"); - object_hash = calculate_object_hash( - args_to_hash, preprocessor_args, cpp_hash, 0); - MTR_END("hash", "cpp_hash"); - if (!object_hash) { - fatal("internal error: object hash from cpp returned NULL"); - } - update_cached_result_globals(object_hash); - - if (object_hash_from_manifest - && !file_hashes_equal(object_hash_from_manifest, object_hash)) { - // The hash from manifest differs from the hash of the preprocessor - // output. This could be because: - // - // - The preprocessor produces different output for the same input (not - // likely). - // - There's a bug in ccache (maybe incorrect handling of compiler - // arguments). - // - The user has used a different CCACHE_BASEDIR (most likely). - // - // The best thing here would probably be to remove the hash entry from - // the manifest. For now, we use a simpler method: just remove the - // manifest file. - cc_log("Hash from manifest doesn't match preprocessor output"); - cc_log("Likely reason: different CCACHE_BASEDIRs used"); - cc_log("Removing manifest as a safety measure"); - x_unlink(manifest_path); - - put_object_in_manifest = true; - } - - // If we can return from cache at this point then do. - from_cache(FROMCACHE_CPP_MODE, put_object_in_manifest); - } - - if (conf->read_only) { - cc_log("Read-only mode; running real compiler"); - stats_update(STATS_CACHEMISS); - failed(); - } - - add_prefix(compiler_args, conf->prefix_command); - - // In depend_mode, extend the direct hash. - struct hash *depend_mode_hash = conf->depend_mode ? direct_hash : NULL; - - // Run real compiler, sending output to cache. - MTR_BEGIN("cache", "to_cache"); - to_cache(compiler_args, depend_mode_hash); - MTR_END("cache", "to_cache"); - - x_exit(0); -} - -static void -configuration_printer(const char *descr, const char *origin, void *context) -{ - assert(context); - fprintf(context, "(%s) %s\n", origin, descr); -} - -// The main program when not doing a compile. -static int -ccache_main_options(int argc, char *argv[]) -{ - enum longopts { - DUMP_MANIFEST, - HASH_FILE, - PRINT_STATS, - }; - static const struct option options[] = { - {"cleanup", no_argument, 0, 'c'}, - {"clear", no_argument, 0, 'C'}, - {"dump-manifest", required_argument, 0, DUMP_MANIFEST}, - {"get-config", required_argument, 0, 'k'}, - {"hash-file", required_argument, 0, HASH_FILE}, - {"help", no_argument, 0, 'h'}, - {"max-files", required_argument, 0, 'F'}, - {"max-size", required_argument, 0, 'M'}, - {"print-stats", no_argument, 0, PRINT_STATS}, - {"set-config", required_argument, 0, 'o'}, - {"show-config", no_argument, 0, 'p'}, - {"show-stats", no_argument, 0, 's'}, - {"version", no_argument, 0, 'V'}, - {"zero-stats", no_argument, 0, 'z'}, - {0, 0, 0, 0} - }; - - int c; - while ((c = getopt_long(argc, argv, "cCk:hF:M:po:sVz", options, NULL)) - != -1) { - switch (c) { - case DUMP_MANIFEST: - initialize(); - manifest_dump(optarg, stdout); - break; - - case HASH_FILE: - { - initialize(); - struct hash *hash = hash_init(); - if (str_eq(optarg, "-")) { - hash_fd(hash, STDIN_FILENO); - } else { - hash_file(hash, optarg); - } - char *result = hash_result(hash); - puts(result); - free(result); - hash_free(hash); - break; - } - - case PRINT_STATS: - initialize(); - stats_print(); - break; - - case 'c': // --cleanup - initialize(); - clean_up_all(conf); - printf("Cleaned cache\n"); - break; - - case 'C': // --clear - initialize(); - wipe_all(conf); - printf("Cleared cache\n"); - break; - - case 'h': // --help - fputs(USAGE_TEXT, stdout); - x_exit(0); - - case 'k': // --get-config - { - initialize(); - char *errmsg; - if (!conf_print_value(conf, optarg, stdout, &errmsg)) { - fatal("%s", errmsg); - } - } - break; - - case 'F': // --max-files - { - initialize(); - char *errmsg; - if (conf_set_value_in_file(primary_config_path, "max_files", optarg, - &errmsg)) { - unsigned files = atoi(optarg); - if (files == 0) { - printf("Unset cache file limit\n"); - } else { - printf("Set cache file limit to %u\n", files); - } - } else { - fatal("could not set cache file limit: %s", errmsg); - } - } - break; - - case 'M': // --max-size - { - initialize(); - uint64_t size; - if (!parse_size_with_suffix(optarg, &size)) { - fatal("invalid size: %s", optarg); - } - char *errmsg; - if (conf_set_value_in_file(primary_config_path, "max_size", optarg, - &errmsg)) { - if (size == 0) { - printf("Unset cache size limit\n"); - } else { - char *s = format_human_readable_size(size); - printf("Set cache size limit to %s\n", s); - free(s); - } - } else { - fatal("could not set cache size limit: %s", errmsg); - } - } - break; - - case 'o': // --set-config - { - initialize(); - char *p = strchr(optarg, '='); - if (!p) { - fatal("missing equal sign in \"%s\"", optarg); - } - char *key = x_strndup(optarg, p - optarg); - char *value = p + 1; - char *errmsg; - if (!conf_set_value_in_file(primary_config_path, key, value, &errmsg)) { - fatal("%s", errmsg); - } - free(key); - } - break; - - case 'p': // --show-config - initialize(); - conf_print_items(conf, configuration_printer, stdout); - break; - - case 's': // --show-stats - initialize(); - stats_summary(); - break; - - case 'V': // --version - fprintf(stdout, VERSION_TEXT, CCACHE_VERSION); - x_exit(0); - - case 'z': // --zero-stats - initialize(); - stats_zero(); - printf("Statistics zeroed\n"); - break; - - default: - fputs(USAGE_TEXT, stderr); - x_exit(1); - } - } - - return 0; -} - -int ccache_main(int argc, char *argv[]); - -int -ccache_main(int argc, char *argv[]) -{ - // Check if we are being invoked as "ccache". - char *program_name = basename(argv[0]); - if (same_executable_name(program_name, MYNAME)) { - if (argc < 2) { - fputs(USAGE_TEXT, stderr); - x_exit(1); - } - // If the first argument isn't an option, then assume we are being passed a - // compiler name and options. - if (argv[1][0] == '-') { - return ccache_main_options(argc, argv); - } - } - free(program_name); - - ccache(argc, argv); -} diff --git a/src/ccache.cpp b/src/ccache.cpp new file mode 100644 index 0000000..676f9c0 --- /dev/null +++ b/src/ccache.cpp @@ -0,0 +1,2772 @@ +// Copyright (C) 2002-2007 Andrew Tridgell +// Copyright (C) 2009-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "ccache.hpp" + +#include "Args.hpp" +#include "ArgsInfo.hpp" +#include "Checksum.hpp" +#include "Compression.hpp" +#include "Context.hpp" +#include "Fd.hpp" +#include "File.hpp" +#include "Finalizer.hpp" +#include "FormatNonstdStringView.hpp" +#include "Hash.hpp" +#include "Lockfile.hpp" +#include "Logging.hpp" +#include "Manifest.hpp" +#include "MiniTrace.hpp" +#include "ProgressBar.hpp" +#include "Result.hpp" +#include "ResultDumper.hpp" +#include "ResultExtractor.hpp" +#include "ResultRetriever.hpp" +#include "SignalHandler.hpp" +#include "StdMakeUnique.hpp" +#include "TemporaryFile.hpp" +#include "UmaskScope.hpp" +#include "Util.hpp" +#include "argprocessing.hpp" +#include "cleanup.hpp" +#include "compopt.hpp" +#include "compress.hpp" +#include "exceptions.hpp" +#include "execute.hpp" +#include "hashutil.hpp" +#include "language.hpp" + +#include "third_party/fmt/core.h" +#include "third_party/nonstd/optional.hpp" +#include "third_party/nonstd/string_view.hpp" + +#ifdef HAVE_GETOPT_LONG +# include +#elif defined(_WIN32) +# include "third_party/win32/getopt.h" +#else +# include "third_party/getopt_long.h" +#endif + +#ifdef _WIN32 +# include "Win32Util.hpp" +#endif + +#include +#include +#include + +#ifndef MYNAME +# define MYNAME "ccache" +#endif +const char CCACHE_NAME[] = MYNAME; + +using Logging::log; +using nonstd::nullopt; +using nonstd::optional; +using nonstd::string_view; + +const char VERSION_TEXT[] = + R"({} version {} + +Copyright (C) 2002-2007 Andrew Tridgell +Copyright (C) 2009-2020 Joel Rosdahl and other contributors + +See for a complete list of contributors. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. +)"; + +const char USAGE_TEXT[] = + R"(Usage: + {} [options] + {} compiler [compiler options] + compiler [compiler options] (via symbolic link) + +Common options: + -c, --cleanup delete old files and recalculate size counters + (normally not needed as this is done + automatically) + -C, --clear clear the cache completely (except configuration) + -d, --directory PATH operate on cache directory PATH instead of the + default + --evict-older-than AGE remove files older than AGE (unsigned integer + with a d (days) or s (seconds) suffix) + -F, --max-files NUM set maximum number of files in cache to NUM (use + 0 for no limit) + -M, --max-size SIZE set maximum size of cache to SIZE (use 0 for no + limit); available suffixes: k, M, G, T (decimal) + and Ki, Mi, Gi, Ti (binary); default suffix: G + -X, --recompress LEVEL recompress the cache to LEVEL (integer level or + "uncompressed") + -o, --set-config KEY=VAL set configuration item KEY to value VAL + -x, --show-compression show compression statistics + -p, --show-config show current configuration options in + human-readable format + -s, --show-stats show summary of configuration and statistics + counters in human-readable format + -z, --zero-stats zero statistics counters + + -h, --help print this help text + -V, --version print version and copyright information + +Options for scripting or debugging: + --checksum-file PATH print the checksum (64 bit XXH3) of the file at + PATH + --dump-manifest PATH dump manifest file at PATH in text format + --dump-result PATH dump result file at PATH in text format + --extract-result PATH extract data stored in result file at PATH to the + current working directory + -k, --get-config KEY print the value of configuration key KEY + --hash-file PATH print the hash (160 bit BLAKE3) of the file at + PATH + --print-stats print statistics counter IDs and corresponding + values in machine-parsable format + +See also . +)"; + +// How often (in seconds) to scan $CCACHE_DIR/tmp for left-over temporary +// files. +const int k_tempdir_cleanup_interval = 2 * 24 * 60 * 60; // 2 days + +// Maximum files per cache directory. This constant is somewhat arbitrarily +// chosen to be large enough to avoid unnecessary cache levels but small enough +// not to make esoteric file systems (with bad performance for large +// directories) too slow. It could be made configurable, but hopefully there +// will be no need to do that. +const uint64_t k_max_cache_files_per_directory = 2000; + +// Minimum number of cache levels ($CCACHE_DIR/1/2/stored_file). +const uint8_t k_min_cache_levels = 2; + +// Maximum number of cache levels ($CCACHE_DIR/1/2/3/stored_file). +// +// On a cache miss, (k_max_cache_levels - k_min_cache_levels + 1) cache lookups +// (i.e. stat system calls) will be performed for a cache entry. +// +// An assumption made here is that if a cache is so large that it holds more +// than 16^4 * k_max_cache_files_per_directory files then we can assume that the +// file system is sane enough to handle more than +// k_max_cache_files_per_directory. +const uint8_t k_max_cache_levels = 4; + +// This is a string that identifies the current "version" of the hash sum +// computed by ccache. If, for any reason, we want to force the hash sum to be +// different for the same input in a new ccache version, we can just change +// this string. A typical example would be if the format of one of the files +// stored in the cache changes in a backwards-incompatible way. +const char HASH_PREFIX[] = "3"; + +static void +add_prefix(const Context& ctx, Args& args, const std::string& prefix_command) +{ + if (prefix_command.empty()) { + return; + } + + Args prefix; + for (const auto& word : Util::split_into_strings(prefix_command, " ")) { + std::string path = find_executable(ctx, word, CCACHE_NAME); + if (path.empty()) { + throw Fatal("{}: {}", word, strerror(errno)); + } + + prefix.push_back(path); + } + + log("Using command-line prefix {}", prefix_command); + for (size_t i = prefix.size(); i != 0; i--) { + args.push_front(prefix[i - 1]); + } +} + +static void +clean_up_internal_tempdir(const Config& config) +{ + time_t now = time(nullptr); + auto dir_st = Stat::stat(config.cache_dir(), Stat::OnError::log); + if (!dir_st || dir_st.mtime() + k_tempdir_cleanup_interval >= now) { + // No cleanup needed. + return; + } + + Util::update_mtime(config.cache_dir()); + + const std::string& temp_dir = config.temporary_dir(); + if (!Stat::lstat(temp_dir)) { + return; + } + + Util::traverse(temp_dir, [now](const std::string& path, bool is_dir) { + if (is_dir) { + return; + } + auto st = Stat::lstat(path, Stat::OnError::log); + if (st && st.mtime() + k_tempdir_cleanup_interval < now) { + Util::unlink_tmp(path); + } + }); +} + +static void +init_hash_debug(Context& ctx, + Hash& hash, + string_view obj_path, + char type, + string_view section_name, + FILE* debug_text_file) +{ + if (!ctx.config.debug()) { + return; + } + + std::string path = fmt::format("{}.ccache-input-{}", obj_path, type); + File debug_binary_file(path, "wb"); + if (debug_binary_file) { + hash.enable_debug(section_name, debug_binary_file.get(), debug_text_file); + ctx.hash_debug_files.push_back(std::move(debug_binary_file)); + } else { + log("Failed to open {}: {}", path, strerror(errno)); + } +} + +static GuessedCompiler +guess_compiler(string_view path) +{ + string_view name = Util::base_name(path); + GuessedCompiler result = GuessedCompiler::unknown; + if (name.find("clang") != std::string::npos) { + result = GuessedCompiler::clang; + } else if (name.find("gcc") != std::string::npos + || name.find("g++") != std::string::npos) { + result = GuessedCompiler::gcc; + } else if (name.find("nvcc") != std::string::npos) { + result = GuessedCompiler::nvcc; + } else if (name == "pump" || name == "distcc-pump") { + result = GuessedCompiler::pump; + } + return result; +} + +static bool +do_remember_include_file(Context& ctx, + std::string path, + Hash& cpp_hash, + bool system, + Hash* depend_mode_hash) +{ + bool is_pch = false; + + if (path.length() >= 2 && path[0] == '<' && path[path.length() - 1] == '>') { + // Typically or . + return true; + } + + if (path == ctx.args_info.input_file) { + // Don't remember the input file. + return true; + } + + if (system && (ctx.config.sloppiness() & SLOPPY_SYSTEM_HEADERS)) { + // Don't remember this system header. + return true; + } + + if (ctx.included_files.find(path) != ctx.included_files.end()) { + // Already known include file. + return true; + } + + // Canonicalize path for comparison; Clang uses ./header.h. + if (Util::starts_with(path, "./")) { + path.erase(0, 2); + } + +#ifdef _WIN32 + { + // stat fails on directories on win32. + DWORD attributes = GetFileAttributes(path.c_str()); + if (attributes != INVALID_FILE_ATTRIBUTES + && attributes & FILE_ATTRIBUTE_DIRECTORY) { + return true; + } + } +#endif + + auto st = Stat::stat(path, Stat::OnError::log); + if (!st) { + return false; + } + if (st.is_directory()) { + // Ignore directory, typically $PWD. + return true; + } + if (!st.is_regular()) { + // Device, pipe, socket or other strange creature. + log("Non-regular include file {}", path); + return false; + } + + for (const auto& ignore_header_path : ctx.ignore_header_paths) { + if (Util::matches_dir_prefix_or_file(ignore_header_path, path)) { + return true; + } + } + + // The comparison using >= is intentional, due to a possible race between + // starting compilation and writing the include file. See also the notes + // under "Performance" in doc/MANUAL.adoc. + if (!(ctx.config.sloppiness() & SLOPPY_INCLUDE_FILE_MTIME) + && st.mtime() >= ctx.time_of_compilation) { + log("Include file {} too new", path); + return false; + } + + // The same >= logic as above applies to the change time of the file. + if (!(ctx.config.sloppiness() & SLOPPY_INCLUDE_FILE_CTIME) + && st.ctime() >= ctx.time_of_compilation) { + log("Include file {} ctime too new", path); + return false; + } + + // Let's hash the include file content. + Hash fhash; + + is_pch = Util::is_precompiled_header(path); + if (is_pch) { + if (ctx.included_pch_file.empty()) { + log("Detected use of precompiled header: {}", path); + } + bool using_pch_sum = false; + if (ctx.config.pch_external_checksum()) { + // hash pch.sum instead of pch when it exists + // to prevent hashing a very large .pch file every time + std::string pch_sum_path = fmt::format("{}.sum", path); + if (Stat::stat(pch_sum_path, Stat::OnError::log)) { + path = std::move(pch_sum_path); + using_pch_sum = true; + log("Using pch.sum file {}", path); + } + } + + if (!hash_binary_file(ctx, fhash, path)) { + return false; + } + cpp_hash.hash_delimiter(using_pch_sum ? "pch_sum_hash" : "pch_hash"); + cpp_hash.hash(fhash.digest().to_string()); + } + + if (ctx.config.direct_mode()) { + if (!is_pch) { // else: the file has already been hashed. + int result = hash_source_code_file(ctx, fhash, path); + if (result & HASH_SOURCE_CODE_ERROR + || result & HASH_SOURCE_CODE_FOUND_TIME) { + return false; + } + } + + Digest d = fhash.digest(); + ctx.included_files.emplace(path, d); + + if (depend_mode_hash) { + depend_mode_hash->hash_delimiter("include"); + depend_mode_hash->hash(d.to_string()); + } + } + + return true; +} + +// This function hashes an include file and stores the path and hash in +// ctx.included_files. If the include file is a PCH, cpp_hash is also updated. +static void +remember_include_file(Context& ctx, + const std::string& path, + Hash& cpp_hash, + bool system, + Hash* depend_mode_hash) +{ + if (!do_remember_include_file(ctx, path, cpp_hash, system, depend_mode_hash) + && ctx.config.direct_mode()) { + log("Disabling direct mode"); + ctx.config.set_direct_mode(false); + } +} + +static void +print_included_files(const Context& ctx, FILE* fp) +{ + for (const auto& item : ctx.included_files) { + fmt::print(fp, "{}\n", item.first); + } +} + +// This function reads and hashes a file. While doing this, it also does these +// things: +// +// - Makes include file paths for which the base directory is a prefix relative +// when computing the hash sum. +// - Stores the paths and hashes of included files in ctx.included_files. +static bool +process_preprocessed_file(Context& ctx, + Hash& hash, + const std::string& path, + bool pump) +{ + std::string data; + try { + data = Util::read_file(path); + } catch (Error&) { + return false; + } + + // Bytes between p and q are pending to be hashed. + const char* p = &data[0]; + char* q = &data[0]; + const char* end = p + data.length(); + + // There must be at least 7 characters (# 1 "x") left to potentially find an + // include file path. + while (q < end - 7) { + static const string_view pragma_gcc_pch_preprocess = + "pragma GCC pch_preprocess "; + static const string_view hash_31_command_line_newline = + "# 31 \"\"\n"; + static const string_view hash_32_command_line_2_newline = + "# 32 \"\" 2\n"; + + // Check if we look at a line containing the file name of an included file. + // At least the following formats exist (where N is a positive integer): + // + // GCC: + // + // # N "file" + // # N "file" N + // #pragma GCC pch_preprocess "file" + // + // HP's compiler: + // + // #line N "file" + // + // AIX's compiler: + // + // #line N "file" + // #line N + // + // Note that there may be other lines starting with '#' left after + // preprocessing as well, for instance "# pragma". + if (q[0] == '#' + // GCC: + && ((q[1] == ' ' && q[2] >= '0' && q[2] <= '9') + // GCC precompiled header: + || Util::starts_with(&q[1], pragma_gcc_pch_preprocess) + // HP/AIX: + || (q[1] == 'l' && q[2] == 'i' && q[3] == 'n' && q[4] == 'e' + && q[5] == ' ')) + && (q == data.data() || q[-1] == '\n')) { + // Workarounds for preprocessor linemarker bugs in GCC version 6. + if (q[2] == '3') { + if (Util::starts_with(q, hash_31_command_line_newline)) { + // Bogus extra line with #31, after the regular #1: Ignore the whole + // line, and continue parsing. + hash.hash(p, q - p); + while (q < end && *q != '\n') { + q++; + } + q++; + p = q; + continue; + } else if (Util::starts_with(q, hash_32_command_line_2_newline)) { + // Bogus wrong line with #32, instead of regular #1: Replace the line + // number with the usual one. + hash.hash(p, q - p); + q += 1; + q[0] = '#'; + q[1] = ' '; + q[2] = '1'; + p = q; + } + } + + while (q < end && *q != '"' && *q != '\n') { + q++; + } + if (q < end && *q == '\n') { + // A newline before the quotation mark -> no match. + continue; + } + q++; + if (q >= end) { + log("Failed to parse included file path"); + return false; + } + // q points to the beginning of an include file path + hash.hash(p, q - p); + p = q; + while (q < end && *q != '"') { + q++; + } + // Look for preprocessor flags, after the "filename". + bool system = false; + const char* r = q + 1; + while (r < end && *r != '\n') { + if (*r == '3') { // System header. + system = true; + } + r++; + } + // p and q span the include file path. + std::string inc_path(p, q - p); + if (!ctx.has_absolute_include_headers) { + ctx.has_absolute_include_headers = Util::is_absolute_path(inc_path); + } + inc_path = Util::make_relative_path(ctx, inc_path); + + bool should_hash_inc_path = true; + if (!ctx.config.hash_dir()) { + if (Util::starts_with(inc_path, ctx.apparent_cwd) + && Util::ends_with(inc_path, "//")) { + // When compiling with -g or similar, GCC adds the absolute path to + // CWD like this: + // + // # 1 "CWD//" + // + // If the user has opted out of including the CWD in the hash, don't + // hash it. See also how debug_prefix_map is handled. + should_hash_inc_path = false; + } + } + if (should_hash_inc_path) { + hash.hash(inc_path); + } + + remember_include_file(ctx, inc_path, hash, system, nullptr); + p = q; // Everything of interest between p and q has been hashed now. + } else if (q[0] == '.' && q[1] == 'i' && q[2] == 'n' && q[3] == 'c' + && q[4] == 'b' && q[5] == 'i' && q[6] == 'n') { + // An assembler .inc bin (without the space) statement, which could be + // part of inline assembly, refers to an external file. If the file + // changes, the hash should change as well, but finding out what file to + // hash is too hard for ccache, so just bail out. + log( + "Found unsupported .inc" + "bin directive in source code"); + throw Failure(Statistic::unsupported_code_directive); + } else if (pump && strncmp(q, "_________", 9) == 0) { + // Unfortunately the distcc-pump wrapper outputs standard output lines: + // __________Using distcc-pump from /usr/bin + // __________Using # distcc servers in pump mode + // __________Shutting down distcc-pump include server + while (q < end && *q != '\n') { + q++; + } + if (*q == '\n') { + q++; + } + p = q; + continue; + } else { + q++; + } + } + + hash.hash(p, (end - p)); + + // Explicitly check the .gch/.pch/.pth file as Clang does not include any + // mention of it in the preprocessed output. + if (!ctx.included_pch_file.empty()) { + std::string pch_path = Util::make_relative_path(ctx, ctx.included_pch_file); + hash.hash(pch_path); + remember_include_file(ctx, pch_path, hash, false, nullptr); + } + + bool debug_included = getenv("CCACHE_DEBUG_INCLUDED"); + if (debug_included) { + print_included_files(ctx, stdout); + } + + return true; +} + +nonstd::optional +rewrite_dep_file_paths(const Context& ctx, const std::string& file_content) +{ + ASSERT(!ctx.config.base_dir().empty()); + ASSERT(ctx.has_absolute_include_headers); + + // Fast path for the common case: + if (file_content.find(ctx.config.base_dir()) == std::string::npos) { + return nonstd::nullopt; + } + + std::string adjusted_file_content; + adjusted_file_content.reserve(file_content.size()); + + bool content_rewritten = false; + for (const auto& line : Util::split_into_views(file_content, "\n")) { + const auto tokens = Util::split_into_views(line, " \t"); + for (size_t i = 0; i < tokens.size(); ++i) { + DEBUG_ASSERT(line.length() > 0); // line.empty() -> no tokens + if (i > 0 || line[0] == ' ' || line[0] == '\t') { + adjusted_file_content.push_back(' '); + } + + const auto& token = tokens[i]; + bool token_rewritten = false; + if (Util::is_absolute_path(token)) { + const auto new_path = Util::make_relative_path(ctx, token); + if (new_path != token) { + adjusted_file_content.append(new_path); + token_rewritten = true; + } + } + if (token_rewritten) { + content_rewritten = true; + } else { + adjusted_file_content.append(token.begin(), token.end()); + } + } + adjusted_file_content.push_back('\n'); + } + + if (content_rewritten) { + return adjusted_file_content; + } else { + return nonstd::nullopt; + } +} + +// Replace absolute paths with relative paths in the provided dependency file. +static void +use_relative_paths_in_depfile(const Context& ctx) +{ + if (ctx.config.base_dir().empty()) { + log("Base dir not set, skip using relative paths"); + return; // nothing to do + } + if (!ctx.has_absolute_include_headers) { + log("No absolute path for included files found, skip using relative paths"); + return; // nothing to do + } + + const std::string& output_dep = ctx.args_info.output_dep; + std::string file_content; + try { + file_content = Util::read_file(output_dep); + } catch (const Error& e) { + log("Cannot open dependency file {}: {}", output_dep, e.what()); + return; + } + const auto new_content = rewrite_dep_file_paths(ctx, file_content); + if (new_content) { + Util::write_file(output_dep, *new_content); + } else { + log("No paths in dependency file {} made relative", output_dep); + } +} + +// Extract the used includes from the dependency file. Note that we cannot +// distinguish system headers from other includes here. +static optional +result_name_from_depfile(Context& ctx, Hash& hash) +{ + std::string file_content; + try { + file_content = Util::read_file(ctx.args_info.output_dep); + } catch (const Error& e) { + log( + "Cannot open dependency file {}: {}", ctx.args_info.output_dep, e.what()); + return nullopt; + } + + for (string_view token : Util::split_into_views(file_content, " \t\r\n")) { + if (token == "\\" || token.ends_with(":")) { + continue; + } + if (!ctx.has_absolute_include_headers) { + ctx.has_absolute_include_headers = Util::is_absolute_path(token); + } + std::string path = Util::make_relative_path(ctx, token); + remember_include_file(ctx, path, hash, false, &hash); + } + + // Explicitly check the .gch/.pch/.pth file as it may not be mentioned in the + // dependencies output. + if (!ctx.included_pch_file.empty()) { + std::string pch_path = Util::make_relative_path(ctx, ctx.included_pch_file); + hash.hash(pch_path); + remember_include_file(ctx, pch_path, hash, false, nullptr); + } + + bool debug_included = getenv("CCACHE_DEBUG_INCLUDED"); + if (debug_included) { + print_included_files(ctx, stdout); + } + + return hash.digest(); +} + +// Execute the compiler/preprocessor, with logic to retry without requesting +// colored diagnostics messages if that fails. +static int +do_execute(Context& ctx, + Args& args, + TemporaryFile&& tmp_stdout, + TemporaryFile&& tmp_stderr) +{ + UmaskScope umask_scope(ctx.original_umask); + + if (ctx.diagnostics_color_failed + && ctx.guessed_compiler == GuessedCompiler::gcc) { + args.erase_with_prefix("-fdiagnostics-color"); + } + int status = execute(args.to_argv().data(), + std::move(tmp_stdout.fd), + std::move(tmp_stderr.fd), + &ctx.compiler_pid); + if (status != 0 && !ctx.diagnostics_color_failed + && ctx.guessed_compiler == GuessedCompiler::gcc) { + auto errors = Util::read_file(tmp_stderr.path); + if (errors.find("unrecognized command line option") != std::string::npos + && errors.find("-fdiagnostics-color") != std::string::npos) { + // Old versions of GCC do not support colored diagnostics. + log("-fdiagnostics-color is unsupported; trying again without it"); + + tmp_stdout.fd = Fd(open( + tmp_stdout.path.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0600)); + if (!tmp_stdout.fd) { + log("Failed to truncate {}: {}", tmp_stdout.path, strerror(errno)); + throw Failure(Statistic::internal_error); + } + + tmp_stderr.fd = Fd(open( + tmp_stderr.path.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0600)); + if (!tmp_stderr.fd) { + log("Failed to truncate {}: {}", tmp_stderr.path, strerror(errno)); + throw Failure(Statistic::internal_error); + } + + ctx.diagnostics_color_failed = true; + return do_execute( + ctx, args, std::move(tmp_stdout), std::move(tmp_stderr)); + } + } + return status; +} + +struct LookUpCacheFileResult +{ + std::string path; + Stat stat; + uint8_t level; +}; + +static LookUpCacheFileResult +look_up_cache_file(const std::string& cache_dir, + const Digest& name, + nonstd::string_view suffix) +{ + const auto name_string = fmt::format("{}{}", name.to_string(), suffix); + + for (uint8_t level = k_min_cache_levels; level <= k_max_cache_levels; + ++level) { + const auto path = Util::get_path_in_cache(cache_dir, level, name_string); + const auto stat = Stat::stat(path); + if (stat) { + return {path, stat, level}; + } + } + + const auto shallowest_path = + Util::get_path_in_cache(cache_dir, k_min_cache_levels, name_string); + return {shallowest_path, Stat(), k_min_cache_levels}; +} + +// Create or update the manifest file. +static void +update_manifest_file(Context& ctx) +{ + if (!ctx.config.direct_mode() || ctx.config.read_only() + || ctx.config.read_only_direct()) { + return; + } + + ASSERT(ctx.manifest_path()); + ASSERT(ctx.result_path()); + + MTR_BEGIN("manifest", "manifest_put"); + + const auto old_stat = Stat::stat(*ctx.manifest_path()); + + // See comment in get_file_hash_index for why saving of timestamps is forced + // for precompiled headers. + const bool save_timestamp = + (ctx.config.sloppiness() & SLOPPY_FILE_STAT_MATCHES) + || ctx.args_info.output_is_precompiled_header; + + log("Adding result name to {}", *ctx.manifest_path()); + if (!Manifest::put(ctx.config, + *ctx.manifest_path(), + *ctx.result_name(), + ctx.included_files, + ctx.time_of_compilation, + save_timestamp)) { + log("Failed to add result name to {}", *ctx.manifest_path()); + } else { + const auto new_stat = Stat::stat(*ctx.manifest_path(), Stat::OnError::log); + ctx.manifest_counter_updates.increment( + Statistic::cache_size_kibibyte, + Util::size_change_kibibyte(old_stat, new_stat)); + ctx.manifest_counter_updates.increment(Statistic::files_in_cache, + !old_stat && new_stat ? 1 : 0); + } + MTR_END("manifest", "manifest_put"); +} + +static void +create_cachedir_tag(const Context& ctx) +{ + constexpr char cachedir_tag[] = + "Signature: 8a477f597d28d172789f06886806bc55\n" + "# This file is a cache directory tag created by ccache.\n" + "# For information about cache directory tags, see:\n" + "#\thttp://www.brynosaurus.com/cachedir/\n"; + + const std::string path = fmt::format("{}/{}/CACHEDIR.TAG", + ctx.config.cache_dir(), + ctx.result_name()->to_string()[0]); + const auto stat = Stat::stat(path); + if (stat) { + return; + } + try { + Util::write_file(path, cachedir_tag); + } catch (const Error& e) { + log("Failed to create {}: {}", path, e.what()); + } +} + +struct FindCoverageFileResult +{ + bool found; + std::string path; + bool mangled; +}; + +static FindCoverageFileResult +find_coverage_file(const Context& ctx) +{ + // GCC 9+ writes coverage data for /dir/to/example.o to #dir#to#example.gcno + // (in CWD) if -fprofile-dir=DIR is present (regardless of DIR) instead of the + // traditional /dir/to/example.gcno. + + std::string mangled_form = Result::gcno_file_in_mangled_form(ctx); + std::string unmangled_form = Result::gcno_file_in_unmangled_form(ctx); + std::string found_file; + if (Stat::stat(mangled_form)) { + log("Found coverage file {}", mangled_form); + found_file = mangled_form; + } + if (Stat::stat(unmangled_form)) { + log("Found coverage file {}", unmangled_form); + if (!found_file.empty()) { + log("Found two coverage files, cannot continue"); + return {}; + } + found_file = unmangled_form; + } + if (found_file.empty()) { + log("No coverage file found (tried {} and {}), cannot continue", + unmangled_form, + mangled_form); + return {}; + } + return {true, found_file, found_file == mangled_form}; +} + +// Run the real compiler and put the result in cache. +static void +to_cache(Context& ctx, + Args& args, + const Args& depend_extra_args, + Hash* depend_mode_hash) +{ + args.push_back("-o"); + args.push_back(ctx.args_info.output_obj); + + if (ctx.config.hard_link() && ctx.args_info.output_obj != "/dev/null") { + // Workaround for Clang bug where it overwrites an existing object file + // when it's compiling an assembler file, see + // . + Util::unlink_safe(ctx.args_info.output_obj); + } + + if (ctx.args_info.generating_diagnostics) { + args.push_back("--serialize-diagnostics"); + args.push_back(ctx.args_info.output_dia); + } + + // Turn off DEPENDENCIES_OUTPUT when running cc1, because otherwise it will + // emit a line like this: + // + // tmp.stdout.vexed.732.o: /home/mbp/.ccache/tmp.stdout.vexed.732.i + Util::unsetenv("DEPENDENCIES_OUTPUT"); + Util::unsetenv("SUNPRO_DEPENDENCIES"); + + if (ctx.config.run_second_cpp()) { + args.push_back(ctx.args_info.input_file); + } else { + args.push_back(ctx.i_tmpfile); + } + + if (ctx.args_info.seen_split_dwarf) { + // Remove any pre-existing .dwo file since we want to check if the compiler + // produced one, intentionally not using x_unlink or tmp_unlink since we're + // not interested in logging successful deletions or failures due to + // non-existent .dwo files. + if (unlink(ctx.args_info.output_dwo.c_str()) != 0 && errno != ENOENT + && errno != ESTALE) { + log("Failed to unlink {}: {}", ctx.args_info.output_dwo, strerror(errno)); + throw Failure(Statistic::bad_output_file); + } + } + + log("Running real compiler"); + MTR_BEGIN("execute", "compiler"); + + TemporaryFile tmp_stdout( + fmt::format("{}/tmp.stdout", ctx.config.temporary_dir())); + ctx.register_pending_tmp_file(tmp_stdout.path); + std::string tmp_stdout_path = tmp_stdout.path; + + TemporaryFile tmp_stderr( + fmt::format("{}/tmp.stderr", ctx.config.temporary_dir())); + ctx.register_pending_tmp_file(tmp_stderr.path); + std::string tmp_stderr_path = tmp_stderr.path; + + int status; + if (!ctx.config.depend_mode()) { + status = + do_execute(ctx, args, std::move(tmp_stdout), std::move(tmp_stderr)); + args.pop_back(3); + } else { + // Use the original arguments (including dependency options) in depend + // mode. + Args depend_mode_args = ctx.orig_args; + depend_mode_args.erase_with_prefix("--ccache-"); + depend_mode_args.push_back(depend_extra_args); + add_prefix(ctx, depend_mode_args, ctx.config.prefix_command()); + + ctx.time_of_compilation = time(nullptr); + status = do_execute( + ctx, depend_mode_args, std::move(tmp_stdout), std::move(tmp_stderr)); + } + MTR_END("execute", "compiler"); + + auto st = Stat::stat(tmp_stdout_path, Stat::OnError::log); + if (!st) { + // The stdout file was removed - cleanup in progress? Better bail out. + throw Failure(Statistic::missing_cache_file); + } + + // distcc-pump outputs lines like this: + // __________Using # distcc servers in pump mode + if (st.size() != 0 && ctx.guessed_compiler != GuessedCompiler::pump) { + log("Compiler produced stdout"); + throw Failure(Statistic::compiler_produced_stdout); + } + + // Merge stderr from the preprocessor (if any) and stderr from the real + // compiler into tmp_stderr. + if (!ctx.cpp_stderr.empty()) { + std::string combined_stderr = + Util::read_file(ctx.cpp_stderr) + Util::read_file(tmp_stderr_path); + Util::write_file(tmp_stderr_path, combined_stderr); + } + + if (status != 0) { + log("Compiler gave exit status {}", status); + + // We can output stderr immediately instead of rerunning the compiler. + Util::send_to_stderr(ctx, Util::read_file(tmp_stderr_path)); + + throw Failure(Statistic::compile_failed, status); + } + + if (ctx.config.depend_mode()) { + ASSERT(depend_mode_hash); + auto result_name = result_name_from_depfile(ctx, *depend_mode_hash); + if (!result_name) { + throw Failure(Statistic::internal_error); + } + ctx.set_result_name(*result_name); + } + + bool produce_dep_file = ctx.args_info.generating_dependencies + && ctx.args_info.output_dep != "/dev/null"; + + if (produce_dep_file) { + use_relative_paths_in_depfile(ctx); + } + + const auto obj_stat = Stat::stat(ctx.args_info.output_obj); + if (!obj_stat) { + log("Compiler didn't produce an object file"); + throw Failure(Statistic::compiler_produced_no_output); + } + if (obj_stat.size() == 0) { + log("Compiler produced an empty object file"); + throw Failure(Statistic::compiler_produced_empty_output); + } + + const auto stderr_stat = Stat::stat(tmp_stderr_path, Stat::OnError::log); + if (!stderr_stat) { + throw Failure(Statistic::internal_error); + } + + const auto result_file = look_up_cache_file( + ctx.config.cache_dir(), *ctx.result_name(), Result::k_file_suffix); + ctx.set_result_path(result_file.path); + Result::Writer result_writer(ctx, result_file.path); + + if (stderr_stat.size() > 0) { + result_writer.write(Result::FileType::stderr_output, tmp_stderr_path); + } + result_writer.write(Result::FileType::object, ctx.args_info.output_obj); + if (ctx.args_info.generating_dependencies) { + result_writer.write(Result::FileType::dependency, ctx.args_info.output_dep); + } + if (ctx.args_info.generating_coverage) { + const auto coverage_file = find_coverage_file(ctx); + if (!coverage_file.found) { + throw Failure(Statistic::internal_error); + } + result_writer.write(coverage_file.mangled + ? Result::FileType::coverage_mangled + : Result::FileType::coverage_unmangled, + coverage_file.path); + } + if (ctx.args_info.generating_stackusage) { + result_writer.write(Result::FileType::stackusage, ctx.args_info.output_su); + } + if (ctx.args_info.generating_diagnostics) { + result_writer.write(Result::FileType::diagnostic, ctx.args_info.output_dia); + } + if (ctx.args_info.seen_split_dwarf && Stat::stat(ctx.args_info.output_dwo)) { + // Only store .dwo file if it was created by the compiler (GCC and Clang + // behave differently e.g. for "-gsplit-dwarf -g1"). + result_writer.write(Result::FileType::dwarf_object, + ctx.args_info.output_dwo); + } + + auto error = result_writer.finalize(); + if (error) { + log("Error: {}", *error); + } else { + log("Stored in cache: {}", result_file.path); + } + + auto new_result_stat = Stat::stat(result_file.path, Stat::OnError::log); + if (!new_result_stat) { + throw Failure(Statistic::internal_error); + } + ctx.counter_updates.increment( + Statistic::cache_size_kibibyte, + Util::size_change_kibibyte(result_file.stat, new_result_stat)); + ctx.counter_updates.increment(Statistic::files_in_cache, + result_file.stat ? 0 : 1); + + MTR_END("file", "file_put"); + + // Make sure we have a CACHEDIR.TAG in the cache part of cache_dir. This can + // be done almost anywhere, but we might as well do it near the end as we save + // the stat call if we exit early. + create_cachedir_tag(ctx); + + // Everything OK. + Util::send_to_stderr(ctx, Util::read_file(tmp_stderr_path)); +} + +// Find the result name by running the compiler in preprocessor mode and +// hashing the result. +static Digest +get_result_name_from_cpp(Context& ctx, Args& args, Hash& hash) +{ + ctx.time_of_compilation = time(nullptr); + + std::string stderr_path; + std::string stdout_path; + int status; + if (ctx.args_info.direct_i_file) { + // We are compiling a .i or .ii file - that means we can skip the cpp stage + // and directly form the correct i_tmpfile. + stdout_path = ctx.args_info.input_file; + status = 0; + } else { + // Run cpp on the input file to obtain the .i. + + TemporaryFile tmp_stdout( + fmt::format("{}/tmp.cpp_stdout", ctx.config.temporary_dir())); + stdout_path = tmp_stdout.path; + ctx.register_pending_tmp_file(stdout_path); + + TemporaryFile tmp_stderr( + fmt::format("{}/tmp.cpp_stderr", ctx.config.temporary_dir())); + stderr_path = tmp_stderr.path; + ctx.register_pending_tmp_file(stderr_path); + + size_t args_added = 2; + args.push_back("-E"); + if (ctx.args_info.actual_language == "hip") { + args.push_back("-o"); + args.push_back("-"); + args_added += 2; + } + if (ctx.config.keep_comments_cpp()) { + args.push_back("-C"); + args_added++; + } + args.push_back(ctx.args_info.input_file); + add_prefix(ctx, args, ctx.config.prefix_command_cpp()); + log("Running preprocessor"); + MTR_BEGIN("execute", "preprocessor"); + status = + do_execute(ctx, args, std::move(tmp_stdout), std::move(tmp_stderr)); + MTR_END("execute", "preprocessor"); + args.pop_back(args_added); + } + + if (status != 0) { + log("Preprocessor gave exit status {}", status); + throw Failure(Statistic::preprocessor_error); + } + + hash.hash_delimiter("cpp"); + bool is_pump = ctx.guessed_compiler == GuessedCompiler::pump; + if (!process_preprocessed_file(ctx, hash, stdout_path, is_pump)) { + throw Failure(Statistic::internal_error); + } + + hash.hash_delimiter("cppstderr"); + if (!ctx.args_info.direct_i_file && !hash.hash_file(stderr_path)) { + // Somebody removed the temporary file? + log("Failed to open {}: {}", stderr_path, strerror(errno)); + throw Failure(Statistic::internal_error); + } + + if (ctx.args_info.direct_i_file) { + ctx.i_tmpfile = ctx.args_info.input_file; + } else { + // i_tmpfile needs the proper cpp_extension for the compiler to do its + // thing correctly + ctx.i_tmpfile = + fmt::format("{}.{}", stdout_path, ctx.config.cpp_extension()); + Util::rename(stdout_path, ctx.i_tmpfile); + ctx.register_pending_tmp_file(ctx.i_tmpfile); + } + + if (!ctx.config.run_second_cpp()) { + // If we are using the CPP trick, we need to remember this stderr data and + // output it just before the main stderr from the compiler pass. + ctx.cpp_stderr = stderr_path; + hash.hash_delimiter("runsecondcpp"); + hash.hash("false"); + } + + return hash.digest(); +} + +// Hash mtime or content of a file, or the output of a command, according to +// the CCACHE_COMPILERCHECK setting. +static void +hash_compiler(const Context& ctx, + Hash& hash, + const Stat& st, + const std::string& path, + bool allow_command) +{ + if (ctx.config.compiler_check() == "none") { + // Do nothing. + } else if (ctx.config.compiler_check() == "mtime") { + hash.hash_delimiter("cc_mtime"); + hash.hash(st.size()); + hash.hash(st.mtime()); + } else if (Util::starts_with(ctx.config.compiler_check(), "string:")) { + hash.hash_delimiter("cc_hash"); + hash.hash(&ctx.config.compiler_check()[7]); + } else if (ctx.config.compiler_check() == "content" || !allow_command) { + hash.hash_delimiter("cc_content"); + hash_binary_file(ctx, hash, path); + } else { // command string + if (!hash_multicommand_output( + hash, ctx.config.compiler_check(), ctx.orig_args[0])) { + log("Failure running compiler check command: {}", + ctx.config.compiler_check()); + throw Failure(Statistic::compiler_check_failed); + } + } +} + +// Hash the host compiler(s) invoked by nvcc. +// +// If `ccbin_st` and `ccbin` are set, they refer to a directory or compiler set +// with -ccbin/--compiler-bindir. If `ccbin_st` is nullptr or `ccbin` is the +// empty string, the compilers are looked up in PATH instead. +static void +hash_nvcc_host_compiler(const Context& ctx, + Hash& hash, + const Stat* ccbin_st = nullptr, + const std::string& ccbin = {}) +{ + // From : + // + // "[...] Specify the directory in which the compiler executable resides. + // The host compiler executable name can be also specified to ensure that + // the correct host compiler is selected." + // + // and + // + // "On all platforms, the default host compiler executable (gcc and g++ on + // Linux, clang and clang++ on Mac OS X, and cl.exe on Windows) found in + // the current execution search path will be used". + + if (ccbin.empty() || !ccbin_st || ccbin_st->is_directory()) { +#if defined(__APPLE__) + const char* compilers[] = {"clang", "clang++"}; +#elif defined(_WIN32) + const char* compilers[] = {"cl.exe"}; +#else + const char* compilers[] = {"gcc", "g++"}; +#endif + for (const char* compiler : compilers) { + if (!ccbin.empty()) { + std::string path = fmt::format("{}/{}", ccbin, compiler); + auto st = Stat::stat(path); + if (st) { + hash_compiler(ctx, hash, st, path, false); + } + } else { + std::string path = find_executable(ctx, compiler, CCACHE_NAME); + if (!path.empty()) { + auto st = Stat::stat(path, Stat::OnError::log); + hash_compiler(ctx, hash, st, ccbin, false); + } + } + } + } else { + hash_compiler(ctx, hash, *ccbin_st, ccbin, false); + } +} + +static bool +should_rewrite_dependency_target(const ArgsInfo& args_info) +{ + return !args_info.dependency_target_specified && args_info.seen_MD_MMD; +} + +// update a hash with information common for the direct and preprocessor modes. +static void +hash_common_info(const Context& ctx, + const Args& args, + Hash& hash, + const ArgsInfo& args_info) +{ + hash.hash(HASH_PREFIX); + + // We have to hash the extension, as a .i file isn't treated the same by the + // compiler as a .ii file. + hash.hash_delimiter("ext"); + hash.hash(ctx.config.cpp_extension()); + +#ifdef _WIN32 + const std::string compiler_path = Win32Util::add_exe_suffix(args[0]); +#else + const std::string compiler_path = args[0]; +#endif + + auto st = Stat::stat(compiler_path, Stat::OnError::log); + if (!st) { + throw Failure(Statistic::could_not_find_compiler); + } + + // Hash information about the compiler. + hash_compiler(ctx, hash, st, compiler_path, true); + + // Also hash the compiler name as some compilers use hard links and behave + // differently depending on the real name. + hash.hash_delimiter("cc_name"); + hash.hash(Util::base_name(args[0])); + + // Hash variables that may affect the compilation. + const char* always_hash_env_vars[] = { + // From : + "COMPILER_PATH", + "GCC_COMPARE_DEBUG", + "GCC_EXEC_PREFIX", + "SOURCE_DATE_EPOCH", + }; + for (const char* name : always_hash_env_vars) { + const char* value = getenv(name); + if (value) { + hash.hash_delimiter(name); + hash.hash(value); + } + } + + if (!(ctx.config.sloppiness() & SLOPPY_LOCALE)) { + // Hash environment variables that may affect localization of compiler + // warning messages. + const char* envvars[] = { + "LANG", "LC_ALL", "LC_CTYPE", "LC_MESSAGES", nullptr}; + for (const char** p = envvars; *p; ++p) { + const char* v = getenv(*p); + if (v) { + hash.hash_delimiter(*p); + hash.hash(v); + } + } + } + + // Possibly hash the current working directory. + if (args_info.generating_debuginfo && ctx.config.hash_dir()) { + std::string dir_to_hash = ctx.apparent_cwd; + for (const auto& map : args_info.debug_prefix_maps) { + size_t sep_pos = map.find('='); + if (sep_pos != std::string::npos) { + std::string old_path = map.substr(0, sep_pos); + std::string new_path = map.substr(sep_pos + 1); + log("Relocating debuginfo from {} to {} (CWD: {})", + old_path, + new_path, + ctx.apparent_cwd); + if (Util::starts_with(ctx.apparent_cwd, old_path)) { + dir_to_hash = new_path + ctx.apparent_cwd.substr(old_path.size()); + } + } + } + log("Hashing CWD {}", dir_to_hash); + hash.hash_delimiter("cwd"); + hash.hash(dir_to_hash); + } + + if ((!should_rewrite_dependency_target(ctx.args_info) + && ctx.args_info.generating_dependencies) + || ctx.args_info.seen_split_dwarf) { + // The output object file name is part of the .d file, so include the path + // in the hash if generating dependencies. + // + // Object files include a link to the corresponding .dwo file based on the + // target object filename when using -gsplit-dwarf, so hashing the object + // file path will do it, although just hashing the object file base name + // would be enough. + hash.hash_delimiter("object file"); + hash.hash(ctx.args_info.output_obj); + } + + // Possibly hash the coverage data file path. + if (ctx.args_info.generating_coverage && ctx.args_info.profile_arcs) { + std::string dir; + if (!ctx.args_info.profile_path.empty()) { + dir = ctx.args_info.profile_path; + } else { + dir = + Util::real_path(std::string(Util::dir_name(ctx.args_info.output_obj))); + } + string_view stem = + Util::remove_extension(Util::base_name(ctx.args_info.output_obj)); + std::string gcda_path = fmt::format("{}/{}.gcda", dir, stem); + log("Hashing coverage path {}", gcda_path); + hash.hash_delimiter("gcda"); + hash.hash(gcda_path); + } + + // Possibly hash the sanitize blacklist file path. + for (const auto& sanitize_blacklist : args_info.sanitize_blacklists) { + log("Hashing sanitize blacklist {}", sanitize_blacklist); + hash.hash("sanitizeblacklist"); + if (!hash_binary_file(ctx, hash, sanitize_blacklist)) { + throw Failure(Statistic::error_hashing_extra_file); + } + } + + if (!ctx.config.extra_files_to_hash().empty()) { + for (const std::string& path : Util::split_into_strings( + ctx.config.extra_files_to_hash(), PATH_DELIM)) { + log("Hashing extra file {}", path); + hash.hash_delimiter("extrafile"); + if (!hash_binary_file(ctx, hash, path)) { + throw Failure(Statistic::error_hashing_extra_file); + } + } + } + + // Possibly hash GCC_COLORS (for color diagnostics). + if (ctx.guessed_compiler == GuessedCompiler::gcc) { + const char* gcc_colors = getenv("GCC_COLORS"); + if (gcc_colors) { + hash.hash_delimiter("gcccolors"); + hash.hash(gcc_colors); + } + } +} + +static bool +hash_profile_data_file(const Context& ctx, Hash& hash) +{ + const std::string& profile_path = ctx.args_info.profile_path; + string_view base_name = Util::remove_extension(ctx.args_info.output_obj); + std::string hashified_cwd = ctx.apparent_cwd; + std::replace(hashified_cwd.begin(), hashified_cwd.end(), '/', '#'); + + std::vector paths_to_try{ + // -fprofile-use[=dir]/-fbranch-probabilities (GCC <9) + fmt::format("{}/{}.gcda", profile_path, base_name), + // -fprofile-use[=dir]/-fbranch-probabilities (GCC >=9) + fmt::format("{}/{}#{}.gcda", profile_path, hashified_cwd, base_name), + // -fprofile(-instr|-sample)-use=file (Clang), -fauto-profile=file (GCC >=5) + profile_path, + // -fprofile(-instr|-sample)-use=dir (Clang) + fmt::format("{}/default.profdata", profile_path), + // -fauto-profile (GCC >=5) + "fbdata.afdo", // -fprofile-dir is not used + }; + + bool found = false; + for (const std::string& p : paths_to_try) { + log("Checking for profile data file {}", p); + auto st = Stat::stat(p); + if (st && !st.is_directory()) { + log("Adding profile data {} to the hash", p); + hash.hash_delimiter("-fprofile-use"); + if (hash_binary_file(ctx, hash, p)) { + found = true; + } + } + } + + return found; +} + +static bool +option_should_be_ignored(const std::string& arg, + const std::vector& patterns) +{ + return std::any_of( + patterns.cbegin(), patterns.cend(), [&arg](const std::string& pattern) { + const auto& prefix = string_view(pattern).substr(0, pattern.length() - 1); + return ( + pattern == arg + || (Util::ends_with(pattern, "*") && Util::starts_with(arg, prefix))); + }); +} + +// Update a hash sum with information specific to the direct and preprocessor +// modes and calculate the result name. Returns the result name on success, +// otherwise nullopt. +static optional +calculate_result_name(Context& ctx, + const Args& args, + Args& preprocessor_args, + Hash& hash, + bool direct_mode) +{ + bool found_ccbin = false; + + hash.hash_delimiter("result version"); + hash.hash(Result::k_version); + + if (direct_mode) { + hash.hash_delimiter("manifest version"); + hash.hash(Manifest::k_version); + } + + // clang will emit warnings for unused linker flags, so we shouldn't skip + // those arguments. + int is_clang = ctx.guessed_compiler == GuessedCompiler::clang + || ctx.guessed_compiler == GuessedCompiler::unknown; + + // First the arguments. + for (size_t i = 1; i < args.size(); i++) { + // Trust the user if they've said we should not hash a given option. + if (option_should_be_ignored(args[i], ctx.ignore_options())) { + log("Not hashing ignored option: {}", args[i]); + if (i + 1 < args.size() && compopt_takes_arg(args[i])) { + i++; + log("Not hashing argument of ignored option: {}", args[i]); + } + continue; + } + + // -L doesn't affect compilation (except for clang). + if (i < args.size() - 1 && args[i] == "-L" && !is_clang) { + i++; + continue; + } + if (Util::starts_with(args[i], "-L") && !is_clang) { + continue; + } + + // -Wl,... doesn't affect compilation (except for clang). + if (Util::starts_with(args[i], "-Wl,") && !is_clang) { + continue; + } + + // The -fdebug-prefix-map option may be used in combination with + // CCACHE_BASEDIR to reuse results across different directories. Skip using + // the value of the option from hashing but still hash the existence of the + // option. + if (Util::starts_with(args[i], "-fdebug-prefix-map=")) { + hash.hash_delimiter("arg"); + hash.hash("-fdebug-prefix-map="); + continue; + } + if (Util::starts_with(args[i], "-ffile-prefix-map=")) { + hash.hash_delimiter("arg"); + hash.hash("-ffile-prefix-map="); + continue; + } + if (Util::starts_with(args[i], "-fmacro-prefix-map=")) { + hash.hash_delimiter("arg"); + hash.hash("-fmacro-prefix-map="); + continue; + } + + // When using the preprocessor, some arguments don't contribute to the + // hash. The theory is that these arguments will change the output of -E if + // they are going to have any effect at all. For precompiled headers this + // might not be the case. + if (!direct_mode && !ctx.args_info.output_is_precompiled_header + && !ctx.args_info.using_precompiled_header) { + if (compopt_affects_cpp_output(args[i])) { + if (compopt_takes_arg(args[i])) { + i++; + } + continue; + } + if (compopt_affects_cpp_output(args[i].substr(0, 2))) { + continue; + } + } + + // If we're generating dependencies, we make sure to skip the filename of + // the dependency file, since it doesn't impact the output. + if (ctx.args_info.generating_dependencies) { + if (Util::starts_with(args[i], "-Wp,")) { + if (Util::starts_with(args[i], "-Wp,-MD,") + && args[i].find(',', 8) == std::string::npos) { + hash.hash(args[i].data(), 8); + continue; + } else if (Util::starts_with(args[i], "-Wp,-MMD,") + && args[i].find(',', 9) == std::string::npos) { + hash.hash(args[i].data(), 9); + continue; + } + } else if (Util::starts_with(args[i], "-MF")) { + // In either case, hash the "-MF" part. + hash.hash_delimiter("arg"); + hash.hash(args[i].data(), 3); + + if (ctx.args_info.output_dep != "/dev/null") { + bool separate_argument = (args[i].size() == 3); + if (separate_argument) { + // Next argument is dependency name, so skip it. + i++; + } + } + continue; + } + } + + if (Util::starts_with(args[i], "-specs=") + || Util::starts_with(args[i], "--specs=")) { + std::string path = args[i].substr(args[i].find('=') + 1); + auto st = Stat::stat(path, Stat::OnError::log); + if (st) { + // If given an explicit specs file, then hash that file, but don't + // include the path to it in the hash. + hash.hash_delimiter("specs"); + hash_compiler(ctx, hash, st, path, false); + continue; + } + } + + if (Util::starts_with(args[i], "-fplugin=")) { + auto st = Stat::stat(&args[i][9], Stat::OnError::log); + if (st) { + hash.hash_delimiter("plugin"); + hash_compiler(ctx, hash, st, &args[i][9], false); + continue; + } + } + + if (args[i] == "-Xclang" && i + 3 < args.size() && args[i + 1] == "-load" + && args[i + 2] == "-Xclang") { + auto st = Stat::stat(args[i + 3], Stat::OnError::log); + if (st) { + hash.hash_delimiter("plugin"); + hash_compiler(ctx, hash, st, args[i + 3], false); + i += 3; + continue; + } + } + + if ((args[i] == "-ccbin" || args[i] == "--compiler-bindir") + && i + 1 < args.size()) { + auto st = Stat::stat(args[i + 1]); + if (st) { + found_ccbin = true; + hash.hash_delimiter("ccbin"); + hash_nvcc_host_compiler(ctx, hash, &st, args[i + 1]); + i++; + continue; + } + } + + // All other arguments are included in the hash. + hash.hash_delimiter("arg"); + hash.hash(args[i]); + if (i + 1 < args.size() && compopt_takes_arg(args[i])) { + i++; + hash.hash_delimiter("arg"); + hash.hash(args[i]); + } + } + + // Make results with dependency file /dev/null different from those without + // it. + if (ctx.args_info.generating_dependencies + && ctx.args_info.output_dep == "/dev/null") { + hash.hash_delimiter("/dev/null dependency file"); + } + + if (!found_ccbin && ctx.args_info.actual_language == "cu") { + hash_nvcc_host_compiler(ctx, hash); + } + + // For profile generation (-fprofile(-instr)-generate[=path]) + // - hash profile path + // + // For profile usage (-fprofile(-instr|-sample)-use, -fbranch-probabilities): + // - hash profile data + // + // -fbranch-probabilities and -fvpt usage is covered by + // -fprofile-generate/-fprofile-use. + // + // The profile directory can be specified as an argument to + // -fprofile(-instr)-generate=, -fprofile(-instr|-sample)-use= or + // -fprofile-dir=. + + if (ctx.args_info.profile_generate) { + ASSERT(!ctx.args_info.profile_path.empty()); + log("Adding profile directory {} to our hash", ctx.args_info.profile_path); + hash.hash_delimiter("-fprofile-dir"); + hash.hash(ctx.args_info.profile_path); + } + + if (ctx.args_info.profile_use && !hash_profile_data_file(ctx, hash)) { + log("No profile data file found"); + throw Failure(Statistic::no_input_file); + } + + // Adding -arch to hash since cpp output is affected. + for (const auto& arch : ctx.args_info.arch_args) { + hash.hash_delimiter("-arch"); + hash.hash(arch); + } + + optional result_name; + if (direct_mode) { + // Hash environment variables that affect the preprocessor output. + const char* envvars[] = {"CPATH", + "C_INCLUDE_PATH", + "CPLUS_INCLUDE_PATH", + "OBJC_INCLUDE_PATH", + "OBJCPLUS_INCLUDE_PATH", // clang + nullptr}; + for (const char** p = envvars; *p; ++p) { + const char* v = getenv(*p); + if (v) { + hash.hash_delimiter(*p); + hash.hash(v); + } + } + + // Make sure that the direct mode hash is unique for the input file path. + // If this would not be the case: + // + // * An false cache hit may be produced. Scenario: + // - a/r.h exists. + // - a/x.c has #include "r.h". + // - b/x.c is identical to a/x.c. + // - Compiling a/x.c records a/r.h in the manifest. + // - Compiling b/x.c results in a false cache hit since a/x.c and b/x.c + // share manifests and a/r.h exists. + // * The expansion of __FILE__ may be incorrect. + hash.hash_delimiter("inputfile"); + hash.hash(ctx.args_info.input_file); + + hash.hash_delimiter("sourcecode"); + int result = hash_source_code_file(ctx, hash, ctx.args_info.input_file); + if (result & HASH_SOURCE_CODE_ERROR) { + throw Failure(Statistic::internal_error); + } + if (result & HASH_SOURCE_CODE_FOUND_TIME) { + log("Disabling direct mode"); + ctx.config.set_direct_mode(false); + return nullopt; + } + + const auto manifest_name = hash.digest(); + ctx.set_manifest_name(manifest_name); + + const auto manifest_file = look_up_cache_file( + ctx.config.cache_dir(), manifest_name, Manifest::k_file_suffix); + ctx.set_manifest_path(manifest_file.path); + + if (manifest_file.stat) { + log("Looking for result name in {}", manifest_file.path); + MTR_BEGIN("manifest", "manifest_get"); + result_name = Manifest::get(ctx, manifest_file.path); + MTR_END("manifest", "manifest_get"); + if (result_name) { + log("Got result name from manifest"); + } else { + log("Did not find result name in manifest"); + } + } else { + log("No manifest with name {} in the cache", manifest_name.to_string()); + } + } else { + if (ctx.args_info.arch_args.empty()) { + result_name = get_result_name_from_cpp(ctx, preprocessor_args, hash); + log("Got result name from preprocessor"); + } else { + preprocessor_args.push_back("-arch"); + for (size_t i = 0; i < ctx.args_info.arch_args.size(); ++i) { + preprocessor_args.push_back(ctx.args_info.arch_args[i]); + result_name = get_result_name_from_cpp(ctx, preprocessor_args, hash); + log("Got result name from preprocessor with -arch {}", + ctx.args_info.arch_args[i]); + if (i != ctx.args_info.arch_args.size() - 1) { + result_name = nullopt; + } + preprocessor_args.pop_back(); + } + preprocessor_args.pop_back(); + } + } + + return result_name; +} + +enum class FromCacheCallMode { direct, cpp }; + +// Try to return the compile result from cache. +static optional +from_cache(Context& ctx, FromCacheCallMode mode) +{ + UmaskScope umask_scope(ctx.original_umask); + + // The user might be disabling cache hits. + if (ctx.config.recache()) { + return nullopt; + } + + // If we're using Clang, we can't trust a precompiled header object based on + // running the preprocessor since clang will produce a fatal error when the + // precompiled header is used and one of the included files has an updated + // timestamp: + // + // file 'foo.h' has been modified since the precompiled header 'foo.pch' + // was built + if ((ctx.guessed_compiler == GuessedCompiler::clang + || ctx.guessed_compiler == GuessedCompiler::unknown) + && ctx.args_info.output_is_precompiled_header + && !ctx.args_info.fno_pch_timestamp && mode == FromCacheCallMode::cpp) { + log("Not considering cached precompiled header in preprocessor mode"); + return nullopt; + } + + MTR_BEGIN("cache", "from_cache"); + + // Get result from cache. + const auto result_file = look_up_cache_file( + ctx.config.cache_dir(), *ctx.result_name(), Result::k_file_suffix); + if (!result_file.stat) { + log("No result with name {} in the cache", ctx.result_name()->to_string()); + return nullopt; + } + ctx.set_result_path(result_file.path); + Result::Reader result_reader(result_file.path); + ResultRetriever result_retriever( + ctx, should_rewrite_dependency_target(ctx.args_info)); + + auto error = result_reader.read(result_retriever); + MTR_END("cache", "from_cache"); + if (error) { + log("Failed to get result from cache: {}", *error); + return nullopt; + } + + // Update modification timestamp to save file from LRU cleanup. + Util::update_mtime(*ctx.result_path()); + + log("Succeeded getting cached result"); + + return mode == FromCacheCallMode::direct ? Statistic::direct_cache_hit + : Statistic::preprocessed_cache_hit; +} + +// Find the real compiler and put it into ctx.orig_args[0]. We just search the +// PATH to find an executable of the same name that isn't a link to ourselves. +// Pass find_executable function as second parameter. +void +find_compiler(Context& ctx, + const FindExecutableFunction& find_executable_function) +{ + const nonstd::string_view first_param_base_name = + Util::base_name(ctx.orig_args[0]); + const bool first_param_is_ccache = + Util::same_program_name(first_param_base_name, CCACHE_NAME); + + // Support user override of the compiler. + const std::string compiler = + !ctx.config.compiler().empty() + ? ctx.config.compiler() + : (first_param_is_ccache ? ctx.orig_args[1] + // ccache is masquerading as compiler: + : std::string(first_param_base_name)); + + const std::string resolved_compiler = + Util::is_full_path(compiler) + ? compiler + : find_executable_function(ctx, compiler, CCACHE_NAME); + + if (resolved_compiler.empty()) { + throw Fatal("Could not find compiler \"{}\" in PATH", compiler); + } + + if (Util::same_program_name(Util::base_name(resolved_compiler), + CCACHE_NAME)) { + throw Fatal( + "Recursive invocation (the name of the ccache binary must be \"{}\")", + CCACHE_NAME); + } + + if (first_param_is_ccache) { + ctx.orig_args.pop_front(); + } + ctx.orig_args[0] = resolved_compiler; +} + +static std::string +default_cache_dir(const std::string& home_dir) +{ +#ifdef _WIN32 + return home_dir + "/ccache"; +#elif defined(__APPLE__) + return home_dir + "/Library/Caches/ccache"; +#else + return home_dir + "/.cache/ccache"; +#endif +} + +static std::string +default_config_dir(const std::string& home_dir) +{ +#ifdef _WIN32 + return home_dir + "/ccache"; +#elif defined(__APPLE__) + return home_dir + "/Library/Preferences/ccache"; +#else + return home_dir + "/.config/ccache"; +#endif +} + +// Read config file(s), populate variables, create configuration file in cache +// directory if missing, etc. +static void +set_up_config(Config& config) +{ + const std::string home_dir = Util::get_home_directory(); + const std::string legacy_ccache_dir = home_dir + "/.ccache"; + const bool legacy_ccache_dir_exists = + Stat::stat(legacy_ccache_dir).is_directory(); + const char* const env_xdg_cache_home = getenv("XDG_CACHE_HOME"); + const char* const env_xdg_config_home = getenv("XDG_CONFIG_HOME"); + + const char* env_ccache_configpath = getenv("CCACHE_CONFIGPATH"); + if (env_ccache_configpath) { + config.set_primary_config_path(env_ccache_configpath); + } else { + // Only used for ccache tests: + const char* const env_ccache_configpath2 = getenv("CCACHE_CONFIGPATH2"); + + config.set_secondary_config_path( + env_ccache_configpath2 ? env_ccache_configpath2 + : fmt::format("{}/ccache.conf", SYSCONFDIR)); + MTR_BEGIN("config", "conf_read_secondary"); + // A missing config file in SYSCONFDIR is OK so don't check return value. + config.update_from_file(config.secondary_config_path()); + MTR_END("config", "conf_read_secondary"); + + const char* const env_ccache_dir = getenv("CCACHE_DIR"); + std::string primary_config_dir; + if (env_ccache_dir && *env_ccache_dir) { + primary_config_dir = env_ccache_dir; + } else if (!config.cache_dir().empty() && !env_ccache_dir) { + primary_config_dir = config.cache_dir(); + } else if (legacy_ccache_dir_exists) { + primary_config_dir = legacy_ccache_dir; + } else if (env_xdg_config_home) { + primary_config_dir = fmt::format("{}/ccache", env_xdg_config_home); + } else { + primary_config_dir = default_config_dir(home_dir); + } + config.set_primary_config_path(primary_config_dir + "/ccache.conf"); + } + + const std::string& cache_dir_before_primary_config = config.cache_dir(); + + MTR_BEGIN("config", "conf_read_primary"); + config.update_from_file(config.primary_config_path()); + MTR_END("config", "conf_read_primary"); + + // Ignore cache_dir set in primary config. + config.set_cache_dir(cache_dir_before_primary_config); + + MTR_BEGIN("config", "conf_update_from_environment"); + config.update_from_environment(); + // (config.cache_dir is set above if CCACHE_DIR is set.) + MTR_END("config", "conf_update_from_environment"); + + if (config.cache_dir().empty()) { + if (legacy_ccache_dir_exists) { + config.set_cache_dir(legacy_ccache_dir); + } else if (env_xdg_cache_home) { + config.set_cache_dir(fmt::format("{}/ccache", env_xdg_cache_home)); + } else { + config.set_cache_dir(default_cache_dir(home_dir)); + } + } + // else: cache_dir was set explicitly via environment or via secondary config. + + // We have now determined config.cache_dir and populated the rest of config in + // prio order (1. environment, 2. primary config, 3. secondary config). +} + +static void +set_up_context(Context& ctx, int argc, const char* const* argv) +{ + ctx.orig_args = Args::from_argv(argc, argv); + ctx.ignore_header_paths = Util::split_into_strings( + ctx.config.ignore_headers_in_manifest(), PATH_DELIM); + ctx.set_ignore_options( + Util::split_into_strings(ctx.config.ignore_options(), " ")); +} + +// Initialize ccache, must be called once before anything else is run. +static void +initialize(Context& ctx, int argc, const char* const* argv) +{ + set_up_config(ctx.config); + set_up_context(ctx, argc, argv); + Logging::init(ctx.config); + + // Set default umask for all files created by ccache from now on (if + // configured to). This is intentionally done after calling init_log so that + // the log file won't be affected by the umask but before creating the initial + // configuration file. The intention is that all files and directories in the + // cache directory should be affected by the configured umask and that no + // other files and directories should. + if (ctx.config.umask() != std::numeric_limits::max()) { + ctx.original_umask = umask(ctx.config.umask()); + } + + log("=== CCACHE {} STARTED =========================================", + CCACHE_VERSION); + + if (getenv("CCACHE_INTERNAL_TRACE")) { +#ifdef MTR_ENABLED + ctx.mini_trace = std::make_unique(ctx.args_info); +#else + log("Error: tracing is not enabled!"); +#endif + } +} + +// Make a copy of stderr that will not be cached, so things like distcc can +// send networking errors to it. +static void +set_up_uncached_err() +{ + int uncached_fd = + dup(STDERR_FILENO); // The file descriptor is intentionally leaked. + if (uncached_fd == -1) { + log("dup(2) failed: {}", strerror(errno)); + throw Failure(Statistic::internal_error); + } + + Util::setenv("UNCACHED_ERR_FD", fmt::format("{}", uncached_fd)); +} + +static void +configuration_logger(const std::string& key, + const std::string& value, + const std::string& origin) +{ + Logging::bulk_log("Config: ({}) {} = {}", origin, key, value); +} + +static void +configuration_printer(const std::string& key, + const std::string& value, + const std::string& origin) +{ + fmt::print("({}) {} = {}\n", origin, key, value); +} + +static int cache_compilation(int argc, const char* const* argv); +static Statistic do_cache_compilation(Context& ctx, const char* const* argv); + +static uint8_t +calculate_wanted_cache_level(uint64_t files_in_level_1) +{ + uint64_t files_per_directory = files_in_level_1 / 16; + for (uint8_t i = k_min_cache_levels; i <= k_max_cache_levels; ++i) { + if (files_per_directory < k_max_cache_files_per_directory) { + return i; + } + files_per_directory /= 16; + } + return k_max_cache_levels; +} + +static optional +update_stats_and_maybe_move_cache_file(const Context& ctx, + const Digest& name, + const std::string& current_path, + const Counters& counter_updates, + const std::string& file_suffix) +{ + if (counter_updates.all_zero()) { + return nullopt; + } + + // Use stats file in the level one subdirectory for cache bookkeeping counters + // since cleanup is performed on level one. Use stats file in the level two + // subdirectory for other counters to reduce lock contention. + const bool use_stats_on_level_1 = + counter_updates.get(Statistic::cache_size_kibibyte) != 0 + || counter_updates.get(Statistic::files_in_cache) != 0; + std::string level_string = fmt::format("{:x}", name.bytes()[0] >> 4); + if (!use_stats_on_level_1) { + level_string += fmt::format("/{:x}", name.bytes()[0] & 0xF); + } + const auto stats_file = + fmt::format("{}/{}/stats", ctx.config.cache_dir(), level_string); + + auto counters = + Statistics::update(stats_file, [&counter_updates](Counters& cs) { + cs.increment(counter_updates); + }); + if (!counters) { + return nullopt; + } + + if (use_stats_on_level_1) { + // Only consider moving the cache file to another level when we have read + // the level 1 stats file since it's only then we know the proper + // files_in_cache value. + const auto wanted_level = + calculate_wanted_cache_level(counters->get(Statistic::files_in_cache)); + const auto wanted_path = Util::get_path_in_cache( + ctx.config.cache_dir(), wanted_level, name.to_string() + file_suffix); + if (current_path != wanted_path) { + Util::ensure_dir_exists(Util::dir_name(wanted_path)); + log("Moving {} to {}", current_path, wanted_path); + try { + Util::rename(current_path, wanted_path); + } catch (const Error&) { + // Two ccache processes may move the file at the same time, so failure + // to rename is OK. + } + } + } + return counters; +} + +static void +finalize_stats_and_trigger_cleanup(Context& ctx) +{ + const auto& config = ctx.config; + + if (config.disable()) { + // Just log result, don't update statistics. + log("Result: disabled"); + return; + } + + if (!config.log_file().empty() || config.debug()) { + const auto result = Statistics::get_result(ctx.counter_updates); + if (result) { + log("Result: {}", *result); + } + } + + if (!config.stats()) { + return; + } + + if (!ctx.result_path()) { + ASSERT(ctx.counter_updates.get(Statistic::cache_size_kibibyte) == 0); + ASSERT(ctx.counter_updates.get(Statistic::files_in_cache) == 0); + + // Context::set_result_path hasn't been called yet, so we just choose one of + // the stats files in the 256 level 2 directories. + const auto bucket = getpid() % 256; + const auto stats_file = fmt::format( + "{}/{:x}/{:x}/stats", config.cache_dir(), bucket / 16, bucket % 16); + Statistics::update( + stats_file, [&ctx](Counters& cs) { cs.increment(ctx.counter_updates); }); + return; + } + + if (ctx.manifest_path()) { + update_stats_and_maybe_move_cache_file(ctx, + *ctx.manifest_name(), + *ctx.manifest_path(), + ctx.manifest_counter_updates, + Manifest::k_file_suffix); + } + + const auto counters = + update_stats_and_maybe_move_cache_file(ctx, + *ctx.result_name(), + *ctx.result_path(), + ctx.counter_updates, + Result::k_file_suffix); + if (!counters) { + return; + } + + const auto subdir = fmt::format( + "{}/{:x}", config.cache_dir(), ctx.result_name()->bytes()[0] >> 4); + bool need_cleanup = false; + + if (config.max_files() != 0 + && counters->get(Statistic::files_in_cache) > config.max_files() / 16) { + log("Need to clean up {} since it holds {} files (limit: {} files)", + subdir, + counters->get(Statistic::files_in_cache), + config.max_files() / 16); + need_cleanup = true; + } + if (config.max_size() != 0 + && counters->get(Statistic::cache_size_kibibyte) + > config.max_size() / 1024 / 16) { + log("Need to clean up {} since it holds {} KiB (limit: {} KiB)", + subdir, + counters->get(Statistic::cache_size_kibibyte), + config.max_size() / 1024 / 16); + need_cleanup = true; + } + + if (need_cleanup) { + const double factor = config.limit_multiple() / 16; + const uint64_t max_size = round(config.max_size() * factor); + const uint32_t max_files = round(config.max_files() * factor); + const time_t max_age = 0; + clean_up_dir( + subdir, max_size, max_files, max_age, [](double /*progress*/) {}); + } +} + +static void +finalize_at_exit(Context& ctx) +{ + try { + finalize_stats_and_trigger_cleanup(ctx); + } catch (const ErrorBase& e) { + // finalize_at_exit must not throw since it's called by a destructor. + log("Error while finalizing stats: {}", e.what()); + } + + // Dump log buffer last to not lose any logs. + if (ctx.config.debug() && !ctx.args_info.output_obj.empty()) { + const auto path = fmt::format("{}.ccache-log", ctx.args_info.output_obj); + Logging::dump_log(path); + } +} + +// The entry point when invoked to cache a compilation. +static int +cache_compilation(int argc, const char* const* argv) +{ + tzset(); // Needed for localtime_r. + + bool fall_back_to_original_compiler = false; + Args saved_orig_args; + + { + Context ctx; + SignalHandler signal_handler(ctx); + Finalizer finalizer([&ctx] { finalize_at_exit(ctx); }); + + initialize(ctx, argc, argv); + + MTR_BEGIN("main", "find_compiler"); + find_compiler(ctx, &find_executable); + MTR_END("main", "find_compiler"); + + try { + Statistic statistic = do_cache_compilation(ctx, argv); + ctx.counter_updates.increment(statistic); + } catch (const Failure& e) { + if (e.statistic() != Statistic::none) { + ctx.counter_updates.increment(e.statistic()); + } + + if (e.exit_code()) { + return *e.exit_code(); + } + // Else: Fall back to running the real compiler. + fall_back_to_original_compiler = true; + + if (ctx.original_umask) { + umask(*ctx.original_umask); + } + + ASSERT(!ctx.orig_args.empty()); + + ctx.orig_args.erase_with_prefix("--ccache-"); + add_prefix(ctx, ctx.orig_args, ctx.config.prefix_command()); + + log("Failed; falling back to running the real compiler"); + + saved_orig_args = std::move(ctx.orig_args); + auto execv_argv = saved_orig_args.to_argv(); + log("Executing {}", Util::format_argv_for_logging(execv_argv.data())); + // Run execv below after ctx and finalizer have been destructed. + } + } + + if (fall_back_to_original_compiler) { + auto execv_argv = saved_orig_args.to_argv(); + execv(execv_argv[0], const_cast(execv_argv.data())); + throw Fatal("execv of {} failed: {}", execv_argv[0], strerror(errno)); + } + + return EXIT_SUCCESS; +} + +static Statistic +do_cache_compilation(Context& ctx, const char* const* argv) +{ + if (ctx.actual_cwd.empty()) { + log("Unable to determine current working directory: {}", strerror(errno)); + throw Failure(Statistic::internal_error); + } + + MTR_BEGIN("main", "clean_up_internal_tempdir"); + if (ctx.config.temporary_dir() == ctx.config.cache_dir() + "/tmp") { + clean_up_internal_tempdir(ctx.config); + } + MTR_END("main", "clean_up_internal_tempdir"); + + if (!ctx.config.log_file().empty() || ctx.config.debug()) { + ctx.config.visit_items(configuration_logger); + } + + if (ctx.config.disable()) { + log("ccache is disabled"); + // Statistic::cache_miss is a dummy to trigger stats_flush. + throw Failure(Statistic::cache_miss); + } + + MTR_BEGIN("main", "set_up_uncached_err"); + set_up_uncached_err(); + MTR_END("main", "set_up_uncached_err"); + + log("Command line: {}", Util::format_argv_for_logging(argv)); + log("Hostname: {}", Util::get_hostname()); + log("Working directory: {}", ctx.actual_cwd); + if (ctx.apparent_cwd != ctx.actual_cwd) { + log("Apparent working directory: {}", ctx.apparent_cwd); + } + + ctx.config.set_limit_multiple( + Util::clamp(ctx.config.limit_multiple(), 0.0, 1.0)); + + MTR_BEGIN("main", "guess_compiler"); + ctx.guessed_compiler = guess_compiler(ctx.orig_args[0]); + MTR_END("main", "guess_compiler"); + + MTR_BEGIN("main", "process_args"); + ProcessArgsResult processed = process_args(ctx); + MTR_END("main", "process_args"); + + if (processed.error) { + throw Failure(*processed.error); + } + + if (ctx.config.depend_mode() + && (!ctx.args_info.generating_dependencies + || ctx.args_info.output_dep == "/dev/null" + || !ctx.config.run_second_cpp())) { + log("Disabling depend mode"); + ctx.config.set_depend_mode(false); + } + + log("Source file: {}", ctx.args_info.input_file); + if (ctx.args_info.generating_dependencies) { + log("Dependency file: {}", ctx.args_info.output_dep); + } + if (ctx.args_info.generating_coverage) { + log("Coverage file is being generated"); + } + if (ctx.args_info.generating_stackusage) { + log("Stack usage file: {}", ctx.args_info.output_su); + } + if (ctx.args_info.generating_diagnostics) { + log("Diagnostics file: {}", ctx.args_info.output_dia); + } + if (!ctx.args_info.output_dwo.empty()) { + log("Split dwarf file: {}", ctx.args_info.output_dwo); + } + + log("Object file: {}", ctx.args_info.output_obj); + MTR_META_THREAD_NAME(ctx.args_info.output_obj.c_str()); + + if (ctx.config.debug()) { + std::string path = + fmt::format("{}.ccache-input-text", ctx.args_info.output_obj); + File debug_text_file(path, "w"); + if (debug_text_file) { + ctx.hash_debug_files.push_back(std::move(debug_text_file)); + } else { + log("Failed to open {}: {}", path, strerror(errno)); + } + } + + FILE* debug_text_file = !ctx.hash_debug_files.empty() + ? ctx.hash_debug_files.front().get() + : nullptr; + + Hash common_hash; + init_hash_debug( + ctx, common_hash, ctx.args_info.output_obj, 'c', "COMMON", debug_text_file); + + MTR_BEGIN("hash", "common_hash"); + hash_common_info( + ctx, processed.preprocessor_args, common_hash, ctx.args_info); + MTR_END("hash", "common_hash"); + + // Try to find the hash using the manifest. + Hash direct_hash = common_hash; + init_hash_debug(ctx, + direct_hash, + ctx.args_info.output_obj, + 'd', + "DIRECT MODE", + debug_text_file); + + Args args_to_hash = processed.preprocessor_args; + args_to_hash.push_back(processed.extra_args_to_hash); + + bool put_result_in_manifest = false; + optional result_name; + optional result_name_from_manifest; + if (ctx.config.direct_mode()) { + log("Trying direct lookup"); + MTR_BEGIN("hash", "direct_hash"); + Args dummy_args; + result_name = + calculate_result_name(ctx, args_to_hash, dummy_args, direct_hash, true); + MTR_END("hash", "direct_hash"); + if (result_name) { + ctx.set_result_name(*result_name); + + // If we can return from cache at this point then do so. + auto result = from_cache(ctx, FromCacheCallMode::direct); + if (result) { + return *result; + } + + // Wasn't able to return from cache at this point. However, the result + // was already found in manifest, so don't re-add it later. + put_result_in_manifest = false; + + result_name_from_manifest = result_name; + } else { + // Add result to manifest later. + put_result_in_manifest = true; + } + } + + if (ctx.config.read_only_direct()) { + log("Read-only direct mode; running real compiler"); + throw Failure(Statistic::cache_miss); + } + + if (!ctx.config.depend_mode()) { + // Find the hash using the preprocessed output. Also updates + // ctx.included_files. + Hash cpp_hash = common_hash; + init_hash_debug(ctx, + cpp_hash, + ctx.args_info.output_obj, + 'p', + "PREPROCESSOR MODE", + debug_text_file); + + MTR_BEGIN("hash", "cpp_hash"); + result_name = calculate_result_name( + ctx, args_to_hash, processed.preprocessor_args, cpp_hash, false); + MTR_END("hash", "cpp_hash"); + + // calculate_result_name does not return nullopt if the last (direct_mode) + // argument is false. + ASSERT(result_name); + ctx.set_result_name(*result_name); + + if (result_name_from_manifest && result_name_from_manifest != result_name) { + // manifest_path is guaranteed to be set when calculate_result_name + // returns a non-nullopt result in direct mode, i.e. when + // result_name_from_manifest is set. + ASSERT(ctx.manifest_path()); + + // The hash from manifest differs from the hash of the preprocessor + // output. This could be because: + // + // - The preprocessor produces different output for the same input (not + // likely). + // - There's a bug in ccache (maybe incorrect handling of compiler + // arguments). + // - The user has used a different CCACHE_BASEDIR (most likely). + // + // The best thing here would probably be to remove the hash entry from + // the manifest. For now, we use a simpler method: just remove the + // manifest file. + log("Hash from manifest doesn't match preprocessor output"); + log("Likely reason: different CCACHE_BASEDIRs used"); + log("Removing manifest as a safety measure"); + Util::unlink_safe(*ctx.manifest_path()); + + put_result_in_manifest = true; + } + + // If we can return from cache at this point then do. + auto result = from_cache(ctx, FromCacheCallMode::cpp); + if (result) { + if (put_result_in_manifest) { + update_manifest_file(ctx); + } + return *result; + } + } + + if (ctx.config.read_only()) { + log("Read-only mode; running real compiler"); + throw Failure(Statistic::cache_miss); + } + + add_prefix(ctx, processed.compiler_args, ctx.config.prefix_command()); + + // In depend_mode, extend the direct hash. + Hash* depend_mode_hash = ctx.config.depend_mode() ? &direct_hash : nullptr; + + // Run real compiler, sending output to cache. + MTR_BEGIN("cache", "to_cache"); + to_cache(ctx, + processed.compiler_args, + ctx.args_info.depend_extra_args, + depend_mode_hash); + update_manifest_file(ctx); + MTR_END("cache", "to_cache"); + + return Statistic::cache_miss; +} + +// The main program when not doing a compile. +static int +handle_main_options(int argc, const char* const* argv) +{ + enum longopts { + CHECKSUM_FILE, + DUMP_MANIFEST, + DUMP_RESULT, + EVICT_OLDER_THAN, + EXTRACT_RESULT, + HASH_FILE, + PRINT_STATS, + }; + static const struct option options[] = { + {"checksum-file", required_argument, nullptr, CHECKSUM_FILE}, + {"cleanup", no_argument, nullptr, 'c'}, + {"clear", no_argument, nullptr, 'C'}, + {"directory", no_argument, nullptr, 'd'}, + {"dump-manifest", required_argument, nullptr, DUMP_MANIFEST}, + {"dump-result", required_argument, nullptr, DUMP_RESULT}, + {"evict-older-than", required_argument, nullptr, EVICT_OLDER_THAN}, + {"extract-result", required_argument, nullptr, EXTRACT_RESULT}, + {"get-config", required_argument, nullptr, 'k'}, + {"hash-file", required_argument, nullptr, HASH_FILE}, + {"help", no_argument, nullptr, 'h'}, + {"max-files", required_argument, nullptr, 'F'}, + {"max-size", required_argument, nullptr, 'M'}, + {"print-stats", no_argument, nullptr, PRINT_STATS}, + {"recompress", required_argument, nullptr, 'X'}, + {"set-config", required_argument, nullptr, 'o'}, + {"show-compression", no_argument, nullptr, 'x'}, + {"show-config", no_argument, nullptr, 'p'}, + {"show-stats", no_argument, nullptr, 's'}, + {"version", no_argument, nullptr, 'V'}, + {"zero-stats", no_argument, nullptr, 'z'}, + {nullptr, 0, nullptr, 0}}; + + Context ctx; + initialize(ctx, argc, argv); + + int c; + while ((c = getopt_long(argc, + const_cast(argv), + "cCd:k:hF:M:po:sVxX:z", + options, + nullptr)) + != -1) { + std::string arg = optarg ? optarg : std::string(); + + switch (c) { + case CHECKSUM_FILE: { + Checksum checksum; + Fd fd(arg == "-" ? STDIN_FILENO : open(arg.c_str(), O_RDONLY)); + Util::read_fd(*fd, [&checksum](const void* data, size_t size) { + checksum.update(data, size); + }); + fmt::print("{:016x}\n", checksum.digest()); + break; + } + + case DUMP_MANIFEST: + return Manifest::dump(arg, stdout) ? 0 : 1; + + case DUMP_RESULT: { + ResultDumper result_dumper(stdout); + Result::Reader result_reader(arg); + auto error = result_reader.read(result_dumper); + if (error) { + fmt::print(stderr, "Error: {}\n", *error); + } + return error ? EXIT_FAILURE : EXIT_SUCCESS; + } + + case EVICT_OLDER_THAN: { + auto seconds = Util::parse_duration(arg); + ProgressBar progress_bar("Evicting..."); + clean_old( + ctx, [&](double progress) { progress_bar.update(progress); }, seconds); + if (isatty(STDOUT_FILENO)) { + fmt::print("\n"); + } + break; + } + + case EXTRACT_RESULT: { + ResultExtractor result_extractor("."); + Result::Reader result_reader(arg); + auto error = result_reader.read(result_extractor); + if (error) { + fmt::print(stderr, "Error: {}\n", *error); + } + return error ? EXIT_FAILURE : EXIT_SUCCESS; + } + + case HASH_FILE: { + Hash hash; + if (arg == "-") { + hash.hash_fd(STDIN_FILENO); + } else { + hash.hash_file(arg); + } + fmt::print("{}\n", hash.digest().to_string()); + break; + } + + case PRINT_STATS: + fmt::print(Statistics::format_machine_readable(ctx.config)); + break; + + case 'c': // --cleanup + { + ProgressBar progress_bar("Cleaning..."); + clean_up_all(ctx.config, + [&](double progress) { progress_bar.update(progress); }); + if (isatty(STDOUT_FILENO)) { + fmt::print("\n"); + } + break; + } + + case 'C': // --clear + { + ProgressBar progress_bar("Clearing..."); + wipe_all(ctx, [&](double progress) { progress_bar.update(progress); }); + if (isatty(STDOUT_FILENO)) { + fmt::print("\n"); + } + break; + } + + case 'd': // --directory + Util::setenv("CCACHE_DIR", arg); + break; + + case 'h': // --help + fmt::print(stdout, USAGE_TEXT, CCACHE_NAME, CCACHE_NAME); + exit(EXIT_SUCCESS); + + case 'k': // --get-config + fmt::print("{}\n", ctx.config.get_string_value(arg)); + break; + + case 'F': { // --max-files + auto files = Util::parse_unsigned(arg); + Config::set_value_in_file( + ctx.config.primary_config_path(), "max_files", arg); + if (files == 0) { + fmt::print("Unset cache file limit\n"); + } else { + fmt::print("Set cache file limit to {}\n", files); + } + break; + } + + case 'M': { // --max-size + uint64_t size = Util::parse_size(arg); + Config::set_value_in_file( + ctx.config.primary_config_path(), "max_size", arg); + if (size == 0) { + fmt::print("Unset cache size limit\n"); + } else { + fmt::print("Set cache size limit to {}\n", + Util::format_human_readable_size(size)); + } + break; + } + + case 'o': { // --set-config + // Start searching for equal sign at position 1 to improve error message + // for the -o=K=V case (key "=K" and value "V"). + size_t eq_pos = arg.find('=', 1); + if (eq_pos == std::string::npos) { + throw Error("missing equal sign in \"{}\"", arg); + } + std::string key = arg.substr(0, eq_pos); + std::string value = arg.substr(eq_pos + 1); + Config::set_value_in_file(ctx.config.primary_config_path(), key, value); + break; + } + + case 'p': // --show-config + ctx.config.visit_items(configuration_printer); + break; + + case 's': // --show-stats + fmt::print(Statistics::format_human_readable(ctx.config)); + break; + + case 'V': // --version + fmt::print(VERSION_TEXT, CCACHE_NAME, CCACHE_VERSION); + exit(EXIT_SUCCESS); + + case 'x': // --show-compression + { + ProgressBar progress_bar("Scanning..."); + compress_stats(ctx.config, + [&](double progress) { progress_bar.update(progress); }); + break; + } + + case 'X': // --recompress + { + optional wanted_level; + if (arg == "uncompressed") { + wanted_level = nullopt; + } else { + wanted_level = + Util::parse_signed(arg, INT8_MIN, INT8_MAX, "compression level"); + } + + ProgressBar progress_bar("Recompressing..."); + compress_recompress(ctx, wanted_level, [&](double progress) { + progress_bar.update(progress); + }); + break; + } + + case 'z': // --zero-stats + Statistics::zero_all_counters(ctx.config); + fmt::print("Statistics zeroed\n"); + break; + + default: + fmt::print(stderr, USAGE_TEXT, CCACHE_NAME, CCACHE_NAME); + exit(EXIT_FAILURE); + } + + // Some of the above switches might have changed config settings, so run the + // setup again. + ctx.config = Config(); + set_up_config(ctx.config); + } + + return 0; +} + +int ccache_main(int argc, const char* const* argv); + +int +ccache_main(int argc, const char* const* argv) +{ + try { + // Check if we are being invoked as "ccache". + std::string program_name(Util::base_name(argv[0])); + if (Util::same_program_name(program_name, CCACHE_NAME)) { + if (argc < 2) { + fmt::print(stderr, USAGE_TEXT, CCACHE_NAME, CCACHE_NAME); + exit(EXIT_FAILURE); + } + // If the first argument isn't an option, then assume we are being passed + // a compiler name and options. + if (argv[1][0] == '-') { + return handle_main_options(argc, argv); + } + } + + return cache_compilation(argc, argv); + } catch (const ErrorBase& e) { + fmt::print(stderr, "ccache: error: {}\n", e.what()); + return EXIT_FAILURE; + } +} diff --git a/src/ccache.h b/src/ccache.h deleted file mode 100644 index e5524a6..0000000 --- a/src/ccache.h +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright (C) 2002-2007 Andrew Tridgell -// Copyright (C) 2009-2020 Joel Rosdahl -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -#ifndef CCACHE_H -#define CCACHE_H - -#include "system.h" -#include "conf.h" -#include "counters.h" -#include "minitrace.h" - -#ifdef __GNUC__ -#define ATTR_FORMAT(x, y, z) __attribute__((format (x, y, z))) -#define ATTR_NORETURN __attribute__((noreturn)) -#else -#define ATTR_FORMAT(x, y, z) -#define ATTR_NORETURN -#endif - -#ifndef MYNAME -#define MYNAME "ccache" -#endif - -extern const char CCACHE_VERSION[]; - -// Statistics fields in storage order. -enum stats { - STATS_NONE = 0, - STATS_STDOUT = 1, - STATS_STATUS = 2, - STATS_ERROR = 3, - STATS_CACHEMISS = 4, - STATS_PREPROCESSOR = 5, - STATS_COMPILER = 6, - STATS_MISSING = 7, - STATS_CACHEHIT_CPP = 8, - STATS_ARGS = 9, - STATS_LINK = 10, - STATS_NUMFILES = 11, - STATS_TOTALSIZE = 12, - STATS_OBSOLETE_MAXFILES = 13, - STATS_OBSOLETE_MAXSIZE = 14, - STATS_SOURCELANG = 15, - STATS_BADOUTPUTFILE = 16, - STATS_NOINPUT = 17, - STATS_MULTIPLE = 18, - STATS_CONFTEST = 19, - STATS_UNSUPPORTED_OPTION = 20, - STATS_OUTSTDOUT = 21, - STATS_CACHEHIT_DIR = 22, - STATS_NOOUTPUT = 23, - STATS_EMPTYOUTPUT = 24, - STATS_BADEXTRAFILE = 25, - STATS_COMPCHECK = 26, - STATS_CANTUSEPCH = 27, - STATS_PREPROCESSING = 28, - STATS_NUMCLEANUPS = 29, - STATS_UNSUPPORTED_DIRECTIVE = 30, - STATS_ZEROTIMESTAMP = 31, - - STATS_END -}; - -enum guessed_compiler { - GUESSED_CLANG, - GUESSED_GCC, - GUESSED_NVCC, - GUESSED_PUMP, - GUESSED_UNKNOWN -}; - -extern enum guessed_compiler guessed_compiler; - -#define SLOPPY_INCLUDE_FILE_MTIME (1U << 0) -#define SLOPPY_INCLUDE_FILE_CTIME (1U << 1) -#define SLOPPY_TIME_MACROS (1U << 2) -#define SLOPPY_PCH_DEFINES (1U << 3) -// Allow us to match files based on their stats (size, mtime, ctime), without -// looking at their contents. -#define SLOPPY_FILE_STAT_MATCHES (1U << 4) -// Allow us to not include any system headers in the manifest include files, -// similar to -MM versus -M for dependencies. -#define SLOPPY_SYSTEM_HEADERS (1U << 5) -// Allow us to ignore ctimes when comparing file stats, so we can fake mtimes -// if we want to (it is much harder to fake ctimes, requires changing clock) -#define SLOPPY_FILE_STAT_MATCHES_CTIME (1U << 6) -// Allow us to not include the -index-store-path option in the manifest hash. -#define SLOPPY_CLANG_INDEX_STORE (1U << 7) -// Ignore locale settings. -#define SLOPPY_LOCALE (1U << 8) - -#define str_eq(s1, s2) (strcmp((s1), (s2)) == 0) -#define str_startswith(s, prefix) \ - (strncmp((s), (prefix), strlen((prefix))) == 0) -#define str_endswith(s, suffix) \ - (strlen(s) >= strlen(suffix) \ - && str_eq((s) + strlen(s) - strlen(suffix), (suffix))) -#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) - -// Buffer size for I/O operations. Should be a multiple of 4 KiB. -#define READ_BUFFER_SIZE 65536 - -// ---------------------------------------------------------------------------- -// args.c - -struct args { - char **argv; - int argc; -}; - -struct args *args_init(int, char **); -struct args *args_init_from_string(const char *); -struct args *args_init_from_gcc_atfile(const char *filename); -struct args *args_copy(struct args *args); -void args_free(struct args *args); -void args_add(struct args *args, const char *s); -void args_add_prefix(struct args *args, const char *s); -void args_extend(struct args *args, struct args *to_append); -void args_insert(struct args *dest, int index, struct args *src, bool replace); -void args_pop(struct args *args, int n); -void args_set(struct args *args, int index, const char *value); -void args_strip(struct args *args, const char *prefix); -void args_remove_first(struct args *args); -char *args_to_string(struct args *args); -bool args_equal(struct args *args1, struct args *args2); - -// ---------------------------------------------------------------------------- -// util.c - -void cc_log(const char *format, ...) ATTR_FORMAT(printf, 1, 2); -void cc_bulklog(const char *format, ...) ATTR_FORMAT(printf, 1, 2); -void cc_log_argv(const char *prefix, char **argv); -void cc_dump_debug_log_buffer(const char *path); -void fatal(const char *format, ...) ATTR_FORMAT(printf, 1, 2) ATTR_NORETURN; -void warn(const char *format, ...) ATTR_FORMAT(printf, 1, 2); - -void copy_fd(int fd_in, int fd_out); -int copy_file(const char *src, const char *dest, int compress_level, - bool via_tmp_file); -int move_file(const char *src, const char *dest, int compress_level); -int move_uncompressed_file(const char *src, const char *dest, - int compress_level); -bool file_is_compressed(const char *filename); -int create_dir(const char *dir); -int create_parent_dirs(const char *path); -const char *get_hostname(void); -const char *tmp_string(void); -char *format_hash_as_string(const unsigned char *hash, int size); -int create_cachedirtag(const char *dir); -char *format(const char *format, ...) ATTR_FORMAT(printf, 1, 2); -void reformat(char **ptr, const char *format, ...) ATTR_FORMAT(printf, 2, 3); -char *x_strdup(const char *s); -char *x_strndup(const char *s, size_t n); -void *x_malloc(size_t size); -void *x_calloc(size_t nmemb, size_t size); -void *x_realloc(void *ptr, size_t size); -void x_setenv(const char *name, const char *value); -void x_unsetenv(const char *name); -int x_fstat(int fd, struct stat *buf); -int x_lstat(const char *pathname, struct stat *buf); -int x_stat(const char *pathname, struct stat *buf); -void traverse(const char *dir, void (*fn)(const char *, struct stat *)); -char *basename(const char *path); -char *dirname(const char *path); -const char *get_extension(const char *path); -char *remove_extension(const char *path); -size_t file_size(struct stat *st); -char *format_human_readable_size(uint64_t size); -char *format_parsable_size_with_suffix(uint64_t size); -bool parse_size_with_suffix(const char *str, uint64_t *size); -char *x_realpath(const char *path); -char *gnu_getcwd(void); -#ifndef HAVE_LOCALTIME_R -struct tm *localtime_r(const time_t *timep, struct tm *result); -#endif -#ifndef HAVE_STRTOK_R -char *strtok_r(char *str, const char *delim, char **saveptr); -#endif -int create_tmp_fd(char **fname); -FILE *create_tmp_file(char **fname, const char *mode); -const char *get_home_directory(void); -char *get_cwd(void); -bool same_executable_name(const char *s1, const char *s2); -size_t common_dir_prefix_length(const char *s1, const char *s2); -char *get_relative_path(const char *from, const char *to); -bool is_absolute_path(const char *path); -bool is_full_path(const char *path); -bool is_symlink(const char *path); -void update_mtime(const char *path); -void x_exit(int status) ATTR_NORETURN; -int x_rename(const char *oldpath, const char *newpath); -int tmp_unlink(const char *path); -int x_unlink(const char *path); -int x_try_unlink(const char *path); -#ifndef _WIN32 -char *x_readlink(const char *path); -#endif -bool read_file(const char *path, size_t size_hint, char **data, size_t *size); -char *read_text_file(const char *path, size_t size_hint); -char *subst_env_in_string(const char *str, char **errmsg); -void set_cloexec_flag(int fd); -double time_seconds(void); - -// ---------------------------------------------------------------------------- -// stats.c - -void stats_update(enum stats stat); -void stats_flush(void); -unsigned stats_get_pending(enum stats stat); -void stats_zero(void); -void stats_summary(void); -void stats_print(void); -void stats_update_size(const char *sfile, int64_t size, int files); -void stats_get_obsolete_limits(const char *dir, unsigned *maxfiles, - uint64_t *maxsize); -void stats_set_sizes(const char *dir, unsigned num_files, uint64_t total_size); -void stats_add_cleanup(const char *dir, unsigned count); -void stats_timestamp(time_t time, struct counters *counters); -void stats_read(const char *path, struct counters *counters); -void stats_write(const char *path, struct counters *counters); - -// ---------------------------------------------------------------------------- -// exitfn.c - -void exitfn_init(void); -void exitfn_add_nullary(void (*function)(void)); -void exitfn_add(void (*function)(void *), void *context); -void exitfn_add_last(void (*function)(void *), void *context); -void exitfn_call(void); - -// ---------------------------------------------------------------------------- -// cleanup.c - -void clean_up_dir(struct conf *conf, const char *dir, double limit_multiple); -void clean_up_all(struct conf *conf); -void wipe_all(struct conf *conf); - -// ---------------------------------------------------------------------------- -// execute.c - -int execute(char **argv, int fd_out, int fd_err, pid_t *pid); -char *find_executable(const char *name, const char *exclude_name); -void print_command(FILE *fp, char **argv); -char *format_command(char **argv); - -// ---------------------------------------------------------------------------- -// lockfile.c - -bool lockfile_acquire(const char *path, unsigned staleness_limit); -void lockfile_release(const char *path); - -// ---------------------------------------------------------------------------- -// ccache.c - -extern time_t time_of_compilation; -extern bool output_is_precompiled_header; -void block_signals(void); -void unblock_signals(void); -bool cc_process_args(struct args *args, - struct args **preprocessor_args, - struct args **extra_args_to_hash, - struct args **compiler_args); -void cc_reset(void); -bool is_precompiled_header(const char *path); - -// ---------------------------------------------------------------------------- - -#ifdef HAVE_COMPAR_FN_T -#define COMPAR_FN_T __compar_fn_t -#else -typedef int (*COMPAR_FN_T)(const void *, const void *); -#endif - -// Work with silly DOS binary open. -#ifndef O_BINARY -#define O_BINARY 0 -#endif - -#ifdef _WIN32 -char *win32argvtos(char *prefix, char **argv, int *length); -char *win32getshell(char *path); -int win32execute(char *path, char **argv, int doreturn, - int fd_stdout, int fd_stderr); -void add_exe_ext_if_no_to_fullpath(char *full_path_win_ext, size_t max_size, - const char *ext, const char *path); -# ifndef _WIN32_WINNT -# define _WIN32_WINNT 0x0501 -# endif -# include -# define mkdir(a,b) mkdir(a) -# define link(src,dst) (CreateHardLink(dst,src,NULL) ? 0 : -1) -# define lstat(a,b) stat(a,b) -# define execv(a,b) win32execute(a,b,0,-1,-1) -# define execute(a,b,c,d) win32execute(*(a),a,1,b,c) -# define DIR_DELIM_CH '\\' -# define PATH_DELIM ";" -# define F_RDLCK 0 -# define F_WRLCK 0 -#else -# define DIR_DELIM_CH '/' -# define PATH_DELIM ":" -#endif - -#ifndef MAX -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif -#ifndef MIN -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif - -#endif // ifndef CCACHE_H diff --git a/src/ccache.hpp b/src/ccache.hpp new file mode 100644 index 0000000..bc8707f --- /dev/null +++ b/src/ccache.hpp @@ -0,0 +1,64 @@ +// Copyright (C) 2002-2007 Andrew Tridgell +// Copyright (C) 2009-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "third_party/nonstd/optional.hpp" + +#include +#include + +class Context; + +extern const char CCACHE_VERSION[]; + +enum class GuessedCompiler { clang, gcc, nvcc, pump, unknown }; + +const uint32_t SLOPPY_INCLUDE_FILE_MTIME = 1 << 0; +const uint32_t SLOPPY_INCLUDE_FILE_CTIME = 1 << 1; +const uint32_t SLOPPY_TIME_MACROS = 1 << 2; +const uint32_t SLOPPY_PCH_DEFINES = 1 << 3; +// Allow us to match files based on their stats (size, mtime, ctime), without +// looking at their contents. +const uint32_t SLOPPY_FILE_STAT_MATCHES = 1 << 4; +// Allow us to not include any system headers in the manifest include files, +// similar to -MM versus -M for dependencies. +const uint32_t SLOPPY_SYSTEM_HEADERS = 1 << 5; +// Allow us to ignore ctimes when comparing file stats, so we can fake mtimes +// if we want to (it is much harder to fake ctimes, requires changing clock) +const uint32_t SLOPPY_FILE_STAT_MATCHES_CTIME = 1 << 6; +// Allow us to not include the -index-store-path option in the manifest hash. +const uint32_t SLOPPY_CLANG_INDEX_STORE = 1 << 7; +// Ignore locale settings. +const uint32_t SLOPPY_LOCALE = 1 << 8; +// Allow caching even if -fmodules is used. +const uint32_t SLOPPY_MODULES = 1 << 9; + +using FindExecutableFunction = + std::function; + +// Tested by unit tests. +void find_compiler(Context& ctx, + const FindExecutableFunction& find_executable_function); +nonstd::optional +rewrite_dep_file_paths(const Context& ctx, const std::string& file_content); diff --git a/src/cleanup.c b/src/cleanup.c deleted file mode 100644 index 0b66caf..0000000 --- a/src/cleanup.c +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright (C) 2002-2006 Andrew Tridgell -// Copyright (C) 2009-2018 Joel Rosdahl -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -#include "ccache.h" - -#include - -static struct files { - char *fname; - time_t mtime; - uint64_t size; -} **files; -static unsigned allocated; // Size of the files array. -static unsigned num_files; // Number of used entries in the files array. - -static uint64_t cache_size; -static size_t files_in_cache; -static uint64_t cache_size_threshold; -static size_t files_in_cache_threshold; - -// File comparison function that orders files in mtime order, oldest first. -static int -files_compare(struct files **f1, struct files **f2) -{ - if ((*f2)->mtime == (*f1)->mtime) { - return strcmp((*f1)->fname, (*f2)->fname); - } - if ((*f2)->mtime > (*f1)->mtime) { - return -1; - } - return 1; -} - -// This builds the list of files in the cache. -static void -traverse_fn(const char *fname, struct stat *st) -{ - if (!S_ISREG(st->st_mode)) { - return; - } - - char *p = basename(fname); - if (str_eq(p, "stats")) { - goto out; - } - - if (str_startswith(p, ".nfs")) { - // Ignore temporary NFS files that may be left for open but deleted files. - goto out; - } - - // Delete any tmp files older than 1 hour. - if (strstr(p, ".tmp.") && st->st_mtime + 3600 < time(NULL)) { - x_unlink(fname); - goto out; - } - - if (strstr(p, "CACHEDIR.TAG")) { - goto out; - } - - if (num_files == allocated) { - allocated = 10000 + num_files*2; - files = (struct files **)x_realloc(files, sizeof(struct files *)*allocated); - } - - files[num_files] = (struct files *)x_malloc(sizeof(struct files)); - files[num_files]->fname = x_strdup(fname); - files[num_files]->mtime = st->st_mtime; - files[num_files]->size = file_size(st); - cache_size += files[num_files]->size; - files_in_cache++; - num_files++; - -out: - free(p); -} - -static void -delete_file(const char *path, size_t size, bool update_counters) -{ - bool deleted = x_try_unlink(path) == 0; - if (!deleted && errno != ENOENT && errno != ESTALE) { - cc_log("Failed to unlink %s (%s)", path, strerror(errno)); - } else if (update_counters) { - // The counters are intentionally subtracted even if there was no file to - // delete since the final cache size calculation will be incorrect if they - // aren't. (This can happen when there are several parallel ongoing - // cleanups of the same directory.) - cache_size -= size; - files_in_cache--; - } -} - -// Sort the files we've found and delete the oldest ones until we are below the -// thresholds. -static bool -sort_and_clean(void) -{ - if (num_files > 1) { - // Sort in ascending mtime order. - qsort(files, num_files, sizeof(struct files *), (COMPAR_FN_T)files_compare); - } - - // Delete enough files to bring us below the threshold. - bool cleaned = false; - for (unsigned i = 0; i < num_files; i++) { - const char *ext; - - if ((cache_size_threshold == 0 - || cache_size <= cache_size_threshold) - && (files_in_cache_threshold == 0 - || files_in_cache <= files_in_cache_threshold)) { - break; - } - - ext = get_extension(files[i]->fname); - if (str_eq(ext, ".stderr")) { - // Make sure that the .o file is deleted before .stderr, because if the - // ccache process gets killed after deleting the .stderr but before - // deleting the .o, the cached result will be inconsistent. (.stderr is - // the only file that is optional; any other file missing from the cache - // will be detected by get_file_from_cache.) - char *base = remove_extension(files[i]->fname); - char *o_file = format("%s.o", base); - - // Don't subtract this extra deletion from the cache size; that - // bookkeeping will be done when the loop reaches the .o file. If the - // loop doesn't reach the .o file since the target limits have been - // reached, the bookkeeping won't happen, but that small counter - // discrepancy won't do much harm and it will correct itself in the next - // cleanup. - delete_file(o_file, 0, false); - - free(o_file); - free(base); - } - delete_file(files[i]->fname, files[i]->size, true); - cleaned = true; - } - return cleaned; -} - -// Clean up one cache subdirectory. -void -clean_up_dir(struct conf *conf, const char *dir, double limit_multiple) -{ - cc_log("Cleaning up cache directory %s", dir); - - // When "max files" or "max cache size" is reached, one of the 16 cache - // subdirectories is cleaned up. When doing so, files are deleted (in LRU - // order) until the levels are below limit_multiple. - double cache_size_float = round(conf->max_size * limit_multiple / 16); - cache_size_threshold = (uint64_t)cache_size_float; - double files_in_cache_float = round(conf->max_files * limit_multiple / 16); - files_in_cache_threshold = (size_t)files_in_cache_float; - - num_files = 0; - cache_size = 0; - files_in_cache = 0; - - // Build a list of files. - traverse(dir, traverse_fn); - - // Clean the cache. - cc_log("Before cleanup: %.0f KiB, %.0f files", - (double)cache_size / 1024, - (double)files_in_cache); - bool cleaned = sort_and_clean(); - cc_log("After cleanup: %.0f KiB, %.0f files", - (double)cache_size / 1024, - (double)files_in_cache); - - if (cleaned) { - cc_log("Cleaned up cache directory %s", dir); - stats_add_cleanup(dir, 1); - } - - stats_set_sizes(dir, files_in_cache, cache_size); - - // Free it up. - for (unsigned i = 0; i < num_files; i++) { - free(files[i]->fname); - free(files[i]); - files[i] = NULL; - } - if (files) { - free(files); - } - allocated = 0; - files = NULL; - - num_files = 0; - cache_size = 0; - files_in_cache = 0; -} - -// Clean up all cache subdirectories. -void clean_up_all(struct conf *conf) -{ - for (int i = 0; i <= 0xF; i++) { - char *dname = format("%s/%1x", conf->cache_dir, i); - clean_up_dir(conf, dname, 1.0); - free(dname); - } -} - -// Traverse function for wiping files. -static void wipe_fn(const char *fname, struct stat *st) -{ - if (!S_ISREG(st->st_mode)) { - return; - } - - char *p = basename(fname); - if (str_eq(p, "stats")) { - free(p); - return; - } - free(p); - - files_in_cache++; - - x_unlink(fname); -} - -// Wipe one cache subdirectory. -static void -wipe_dir(const char *dir) -{ - cc_log("Clearing out cache directory %s", dir); - - files_in_cache = 0; - - traverse(dir, wipe_fn); - - if (files_in_cache > 0) { - cc_log("Cleared out cache directory %s", dir); - stats_add_cleanup(dir, 1); - } - - files_in_cache = 0; -} - -// Wipe all cached files in all subdirectories. -void wipe_all(struct conf *conf) -{ - for (int i = 0; i <= 0xF; i++) { - char *dname = format("%s/%1x", conf->cache_dir, i); - wipe_dir(dname); - free(dname); - } - - // Fix the counters. - clean_up_all(conf); -} diff --git a/src/cleanup.cpp b/src/cleanup.cpp new file mode 100644 index 0000000..3754c32 --- /dev/null +++ b/src/cleanup.cpp @@ -0,0 +1,237 @@ +// Copyright (C) 2002-2006 Andrew Tridgell +// Copyright (C) 2009-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "cleanup.hpp" + +#include "CacheFile.hpp" +#include "Config.hpp" +#include "Context.hpp" +#include "Logging.hpp" +#include "Util.hpp" + +#ifdef INODE_CACHE_SUPPORTED +# include "InodeCache.hpp" +#endif + +#include + +using Logging::log; + +static void +delete_file(const std::string& path, + uint64_t size, + uint64_t* cache_size, + uint64_t* files_in_cache) +{ + bool deleted = Util::unlink_safe(path, Util::UnlinkLog::ignore_failure); + if (!deleted && errno != ENOENT && errno != ESTALE) { + log("Failed to unlink {} ({})", path, strerror(errno)); + } else if (cache_size && files_in_cache) { + // The counters are intentionally subtracted even if there was no file to + // delete since the final cache size calculation will be incorrect if they + // aren't. (This can happen when there are several parallel ongoing + // cleanups of the same directory.) + *cache_size -= size; + --*files_in_cache; + } +} + +static void +update_counters(const std::string& dir, + uint64_t files_in_cache, + uint64_t cache_size, + bool cleanup_performed) +{ + const std::string stats_file = dir + "/stats"; + Statistics::update(stats_file, [=](Counters& cs) { + if (cleanup_performed) { + cs.increment(Statistic::cleanups_performed); + } + cs.set(Statistic::files_in_cache, files_in_cache); + cs.set(Statistic::cache_size_kibibyte, cache_size / 1024); + }); +} + +void +clean_old(const Context& ctx, + const Util::ProgressReceiver& progress_receiver, + uint64_t max_age) +{ + Util::for_each_level_1_subdir( + ctx.config.cache_dir(), + [&](const std::string& subdir, + const Util::ProgressReceiver& sub_progress_receiver) { + clean_up_dir(subdir, 0, 0, max_age, sub_progress_receiver); + }, + progress_receiver); +} + +// Clean up one cache subdirectory. +void +clean_up_dir(const std::string& subdir, + uint64_t max_size, + uint64_t max_files, + uint64_t max_age, + const Util::ProgressReceiver& progress_receiver) +{ + log("Cleaning up cache directory {}", subdir); + + std::vector> files; + Util::get_level_1_files( + subdir, [&](double progress) { progress_receiver(progress / 3); }, files); + + uint64_t cache_size = 0; + uint64_t files_in_cache = 0; + time_t current_time = time(nullptr); + + for (size_t i = 0; i < files.size(); + ++i, progress_receiver(1.0 / 3 + 1.0 * i / files.size() / 3)) { + const auto& file = files[i]; + + if (!file->lstat().is_regular()) { + // Not a file or missing file. + continue; + } + + // Delete any tmp files older than 1 hour right away. + if (file->lstat().mtime() + 3600 < current_time + && Util::base_name(file->path()).find(".tmp.") != std::string::npos) { + Util::unlink_tmp(file->path()); + continue; + } + + cache_size += file->lstat().size_on_disk(); + files_in_cache += 1; + } + + // Sort according to modification time, oldest first. + std::sort(files.begin(), + files.end(), + [](const std::shared_ptr& f1, + const std::shared_ptr& f2) { + return f1->lstat().mtime() < f2->lstat().mtime(); + }); + + log("Before cleanup: {:.0f} KiB, {:.0f} files", + static_cast(cache_size) / 1024, + static_cast(files_in_cache)); + + bool cleaned = false; + for (size_t i = 0; i < files.size(); + ++i, progress_receiver(2.0 / 3 + 1.0 * i / files.size() / 3)) { + const auto& file = files[i]; + + if (!file->lstat() || file->lstat().is_directory()) { + continue; + } + + if ((max_size == 0 || cache_size <= max_size) + && (max_files == 0 || files_in_cache <= max_files) + && (max_age == 0 + || file->lstat().mtime() + > (current_time - static_cast(max_age)))) { + break; + } + + if (Util::ends_with(file->path(), ".stderr")) { + // In order to be nice to legacy ccache versions, make sure that the .o + // file is deleted before .stderr, because if the ccache process gets + // killed after deleting the .stderr but before deleting the .o, the + // cached result will be inconsistent. (.stderr is the only file that is + // optional for legacy ccache versions; any other file missing from the + // cache will be detected.) + std::string o_file = + file->path().substr(0, file->path().size() - 6) + "o"; + + // Don't subtract this extra deletion from the cache size; that + // bookkeeping will be done when the loop reaches the .o file. If the + // loop doesn't reach the .o file since the target limits have been + // reached, the bookkeeping won't happen, but that small counter + // discrepancy won't do much harm and it will correct itself in the next + // cleanup. + delete_file(o_file, 0, nullptr, nullptr); + } + + delete_file( + file->path(), file->lstat().size_on_disk(), &cache_size, &files_in_cache); + cleaned = true; + } + + log("After cleanup: {:.0f} KiB, {:.0f} files", + static_cast(cache_size) / 1024, + static_cast(files_in_cache)); + + if (cleaned) { + log("Cleaned up cache directory {}", subdir); + } + + update_counters(subdir, files_in_cache, cache_size, cleaned); +} + +// Clean up all cache subdirectories. +void +clean_up_all(const Config& config, + const Util::ProgressReceiver& progress_receiver) +{ + Util::for_each_level_1_subdir( + config.cache_dir(), + [&](const std::string& subdir, + const Util::ProgressReceiver& sub_progress_receiver) { + clean_up_dir(subdir, + config.max_size() / 16, + config.max_files() / 16, + 0, + sub_progress_receiver); + }, + progress_receiver); +} + +// Wipe one cache subdirectory. +static void +wipe_dir(const std::string& subdir, + const Util::ProgressReceiver& progress_receiver) +{ + log("Clearing out cache directory {}", subdir); + + std::vector> files; + Util::get_level_1_files( + subdir, [&](double progress) { progress_receiver(progress / 2); }, files); + + for (size_t i = 0; i < files.size(); ++i) { + Util::unlink_safe(files[i]->path()); + progress_receiver(0.5 + 0.5 * i / files.size()); + } + + const bool cleared = !files.empty(); + if (cleared) { + log("Cleared out cache directory {}", subdir); + } + update_counters(subdir, 0, 0, cleared); +} + +// Wipe all cached files in all subdirectories. +void +wipe_all(const Context& ctx, const Util::ProgressReceiver& progress_receiver) +{ + Util::for_each_level_1_subdir( + ctx.config.cache_dir(), wipe_dir, progress_receiver); +#ifdef INODE_CACHE_SUPPORTED + ctx.inode_cache.drop(); +#endif +} diff --git a/src/cleanup.hpp b/src/cleanup.hpp new file mode 100644 index 0000000..053eb53 --- /dev/null +++ b/src/cleanup.hpp @@ -0,0 +1,44 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Util.hpp" + +#include + +class Config; +class Context; + +void clean_old(const Context& ctx, + const Util::ProgressReceiver& progress_receiver, + uint64_t max_age); + +void clean_up_dir(const std::string& subdir, + uint64_t max_size, + uint64_t max_files, + uint64_t max_age, + const Util::ProgressReceiver& progress_receiver); + +void clean_up_all(const Config& config, + const Util::ProgressReceiver& progress_receiver); + +void wipe_all(const Context& ctx, + const Util::ProgressReceiver& progress_receiver); diff --git a/src/compopt.c b/src/compopt.c deleted file mode 100644 index cbf9f40..0000000 --- a/src/compopt.c +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright (C) 2010-2020 Joel Rosdahl -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -#include "ccache.h" -#include "compopt.h" - -// The option it too hard to handle at all. -#define TOO_HARD (1 << 0) - -// The option it too hard for the direct mode. -#define TOO_HARD_DIRECT (1 << 1) - -// The option takes a separate argument, e.g. "-D FOO=1". -#define TAKES_ARG (1 << 2) - -// The option takes a concatenated argument, e.g. "-DFOO=1". -#define TAKES_CONCAT_ARG (1 << 3) - -// The argument to the option is a path that may be rewritten if base_dir is -// used. -#define TAKES_PATH (1 << 4) - -// The option only affects preprocessing; not passed to the compiler if -// run_second_cpp is false. -#define AFFECTS_CPP (1 << 5) - -// The option only affects compilation; not passed to the preprocessor. -#define AFFECTS_COMP (1 << 6) - -struct compopt { - const char *name; - int type; -}; - -static const struct compopt compopts[] = { - {"--Werror", TAKES_ARG}, // nvcc - {"--analyze", TOO_HARD}, // clang - {"--compiler-bindir", AFFECTS_CPP | TAKES_ARG}, // nvcc - {"--libdevice-directory", AFFECTS_CPP | TAKES_ARG}, // nvcc - {"--output-directory", AFFECTS_CPP | TAKES_ARG}, // nvcc - {"--param", TAKES_ARG}, - {"--save-temps", TOO_HARD}, - {"--save-temps=cwd", TOO_HARD}, - {"--save-temps=obj", TOO_HARD}, - {"--serialize-diagnostics", TAKES_ARG | TAKES_PATH}, - {"-A", TAKES_ARG}, - {"-B", TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-D", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG}, - {"-E", TOO_HARD}, - {"-F", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-G", TAKES_ARG}, - {"-I", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-L", TAKES_ARG}, - {"-M", TOO_HARD}, - {"-MF", TAKES_ARG}, - {"-MM", TOO_HARD}, - {"-MQ", TAKES_ARG}, - {"-MT", TAKES_ARG}, - {"-P", TOO_HARD}, - {"-U", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG}, - {"-V", TAKES_ARG}, - {"-Wa,", TAKES_CONCAT_ARG | AFFECTS_COMP}, - {"-Werror", AFFECTS_COMP}, // don't exit with error when preprocessing - {"-Wl,", TAKES_CONCAT_ARG | AFFECTS_COMP}, - {"-Wno-error", AFFECTS_COMP}, - {"-Xassembler", TAKES_ARG | TAKES_CONCAT_ARG | AFFECTS_COMP}, - {"-Xclang", TAKES_ARG}, - {"-Xlinker", TAKES_ARG | TAKES_CONCAT_ARG | AFFECTS_COMP}, - {"-Xpreprocessor", AFFECTS_CPP | TOO_HARD_DIRECT | TAKES_ARG}, - {"-all_load", AFFECTS_COMP}, - {"-analyze", TOO_HARD}, // clang - {"-arch", TAKES_ARG}, - {"-aux-info", TAKES_ARG}, - {"-b", TAKES_ARG}, - {"-bind_at_load", AFFECTS_COMP}, - {"-bundle", AFFECTS_COMP}, - {"-ccbin", AFFECTS_CPP | TAKES_ARG}, // nvcc - {"-fmodules", TOO_HARD}, - {"-fno-working-directory", AFFECTS_CPP}, - {"-fplugin=libcc1plugin", TOO_HARD}, // interaction with GDB - {"-frepo", TOO_HARD}, - {"-ftime-trace", TOO_HARD}, // clang - {"-fworking-directory", AFFECTS_CPP}, - {"-gtoggle", TOO_HARD}, - {"-idirafter", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-iframework", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-imacros", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-imultilib", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-include", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-include-pch", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-install_name", TAKES_ARG}, // Darwin linker option - {"-iprefix", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-iquote", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-isysroot", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-isystem", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-iwithprefix", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-iwithprefixbefore", - AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-ldir", AFFECTS_CPP | TAKES_ARG}, // nvcc - {"-nolibc", AFFECTS_COMP}, - {"-nostdinc", AFFECTS_CPP}, - {"-nostdinc++", AFFECTS_CPP}, - {"-odir", AFFECTS_CPP | TAKES_ARG}, // nvcc - {"-pie", AFFECTS_COMP}, - {"-prebind", AFFECTS_COMP}, - {"-preload", AFFECTS_COMP}, - {"-rdynamic", AFFECTS_COMP}, - {"-remap", AFFECTS_CPP}, - {"-save-temps", TOO_HARD}, - {"-save-temps=cwd", TOO_HARD}, - {"-save-temps=obj", TOO_HARD}, - {"-stdlib=", AFFECTS_CPP | TAKES_CONCAT_ARG}, - {"-trigraphs", AFFECTS_CPP}, - {"-u", TAKES_ARG | TAKES_CONCAT_ARG}, -}; - - -static int -compare_compopts(const void *key1, const void *key2) -{ - const struct compopt *opt1 = (const struct compopt *)key1; - const struct compopt *opt2 = (const struct compopt *)key2; - return strcmp(opt1->name, opt2->name); -} - -static int -compare_prefix_compopts(const void *key1, const void *key2) -{ - const struct compopt *opt1 = (const struct compopt *)key1; - const struct compopt *opt2 = (const struct compopt *)key2; - return strncmp(opt1->name, opt2->name, strlen(opt2->name)); -} - -static const struct compopt * -find(const char *option) -{ - struct compopt key; - key.name = option; - return bsearch( - &key, compopts, ARRAY_SIZE(compopts), sizeof(compopts[0]), - compare_compopts); -} - -static const struct compopt * -find_prefix(const char *option) -{ - struct compopt key; - key.name = option; - return bsearch( - &key, compopts, ARRAY_SIZE(compopts), sizeof(compopts[0]), - compare_prefix_compopts); -} - -// Runs fn on the first two characters of option. -bool -compopt_short(bool (*fn)(const char *), const char *option) -{ - char *short_opt = x_strndup(option, 2); - bool retval = fn(short_opt); - free(short_opt); - return retval; -} - -// Used by unittest/test_compopt.c. -bool compopt_verify_sortedness_and_flags(void); - -// For test purposes. -bool -compopt_verify_sortedness_and_flags(void) -{ - for (size_t i = 0; i < ARRAY_SIZE(compopts); i++) { - if (compopts[i].type & TOO_HARD && compopts[i].type & TAKES_CONCAT_ARG) { - fprintf(stderr, - "type (TOO_HARD | TAKES_CONCAT_ARG) not allowed, used by %s\n", - compopts[i].name); - return false; - } - - if (i == 0) { - continue; - } - - if (strcmp(compopts[i-1].name, compopts[i].name) >= 0) { - fprintf(stderr, - "compopt_verify_sortedness_and_flags: %s >= %s\n", - compopts[i-1].name, - compopts[i].name); - return false; - } - } - return true; -} - -bool -compopt_affects_cpp(const char *option) -{ - const struct compopt *co = find(option); - return co && (co->type & AFFECTS_CPP); -} - -bool -compopt_affects_comp(const char *option) -{ - const struct compopt *co = find(option); - return co && (co->type & AFFECTS_COMP); -} - -bool -compopt_too_hard(const char *option) -{ - const struct compopt *co = find(option); - return co && (co->type & TOO_HARD); -} - -bool -compopt_too_hard_for_direct_mode(const char *option) -{ - const struct compopt *co = find(option); - return co && (co->type & TOO_HARD_DIRECT); -} - -bool -compopt_takes_path(const char *option) -{ - const struct compopt *co = find(option); - return co && (co->type & TAKES_PATH); -} - -bool -compopt_takes_arg(const char *option) -{ - const struct compopt *co = find(option); - return co && (co->type & TAKES_ARG); -} - -bool -compopt_takes_concat_arg(const char *option) -{ - const struct compopt *co = find(option); - return co && (co->type & TAKES_CONCAT_ARG); -} - -// Determines if the prefix of the option matches any option and affects the -// preprocessor. -bool -compopt_prefix_affects_cpp(const char *option) -{ - // Prefix options have to take concatenated args. - const struct compopt *co = find_prefix(option); - return co && (co->type & TAKES_CONCAT_ARG) && (co->type & AFFECTS_CPP); -} - -// Determines if the prefix of the option matches any option and affects the -// preprocessor. -bool -compopt_prefix_affects_comp(const char *option) -{ - // Prefix options have to take concatenated args. - const struct compopt *co = find_prefix(option); - return co && (co->type & TAKES_CONCAT_ARG) && (co->type & AFFECTS_COMP); -} diff --git a/src/compopt.cpp b/src/compopt.cpp new file mode 100644 index 0000000..518d004 --- /dev/null +++ b/src/compopt.cpp @@ -0,0 +1,276 @@ +// Copyright (C) 2010-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "compopt.hpp" + +#include "third_party/fmt/core.h" + +// The option it too hard to handle at all. +#define TOO_HARD (1 << 0) + +// The option it too hard for the direct mode. +#define TOO_HARD_DIRECT (1 << 1) + +// The option takes a separate argument, e.g. "-D FOO=1". +#define TAKES_ARG (1 << 2) + +// The option takes a concatenated argument, e.g. "-DFOO=1". +#define TAKES_CONCAT_ARG (1 << 3) + +// The argument to the option is a path that may be rewritten if base_dir is +// used. +#define TAKES_PATH (1 << 4) + +// The option only affects preprocessing; not passed to the compiler if +// run_second_cpp is false. +#define AFFECTS_CPP (1 << 5) + +// The option only affects compilation; not passed to the preprocessor. +#define AFFECTS_COMP (1 << 6) + +struct CompOpt +{ + const char* name; + int type; +}; + +const CompOpt compopts[] = { + {"--Werror", TAKES_ARG}, // nvcc + {"--analyze", TOO_HARD}, // Clang + {"--compiler-bindir", AFFECTS_CPP | TAKES_ARG}, // nvcc + {"--libdevice-directory", AFFECTS_CPP | TAKES_ARG}, // nvcc + {"--output-directory", AFFECTS_CPP | TAKES_ARG}, // nvcc + {"--param", TAKES_ARG}, + {"--save-temps", TOO_HARD}, + {"--save-temps=cwd", TOO_HARD}, + {"--save-temps=obj", TOO_HARD}, + {"--serialize-diagnostics", TAKES_ARG | TAKES_PATH}, + {"-A", TAKES_ARG}, + {"-B", TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-D", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG}, + {"-E", TOO_HARD}, + {"-F", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-G", TAKES_ARG}, + {"-I", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-L", TAKES_ARG}, + {"-M", TOO_HARD}, + {"-MF", TAKES_ARG}, + {"-MJ", TAKES_ARG | TOO_HARD}, + {"-MM", TOO_HARD}, + {"-MQ", TAKES_ARG}, + {"-MT", TAKES_ARG}, + {"-P", TOO_HARD}, + {"-U", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG}, + {"-V", TAKES_ARG}, + {"-Wa,", TAKES_CONCAT_ARG | AFFECTS_COMP}, + {"-Werror", AFFECTS_COMP}, // don't exit with error when preprocessing + {"-Wl,", TAKES_CONCAT_ARG | AFFECTS_COMP}, + {"-Wno-error", AFFECTS_COMP}, + {"-Xassembler", TAKES_ARG | TAKES_CONCAT_ARG | AFFECTS_COMP}, + {"-Xclang", TAKES_ARG}, + {"-Xlinker", TAKES_ARG | TAKES_CONCAT_ARG | AFFECTS_COMP}, + {"-Xpreprocessor", AFFECTS_CPP | TOO_HARD_DIRECT | TAKES_ARG}, + {"-all_load", AFFECTS_COMP}, + {"-analyze", TOO_HARD}, // Clang + {"-arch", TAKES_ARG}, + {"-aux-info", TAKES_ARG}, + {"-b", TAKES_ARG}, + {"-bind_at_load", AFFECTS_COMP}, + {"-bundle", AFFECTS_COMP}, + {"-ccbin", AFFECTS_CPP | TAKES_ARG}, // nvcc + {"-emit-pch", AFFECTS_COMP}, // Clang + {"-emit-pth", AFFECTS_COMP}, // Clang + {"-fno-working-directory", AFFECTS_CPP}, + {"-fplugin=libcc1plugin", TOO_HARD}, // interaction with GDB + {"-frepo", TOO_HARD}, + {"-ftime-trace", TOO_HARD}, // Clang + {"-fworking-directory", AFFECTS_CPP}, + {"-gtoggle", TOO_HARD}, + {"-idirafter", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-iframework", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-imacros", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-imultilib", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-include", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-include-pch", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-include-pth", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-install_name", TAKES_ARG}, // Darwin linker option + {"-iprefix", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-iquote", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-isysroot", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-isystem", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-iwithprefix", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-iwithprefixbefore", + AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-ldir", AFFECTS_CPP | TAKES_ARG}, // nvcc + {"-nolibc", AFFECTS_COMP}, + {"-nostdinc", AFFECTS_CPP}, + {"-nostdinc++", AFFECTS_CPP}, + {"-odir", AFFECTS_CPP | TAKES_ARG}, // nvcc + {"-pie", AFFECTS_COMP}, + {"-prebind", AFFECTS_COMP}, + {"-preload", AFFECTS_COMP}, + {"-rdynamic", AFFECTS_COMP}, + {"-remap", AFFECTS_CPP}, + {"-save-temps", TOO_HARD}, + {"-save-temps=cwd", TOO_HARD}, + {"-save-temps=obj", TOO_HARD}, + {"-stdlib=", AFFECTS_CPP | TAKES_CONCAT_ARG}, + {"-trigraphs", AFFECTS_CPP}, + {"-u", TAKES_ARG | TAKES_CONCAT_ARG}, +}; + +static int +compare_compopts(const void* key1, const void* key2) +{ + const CompOpt* opt1 = static_cast(key1); + const CompOpt* opt2 = static_cast(key2); + return strcmp(opt1->name, opt2->name); +} + +static int +compare_prefix_compopts(const void* key1, const void* key2) +{ + const CompOpt* opt1 = static_cast(key1); + const CompOpt* opt2 = static_cast(key2); + return strncmp(opt1->name, opt2->name, strlen(opt2->name)); +} + +static const CompOpt* +find(const std::string& option) +{ + CompOpt key; + key.name = option.c_str(); + void* result = bsearch(&key, + compopts, + ARRAY_SIZE(compopts), + sizeof(compopts[0]), + compare_compopts); + return static_cast(result); +} + +static const CompOpt* +find_prefix(const std::string& option) +{ + CompOpt key; + key.name = option.c_str(); + void* result = bsearch(&key, + compopts, + ARRAY_SIZE(compopts), + sizeof(compopts[0]), + compare_prefix_compopts); + return static_cast(result); +} + +// Used by unittest/test_compopt.cpp. +bool compopt_verify_sortedness_and_flags(); + +// For test purposes. +bool +compopt_verify_sortedness_and_flags() +{ + for (size_t i = 0; i < ARRAY_SIZE(compopts); i++) { + if (compopts[i].type & TOO_HARD && compopts[i].type & TAKES_CONCAT_ARG) { + fmt::print(stderr, + "type (TOO_HARD | TAKES_CONCAT_ARG) not allowed, used by {}\n", + compopts[i].name); + return false; + } + + if (i == 0) { + continue; + } + + if (strcmp(compopts[i - 1].name, compopts[i].name) >= 0) { + fmt::print(stderr, + "compopt_verify_sortedness: {} >= {}\n", + compopts[i - 1].name, + compopts[i].name); + return false; + } + } + return true; +} + +bool +compopt_affects_cpp_output(const std::string& option) +{ + const CompOpt* co = find(option); + return co && (co->type & AFFECTS_CPP); +} + +bool +compopt_affects_compiler_output(const std::string& option) +{ + const CompOpt* co = find(option); + return co && (co->type & AFFECTS_COMP); +} + +bool +compopt_too_hard(const std::string& option) +{ + const CompOpt* co = find(option); + return co && (co->type & TOO_HARD); +} + +bool +compopt_too_hard_for_direct_mode(const std::string& option) +{ + const CompOpt* co = find(option); + return co && (co->type & TOO_HARD_DIRECT); +} + +bool +compopt_takes_path(const std::string& option) +{ + const CompOpt* co = find(option); + return co && (co->type & TAKES_PATH); +} + +bool +compopt_takes_arg(const std::string& option) +{ + const CompOpt* co = find(option); + return co && (co->type & TAKES_ARG); +} + +bool +compopt_takes_concat_arg(const std::string& option) +{ + const CompOpt* co = find(option); + return co && (co->type & TAKES_CONCAT_ARG); +} + +// Determines if the prefix of the option matches any option and affects the +// preprocessor. +bool +compopt_prefix_affects_cpp_output(const std::string& option) +{ + // Prefix options have to take concatenated args. + const CompOpt* co = find_prefix(option); + return co && (co->type & TAKES_CONCAT_ARG) && (co->type & AFFECTS_CPP); +} + +// Determines if the prefix of the option matches any option and affects the +// preprocessor. +bool +compopt_prefix_affects_compiler_output(const std::string& option) +{ + // Prefix options have to take concatenated args. + const CompOpt* co = find_prefix(option); + return co && (co->type & TAKES_CONCAT_ARG) && (co->type & AFFECTS_COMP); +} diff --git a/src/compopt.h b/src/compopt.h deleted file mode 100644 index 4f1f760..0000000 --- a/src/compopt.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef CCACHE_COMPOPT_H -#define CCACHE_COMPOPT_H - -#include "system.h" - -bool compopt_short(bool (*fn)(const char *option), const char *option); -bool compopt_affects_cpp(const char *option); -bool compopt_affects_comp(const char *option); -bool compopt_too_hard(const char *option); -bool compopt_too_hard_for_direct_mode(const char *option); -bool compopt_takes_path(const char *option); -bool compopt_takes_arg(const char *option); -bool compopt_takes_concat_arg(const char *option); -bool compopt_prefix_affects_cpp(const char *option); -bool compopt_prefix_affects_comp(const char *option); - -#endif // CCACHE_COMPOPT_H diff --git a/src/compopt.hpp b/src/compopt.hpp new file mode 100644 index 0000000..a016928 --- /dev/null +++ b/src/compopt.hpp @@ -0,0 +1,35 @@ +// Copyright (C) 2010-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include + +bool compopt_short(bool (*fn)(const std::string& option), + const std::string& option); +bool compopt_affects_cpp_output(const std::string& option); +bool compopt_affects_compiler_output(const std::string& option); +bool compopt_too_hard(const std::string& option); +bool compopt_too_hard_for_direct_mode(const std::string& option); +bool compopt_takes_path(const std::string& option); +bool compopt_takes_arg(const std::string& option); +bool compopt_takes_concat_arg(const std::string& option); +bool compopt_prefix_affects_cpp_output(const std::string& option); +bool compopt_prefix_affects_compiler_output(const std::string& option); diff --git a/src/compress.cpp b/src/compress.cpp new file mode 100644 index 0000000..1e0982f --- /dev/null +++ b/src/compress.cpp @@ -0,0 +1,369 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "compress.hpp" + +#include "AtomicFile.hpp" +#include "CacheEntryReader.hpp" +#include "CacheEntryWriter.hpp" +#include "Context.hpp" +#include "File.hpp" +#include "Logging.hpp" +#include "Manifest.hpp" +#include "Result.hpp" +#include "Statistics.hpp" +#include "StdMakeUnique.hpp" +#include "ThreadPool.hpp" +#include "ZstdCompressor.hpp" +#include "assertions.hpp" + +#include "third_party/fmt/core.h" + +#include +#include + +using Logging::log; +using nonstd::optional; + +namespace { + +class RecompressionStatistics +{ +public: + void update(uint64_t content_size, + uint64_t old_size, + uint64_t new_size, + uint64_t incompressible_size); + uint64_t content_size() const; + uint64_t old_size() const; + uint64_t new_size() const; + uint64_t incompressible_size() const; + +private: + mutable std::mutex m_mutex; + uint64_t m_content_size = 0; + uint64_t m_old_size = 0; + uint64_t m_new_size = 0; + uint64_t m_incompressible_size = 0; +}; + +void +RecompressionStatistics::update(uint64_t content_size, + uint64_t old_size, + uint64_t new_size, + uint64_t incompressible_size) +{ + std::unique_lock lock(m_mutex); + m_incompressible_size += incompressible_size; + m_content_size += content_size; + m_old_size += old_size; + m_new_size += new_size; +} + +uint64_t +RecompressionStatistics::content_size() const +{ + std::unique_lock lock(m_mutex); + return m_content_size; +} + +uint64_t +RecompressionStatistics::old_size() const +{ + std::unique_lock lock(m_mutex); + return m_old_size; +} + +uint64_t +RecompressionStatistics::new_size() const +{ + std::unique_lock lock(m_mutex); + return m_new_size; +} + +uint64_t +RecompressionStatistics::incompressible_size() const +{ + std::unique_lock lock(m_mutex); + return m_incompressible_size; +} + +File +open_file(const std::string& path, const char* mode) +{ + File f(path, mode); + if (!f) { + throw Error("failed to open {} for reading: {}", path, strerror(errno)); + } + return f; +} + +std::unique_ptr +create_reader(const CacheFile& cache_file, FILE* stream) +{ + if (cache_file.type() == CacheFile::Type::unknown) { + throw Error("unknown file type for {}", cache_file.path()); + } + + switch (cache_file.type()) { + case CacheFile::Type::result: + return std::make_unique( + stream, Result::k_magic, Result::k_version); + + case CacheFile::Type::manifest: + return std::make_unique( + stream, Manifest::k_magic, Manifest::k_version); + + case CacheFile::Type::unknown: + ASSERT(false); // Handled at function entry. + } + + ASSERT(false); +} + +std::unique_ptr +create_writer(FILE* stream, + const CacheEntryReader& reader, + Compression::Type compression_type, + int8_t compression_level) +{ + return std::make_unique(stream, + reader.magic(), + reader.version(), + compression_type, + compression_level, + reader.payload_size()); +} + +void +recompress_file(RecompressionStatistics& statistics, + const std::string& stats_file, + const CacheFile& cache_file, + optional level) +{ + auto file = open_file(cache_file.path(), "rb"); + auto reader = create_reader(cache_file, file.get()); + + auto old_stat = Stat::stat(cache_file.path(), Stat::OnError::log); + uint64_t content_size = reader->content_size(); + int8_t wanted_level = + level ? (*level == 0 ? ZstdCompressor::default_compression_level : *level) + : 0; + + if (reader->compression_level() == wanted_level) { + statistics.update(content_size, old_stat.size(), old_stat.size(), 0); + return; + } + + log("Recompressing {} to {}", + cache_file.path(), + level ? fmt::format("level {}", wanted_level) : "uncompressed"); + AtomicFile atomic_new_file(cache_file.path(), AtomicFile::Mode::binary); + auto writer = + create_writer(atomic_new_file.stream(), + *reader, + level ? Compression::Type::zstd : Compression::Type::none, + wanted_level); + + char buffer[READ_BUFFER_SIZE]; + size_t bytes_left = reader->payload_size(); + while (bytes_left > 0) { + size_t bytes_to_read = std::min(bytes_left, sizeof(buffer)); + reader->read(buffer, bytes_to_read); + writer->write(buffer, bytes_to_read); + bytes_left -= bytes_to_read; + } + reader->finalize(); + writer->finalize(); + + file.close(); + + atomic_new_file.commit(); + auto new_stat = Stat::stat(cache_file.path(), Stat::OnError::log); + + Statistics::update(stats_file, [=](Counters& cs) { + cs.increment(Statistic::cache_size_kibibyte, + Util::size_change_kibibyte(old_stat, new_stat)); + }); + + statistics.update(content_size, old_stat.size(), new_stat.size(), 0); + + log("Recompression of {} done", cache_file.path()); +} + +} // namespace + +void +compress_stats(const Config& config, + const Util::ProgressReceiver& progress_receiver) +{ + uint64_t on_disk_size = 0; + uint64_t compr_size = 0; + uint64_t content_size = 0; + uint64_t incompr_size = 0; + + Util::for_each_level_1_subdir( + config.cache_dir(), + [&](const std::string& subdir, + const Util::ProgressReceiver& sub_progress_receiver) { + std::vector> files; + Util::get_level_1_files( + subdir, + [&](double progress) { sub_progress_receiver(progress / 2); }, + files); + + for (size_t i = 0; i < files.size(); ++i) { + const auto& cache_file = files[i]; + on_disk_size += cache_file->lstat().size_on_disk(); + + try { + auto file = open_file(cache_file->path(), "rb"); + auto reader = create_reader(*cache_file, file.get()); + compr_size += cache_file->lstat().size(); + content_size += reader->content_size(); + } catch (Error&) { + incompr_size += cache_file->lstat().size(); + } + + sub_progress_receiver(1.0 / 2 + 1.0 * i / files.size() / 2); + } + }, + progress_receiver); + + if (isatty(STDOUT_FILENO)) { + fmt::print("\n\n"); + } + + double ratio = + compr_size > 0 ? static_cast(content_size) / compr_size : 0.0; + double savings = ratio > 0.0 ? 100.0 - (100.0 / ratio) : 0.0; + + std::string on_disk_size_str = Util::format_human_readable_size(on_disk_size); + std::string cache_size_str = + Util::format_human_readable_size(compr_size + incompr_size); + std::string compr_size_str = Util::format_human_readable_size(compr_size); + std::string content_size_str = Util::format_human_readable_size(content_size); + std::string incompr_size_str = Util::format_human_readable_size(incompr_size); + + fmt::print("Total data: {:>8s} ({} disk blocks)\n", + cache_size_str, + on_disk_size_str); + fmt::print("Compressed data: {:>8s} ({:.1f}% of original size)\n", + compr_size_str, + 100.0 - savings); + fmt::print(" - Original data: {:>8s}\n", content_size_str); + fmt::print(" - Compression ratio: {:>5.3f} x ({:.1f}% space savings)\n", + ratio, + savings); + fmt::print("Incompressible data: {:>8s}\n", incompr_size_str); +} + +void +compress_recompress(Context& ctx, + optional level, + const Util::ProgressReceiver& progress_receiver) +{ + const size_t threads = std::thread::hardware_concurrency(); + const size_t read_ahead = 2 * threads; + ThreadPool thread_pool(threads, read_ahead); + RecompressionStatistics statistics; + + Util::for_each_level_1_subdir( + ctx.config.cache_dir(), + [&](const std::string& subdir, + const Util::ProgressReceiver& sub_progress_receiver) { + std::vector> files; + Util::get_level_1_files( + subdir, + [&](double progress) { sub_progress_receiver(0.1 * progress); }, + files); + + auto stats_file = subdir + "/stats"; + + for (size_t i = 0; i < files.size(); ++i) { + const auto& file = files[i]; + + if (file->type() != CacheFile::Type::unknown) { + thread_pool.enqueue([&statistics, stats_file, file, level] { + try { + recompress_file(statistics, stats_file, *file, level); + } catch (Error&) { + // Ignore for now. + } + }); + } else { + statistics.update(0, 0, 0, file->lstat().size()); + } + + sub_progress_receiver(0.1 + 0.9 * i / files.size()); + } + + if (Util::ends_with(subdir, "f")) { + // Wait here instead of after Util::for_each_level_1_subdir to avoid + // updating the progress bar to 100% before all work is done. + thread_pool.shut_down(); + } + }, + progress_receiver); + + if (isatty(STDOUT_FILENO)) { + fmt::print("\n\n"); + } + + double old_ratio = + statistics.old_size() > 0 + ? static_cast(statistics.content_size()) / statistics.old_size() + : 0.0; + double old_savings = old_ratio > 0.0 ? 100.0 - (100.0 / old_ratio) : 0.0; + double new_ratio = + statistics.new_size() > 0 + ? static_cast(statistics.content_size()) / statistics.new_size() + : 0.0; + double new_savings = new_ratio > 0.0 ? 100.0 - (100.0 / new_ratio) : 0.0; + int64_t size_difference = static_cast(statistics.new_size()) + - static_cast(statistics.old_size()); + + std::string old_compr_size_str = + Util::format_human_readable_size(statistics.old_size()); + std::string new_compr_size_str = + Util::format_human_readable_size(statistics.new_size()); + std::string content_size_str = + Util::format_human_readable_size(statistics.content_size()); + std::string incompr_size_str = + Util::format_human_readable_size(statistics.incompressible_size()); + std::string size_difference_str = + fmt::format("{}{}", + size_difference < 0 ? "-" : (size_difference > 0 ? "+" : " "), + Util::format_human_readable_size( + size_difference < 0 ? -size_difference : size_difference)); + + fmt::print("Original data: {:>8s}\n", content_size_str); + fmt::print("Old compressed data: {:>8s} ({:.1f}% of original size)\n", + old_compr_size_str, + 100.0 - old_savings); + fmt::print(" - Compression ratio: {:>5.3f} x ({:.1f}% space savings)\n", + old_ratio, + old_savings); + fmt::print("New compressed data: {:>8s} ({:.1f}% of original size)\n", + new_compr_size_str, + 100.0 - new_savings); + fmt::print(" - Compression ratio: {:>5.3f} x ({:.1f}% space savings)\n", + new_ratio, + new_savings); + fmt::print("Size change: {:>9s}\n", size_difference_str); +} diff --git a/src/compress.hpp b/src/compress.hpp new file mode 100644 index 0000000..91eb04d --- /dev/null +++ b/src/compress.hpp @@ -0,0 +1,42 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Util.hpp" + +#include "third_party/nonstd/optional.hpp" + +class Config; +class Context; + +void compress_stats(const Config& config, + const Util::ProgressReceiver& progress_receiver); + +// Recompress the cache. +// +// Arguments: +// - ctx: The context. +// - level: Target compression level (positive or negative value for actual +// level, 0 for default level and nonstd::nullopt for no compression). +// - progress_receiver: Function that will be called for progress updates. +void compress_recompress(Context& ctx, + nonstd::optional level, + const Util::ProgressReceiver& progress_receiver); diff --git a/src/conf.c b/src/conf.c deleted file mode 100644 index 517d840..0000000 --- a/src/conf.c +++ /dev/null @@ -1,447 +0,0 @@ -// Copyright (C) 2011-2020 Joel Rosdahl -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -#include "conf.h" -#include "confitems.h" -#include "envtoconfitems.h" -#include "ccache.h" - -enum handle_conf_result { - HANDLE_CONF_OK, - HANDLE_CONF_UNKNOWN, - HANDLE_CONF_FAIL -}; - -static const struct conf_item * -find_conf(const char *name) -{ - return confitems_get(name, strlen(name)); -} - -static const struct env_to_conf_item * -find_env_to_conf(const char *name) -{ - return envtoconfitems_get(name, strlen(name)); -} - -static enum handle_conf_result -handle_conf_setting(struct conf *conf, const char *key, const char *value, - char **errmsg, const char *env_var_name, - bool negate_boolean, const char *origin) -{ - const struct conf_item *item = find_conf(key); - if (!item) { - return HANDLE_CONF_UNKNOWN; - } - - if (env_var_name && item->parser == confitem_parse_bool) { - // Special rule for boolean settings from the environment: "0", "false", - // "disable" and "no" (case insensitive) are invalid, and all other values - // mean true. - // - // Previously any value meant true, but this was surprising to users, who - // might do something like CCACHE_DISABLE=0 and expect ccache to be - // enabled. - if (str_eq(value, "0") || strcasecmp(value, "false") == 0 - || strcasecmp(value, "disable") == 0 || strcasecmp(value, "no") == 0) { - fatal( - "invalid boolean environment variable value \"%s\" for CCACHE_%s%s" - " (did you mean to set \"CCACHE_%s%s=true\"?)", - value, - negate_boolean ? "NO" : "", - env_var_name, negate_boolean ? "" : "NO", - env_var_name); - } - - bool *boolvalue = (bool *)((char *)conf + item->offset); - *boolvalue = !negate_boolean; - goto out; - } - - if (!item->parser(value, (char *)conf + item->offset, errmsg)) { - return HANDLE_CONF_FAIL; - } - if (item->verifier && !item->verifier((char *)conf + item->offset, errmsg)) { - return HANDLE_CONF_FAIL; - } - -out: - conf->item_origins[item->number] = origin; - return HANDLE_CONF_OK; -} - -static bool -parse_line(const char *line, char **key, char **value, char **errmsg) -{ -#define SKIP_WS(x) do { while (isspace(*x)) { ++x; } } while (false) - - *key = NULL; - *value = NULL; - - const char *p = line; - SKIP_WS(p); - if (*p == '\0' || *p == '#') { - return true; - } - const char *q = p; - while (isalpha(*q) || *q == '_') { - ++q; - } - *key = x_strndup(p, q - p); - p = q; - SKIP_WS(p); - if (*p != '=') { - *errmsg = x_strdup("missing equal sign"); - free(*key); - *key = NULL; - return false; - } - ++p; - - // Skip leading whitespace. - SKIP_WS(p); - q = p; - while (*q) { - ++q; - } - // Skip trailing whitespace. - while (isspace(q[-1])) { - --q; - } - *value = x_strndup(p, q - p); - - return true; - -#undef SKIP_WS -} - -// Create a conf struct with default values. -struct conf * -conf_create(void) -{ - struct conf *conf = x_malloc(sizeof(*conf)); - conf->base_dir = x_strdup(""); - conf->cache_dir = format("%s/.ccache", get_home_directory()); - conf->cache_dir_levels = 2; - conf->compiler = x_strdup(""); - conf->compiler_check = x_strdup("mtime"); - conf->compression = false; - conf->compression_level = 6; - conf->cpp_extension = x_strdup(""); - conf->debug = false; - conf->depend_mode = false; - conf->direct_mode = true; - conf->disable = false; - conf->extra_files_to_hash = x_strdup(""); - conf->hard_link = false; - conf->hash_dir = true; - conf->ignore_headers_in_manifest = x_strdup(""); - conf->keep_comments_cpp = false; - conf->limit_multiple = 0.8; - conf->log_file = x_strdup(""); - conf->max_files = 0; - conf->max_size = (uint64_t)5 * 1000 * 1000 * 1000; - conf->path = x_strdup(""); - conf->pch_external_checksum = false; - conf->prefix_command = x_strdup(""); - conf->prefix_command_cpp = x_strdup(""); - conf->read_only = false; - conf->read_only_direct = false; - conf->recache = false; - conf->run_second_cpp = true; - conf->sloppiness = 0; - conf->stats = true; - conf->temporary_dir = x_strdup(""); - conf->umask = UINT_MAX; // Default: don't set umask. - conf->item_origins = x_malloc(confitems_count() * sizeof(char *)); - for (size_t i = 0; i < confitems_count(); ++i) { - conf->item_origins[i] = "default"; - } - return conf; -} - -void -conf_free(struct conf *conf) -{ - if (!conf) { - return; - } - free(conf->base_dir); - free(conf->cache_dir); - free(conf->compiler); - free(conf->compiler_check); - free(conf->cpp_extension); - free(conf->extra_files_to_hash); - free(conf->ignore_headers_in_manifest); - free(conf->log_file); - free(conf->path); - free(conf->prefix_command); - free(conf->prefix_command_cpp); - free(conf->temporary_dir); - free((void *)conf->item_origins); // Workaround for MSVC warning - free(conf); -} - -// Note: The path pointer is stored in conf, so path must outlive conf. -// -// On failure, if an I/O error occurred errno is set appropriately, otherwise -// errno is set to zero indicating that config itself was invalid. -bool -conf_read(struct conf *conf, const char *path, char **errmsg) -{ - assert(errmsg); - *errmsg = NULL; - - FILE *f = fopen(path, "r"); - if (!f) { - *errmsg = format("%s: %s", path, strerror(errno)); - return false; - } - - unsigned line_number = 0; - bool result = true; - char buf[10000]; - while (fgets(buf, sizeof(buf), f)) { - ++line_number; - - char *key; - char *value; - char *errmsg2; - enum handle_conf_result hcr = HANDLE_CONF_OK; - bool ok = parse_line(buf, &key, &value, &errmsg2); - if (ok && key) { // key == NULL if comment or blank line. - hcr = - handle_conf_setting(conf, key, value, &errmsg2, NULL, false, path); - ok = hcr != HANDLE_CONF_FAIL; // unknown is OK - } - free(key); - free(value); - if (!ok) { - *errmsg = format("%s:%u: %s", path, line_number, errmsg2); - free(errmsg2); - errno = 0; - result = false; - goto out; - } - } - if (ferror(f)) { - *errmsg = x_strdup(strerror(errno)); - result = false; - } - -out: - fclose(f); - return result; -} - -bool -conf_update_from_environment(struct conf *conf, char **errmsg) -{ - for (char **p = environ; *p; ++p) { - if (!str_startswith(*p, "CCACHE_")) { - continue; - } - char *q = strchr(*p, '='); - if (!q) { - continue; - } - - bool negate; - size_t key_start; - if (str_startswith(*p + 7, "NO")) { - negate = true; - key_start = 9; - } else { - negate = false; - key_start = 7; - } - char *key = x_strndup(*p + key_start, q - *p - key_start); - - ++q; // Now points to the value. - - const struct env_to_conf_item *env_to_conf_item = find_env_to_conf(key); - if (!env_to_conf_item) { - free(key); - continue; - } - - char *errmsg2 = NULL; - enum handle_conf_result hcr = handle_conf_setting( - conf, env_to_conf_item->conf_name, q, &errmsg2, - env_to_conf_item->env_name, negate, "environment"); - if (hcr != HANDLE_CONF_OK) { - *errmsg = format("%s: %s", key, errmsg2); - free(errmsg2); - free(key); - return false; - } - - free(key); - } - - return true; -} - -bool -conf_set_value_in_file(const char *conf_path, const char *key, - const char *value, char **errmsg) -{ - const struct conf_item *item = find_conf(key); - if (!item) { - *errmsg = format("unknown configuration option \"%s\"", key); - return false; - } - - char parsed[8] = {0}; // The maximum entry size in struct conf. - if (!item->parser(value, (void *)parsed, errmsg) - || (item->verifier && !item->verifier(&parsed, errmsg))) { - return false; - } - - char *path = x_realpath(conf_path); - if (!path) { - path = x_strdup(conf_path); - } - FILE *infile = fopen(path, "r"); - if (!infile) { - *errmsg = format("%s: %s", path, strerror(errno)); - free(path); - return false; - } - - char *outpath = format("%s.tmp", path); - FILE *outfile = create_tmp_file(&outpath, "w"); - if (!outfile) { - *errmsg = format("%s: %s", outpath, strerror(errno)); - free(outpath); - free(path); - fclose(infile); - return false; - } - - bool found = false; - char buf[10000]; - while (fgets(buf, sizeof(buf), infile)) { - char *key2; - char *value2; - char *errmsg2; - bool ok = parse_line(buf, &key2, &value2, &errmsg2); - if (ok && key2 && str_eq(key2, key)) { - found = true; - fprintf(outfile, "%s = %s\n", key, value); - } else { - fputs(buf, outfile); - } - free(key2); - free(value2); - } - - if (!found) { - fprintf(outfile, "%s = %s\n", key, value); - } - - fclose(infile); - fclose(outfile); - if (x_rename(outpath, path) != 0) { - *errmsg = format("rename %s to %s: %s", outpath, path, strerror(errno)); - free(outpath); - free(path); - return false; - } - free(outpath); - free(path); - - return true; -} - -bool -conf_print_value(struct conf *conf, const char *key, - FILE *file, char **errmsg) -{ - const struct conf_item *item = find_conf(key); - if (!item) { - *errmsg = format("unknown configuration option \"%s\"", key); - return false; - } - void *value = (char *)conf + item->offset; - char *str = item->formatter(value); - fprintf(file, "%s\n", str); - free(str); - return true; -} - -static bool -print_item(struct conf *conf, const char *key, - void (*printer)(const char *descr, const char *origin, - void *context), - void *context) -{ - const struct conf_item *item = find_conf(key); - if (!item) { - return false; - } - void *value = (char *)conf + item->offset; - char *str = item->formatter(value); - char *buf = x_strdup(""); - reformat(&buf, "%s = %s", key, str); - printer(buf, conf->item_origins[item->number], context); - free(buf); - free(str); - return true; -} - -bool -conf_print_items(struct conf *conf, - void (*printer)(const char *descr, const char *origin, - void *context), - void *context) -{ - bool ok = true; - ok &= print_item(conf, "base_dir", printer, context); - ok &= print_item(conf, "cache_dir", printer, context); - ok &= print_item(conf, "cache_dir_levels", printer, context); - ok &= print_item(conf, "compiler", printer, context); - ok &= print_item(conf, "compiler_check", printer, context); - ok &= print_item(conf, "compression", printer, context); - ok &= print_item(conf, "compression_level", printer, context); - ok &= print_item(conf, "cpp_extension", printer, context); - ok &= print_item(conf, "debug", printer, context); - ok &= print_item(conf, "depend_mode", printer, context); - ok &= print_item(conf, "direct_mode", printer, context); - ok &= print_item(conf, "disable", printer, context); - ok &= print_item(conf, "extra_files_to_hash", printer, context); - ok &= print_item(conf, "hard_link", printer, context); - ok &= print_item(conf, "hash_dir", printer, context); - ok &= print_item(conf, "ignore_headers_in_manifest", printer, context); - ok &= print_item(conf, "keep_comments_cpp", printer, context); - ok &= print_item(conf, "limit_multiple", printer, context); - ok &= print_item(conf, "log_file", printer, context); - ok &= print_item(conf, "max_files", printer, context); - ok &= print_item(conf, "max_size", printer, context); - ok &= print_item(conf, "path", printer, context); - ok &= print_item(conf, "pch_external_checksum", printer, context); - ok &= print_item(conf, "prefix_command", printer, context); - ok &= print_item(conf, "prefix_command_cpp", printer, context); - ok &= print_item(conf, "read_only", printer, context); - ok &= print_item(conf, "read_only_direct", printer, context); - ok &= print_item(conf, "recache", printer, context); - ok &= print_item(conf, "run_second_cpp", printer, context); - ok &= print_item(conf, "sloppiness", printer, context); - ok &= print_item(conf, "stats", printer, context); - ok &= print_item(conf, "temporary_dir", printer, context); - ok &= print_item(conf, "umask", printer, context); - return ok; -} diff --git a/src/conf.h b/src/conf.h deleted file mode 100644 index 6fb0844..0000000 --- a/src/conf.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef CONF_H -#define CONF_H - -#include "system.h" - -struct conf { - char *base_dir; - char *cache_dir; - unsigned cache_dir_levels; - char *compiler; - char *compiler_check; - bool compression; - unsigned compression_level; - char *cpp_extension; - bool debug; - bool depend_mode; - bool direct_mode; - bool disable; - char *extra_files_to_hash; - bool hard_link; - bool hash_dir; - char *ignore_headers_in_manifest; - bool keep_comments_cpp; - double limit_multiple; - char *log_file; - unsigned max_files; - uint64_t max_size; - char *path; - bool pch_external_checksum; - char *prefix_command; - char *prefix_command_cpp; - bool read_only; - bool read_only_direct; - bool recache; - bool run_second_cpp; - unsigned sloppiness; - bool stats; - char *temporary_dir; - unsigned umask; - - const char **item_origins; -}; - -struct conf *conf_create(void); -void conf_free(struct conf *conf); -bool conf_read(struct conf *conf, const char *path, char **errmsg); -bool conf_update_from_environment(struct conf *conf, char **errmsg); -bool conf_print_value(struct conf *conf, const char *key, - FILE *file, char **errmsg); -bool conf_set_value_in_file(const char *path, const char *key, - const char *value, char **errmsg); -bool conf_print_items(struct conf *conf, - void (*printer)(const char *descr, const char *origin, - void *context), - void *context); - -#endif diff --git a/src/confitems.c b/src/confitems.c deleted file mode 100644 index 15064e7..0000000 --- a/src/confitems.c +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright (C) 2018-2019 Joel Rosdahl -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -#include "confitems.h" -#include "ccache.h" - -static char * -format_string(const void *value) -{ - const char *const *str = (const char *const *)value; - return x_strdup(*str); -} - -bool -confitem_parse_bool(const char *str, void *result, char **errmsg) -{ - bool *value = (bool *)result; - - if (str_eq(str, "true")) { - *value = true; - return true; - } else if (str_eq(str, "false")) { - *value = false; - return true; - } else { - *errmsg = format("not a boolean value: \"%s\"", str); - return false; - } -} - -char * -confitem_format_bool(const void *value) -{ - const bool *b = (const bool *)value; - return x_strdup(*b ? "true" : "false"); -} - -bool -confitem_parse_env_string(const char *str, void *result, char **errmsg) -{ - char **value = (char **)result; - free(*value); - *value = subst_env_in_string(str, errmsg); - return *value != NULL; -} - -char * -confitem_format_env_string(const void *value) -{ - return format_string(value); -} - -bool -confitem_parse_double(const char *str, void *result, char **errmsg) -{ - double *value = (double *)result; - errno = 0; - char *endptr; - double x = strtod(str, &endptr); - if (errno == 0 && *str != '\0' && *endptr == '\0') { - *value = x; - return true; - } else { - *errmsg = format("invalid floating point: \"%s\"", str); - return false; - } -} - -char * -confitem_format_double(const void *value) -{ - const double *x = (const double *)value; - return format("%.1f", *x); -} - -bool -confitem_parse_size(const char *str, void *result, char **errmsg) -{ - uint64_t *value = (uint64_t *)result; - uint64_t size; - if (parse_size_with_suffix(str, &size)) { - *value = size; - return true; - } else { - *errmsg = format("invalid size: \"%s\"", str); - return false; - } -} - -char * -confitem_format_size(const void *value) -{ - const uint64_t *size = (const uint64_t *)value; - return format_parsable_size_with_suffix(*size); -} - -bool -confitem_parse_sloppiness(const char *str, void *result, char **errmsg) -{ - (void)errmsg; - - unsigned *value = (unsigned *)result; - if (!str) { - return *value; - } - - char *p = x_strdup(str); - char *q = p; - char *word; - char *saveptr = NULL; - while ((word = strtok_r(q, ", ", &saveptr))) { - if (str_eq(word, "file_stat_matches")) { - *value |= SLOPPY_FILE_STAT_MATCHES; - } else if (str_eq(word, "file_stat_matches_ctime")) { - *value |= SLOPPY_FILE_STAT_MATCHES_CTIME; - } else if (str_eq(word, "include_file_ctime")) { - *value |= SLOPPY_INCLUDE_FILE_CTIME; - } else if (str_eq(word, "include_file_mtime")) { - *value |= SLOPPY_INCLUDE_FILE_MTIME; - } else if (str_eq(word, "system_headers") - || str_eq(word, "no_system_headers")) { - *value |= SLOPPY_SYSTEM_HEADERS; - } else if (str_eq(word, "pch_defines")) { - *value |= SLOPPY_PCH_DEFINES; - } else if (str_eq(word, "time_macros")) { - *value |= SLOPPY_TIME_MACROS; - } else if (str_eq(word, "clang_index_store")) { - *value |= SLOPPY_CLANG_INDEX_STORE; - } else if (str_eq(word, "locale")) { - *value |= SLOPPY_LOCALE; - } - // else: ignore unknown value for forward compatibility - q = NULL; - } - free(p); - return true; -} - -char * -confitem_format_sloppiness(const void *value) -{ - const unsigned *sloppiness = (const unsigned *)value; - char *s = x_strdup(""); - if (*sloppiness & SLOPPY_INCLUDE_FILE_MTIME) { - reformat(&s, "%sinclude_file_mtime, ", s); - } - if (*sloppiness & SLOPPY_INCLUDE_FILE_CTIME) { - reformat(&s, "%sinclude_file_ctime, ", s); - } - if (*sloppiness & SLOPPY_TIME_MACROS) { - reformat(&s, "%stime_macros, ", s); - } - if (*sloppiness & SLOPPY_PCH_DEFINES) { - reformat(&s, "%spch_defines, ", s); - } - if (*sloppiness & SLOPPY_FILE_STAT_MATCHES) { - reformat(&s, "%sfile_stat_matches, ", s); - } - if (*sloppiness & SLOPPY_FILE_STAT_MATCHES_CTIME) { - reformat(&s, "%sfile_stat_matches_ctime, ", s); - } - if (*sloppiness & SLOPPY_SYSTEM_HEADERS) { - reformat(&s, "%ssystem_headers, ", s); - } - if (*sloppiness & SLOPPY_CLANG_INDEX_STORE) { - reformat(&s, "%sclang_index_store, ", s); - } - if (*sloppiness & SLOPPY_LOCALE) { - reformat(&s, "%slocale, ", s); - } - if (*sloppiness) { - // Strip last ", ". - s[strlen(s) - 2] = '\0'; - } - return s; -} - -bool -confitem_parse_string(const char *str, void *result, char **errmsg) -{ - (void)errmsg; - - char **value = (char **)result; - free(*value); - *value = x_strdup(str); - return true; -} - -char * -confitem_format_string(const void *value) -{ - return format_string(value); -} - -bool -confitem_parse_umask(const char *str, void *result, char **errmsg) -{ - unsigned *value = (unsigned *)result; - if (str_eq(str, "")) { - *value = UINT_MAX; - return true; - } - - errno = 0; - char *endptr; - *value = strtoul(str, &endptr, 8); - if (errno == 0 && *str != '\0' && *endptr == '\0') { - return true; - } else { - *errmsg = format("not an octal integer: \"%s\"", str); - return false; - } -} - -char * -confitem_format_umask(const void *value) -{ - const unsigned *umask = (const unsigned *)value; - if (*umask == UINT_MAX) { - return x_strdup(""); - } else { - return format("%03o", *umask); - } -} - -bool -confitem_parse_unsigned(const char *str, void *result, char **errmsg) -{ - unsigned *value = (unsigned *)result; - errno = 0; - char *endptr; - long x = strtol(str, &endptr, 10); - if (errno == 0 && x >= 0 && *str != '\0' && *endptr == '\0') { - *value = x; - return true; - } else { - *errmsg = format("invalid unsigned integer: \"%s\"", str); - return false; - } -} - -char * -confitem_format_unsigned(const void *value) -{ - const unsigned *i = (const unsigned *)value; - return format("%u", *i); -} - -bool -confitem_verify_absolute_path(const void *value, char **errmsg) -{ - const char *const *path = (const char *const *)value; - assert(*path); - if (str_eq(*path, "")) { - // The empty string means "disable" in this case. - return true; - } else if (is_absolute_path(*path)) { - return true; - } else { - *errmsg = format("not an absolute path: \"%s\"", *path); - return false; - } -} - -bool -confitem_verify_dir_levels(const void *value, char **errmsg) -{ - const unsigned *levels = (const unsigned *)value; - assert(levels); - if (*levels >= 1 && *levels <= 8) { - return true; - } else { - *errmsg = format("cache directory levels must be between 1 and 8"); - return false; - } -} diff --git a/src/confitems.gperf b/src/confitems.gperf deleted file mode 100644 index 60e48ca..0000000 --- a/src/confitems.gperf +++ /dev/null @@ -1,55 +0,0 @@ -%language=ANSI-C -%enum -%struct-type -%readonly-tables -%define hash-function-name confitems_hash -%define lookup-function-name confitems_get -%define initializer-suffix ,0,0,NULL,NULL,NULL -%{ -#include "confitems.h" -#include "conf.h" - -#undef bool -#define ITEM_ENTRY(name, type, verify_fn) \ - offsetof(struct conf, name), confitem_parse_ ## type, \ - confitem_format_ ## type, verify_fn -#define ITEM(name, type) \ - ITEM_ENTRY(name, type, NULL) -#define ITEM_V(name, type, verification) \ - ITEM_ENTRY(name, type, confitem_verify_ ## verification) -%} -struct conf_item; -%% -base_dir, ITEM_V(base_dir, env_string, absolute_path) -cache_dir, ITEM(cache_dir, env_string) -cache_dir_levels, ITEM_V(cache_dir_levels, unsigned, dir_levels) -compiler, ITEM(compiler, string) -compiler_check, ITEM(compiler_check, string) -compression, ITEM(compression, bool) -compression_level, ITEM(compression_level, unsigned) -cpp_extension, ITEM(cpp_extension, string) -debug, ITEM(debug, bool) -depend_mode, ITEM(depend_mode, bool) -direct_mode, ITEM(direct_mode, bool) -disable, ITEM(disable, bool) -extra_files_to_hash, ITEM(extra_files_to_hash, env_string) -hard_link, ITEM(hard_link, bool) -hash_dir, ITEM(hash_dir, bool) -ignore_headers_in_manifest, ITEM(ignore_headers_in_manifest, env_string) -keep_comments_cpp, ITEM(keep_comments_cpp, bool) -limit_multiple, ITEM(limit_multiple, double) -log_file, ITEM(log_file, env_string) -max_files, ITEM(max_files, unsigned) -max_size, ITEM(max_size, size) -path, ITEM(path, env_string) -pch_external_checksum, ITEM(pch_external_checksum, bool) -prefix_command, ITEM(prefix_command, env_string) -prefix_command_cpp, ITEM(prefix_command_cpp, env_string) -read_only, ITEM(read_only, bool) -read_only_direct, ITEM(read_only_direct, bool) -recache, ITEM(recache, bool) -run_second_cpp, ITEM(run_second_cpp, bool) -sloppiness, ITEM(sloppiness, sloppiness) -stats, ITEM(stats, bool) -temporary_dir, ITEM(temporary_dir, env_string) -umask, ITEM(umask, umask) diff --git a/src/confitems.h b/src/confitems.h deleted file mode 100644 index 8b42cae..0000000 --- a/src/confitems.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef CONFITEMS_H -#define CONFITEMS_H - -#include "system.h" - -typedef bool (*conf_item_parser)(const char *str, void *result, char **errmsg); -typedef bool (*conf_item_verifier)(const void *value, char **errmsg); -typedef char *(*conf_item_formatter)(const void *value); - -struct conf_item { - const char *name; - size_t number; - size_t offset; - conf_item_parser parser; - conf_item_formatter formatter; - conf_item_verifier verifier; -}; - -bool confitem_parse_bool(const char *str, void *result, char **errmsg); -char *confitem_format_bool(const void *value); - -bool confitem_parse_env_string(const char *str, void *result, char **errmsg); -char *confitem_format_env_string(const void *value); - -bool confitem_parse_double(const char *str, void *result, char **errmsg); -char *confitem_format_double(const void *value); - -bool confitem_parse_size(const char *str, void *result, char **errmsg); -char *confitem_format_size(const void *value); - -bool confitem_parse_sloppiness(const char *str, void *result, char **errmsg); -char *confitem_format_sloppiness(const void *value); - -bool confitem_parse_string(const char *str, void *result, char **errmsg); -char *confitem_format_string(const void *value); - -bool confitem_parse_umask(const char *str, void *result, char **errmsg); -char *confitem_format_umask(const void *value); - -bool confitem_parse_unsigned(const char *str, void *result, char **errmsg); -char *confitem_format_unsigned(const void *value); - -bool confitem_verify_absolute_path(const void *value, char **errmsg); -bool confitem_verify_dir_levels(const void *value, char **errmsg); - -const struct conf_item *confitems_get(const char *str, size_t len); -size_t confitems_count(void); - -#endif diff --git a/src/confitems_lookup.c b/src/confitems_lookup.c deleted file mode 100644 index 1bb30a2..0000000 --- a/src/confitems_lookup.c +++ /dev/null @@ -1,163 +0,0 @@ -/* ANSI-C code produced by gperf version 3.1 */ -/* Command-line: gperf */ -/* Computed positions: -k'1-2' */ - -#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ - && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ - && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ - && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ - && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ - && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ - && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ - && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ - && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ - && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ - && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ - && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ - && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ - && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ - && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ - && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ - && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ - && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ - && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ - && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ - && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ - && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ - && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) -/* The character set is not based on ISO-646. */ -#warning "gperf generated tables don't work with this execution character set. Please report a bug to ." -#endif - -#include "confitems.h" -#include "conf.h" - -#undef bool -#define ITEM_ENTRY(name, type, verify_fn) \ - offsetof(struct conf, name), confitem_parse_ ## type, \ - confitem_format_ ## type, verify_fn -#define ITEM(name, type) \ - ITEM_ENTRY(name, type, NULL) -#define ITEM_V(name, type, verification) \ - ITEM_ENTRY(name, type, confitem_verify_ ## verification) -struct conf_item; -/* maximum key range = 46, duplicates = 0 */ - -#ifdef __GNUC__ -__inline -#else -#ifdef __cplusplus -inline -#endif -#endif -static unsigned int -confitems_hash (register const char *str, register size_t len) -{ - static const unsigned char asso_values[] = - { - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 0, 35, 0, - 5, 10, 50, 0, 30, 20, 50, 0, 10, 20, - 50, 0, 0, 50, 5, 10, 10, 15, 50, 50, - 20, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50 - }; - return len + asso_values[(unsigned char)str[1]] + asso_values[(unsigned char)str[0]]; -} - -const struct conf_item * -confitems_get (register const char *str, register size_t len) -{ - enum - { - TOTAL_KEYWORDS = 33, - MIN_WORD_LENGTH = 4, - MAX_WORD_LENGTH = 26, - MIN_HASH_VALUE = 4, - MAX_HASH_VALUE = 49 - }; - - static const struct conf_item wordlist[] = - { - {"",0,0,NULL,NULL,NULL}, {"",0,0,NULL,NULL,NULL}, - {"",0,0,NULL,NULL,NULL}, {"",0,0,NULL,NULL,NULL}, - {"path", 21, ITEM(path, env_string)}, - {"",0,0,NULL,NULL,NULL}, {"",0,0,NULL,NULL,NULL}, - {"",0,0,NULL,NULL,NULL}, - {"compiler", 3, ITEM(compiler, string)}, - {"cache_dir", 1, ITEM(cache_dir, env_string)}, - {"",0,0,NULL,NULL,NULL}, - {"compression", 5, ITEM(compression, bool)}, - {"",0,0,NULL,NULL,NULL}, - {"cpp_extension", 7, ITEM(cpp_extension, string)}, - {"compiler_check", 4, ITEM(compiler_check, string)}, - {"",0,0,NULL,NULL,NULL}, - {"cache_dir_levels", 2, ITEM_V(cache_dir_levels, unsigned, dir_levels)}, - {"compression_level", 6, ITEM(compression_level, unsigned)}, - {"log_file", 18, ITEM(log_file, env_string)}, - {"prefix_command", 23, ITEM(prefix_command, env_string)}, - {"debug", 8, ITEM(debug, bool)}, - {"pch_external_checksum", 22, ITEM(pch_external_checksum, bool)}, - {"recache", 27, ITEM(recache, bool)}, - {"prefix_command_cpp", 24, ITEM(prefix_command_cpp, env_string)}, - {"read_only", 25, ITEM(read_only, bool)}, - {"stats", 30, ITEM(stats, bool)}, - {"depend_mode", 9, ITEM(depend_mode, bool)}, - {"keep_comments_cpp", 16, ITEM(keep_comments_cpp, bool)}, - {"max_size", 20, ITEM(max_size, size)}, - {"max_files", 19, ITEM(max_files, unsigned)}, - {"sloppiness", 29, ITEM(sloppiness, sloppiness)}, - {"read_only_direct", 26, ITEM(read_only_direct, bool)}, - {"disable", 11, ITEM(disable, bool)}, - {"temporary_dir", 31, ITEM(temporary_dir, env_string)}, - {"run_second_cpp", 28, ITEM(run_second_cpp, bool)}, - {"",0,0,NULL,NULL,NULL}, - {"direct_mode", 10, ITEM(direct_mode, bool)}, - {"",0,0,NULL,NULL,NULL}, - {"hash_dir", 14, ITEM(hash_dir, bool)}, - {"hard_link", 13, ITEM(hard_link, bool)}, - {"umask", 32, ITEM(umask, umask)}, - {"",0,0,NULL,NULL,NULL}, {"",0,0,NULL,NULL,NULL}, - {"base_dir", 0, ITEM_V(base_dir, env_string, absolute_path)}, - {"limit_multiple", 17, ITEM(limit_multiple, double)}, - {"",0,0,NULL,NULL,NULL}, - {"ignore_headers_in_manifest", 15, ITEM(ignore_headers_in_manifest, env_string)}, - {"",0,0,NULL,NULL,NULL}, {"",0,0,NULL,NULL,NULL}, - {"extra_files_to_hash", 12, ITEM(extra_files_to_hash, env_string)} - }; - - if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) - { - register unsigned int key = confitems_hash (str, len); - - if (key <= MAX_HASH_VALUE) - { - register const char *s = wordlist[key].name; - - if (*str == *s && !strcmp (str + 1, s + 1)) - return &wordlist[key]; - } - } - return 0; -} -size_t confitems_count(void) { return 33; } diff --git a/src/counters.c b/src/counters.c deleted file mode 100644 index 1441a4f..0000000 --- a/src/counters.c +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (C) 2010-2019 Joel Rosdahl -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -// A simple array of unsigned integers used for the statistics counters. - -#include "ccache.h" - -// Allocate and initialize a struct counters. Data entries up to the size are -// set to 0. -struct counters * -counters_init(size_t initial_size) -{ - struct counters *c = x_malloc(sizeof(*c)); - c->data = NULL; - c->size = 0; - c->allocated = 0; - counters_resize(c, initial_size); - return c; -} - -// Free a counters struct. -void -counters_free(struct counters *c) -{ - if (c) { - free(c->data); - free(c); - } -} - -// Set a new size. New data entries are set to 0. -void -counters_resize(struct counters *c, size_t new_size) -{ - if (new_size > c->size) { - bool realloc = false; - while (c->allocated < new_size) { - c->allocated += 32 + c->allocated; - realloc = true; - } - if (realloc) { - c->data = x_realloc(c->data, c->allocated * sizeof(c->data[0])); - } - for (size_t i = c->size; i < new_size; i++) { - c->data[i] = 0; - } - } - - c->size = new_size; -} diff --git a/src/envtoconfitems.gperf b/src/envtoconfitems.gperf deleted file mode 100644 index ae5f37f..0000000 --- a/src/envtoconfitems.gperf +++ /dev/null @@ -1,47 +0,0 @@ -%language=ANSI-C -%enum -%struct-type -%readonly-tables -%define hash-function-name envtoconfitems_hash -%define lookup-function-name envtoconfitems_get -%define slot-name env_name -%define initializer-suffix ,"" -%{ -#include "envtoconfitems.h" -%} -struct env_to_conf_item; -%% -BASEDIR, "base_dir" -CC, "compiler" -COMPILER, "compiler" -COMPILERCHECK, "compiler_check" -COMPRESS, "compression" -COMPRESSLEVEL, "compression_level" -CPP2, "run_second_cpp" -COMMENTS, "keep_comments_cpp" -DEPEND, "depend_mode" -DIR, "cache_dir" -DEBUG, "debug" -DIRECT, "direct_mode" -DISABLE, "disable" -EXTENSION, "cpp_extension" -EXTRAFILES, "extra_files_to_hash" -HARDLINK, "hard_link" -HASHDIR, "hash_dir" -IGNOREHEADERS, "ignore_headers_in_manifest" -LIMIT_MULTIPLE, "limit_multiple" -LOGFILE, "log_file" -MAXFILES, "max_files" -MAXSIZE, "max_size" -NLEVELS, "cache_dir_levels" -PATH, "path" -PCH_EXTSUM, "pch_external_checksum" -PREFIX, "prefix_command" -PREFIX_CPP, "prefix_command_cpp" -READONLY, "read_only" -READONLY_DIRECT, "read_only_direct" -RECACHE, "recache" -SLOPPINESS, "sloppiness" -STATS, "stats" -TEMPDIR, "temporary_dir" -UMASK, "umask" diff --git a/src/envtoconfitems.h b/src/envtoconfitems.h deleted file mode 100644 index baeee5c..0000000 --- a/src/envtoconfitems.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef ENVTOCONFITEMS_H -#define ENVTOCONFITEMS_H - -#include "system.h" - -struct env_to_conf_item { - const char *env_name; - const char *conf_name; -}; - -const struct env_to_conf_item *envtoconfitems_get(const char *str, size_t len); -size_t envtoconfitems_count(void); - -#endif diff --git a/src/envtoconfitems_lookup.c b/src/envtoconfitems_lookup.c deleted file mode 100644 index b49f2b3..0000000 --- a/src/envtoconfitems_lookup.c +++ /dev/null @@ -1,171 +0,0 @@ -/* ANSI-C code produced by gperf version 3.1 */ -/* Command-line: gperf */ -/* Computed positions: -k'1,5' */ - -#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ - && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ - && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ - && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ - && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ - && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ - && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ - && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ - && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ - && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ - && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ - && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ - && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ - && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ - && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ - && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ - && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ - && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ - && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ - && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ - && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ - && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ - && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) -/* The character set is not based on ISO-646. */ -#warning "gperf generated tables don't work with this execution character set. Please report a bug to ." -#endif - -#include "envtoconfitems.h" -struct env_to_conf_item; -/* maximum key range = 49, duplicates = 0 */ - -#ifdef __GNUC__ -__inline -#else -#ifdef __cplusplus -inline -#endif -#endif -static unsigned int -envtoconfitems_hash (register const char *str, register size_t len) -{ - static const unsigned char asso_values[] = - { - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 30, 0, 0, 0, - 35, 51, 25, 5, 0, 51, 10, 15, 0, 10, - 5, 5, 5, 20, 20, 5, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51 - }; - register unsigned int hval = len; - - switch (hval) - { - default: - hval += asso_values[(unsigned char)str[4]+1]; - /*FALLTHROUGH*/ - case 4: - case 3: - case 2: - case 1: - hval += asso_values[(unsigned char)str[0]]; - break; - } - return hval; -} - -const struct env_to_conf_item * -envtoconfitems_get (register const char *str, register size_t len) -{ - enum - { - TOTAL_KEYWORDS = 34, - MIN_WORD_LENGTH = 2, - MAX_WORD_LENGTH = 15, - MIN_HASH_VALUE = 2, - MAX_HASH_VALUE = 50 - }; - - static const struct env_to_conf_item wordlist[] = - { - {"",""}, {"",""}, - {"CC", "compiler"}, - {"DIR", "cache_dir"}, - {"CPP2", "run_second_cpp"}, - {"",""}, - {"DIRECT", "direct_mode"}, - {"DISABLE", "disable"}, - {"COMPILER", "compiler"}, - {"PATH", "path"}, - {"",""}, - {"PREFIX", "prefix_command"}, - {"RECACHE", "recache"}, - {"COMPILERCHECK", "compiler_check"}, - {"",""}, - {"PREFIX_CPP", "prefix_command_cpp"}, - {"DEPEND", "depend_mode"}, - {"LOGFILE", "log_file"}, - {"READONLY", "read_only"}, - {"EXTENSION", "cpp_extension"}, - {"UMASK", "umask"}, - {"",""}, - {"MAXSIZE", "max_size"}, - {"MAXFILES", "max_files"}, - {"",""}, - {"READONLY_DIRECT", "read_only_direct"}, - {"",""}, - {"TEMPDIR", "temporary_dir"}, - {"COMPRESS", "compression"}, - {"LIMIT_MULTIPLE", "limit_multiple"}, - {"DEBUG", "debug"}, - {"",""}, - {"HASHDIR", "hash_dir"}, - {"COMPRESSLEVEL", "compression_level"}, - {"",""}, - {"SLOPPINESS", "sloppiness"}, - {"",""}, - {"BASEDIR", "base_dir"}, - {"IGNOREHEADERS", "ignore_headers_in_manifest"}, - {"",""}, - {"EXTRAFILES", "extra_files_to_hash"}, - {"",""}, - {"NLEVELS", "cache_dir_levels"}, - {"COMMENTS", "keep_comments_cpp"}, - {"",""}, - {"STATS", "stats"}, - {"",""}, {"",""}, - {"HARDLINK", "hard_link"}, - {"",""}, - {"PCH_EXTSUM", "pch_external_checksum"} - }; - - if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) - { - register unsigned int key = envtoconfitems_hash (str, len); - - if (key <= MAX_HASH_VALUE) - { - register const char *s = wordlist[key].env_name; - - if (*str == *s && !strcmp (str + 1, s + 1)) - return &wordlist[key]; - } - } - return 0; -} -size_t envtoconfitems_count(void) { return 34; } diff --git a/src/exceptions.hpp b/src/exceptions.hpp new file mode 100644 index 0000000..1c73141 --- /dev/null +++ b/src/exceptions.hpp @@ -0,0 +1,117 @@ +// Copyright (C) 2019-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "FormatNonstdStringView.hpp" +#include "Statistics.hpp" + +#include "third_party/fmt/core.h" +#include "third_party/nonstd/optional.hpp" + +#include +#include +#include + +// Don't throw or catch ErrorBase directly, use a subclass. +class ErrorBase : public std::runtime_error +{ + using std::runtime_error::runtime_error; +}; + +// Throw an Error to indicate a potentially non-fatal error that may be caught +// and handled by callers. An uncaught Error that reaches the top level will be +// treated similar to Fatal. +class Error : public ErrorBase +{ +public: + // Special case: If given only one string, don't parse it as a format string. + Error(const std::string& message); + + // `args` are forwarded to `fmt::format`. + template inline Error(T&&... args); +}; + +inline Error::Error(const std::string& message) : ErrorBase(message) +{ +} + +template +inline Error::Error(T&&... args) + : ErrorBase(fmt::format(std::forward(args)...)) +{ +} + +// Throw a Fatal to make ccache print the error message to stderr and exit +// with a non-zero exit code. +class Fatal : public ErrorBase +{ +public: + // Special case: If given only one string, don't parse it as a format string. + Fatal(const std::string& message); + + // `args` are forwarded to `fmt::format`. + template inline Fatal(T&&... args); +}; + +inline Fatal::Fatal(const std::string& message) : ErrorBase(message) +{ +} + +template +inline Fatal::Fatal(T&&... args) + : ErrorBase(fmt::format(std::forward(args)...)) +{ +} + +// Throw a Failure if ccache did not succeed in getting or putting a result in +// the cache. If `exit_code` is set, just exit with that code directly, +// otherwise execute the real compiler and exit with its exit code. Also updates +// statistics counter `statistic` if it's not `Statistic::none`. +class Failure : public std::exception +{ +public: + Failure(Statistic statistic, + nonstd::optional exit_code = nonstd::nullopt); + + nonstd::optional exit_code() const; + Statistic statistic() const; + +private: + Statistic m_statistic; + nonstd::optional m_exit_code; +}; + +inline Failure::Failure(Statistic statistic, nonstd::optional exit_code) + : m_statistic(statistic), m_exit_code(exit_code) +{ +} + +inline nonstd::optional +Failure::exit_code() const +{ + return m_exit_code; +} + +inline Statistic +Failure::statistic() const +{ + return m_statistic; +} diff --git a/src/execute.c b/src/execute.c deleted file mode 100644 index 4ec3a0a..0000000 --- a/src/execute.c +++ /dev/null @@ -1,394 +0,0 @@ -// Copyright (C) 2002 Andrew Tridgell -// Copyright (C) 2011-2019 Joel Rosdahl -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -#include "ccache.h" - -extern struct conf *conf; - -static char * -find_executable_in_path(const char *name, const char *exclude_name, char *path); - -#ifdef _WIN32 -// Re-create a win32 command line string based on **argv. -// http://msdn.microsoft.com/en-us/library/17w5ykft.aspx -char * -win32argvtos(char *prefix, char **argv, int *length) -{ - int i = 0; - int k = 0; - char *arg = prefix ? prefix : argv[i++]; - do { - int bs = 0; - for (int j = 0; arg[j]; j++) { - switch (arg[j]) { - case '\\': - bs++; - break; - case '"': - bs = (bs << 1) + 1; - // Fallthrough. - default: - k += bs + 1; - bs = 0; - } - } - k += (bs << 1) + 3; - } while ((arg = argv[i++])); - - char *ptr = malloc(k + 1); - char *str = ptr; - if (!str) { - *length = 0; - return NULL; - } - - i = 0; - arg = prefix ? prefix : argv[i++]; - do { - int bs = 0; - *ptr++ = '"'; - for (int j = 0; arg[j]; j++) { - switch (arg[j]) { - case '\\': - bs++; - break; - case '"': - bs = (bs << 1) + 1; - default: - while (bs && bs--) { - *ptr++ = '\\'; - } - *ptr++ = arg[j]; - } - } - bs <<= 1; - while (bs && bs--) { - *ptr++ = '\\'; - } - *ptr++ = '"'; - *ptr++ = ' '; - } while ((arg = argv[i++])); - ptr[-1] = '\0'; - - *length = ptr - str - 1; - return str; -} - -char * -win32getshell(char *path) -{ - char *path_env; - char *sh = NULL; - const char *ext = get_extension(path); - if (ext && strcasecmp(ext, ".sh") == 0 && (path_env = getenv("PATH"))) { - sh = find_executable_in_path("sh.exe", NULL, path_env); - } - if (!sh && getenv("CCACHE_DETECT_SHEBANG")) { - // Detect shebang. - FILE *fp = fopen(path, "r"); - if (fp) { - char buf[10]; - fgets(buf, sizeof(buf), fp); - buf[9] = 0; - if (str_eq(buf, "#!/bin/sh") && (path_env = getenv("PATH"))) { - sh = find_executable_in_path("sh.exe", NULL, path_env); - } - fclose(fp); - } - } - - return sh; -} - -void add_exe_ext_if_no_to_fullpath(char *full_path_win_ext, size_t max_size, - const char *ext, const char *path) { - if (!ext || (!str_eq(".exe", ext) - && !str_eq(".sh", ext) - && !str_eq(".bat", ext) - && !str_eq(".EXE", ext) - && !str_eq(".BAT", ext))) { - snprintf(full_path_win_ext, max_size, "%s.exe", path); - } else { - snprintf(full_path_win_ext, max_size, "%s", path); - } -} - -int -win32execute(char *path, char **argv, int doreturn, - int fd_stdout, int fd_stderr) -{ - PROCESS_INFORMATION pi; - memset(&pi, 0x00, sizeof(pi)); - - STARTUPINFO si; - memset(&si, 0x00, sizeof(si)); - - char *sh = win32getshell(path); - if (sh) { - path = sh; - } - - si.cb = sizeof(STARTUPINFO); - if (fd_stdout != -1) { - si.hStdOutput = (HANDLE)_get_osfhandle(fd_stdout); - si.hStdError = (HANDLE)_get_osfhandle(fd_stderr); - si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); - si.dwFlags = STARTF_USESTDHANDLES; - if (si.hStdOutput == INVALID_HANDLE_VALUE - || si.hStdError == INVALID_HANDLE_VALUE) { - return -1; - } - } else { - // Redirect subprocess stdout, stderr into current process. - si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); - si.hStdError = GetStdHandle(STD_ERROR_HANDLE); - si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); - si.dwFlags = STARTF_USESTDHANDLES; - if (si.hStdOutput == INVALID_HANDLE_VALUE - || si.hStdError == INVALID_HANDLE_VALUE) { - return -1; - } - } - - int length; - char *args = win32argvtos(sh, argv, &length); - const char *ext = strrchr(path, '.'); - char full_path_win_ext[MAX_PATH] = {0}; - add_exe_ext_if_no_to_fullpath(full_path_win_ext, MAX_PATH, ext, path); - BOOL ret = FALSE; - if (length > 8192) { - char *tmp_file = format("%s.tmp", path); - FILE *fp = create_tmp_file(&tmp_file, "w"); - char atfile[MAX_PATH + 3]; - fwrite(args, 1, length, fp); - if (ferror(fp)) { - cc_log("Error writing @file; this command will probably fail: %s", args); - } - fclose(fp); - snprintf(atfile, sizeof(atfile), "\"@%s\"", tmp_file); - ret = CreateProcess(NULL, atfile, NULL, NULL, 1, 0, NULL, NULL, - &si, &pi); - tmp_unlink(tmp_file); - free(tmp_file); - } - if (!ret) { - ret = CreateProcess(full_path_win_ext, args, NULL, NULL, 1, 0, NULL, NULL, - &si, &pi); - } - if (fd_stdout != -1) { - close(fd_stdout); - close(fd_stderr); - } - free(args); - if (ret == 0) { - LPVOID lpMsgBuf; - DWORD dw = GetLastError(); - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, - 0, NULL); - - LPVOID lpDisplayBuf = - (LPVOID) LocalAlloc(LMEM_ZEROINIT, - (lstrlen((LPCTSTR) lpMsgBuf) - + lstrlen((LPCTSTR) __FILE__) + 200) - * sizeof(TCHAR)); - _snprintf((LPTSTR) lpDisplayBuf, - LocalSize(lpDisplayBuf) / sizeof(TCHAR), - TEXT("%s failed with error %lu: %s"), __FILE__, dw, - (const char *)lpMsgBuf); - - cc_log("can't execute %s; OS returned error: %s", - full_path_win_ext, (char *)lpDisplayBuf); - - LocalFree(lpMsgBuf); - LocalFree(lpDisplayBuf); - - return -1; - } - WaitForSingleObject(pi.hProcess, INFINITE); - - DWORD exitcode; - GetExitCodeProcess(pi.hProcess, &exitcode); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - if (!doreturn) { - x_exit(exitcode); - } - return exitcode; -} - -#else - -// Execute a compiler backend, capturing all output to the given paths the full -// path to the compiler to run is in argv[0]. -int -execute(char **argv, int fd_out, int fd_err, pid_t *pid) -{ - cc_log_argv("Executing ", argv); - - block_signals(); - *pid = fork(); - unblock_signals(); - - if (*pid == -1) { - fatal("Failed to fork: %s", strerror(errno)); - } - - if (*pid == 0) { - // Child. - dup2(fd_out, 1); - close(fd_out); - dup2(fd_err, 2); - close(fd_err); - x_exit(execv(argv[0], argv)); - } - - close(fd_out); - close(fd_err); - - int status; - if (waitpid(*pid, &status, 0) != *pid) { - fatal("waitpid failed: %s", strerror(errno)); - } - - block_signals(); - *pid = 0; - unblock_signals(); - - if (WEXITSTATUS(status) == 0 && WIFSIGNALED(status)) { - return -1; - } - - return WEXITSTATUS(status); -} -#endif - -// Find an executable by name in $PATH. Exclude any that are links to -// exclude_name. -char * -find_executable(const char *name, const char *exclude_name) -{ - if (is_absolute_path(name)) { - return x_strdup(name); - } - - char *path = conf->path; - if (str_eq(path, "")) { - path = getenv("PATH"); - } - if (!path) { - cc_log("No PATH variable"); - return NULL; - } - - return find_executable_in_path(name, exclude_name, path); -} - -static char * -find_executable_in_path(const char *name, const char *exclude_name, char *path) -{ - path = x_strdup(path); - - // Search the path looking for the first compiler of the right name that - // isn't us. - char *saveptr = NULL; - for (char *tok = strtok_r(path, PATH_DELIM, &saveptr); - tok; - tok = strtok_r(NULL, PATH_DELIM, &saveptr)) { -#ifdef _WIN32 - char namebuf[MAX_PATH]; - int ret = SearchPath(tok, name, NULL, sizeof(namebuf), namebuf, NULL); - if (!ret) { - char *exename = format("%s.exe", name); - ret = SearchPath(tok, exename, NULL, sizeof(namebuf), namebuf, NULL); - free(exename); - } - (void) exclude_name; - if (ret) { - free(path); - return x_strdup(namebuf); - } -#else - struct stat st1, st2; - char *fname = format("%s/%s", tok, name); - // Look for a normal executable file. - if (access(fname, X_OK) == 0 && - lstat(fname, &st1) == 0 && - stat(fname, &st2) == 0 && - S_ISREG(st2.st_mode)) { - if (S_ISLNK(st1.st_mode)) { - char *buf = x_realpath(fname); - if (buf) { - char *p = basename(buf); - if (str_eq(p, exclude_name)) { - // It's a link to "ccache"! - free(p); - free(buf); - continue; - } - free(buf); - free(p); - } - } - - // Found it! - free(path); - return fname; - } - free(fname); -#endif - } - - free(path); - return NULL; -} - -void -print_command(FILE *fp, char **argv) -{ - for (int i = 0; argv[i]; i++) { - fprintf(fp, "%s%s", (i == 0) ? "" : " ", argv[i]); - } - fprintf(fp, "\n"); -} - -char * -format_command(char **argv) -{ - size_t len = 0; - for (int i = 0; argv[i]; i++) { - len += (i == 0) ? 0 : 1; - len += strlen(argv[i]); - } - len += 1; - char *buf = x_malloc(len + 1); - char *p = buf; - for (int i = 0; argv[i]; i++) { - if (i != 0) { - *p++ = ' '; - } - for (char *q = argv[i]; *q != '\0'; q++) { - *p++ = *q; - } - } - *p++ = '\n'; - *p++ = '\0'; - return buf; -} diff --git a/src/execute.cpp b/src/execute.cpp new file mode 100644 index 0000000..211d4ba --- /dev/null +++ b/src/execute.cpp @@ -0,0 +1,279 @@ +// Copyright (C) 2002 Andrew Tridgell +// Copyright (C) 2011-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "execute.hpp" + +#include "Config.hpp" +#include "Context.hpp" +#include "Fd.hpp" +#include "Logging.hpp" +#include "SignalHandler.hpp" +#include "Stat.hpp" +#include "TemporaryFile.hpp" +#include "Util.hpp" + +#ifdef _WIN32 +# include "Win32Util.hpp" +#endif + +using Logging::log; +using nonstd::string_view; + +#ifdef _WIN32 +int +execute(const char* const* argv, Fd&& fd_out, Fd&& fd_err, pid_t* /*pid*/) +{ + return win32execute(argv[0], argv, 1, fd_out.release(), fd_err.release()); +} + +std::string +win32getshell(const std::string& path) +{ + const char* path_env = getenv("PATH"); + std::string sh; + if (Util::to_lowercase(Util::get_extension(path)) == ".sh" && path_env) { + sh = find_executable_in_path("sh.exe", "", path_env); + } + if (sh.empty() && getenv("CCACHE_DETECT_SHEBANG")) { + // Detect shebang. + File fp(path, "r"); + if (fp) { + char buf[10] = {0}; + fgets(buf, sizeof(buf) - 1, fp.get()); + if (std::string(buf) == "#!/bin/sh" && path_env) { + sh = find_executable_in_path("sh.exe", "", path_env); + } + } + } + + return sh; +} + +int +win32execute(const char* path, + const char* const* argv, + int doreturn, + int fd_stdout, + int fd_stderr) +{ + PROCESS_INFORMATION pi; + memset(&pi, 0x00, sizeof(pi)); + + STARTUPINFO si; + memset(&si, 0x00, sizeof(si)); + + std::string sh = win32getshell(path); + if (!sh.empty()) { + path = sh.c_str(); + } + + si.cb = sizeof(STARTUPINFO); + if (fd_stdout != -1) { + si.hStdOutput = (HANDLE)_get_osfhandle(fd_stdout); + si.hStdError = (HANDLE)_get_osfhandle(fd_stderr); + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.dwFlags = STARTF_USESTDHANDLES; + if (si.hStdOutput == INVALID_HANDLE_VALUE + || si.hStdError == INVALID_HANDLE_VALUE) { + return -1; + } + } else { + // Redirect subprocess stdout, stderr into current process. + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.dwFlags = STARTF_USESTDHANDLES; + if (si.hStdOutput == INVALID_HANDLE_VALUE + || si.hStdError == INVALID_HANDLE_VALUE) { + return -1; + } + } + + std::string args = Win32Util::argv_to_string(argv, sh); + std::string full_path = Win32Util::add_exe_suffix(path); + std::string tmp_file_path; + if (args.length() > 8192) { + TemporaryFile tmp_file(path); + Util::write_fd(*tmp_file.fd, args.data(), args.length()); + args = fmt::format("\"@{}\"", tmp_file.path); + tmp_file_path = tmp_file.path; + } + BOOL ret = CreateProcess(full_path.c_str(), + const_cast(args.c_str()), + nullptr, + nullptr, + 1, + 0, + nullptr, + nullptr, + &si, + &pi); + if (!tmp_file_path.empty()) { + Util::unlink_tmp(tmp_file_path); + } + if (fd_stdout != -1) { + close(fd_stdout); + close(fd_stderr); + } + if (ret == 0) { + DWORD error = GetLastError(); + log("failed to execute {}: {} ({})", + full_path, + Win32Util::error_message(error), + error); + return -1; + } + WaitForSingleObject(pi.hProcess, INFINITE); + + DWORD exitcode; + GetExitCodeProcess(pi.hProcess, &exitcode); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + if (!doreturn) { + exit(exitcode); + } + return exitcode; +} + +#else + +// Execute a compiler backend, capturing all output to the given paths the full +// path to the compiler to run is in argv[0]. +int +execute(const char* const* argv, Fd&& fd_out, Fd&& fd_err, pid_t* pid) +{ + log("Executing {}", Util::format_argv_for_logging(argv)); + + { + SignalHandlerBlocker signal_handler_blocker; + *pid = fork(); + } + + if (*pid == -1) { + throw Fatal("Failed to fork: {}", strerror(errno)); + } + + if (*pid == 0) { + // Child. + dup2(*fd_out, STDOUT_FILENO); + fd_out.close(); + dup2(*fd_err, STDERR_FILENO); + fd_err.close(); + exit(execv(argv[0], const_cast(argv))); + } + + fd_out.close(); + fd_err.close(); + + int status; + int result; + + while ((result = waitpid(*pid, &status, 0)) != *pid) { + if (result == -1 && errno == EINTR) { + continue; + } + throw Fatal("waitpid failed: {}", strerror(errno)); + } + + { + SignalHandlerBlocker signal_handler_blocker; + *pid = 0; + } + + if (WEXITSTATUS(status) == 0 && WIFSIGNALED(status)) { + return -1; + } + + return WEXITSTATUS(status); +} +#endif + +std::string +find_executable(const Context& ctx, + const std::string& name, + const std::string& exclude_name) +{ + if (Util::is_absolute_path(name)) { + return name; + } + + std::string path = ctx.config.path(); + if (path.empty()) { + path = getenv("PATH"); + } + if (path.empty()) { + log("No PATH variable"); + return {}; + } + + return find_executable_in_path(name, exclude_name, path); +} + +std::string +find_executable_in_path(const std::string& name, + const std::string& exclude_name, + const std::string& path) +{ + if (path.empty()) { + return {}; + } + + // Search the path looking for the first compiler of the right name that isn't + // us. + for (const std::string& dir : Util::split_into_strings(path, PATH_DELIM)) { +#ifdef _WIN32 + char namebuf[MAX_PATH]; + int ret = SearchPath( + dir.c_str(), name.c_str(), nullptr, sizeof(namebuf), namebuf, nullptr); + if (!ret) { + std::string exename = fmt::format("{}.exe", name); + ret = SearchPath(dir.c_str(), + exename.c_str(), + nullptr, + sizeof(namebuf), + namebuf, + nullptr); + } + (void)exclude_name; + if (ret) { + return namebuf; + } +#else + ASSERT(!exclude_name.empty()); + std::string fname = fmt::format("{}/{}", dir, name); + auto st1 = Stat::lstat(fname); + auto st2 = Stat::stat(fname); + // Look for a normal executable file. + if (st1 && st2 && st2.is_regular() && access(fname.c_str(), X_OK) == 0) { + if (st1.is_symlink()) { + std::string real_path = Util::real_path(fname, true); + if (Util::base_name(real_path) == exclude_name) { + // It's a link to "ccache"! + continue; + } + } + + // Found it! + return fname; + } +#endif + } + + return {}; +} diff --git a/src/execute.hpp b/src/execute.hpp new file mode 100644 index 0000000..9814a73 --- /dev/null +++ b/src/execute.hpp @@ -0,0 +1,48 @@ +// Copyright (C) 2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "Fd.hpp" + +#include + +class Context; + +int execute(const char* const* argv, Fd&& fd_out, Fd&& fd_err, pid_t* pid); + +// Find an executable named `name` in `$PATH`. Exclude any executables that are +// links to `exclude_name`. +std::string find_executable(const Context& ctx, + const std::string& name, + const std::string& exclude_name); + +std::string find_executable_in_path(const std::string& name, + const std::string& exclude_name, + const std::string& path); + +#ifdef _WIN32 +std::string win32getshell(const std::string& path); +int win32execute(const char* path, + const char* const* argv, + int doreturn, + int fd_stdout, + int fd_stderr); +#endif diff --git a/src/exitfn.c b/src/exitfn.c deleted file mode 100644 index 5be5e22..0000000 --- a/src/exitfn.c +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (C) 2010-2018 Joel Rosdahl -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -#include "ccache.h" - -struct exit_function { - void (*function)(void *); - void *context; - struct exit_function *next; -}; - -struct nullary_exit_function { - void (*function)(void); -}; - -static struct exit_function *exit_functions; - -static void -call_nullary_exit_function(void *context) -{ - struct nullary_exit_function *p = (struct nullary_exit_function *)context; - p->function(); - free(p); -} - -// Initialize exit functions. Must be called once before exitfn_add* are used. -void -exitfn_init(void) -{ - if (atexit(exitfn_call) != 0) { - fatal("atexit failed: %s", strerror(errno)); - } -} - -// Add a nullary function to be called when ccache exits. Functions are called -// in reverse order. -void -exitfn_add_nullary(void (*function)(void)) -{ - struct nullary_exit_function *p = x_malloc(sizeof(*p)); - p->function = function; - exitfn_add(call_nullary_exit_function, p); -} - -// Add a function to be called with a context parameter when ccache exits. -// Functions are called in LIFO order except when added via exitfn_add_last. -void -exitfn_add(void (*function)(void *), void *context) -{ - struct exit_function *p = x_malloc(sizeof(*p)); - p->function = function; - p->context = context; - p->next = exit_functions; - exit_functions = p; -} - -// Add a function to be called with a context parameter when ccache exits. In -// contrast to exitfn_add, exitfn_add_last sets up the function to be called -// last. -void -exitfn_add_last(void (*function)(void *), void *context) -{ - struct exit_function *p = x_malloc(sizeof(*p)); - p->function = function; - p->context = context; - p->next = NULL; - - struct exit_function **q = &exit_functions; - while (*q) { - q = &(*q)->next; - } - *q = p; -} - -// Call added functions. -void -exitfn_call(void) -{ - struct exit_function *p = exit_functions; - exit_functions = NULL; - while (p) { - p->function(p->context); - struct exit_function *q = p; - p = p->next; - free(q); - } -} diff --git a/src/hash.c b/src/hash.c deleted file mode 100644 index bf06762..0000000 --- a/src/hash.c +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (C) 2002 Andrew Tridgell -// Copyright (C) 2010-2019 Joel Rosdahl -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -#include "ccache.h" -#include "hash.h" -#include "mdfour.h" - -#define HASH_DELIMITER "\000cCaChE" - -struct hash { - struct mdfour md; - FILE *debug_binary; - FILE *debug_text; -}; - -static void -do_hash_buffer(struct hash *hash, const void *s, size_t len) -{ - assert(s); - - mdfour_update(&hash->md, (const unsigned char *)s, len); - if (len > 0 && hash->debug_binary) { - (void) fwrite(s, 1, len, hash->debug_binary); - } -} - -static void -do_debug_text(struct hash *hash, const void *s, size_t len) -{ - if (len > 0 && hash->debug_text) { - (void) fwrite(s, 1, len, hash->debug_text); - } -} - -struct hash * -hash_init(void) -{ - struct hash *hash = malloc(sizeof(struct hash)); - mdfour_begin(&hash->md); - hash->debug_binary = NULL; - hash->debug_text = NULL; - return hash; -} - -struct hash * -hash_copy(struct hash *hash) -{ - struct hash *result = malloc(sizeof(struct hash)); - result->md = hash->md; - result->debug_binary = NULL; - result->debug_text = NULL; - return result; -} - -void hash_free(struct hash *hash) -{ - free(hash); -} - -void hash_enable_debug( - struct hash *hash, const char *section_name, - FILE *debug_binary, FILE *debug_text) -{ - hash->debug_binary = debug_binary; - hash->debug_text = debug_text; - - do_debug_text(hash, "=== ", 4); - do_debug_text(hash, section_name, strlen(section_name)); - do_debug_text(hash, " ===\n", 5); -} - -size_t -hash_input_size(struct hash *hash) -{ - return hash->md.totalN + hash->md.tail_len; -} - -void -hash_buffer(struct hash *hash, const void *s, size_t len) -{ - do_hash_buffer(hash, s, len); - do_debug_text(hash, s, len); -} - -char * -hash_result(struct hash *hash) -{ - unsigned char sum[16]; - - hash_result_as_bytes(hash, sum); - return format_hash_as_string(sum, hash_input_size(hash)); -} - -void -hash_result_as_bytes(struct hash *hash, unsigned char *out) -{ - mdfour_result(&hash->md, out); -} - -bool -hash_equal(struct hash *hash1, struct hash *hash2) -{ - unsigned char sum1[16]; - hash_result_as_bytes(hash1, sum1); - unsigned char sum2[16]; - hash_result_as_bytes(hash2, sum2); - return memcmp(sum1, sum2, sizeof(sum1)) == 0; -} - -void -hash_delimiter(struct hash *hash, const char *type) -{ - do_hash_buffer(hash, HASH_DELIMITER, sizeof(HASH_DELIMITER)); - do_hash_buffer(hash, type, strlen(type) + 1); // Include NUL. - do_debug_text(hash, "### ", 4); - do_debug_text(hash, type, strlen(type)); - do_debug_text(hash, "\n", 1); -} - -void -hash_string(struct hash *hash, const char *s) -{ - hash_string_buffer(hash, s, strlen(s)); -} - -void -hash_string_buffer(struct hash *hash, const char *s, int length) -{ - hash_buffer(hash, s, length); - do_debug_text(hash, "\n", 1); -} - -void -hash_int(struct hash *hash, int x) -{ - do_hash_buffer(hash, (char *)&x, sizeof(x)); - - char buf[16]; - snprintf(buf, sizeof(buf), "%d", x); - do_debug_text(hash, buf, strlen(buf)); - do_debug_text(hash, "\n", 1); -} - -bool -hash_fd(struct hash *hash, int fd) -{ - char buf[READ_BUFFER_SIZE]; - ssize_t n; - - while ((n = read(fd, buf, sizeof(buf))) != 0) { - if (n == -1 && errno != EINTR) { - break; - } - if (n > 0) { - do_hash_buffer(hash, buf, n); - do_debug_text(hash, buf, n); - } - } - return n == 0; -} - -bool -hash_file(struct hash *hash, const char *fname) -{ - int fd = open(fname, O_RDONLY|O_BINARY); - if (fd == -1) { - cc_log("Failed to open %s: %s", fname, strerror(errno)); - return false; - } - - bool ret = hash_fd(hash, fd); - close(fd); - return ret; -} diff --git a/src/hash.h b/src/hash.h deleted file mode 100644 index 06a698a..0000000 --- a/src/hash.h +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (C) 2018 Joel Rosdahl -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -#ifndef HASH_H -#define HASH_H - -#include "system.h" - -struct hash; - -// Create a new hash. -struct hash *hash_init(void); - -// Create a new hash from an existing hash state. -struct hash *hash_copy(struct hash *hash); - -// Free a hash created by hash_init or hash_copy. -void hash_free(struct hash *hash); - -// Enable debug logging of hashed input to a binary and a text file. -void hash_enable_debug( - struct hash *hash, const char *section_name, FILE *debug_binary, - FILE *debug_text); - -// Return how many bytes have been hashed. -size_t hash_input_size(struct hash *hash); - -// Return the hash result as a hex string. Caller frees. -char *hash_result(struct hash *hash); - -// Return the hash result as 16 binary bytes. -void hash_result_as_bytes(struct hash *hash, unsigned char *out); - -// Return whether hash1 and hash2 are equal. -bool hash_equal(struct hash *hash1, struct hash *hash2); - -// Hash some data that is unlikely to occur in the input. The idea is twofold: -// -// - Delimit things like arguments from each other (e.g., so that -I -O2 and -// -I-O2 hash differently). -// - Tag different types of hashed information so that it's possible to do -// conditional hashing of information in a safe way (e.g., if we want to hash -// information X if CCACHE_A is set and information Y if CCACHE_B is set, -// there should never be a hash collision risk). -void hash_delimiter(struct hash *hash, const char *type); - -// Hash bytes in a buffer. -// -// If hash debugging is enabled, the bytes are written verbatim to the text -// input file. -void hash_buffer(struct hash *hash, const void *s, size_t len); - -// Hash a string. -// -// If hash debugging is enabled, the string is written to the text input file -// followed by a newline. -void hash_string(struct hash *hash, const char *s); - -// Hash a string with a known size. -// -// If hash debugging is enabled, the string is written to the text input file -// followed by a newline. -void hash_string_buffer(struct hash *hash, const char *s, int length); - -// Hash an integer. -// -// If hash debugging is enabled, the integer is written in text form to the -// text input file followed by a newline. -void hash_int(struct hash *hash, int x); - -// Add contents of an open file to the hash. -// -// If hash debugging is enabled, the data is written verbatim to the text input -// file. -// -// Returns true on success, otherwise false. -bool hash_fd(struct hash *hash, int fd); - -// Add contents of a file to the hash. -// -// If hash debugging is enabled, the data is written verbatim to the text input -// file. -// -// Returns true on success, otherwise false. -bool hash_file(struct hash *hash, const char *fname); - -#endif diff --git a/src/hashtable.c b/src/hashtable.c deleted file mode 100644 index 308e72c..0000000 --- a/src/hashtable.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - Copyright (c) 2002, 2004, Christopher Clark - 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 of the original author; nor the names of any - 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 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. -*/ - -#include "hashtable.h" -#define HASHTABLE_INDEXFOR -#include "hashtable_private.h" -#include -#include -#include -#include - -extern const unsigned int prime_table_length; -extern const float max_load_factor; - -/* -Credit for primes table: Aaron Krowne - http://br.endernet.org/~akrowne/ - http://planetmath.org/encyclopedia/GoodHashTablePrimes.html -*/ -static const unsigned int primes[] = { -53, 97, 193, 389, -769, 1543, 3079, 6151, -12289, 24593, 49157, 98317, -196613, 393241, 786433, 1572869, -3145739, 6291469, 12582917, 25165843, -50331653, 100663319, 201326611, 402653189, -805306457, 1610612741 -}; -const unsigned int prime_table_length = sizeof(primes)/sizeof(primes[0]); -const float max_load_factor = 0.65f; - -/*****************************************************************************/ -struct hashtable * -create_hashtable(unsigned int minsize, - unsigned int (*hashf) (void*), - int (*eqf) (void*,void*)) -{ - struct hashtable *h; - unsigned int pindex, size = primes[0]; - /* Check requested hashtable isn't too large */ - if (minsize > (1u << 30)) return NULL; - /* Enforce size as prime */ - for (pindex=0; pindex < prime_table_length; pindex++) { - if (primes[pindex] > minsize) { size = primes[pindex]; break; } - } - h = (struct hashtable *)malloc(sizeof(struct hashtable)); - if (NULL == h) return NULL; /*oom*/ - h->table = (struct entry **)malloc(sizeof(struct entry*) * size); - if (NULL == h->table) { free(h); return NULL; } /*oom*/ - memset(h->table, 0, size * sizeof(struct entry *)); - h->tablelength = size; - h->primeindex = pindex; - h->entrycount = 0; - h->hashfn = hashf; - h->eqfn = eqf; - double loadlimit_float = ceil((double)size * (double)max_load_factor); - h->loadlimit = (unsigned int)loadlimit_float; - return h; -} - -/*****************************************************************************/ -unsigned int -hash(struct hashtable *h, void *k) -{ - /* Aim to protect against poor hash functions by adding logic here - * - logic taken from java 1.4 hashtable source */ - unsigned int i = h->hashfn(k); - i += ~(i << 9); - i ^= ((i >> 14) | (i << 18)); /* >>> */ - i += (i << 4); - i ^= ((i >> 10) | (i << 22)); /* >>> */ - return i; -} - -/*****************************************************************************/ -static int -hashtable_expand(struct hashtable *h) -{ - /* Double the size of the table to accommodate more entries */ - struct entry **newtable; - struct entry *e; - unsigned int newsize, i, index; - /* Check we're not hitting max capacity */ - if (h->primeindex == (prime_table_length - 1)) return 0; - newsize = primes[++(h->primeindex)]; - - newtable = (struct entry **)malloc(sizeof(struct entry*) * newsize); - if (NULL != newtable) - { - memset(newtable, 0, newsize * sizeof(struct entry *)); - /* This algorithm is not 'stable'. ie. it reverses the list - * when it transfers entries between the tables */ - for (i = 0; i < h->tablelength; i++) { - while (NULL != (e = h->table[i])) { - h->table[i] = e->next; - index = indexFor(newsize,e->h); - e->next = newtable[index]; - newtable[index] = e; - } - } - free(h->table); - h->table = newtable; - } - /* Plan B: realloc instead */ - else - { - struct entry **pE; - newtable = (struct entry **) - realloc(h->table, newsize * sizeof(struct entry *)); - if (NULL == newtable) { (h->primeindex)--; return 0; } - h->table = newtable; - memset(newtable[h->tablelength], 0, newsize - h->tablelength); - for (i = 0; i < h->tablelength; i++) { - for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) { - index = indexFor(newsize,e->h); - if (index == i) - { - pE = &(e->next); - } - else - { - *pE = e->next; - e->next = newtable[index]; - newtable[index] = e; - } - } - } - } - h->tablelength = newsize; - double loadlimit_float = ceil((double)newsize* (double)max_load_factor); - h->loadlimit = (unsigned int) loadlimit_float; - return -1; -} - -/*****************************************************************************/ -unsigned int -hashtable_count(struct hashtable *h) -{ - return h->entrycount; -} - -/*****************************************************************************/ -int -hashtable_insert(struct hashtable *h, void *k, void *v) -{ - /* This method allows duplicate keys - but they shouldn't be used */ - unsigned int index; - struct entry *e; - if (++(h->entrycount) > h->loadlimit) - { - /* Ignore the return value. If expand fails, we should - * still try cramming just this value into the existing table - * -- we may not have memory for a larger table, but one more - * element may be ok. Next time we insert, we'll try expanding again.*/ - hashtable_expand(h); - } - e = (struct entry *)malloc(sizeof(struct entry)); - if (NULL == e) { --(h->entrycount); return 0; } /*oom*/ - e->h = hash(h,k); - index = indexFor(h->tablelength,e->h); - e->k = k; - e->v = v; - e->next = h->table[index]; - h->table[index] = e; - return -1; -} - -/*****************************************************************************/ -void * /* returns value associated with key */ -hashtable_search(struct hashtable *h, void *k) -{ - struct entry *e; - unsigned int hashvalue, index; - hashvalue = hash(h,k); - index = indexFor(h->tablelength,hashvalue); - e = h->table[index]; - while (NULL != e) - { - /* Check hash value to short circuit heavier comparison */ - if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v; - e = e->next; - } - return NULL; -} - -/*****************************************************************************/ -void * /* returns value associated with key */ -hashtable_remove(struct hashtable *h, void *k) -{ - /* TODO: consider compacting the table when the load factor drops enough, - * or provide a 'compact' method. */ - - struct entry *e; - struct entry **pE; - void *v; - unsigned int hashvalue, index; - - hashvalue = hash(h,k); - index = indexFor(h->tablelength,hash(h,k)); - pE = &(h->table[index]); - e = *pE; - while (NULL != e) - { - /* Check hash value to short circuit heavier comparison */ - if ((hashvalue == e->h) && (h->eqfn(k, e->k))) - { - *pE = e->next; - h->entrycount--; - v = e->v; - freekey(e->k); - free(e); - return v; - } - pE = &(e->next); - e = e->next; - } - return NULL; -} - -/*****************************************************************************/ -/* destroy */ -void -hashtable_destroy(struct hashtable *h, int free_values) -{ - unsigned int i; - struct entry *e, *f; - struct entry **table = h->table; - if (free_values) - { - for (i = 0; i < h->tablelength; i++) - { - e = table[i]; - while (NULL != e) - { f = e; e = e->next; freekey(f->k); free(f->v); free(f); } - } - } - else - { - for (i = 0; i < h->tablelength; i++) - { - e = table[i]; - while (NULL != e) - { f = e; e = e->next; freekey(f->k); free(f); } - } - } - free(h->table); - free(h); -} - -/* - * Copyright (c) 2002, Christopher Clark - * 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 of the original author; nor the names of any 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 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. -*/ diff --git a/src/hashtable.h b/src/hashtable.h deleted file mode 100644 index 91fb387..0000000 --- a/src/hashtable.h +++ /dev/null @@ -1,230 +0,0 @@ -/* - Copyright (c) 2002, 2004, Christopher Clark - 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 of the original author; nor the names of any - 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 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. -*/ - -#ifndef HASHTABLE_CWC22_H -#define HASHTABLE_CWC22_H - -#include "config.h" - -struct hashtable; - -/* Example of use: - * - * struct hashtable *h; - * struct some_key *k; - * struct some_value *v; - * - * static unsigned int hash_from_key_fn( void *k ); - * static int keys_equal_fn ( void *key1, void *key2 ); - * - * h = create_hashtable(16, hash_from_key_fn, keys_equal_fn); - * k = (struct some_key *) malloc(sizeof(struct some_key)); - * v = (struct some_value *) malloc(sizeof(struct some_value)); - * - * (initialise k and v to suitable values) - * - * if (! hashtable_insert(h,k,v) ) - * { exit(-1); } - * - * if (NULL == (found = hashtable_search(h,k) )) - * { printf("not found!"); } - * - * if (NULL == (found = hashtable_remove(h,k) )) - * { printf("Not found\n"); } - * - */ - -/* Macros may be used to define type-safe(r) hashtable access functions, with - * methods specialized to take known key and value types as parameters. - * - * Example: - * - * Insert this at the start of your file: - * - * DEFINE_HASHTABLE_INSERT(insert_some, struct some_key, struct some_value); - * DEFINE_HASHTABLE_SEARCH(search_some, struct some_key, struct some_value); - * DEFINE_HASHTABLE_REMOVE(remove_some, struct some_key, struct some_value); - * - * This defines the functions 'insert_some', 'search_some' and 'remove_some'. - * These operate just like hashtable_insert etc., with the same parameters, - * but their function signatures have 'struct some_key *' rather than - * 'void *', and hence can generate compile time errors if your program is - * supplying incorrect data as a key (and similarly for value). - * - * Note that the hash and key equality functions passed to create_hashtable - * still take 'void *' parameters instead of 'some key *'. This shouldn't be - * a difficult issue as they're only defined and passed once, and the other - * functions will ensure that only valid keys are supplied to them. - * - * The cost for this checking is increased code size and runtime overhead - * - if performance is important, it may be worth switching back to the - * unsafe methods once your program has been debugged with the safe methods. - * This just requires switching to some simple alternative defines - eg: - * #define insert_some hashtable_insert - * - */ - -/***************************************************************************** - * create_hashtable - - * @name create_hashtable - * @param minsize minimum initial size of hashtable - * @param hashfunction function for hashing keys - * @param key_eq_fn function for determining key equality - * @return newly created hashtable or NULL on failure - */ - -struct hashtable * -create_hashtable(unsigned int minsize, - unsigned int (*hashfunction) (void*), - int (*key_eq_fn) (void*,void*)); - -/***************************************************************************** - * hashtable_insert - - * @name hashtable_insert - * @param h the hashtable to insert into - * @param k the key - hashtable claims ownership and will free on removal - * @param v the value - does not claim ownership - * @return non-zero for successful insertion - * - * This function will cause the table to expand if the insertion would take - * the ratio of entries to table size over the maximum load factor. - * - * This function does not check for repeated insertions with a duplicate key. - * The value returned when using a duplicate key is undefined -- when - * the hashtable changes size, the order of retrieval of duplicate key - * entries is reversed. - * If in doubt, remove before insert. - */ - -int -hashtable_insert(struct hashtable *h, void *k, void *v); - -#define DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \ -int fnname (struct hashtable *h, keytype *k, valuetype *v) \ -{ \ - return hashtable_insert(h,k,v); \ -} - -/***************************************************************************** - * hashtable_search - - * @name hashtable_search - * @param h the hashtable to search - * @param k the key to search for - does not claim ownership - * @return the value associated with the key, or NULL if none found - */ - -void * -hashtable_search(struct hashtable *h, void *k); - -#define DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \ -valuetype * fnname (struct hashtable *h, keytype *k) \ -{ \ - return (valuetype *) (hashtable_search(h,k)); \ -} - -/***************************************************************************** - * hashtable_remove - - * @name hashtable_remove - * @param h the hashtable to remove the item from - * @param k the key to search for - does not claim ownership - * @return the value associated with the key, or NULL if none found - */ - -void * /* returns value */ -hashtable_remove(struct hashtable *h, void *k); - -#define DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \ -valuetype * fnname (struct hashtable *h, keytype *k) \ -{ \ - return (valuetype *) (hashtable_remove(h,k)); \ -} - - -/***************************************************************************** - * hashtable_count - - * @name hashtable_count - * @param h the hashtable - * @return the number of items stored in the hashtable - */ -unsigned int -hashtable_count(struct hashtable *h); - - -/***************************************************************************** - * hashtable_destroy - - * @name hashtable_destroy - * @param h the hashtable - * @param free_values whether to call 'free' on the remaining values - */ - -void -hashtable_destroy(struct hashtable *h, int free_values); - -#endif /* HASHTABLE_CWC22_H */ - -/* - * Copyright (c) 2002, Christopher Clark - * 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 of the original author; nor the names of any 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 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. -*/ diff --git a/src/hashtable_itr.c b/src/hashtable_itr.c deleted file mode 100644 index 1ee43da..0000000 --- a/src/hashtable_itr.c +++ /dev/null @@ -1,189 +0,0 @@ -/* Copyright (C) 2002, 2004 Christopher Clark */ - -#include "hashtable.h" -#define HASHTABLE_INDEXFOR -#include "hashtable_private.h" -#include "hashtable_itr.h" -#include /* defines NULL */ - -/*****************************************************************************/ -/* hashtable_iterator - iterator constructor */ - -struct hashtable_itr * -hashtable_iterator(struct hashtable *h) -{ - unsigned int i, tablelength; - struct hashtable_itr *itr = (struct hashtable_itr *) - malloc(sizeof(struct hashtable_itr)); - if (NULL == itr) return NULL; - itr->h = h; - itr->e = NULL; - itr->parent = NULL; - tablelength = h->tablelength; - itr->index = tablelength; - if (0 == h->entrycount) return itr; - - for (i = 0; i < tablelength; i++) - { - if (NULL != h->table[i]) - { - itr->e = h->table[i]; - itr->index = i; - break; - } - } - return itr; -} - -/*****************************************************************************/ -/* key - return the key of the (key,value) pair at the current position */ -/* value - return the value of the (key,value) pair at the current position */ - -void * -hashtable_iterator_key(struct hashtable_itr *i) -{ return i->e->k; } - -void * -hashtable_iterator_value(struct hashtable_itr *i) -{ return i->e->v; } - -/*****************************************************************************/ -/* advance - advance the iterator to the next element - * returns zero if advanced to end of table */ - -int -hashtable_iterator_advance(struct hashtable_itr *itr) -{ - unsigned int j,tablelength; - struct entry **table; - struct entry *next; - if (NULL == itr->e) return 0; /* stupidity check */ - - next = itr->e->next; - if (NULL != next) - { - itr->parent = itr->e; - itr->e = next; - return -1; - } - tablelength = itr->h->tablelength; - itr->parent = NULL; - if (tablelength <= (j = ++(itr->index))) - { - itr->e = NULL; - return 0; - } - table = itr->h->table; - while (NULL == (next = table[j])) - { - if (++j >= tablelength) - { - itr->index = tablelength; - itr->e = NULL; - return 0; - } - } - itr->index = j; - itr->e = next; - return -1; -} - -/*****************************************************************************/ -/* remove - remove the entry at the current iterator position - * and advance the iterator, if there is a successive - * element. - * If you want the value, read it before you remove: - * beware memory leaks if you don't. - * Returns zero if end of iteration. */ - -int -hashtable_iterator_remove(struct hashtable_itr *itr) -{ - struct entry *remember_e, *remember_parent; - int ret; - - /* Do the removal */ - if (NULL == (itr->parent)) - { - /* element is head of a chain */ - itr->h->table[itr->index] = itr->e->next; - } else { - /* element is mid-chain */ - itr->parent->next = itr->e->next; - } - /* itr->e is now outside the hashtable */ - remember_e = itr->e; - itr->h->entrycount--; - freekey(remember_e->k); - - /* Advance the iterator, correcting the parent */ - remember_parent = itr->parent; - ret = hashtable_iterator_advance(itr); - if (itr->parent == remember_e) { itr->parent = remember_parent; } - free(remember_e); - return ret; -} - -/*****************************************************************************/ -int /* returns zero if not found */ -hashtable_iterator_search(struct hashtable_itr *itr, - struct hashtable *h, void *k) -{ - struct entry *e, *parent; - unsigned int hashvalue, index; - - hashvalue = hash(h,k); - index = indexFor(h->tablelength,hashvalue); - - e = h->table[index]; - parent = NULL; - while (NULL != e) - { - /* Check hash value to short circuit heavier comparison */ - if ((hashvalue == e->h) && (h->eqfn(k, e->k))) - { - itr->index = index; - itr->e = e; - itr->parent = parent; - itr->h = h; - return -1; - } - parent = e; - e = e->next; - } - return 0; -} - - -/* - * Copyright (c) 2002, 2004, Christopher Clark - * 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 of the original author; nor the names of any 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 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. -*/ diff --git a/src/hashtable_itr.h b/src/hashtable_itr.h deleted file mode 100644 index a0ac22c..0000000 --- a/src/hashtable_itr.h +++ /dev/null @@ -1,122 +0,0 @@ -/* Copyright (C) 2002, 2004 Christopher Clark */ - -#ifndef HASHTABLE_ITR_CWC22_H -#define HASHTABLE_ITR_CWC22_H -#include "hashtable.h" -#include "hashtable_private.h" /* needed to enable inlining */ - -/*****************************************************************************/ -/* This struct is only concrete here to allow the inlining of two of the - * accessor functions. */ -struct hashtable_itr -{ - struct hashtable *h; - struct entry *e; - struct entry *parent; - unsigned int index; -}; - - -/*****************************************************************************/ -/* hashtable_iterator - */ - -struct hashtable_itr * -hashtable_iterator(struct hashtable *h); - -/*****************************************************************************/ -/* hashtable_iterator_key - * - return the value of the (key,value) pair at the current position */ - -#ifdef HAVE_EXTERN_INLINE -extern inline void * -hashtable_iterator_key(struct hashtable_itr *i) -{ - return i->e->k; -} -#else -void * -hashtable_iterator_key(struct hashtable_itr *i); -#endif - -/*****************************************************************************/ -/* value - return the value of the (key,value) pair at the current position */ - -#ifdef HAVE_EXTERN_INLINE -extern inline void * -hashtable_iterator_value(struct hashtable_itr *i) -{ - return i->e->v; -} -#else -void * -hashtable_iterator_value(struct hashtable_itr *i); -#endif - -/*****************************************************************************/ -/* advance - advance the iterator to the next element - * returns zero if advanced to end of table */ - -int -hashtable_iterator_advance(struct hashtable_itr *itr); - -/*****************************************************************************/ -/* remove - remove current element and advance the iterator to the next element - * NB: if you need the value to free it, read it before - * removing. ie: beware memory leaks! - * returns zero if advanced to end of table */ - -int -hashtable_iterator_remove(struct hashtable_itr *itr); - -/*****************************************************************************/ -/* search - overwrite the supplied iterator, to point to the entry - * matching the supplied key. - h points to the hashtable to be searched. - * returns zero if not found. */ -int -hashtable_iterator_search(struct hashtable_itr *itr, - struct hashtable *h, void *k); - -#define DEFINE_HASHTABLE_ITERATOR_SEARCH(fnname, keytype) \ -int fnname (struct hashtable_itr *i, struct hashtable *h, keytype *k) \ -{ \ - return (hashtable_iterator_search(i,h,k)); \ -} - - - -#endif /* HASHTABLE_ITR_CWC22_H */ - -/* - * Copyright (c) 2002, 2004, Christopher Clark - * 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 of the original author; nor the names of any 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 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. -*/ diff --git a/src/hashtable_private.h b/src/hashtable_private.h deleted file mode 100644 index 260afca..0000000 --- a/src/hashtable_private.h +++ /dev/null @@ -1,87 +0,0 @@ -/* Copyright (C) 2002, 2004 Christopher Clark */ - -#ifndef HASHTABLE_PRIVATE_CWC22_H -#define HASHTABLE_PRIVATE_CWC22_H - -#include "hashtable.h" - -/*****************************************************************************/ -struct entry -{ - void *k, *v; - unsigned int h; - struct entry *next; -}; - -struct hashtable { - unsigned int tablelength; - struct entry **table; - unsigned int entrycount; - unsigned int loadlimit; - unsigned int primeindex; - unsigned int (*hashfn) (void *k); - int (*eqfn) (void *k1, void *k2); -}; - -/*****************************************************************************/ -unsigned int -hash(struct hashtable *h, void *k); - -/*****************************************************************************/ -#ifdef HASHTABLE_INDEXFOR -/* indexFor */ -static inline unsigned int -indexFor(unsigned int tablelength, unsigned int hashvalue) { - return (hashvalue % tablelength); -} - -/* Only works if tablelength == 2^N */ -/*static inline unsigned int -indexFor(unsigned int tablelength, unsigned int hashvalue) -{ - return (hashvalue & (tablelength - 1u)); -} -*/ -#endif - -/*****************************************************************************/ -#define freekey(X) free(X) -/*define freekey(X) ; */ - - -/*****************************************************************************/ - -#endif /* HASHTABLE_PRIVATE_CWC22_H */ - -/* - * Copyright (c) 2002, Christopher Clark - * 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 of the original author; nor the names of any 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 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. -*/ diff --git a/src/hashutil.c b/src/hashutil.c deleted file mode 100644 index 482ce6b..0000000 --- a/src/hashutil.c +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright (C) 2009-2019 Joel Rosdahl -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -#include "ccache.h" -#include "hashutil.h" -#include "macroskip.h" -#include "murmurhashneutral2.h" - -unsigned -hash_from_string(void *str) -{ - return murmurhashneutral2(str, strlen((const char *)str), 0); -} - -unsigned -hash_from_int(int i) -{ - return murmurhashneutral2(&i, sizeof(int), 0); -} - -int -strings_equal(void *str1, void *str2) -{ - return str_eq((const char *)str1, (const char *)str2); -} - -int -file_hashes_equal(struct file_hash *fh1, struct file_hash *fh2) -{ - return memcmp(fh1->hash, fh2->hash, 16) == 0 - && fh1->size == fh2->size; -} - -// Search for the strings "__DATE__" and "__TIME__" in str. -// -// Returns a bitmask with HASH_SOURCE_CODE_FOUND_DATE and -// HASH_SOURCE_CODE_FOUND_TIME set appropriately. -int -check_for_temporal_macros(const char *str, size_t len) -{ - int result = 0; - - // We're using the Boyer-Moore-Horspool algorithm, which searches starting - // from the *end* of the needle. Our needles are 8 characters long, so i - // starts at 7. - size_t i = 7; - - while (i < len) { - // Check whether the substring ending at str[i] has the form "__...E__". On - // the assumption that 'E' is less common in source than '_', we check - // str[i-2] first. - if (str[i - 2] == 'E' - && str[i - 0] == '_' - && str[i - 7] == '_' - && str[i - 1] == '_' - && str[i - 6] == '_' - && (i < 8 || (str[i - 8] != '_' && !isalnum(str[i - 8]))) - && (i + 1 >= len || (str[i + 1] != '_' && !isalnum(str[i + 1])))) { - // Check the remaining characters to see if the substring is "__DATE__" - // or "__TIME__". - if (str[i - 5] == 'D' && str[i - 4] == 'A' && str[i - 3] == 'T') { - result |= HASH_SOURCE_CODE_FOUND_DATE; - } else if (str[i - 5] == 'T' && str[i - 4] == 'I' && str[i - 3] == 'M') { - result |= HASH_SOURCE_CODE_FOUND_TIME; - } - } - - // macro_skip tells us how far we can skip forward upon seeing str[i] at - // the end of a substring. - i += macro_skip[(uint8_t)str[i]]; - } - - return result; -} - -// Hash a string. Returns a bitmask of HASH_SOURCE_CODE_* results. -int -hash_source_code_string( - struct conf *conf, struct hash *hash, const char *str, size_t len, - const char *path) -{ - int result = HASH_SOURCE_CODE_OK; - - // Check for __DATE__ and __TIME__ if the sloppiness configuration tells us - // we should. - if (!(conf->sloppiness & SLOPPY_TIME_MACROS)) { - result |= check_for_temporal_macros(str, len); - } - - // Hash the source string. - hash_string_buffer(hash, str, len); - - if (result & HASH_SOURCE_CODE_FOUND_DATE) { - cc_log("Found __DATE__ in %s", path); - - // Make sure that the hash sum changes if the (potential) expansion of - // __DATE__ changes. - time_t t = time(NULL); - struct tm now; - hash_delimiter(hash, "date"); - if (!localtime_r(&t, &now)) { - return HASH_SOURCE_CODE_ERROR; - } - hash_int(hash, now.tm_year); - hash_int(hash, now.tm_mon); - hash_int(hash, now.tm_mday); - } - if (result & HASH_SOURCE_CODE_FOUND_TIME) { - // We don't know for sure that the program actually uses the __TIME__ - // macro, but we have to assume it anyway and hash the time stamp. However, - // that's not very useful since the chance that we get a cache hit later - // the same second should be quite slim... So, just signal back to the - // caller that __TIME__ has been found so that the direct mode can be - // disabled. - cc_log("Found __TIME__ in %s", path); - } - - return result; -} - -// Hash a file ignoring comments. Returns a bitmask of HASH_SOURCE_CODE_* -// results. -int -hash_source_code_file(struct conf *conf, struct hash *hash, const char *path) -{ - if (is_precompiled_header(path)) { - if (hash_file(hash, path)) { - return HASH_SOURCE_CODE_OK; - } else { - return HASH_SOURCE_CODE_ERROR; - } - } else { - char *data; - size_t size; - if (!read_file(path, 0, &data, &size)) { - return HASH_SOURCE_CODE_ERROR; - } - int result = hash_source_code_string(conf, hash, data, size, path); - free(data); - return result; - } -} - -bool -hash_command_output(struct hash *hash, const char *command, - const char *compiler) -{ -#ifdef _WIN32 - // Trim leading space. - while (isspace(*command)) { - command++; - } - - // Add "echo" command. - bool cmd; - if (str_startswith(command, "echo")) { - command = format("cmd.exe /c \"%s\"", command); - cmd = true; - } else if (str_startswith(command, - "%compiler%") && str_eq(compiler, "echo")) { - command = format("cmd.exe /c \"%s%s\"", compiler, command + 10); - cmd = true; - } else { - command = x_strdup(command); - cmd = false; - } -#endif - - struct args *args = args_init_from_string(command); - for (int i = 0; i < args->argc; i++) { - if (str_eq(args->argv[i], "%compiler%")) { - args_set(args, i, compiler); - } - } - cc_log_argv("Executing compiler check command ", args->argv); - -#ifdef _WIN32 - PROCESS_INFORMATION pi; - memset(&pi, 0x00, sizeof(pi)); - STARTUPINFO si; - memset(&si, 0x00, sizeof(si)); - - char *path = find_executable(args->argv[0], NULL); - if (!path) { - path = args->argv[0]; - } - char *sh = win32getshell(path); - if (sh) { - path = sh; - } - - si.cb = sizeof(STARTUPINFO); - - HANDLE pipe_out[2]; - SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; - CreatePipe(&pipe_out[0], &pipe_out[1], &sa, 0); - SetHandleInformation(pipe_out[0], HANDLE_FLAG_INHERIT, 0); - si.hStdOutput = pipe_out[1]; - si.hStdError = pipe_out[1]; - si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); - si.dwFlags = STARTF_USESTDHANDLES; - - char *win32args; - if (!cmd) { - int length; - win32args = win32argvtos(sh, args->argv, &length); - } else { - win32args = (char *)command; // quoted - } - BOOL ret = - CreateProcess(path, win32args, NULL, NULL, 1, 0, NULL, NULL, &si, &pi); - CloseHandle(pipe_out[1]); - args_free(args); - free(win32args); - if (!cmd) { - free((char *)command); // Original argument was replaced above. - } - if (ret == 0) { - stats_update(STATS_COMPCHECK); - return false; - } - int fd = _open_osfhandle((intptr_t) pipe_out[0], O_BINARY); - bool ok = hash_fd(hash, fd); - if (!ok) { - cc_log("Error hashing compiler check command output: %s", strerror(errno)); - stats_update(STATS_COMPCHECK); - } - WaitForSingleObject(pi.hProcess, INFINITE); - DWORD exitcode; - GetExitCodeProcess(pi.hProcess, &exitcode); - CloseHandle(pipe_out[0]); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - if (exitcode != 0) { - cc_log("Compiler check command returned %d", (int) exitcode); - stats_update(STATS_COMPCHECK); - return false; - } - return ok; -#else - int pipefd[2]; - if (pipe(pipefd) == -1) { - fatal("pipe failed"); - } - - pid_t pid = fork(); - if (pid == -1) { - fatal("fork failed"); - } - - if (pid == 0) { - // Child. - close(pipefd[0]); - close(0); - dup2(pipefd[1], 1); - dup2(pipefd[1], 2); - _exit(execvp(args->argv[0], args->argv)); - return false; // Never reached. - } else { - // Parent. - args_free(args); - close(pipefd[1]); - bool ok = hash_fd(hash, pipefd[0]); - if (!ok) { - cc_log("Error hashing compiler check command output: %s", - strerror(errno)); - stats_update(STATS_COMPCHECK); - } - close(pipefd[0]); - - int status; - if (waitpid(pid, &status, 0) != pid) { - cc_log("waitpid failed"); - return false; - } - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - cc_log("Compiler check command returned %d", WEXITSTATUS(status)); - stats_update(STATS_COMPCHECK); - return false; - } - return ok; - } -#endif -} - -bool -hash_multicommand_output(struct hash *hash, const char *commands, - const char *compiler) -{ - char *command_string = x_strdup(commands); - char *p = command_string; - char *command; - char *saveptr = NULL; - bool ok = true; - while ((command = strtok_r(p, ";", &saveptr))) { - if (!hash_command_output(hash, command, compiler)) { - ok = false; - } - p = NULL; - } - free(command_string); - return ok; -} diff --git a/src/hashutil.cpp b/src/hashutil.cpp new file mode 100644 index 0000000..d37c100 --- /dev/null +++ b/src/hashutil.cpp @@ -0,0 +1,522 @@ +// Copyright (C) 2009-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "hashutil.hpp" + +#include "Args.hpp" +#include "Config.hpp" +#include "Context.hpp" +#include "Hash.hpp" +#include "Logging.hpp" +#include "Stat.hpp" +#include "ccache.hpp" +#include "execute.hpp" +#include "macroskip.hpp" + +#ifdef INODE_CACHE_SUPPORTED +# include "InodeCache.hpp" +#endif + +#ifdef _WIN32 +# include "Win32Util.hpp" +#endif + +// With older GCC (libgcc), __builtin_cpu_supports("avx2) returns true if AVX2 +// is supported by the CPU but disabled by the OS. This was fixed in GCC 8, 7.4 +// and 6.5 (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85100). +// +// For Clang it seems to be correct if compiler-rt is used as -rtlib, at least +// as of 3.9 (see https://bugs.llvm.org/show_bug.cgi?id=25510). But if libgcc is +// used we have the same problem as mentioned above. Unfortunately there doesn't +// seem to be a way to detect which one is used, or the version of libgcc when +// used by Clang, so assume that it works with Clang >= 3.9. +#if !(__GNUC__ >= 8 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 4) \ + || (__GNUC__ == 6 && __GNUC_MINOR__ >= 5) || __clang_major__ > 3 \ + || (__clang_major__ == 3 && __clang_minor__ >= 9)) +# undef HAVE_AVX2 +#endif + +#ifdef HAVE_AVX2 +# include +#endif + +using Logging::log; +using nonstd::string_view; + +namespace { + +// Returns one of HASH_SOURCE_CODE_FOUND_DATE, HASH_SOURCE_CODE_FOUND_TIME or +// HASH_SOURCE_CODE_FOUND_TIMESTAMP if "_DATE__", "_TIME__" or "_TIMESTAMP__" +// starts at str[pos]. +// +// Pre-condition: str[pos - 1] == '_' +int +check_for_temporal_macros_helper(string_view str, size_t pos) +{ + if (pos + 7 > str.length()) { + return 0; + } + + int found = 0; + int macro_len = 7; + if (memcmp(&str[pos], "_DATE__", 7) == 0) { + found = HASH_SOURCE_CODE_FOUND_DATE; + } else if (memcmp(&str[pos], "_TIME__", 7) == 0) { + found = HASH_SOURCE_CODE_FOUND_TIME; + } else if (pos + 12 <= str.length() + && memcmp(&str[pos], "_TIMESTAMP__", 12) == 0) { + found = HASH_SOURCE_CODE_FOUND_TIMESTAMP; + macro_len = 12; + } else { + return 0; + } + + // Check char before and after macro to verify that the found macro isn't part + // of another identifier. + if ((pos == 1 || (str[pos - 2] != '_' && !isalnum(str[pos - 2]))) + && (pos + macro_len == str.length() + || (str[pos + macro_len] != '_' && !isalnum(str[pos + macro_len])))) { + return found; + } + + return 0; +} + +int +check_for_temporal_macros_bmh(string_view str) +{ + int result = 0; + + // We're using the Boyer-Moore-Horspool algorithm, which searches starting + // from the *end* of the needle. Our needles are 8 characters long, so i + // starts at 7. + size_t i = 7; + + while (i < str.length()) { + // Check whether the substring ending at str[i] has the form "_....E..". On + // the assumption that 'E' is less common in source than '_', we check + // str[i-2] first. + if (str[i - 2] == 'E' && str[i - 7] == '_') { + result |= check_for_temporal_macros_helper(str, i - 6); + } + + // macro_skip tells us how far we can skip forward upon seeing str[i] at + // the end of a substring. + i += macro_skip[(uint8_t)str[i]]; + } + + return result; +} + +#ifdef HAVE_AVX2 +int check_for_temporal_macros_avx2(string_view str) + __attribute__((target("avx2"))); + +// The following algorithm, which uses AVX2 instructions to find __DATE__, +// __TIME__ and __TIMESTAMP__, is heavily inspired by +// . +int +check_for_temporal_macros_avx2(string_view str) +{ + int result = 0; + + // Set all 32 bytes in first and last to '_' and 'E' respectively. + const __m256i first = _mm256_set1_epi8('_'); + const __m256i last = _mm256_set1_epi8('E'); + + size_t pos = 0; + for (; pos + 5 + 32 <= str.length(); pos += 32) { + // Load 32 bytes from the current position in the input string, with + // block_last being offset 5 bytes (i.e. the offset of 'E' in all three + // macros). + const __m256i block_first = + _mm256_loadu_si256(reinterpret_cast(&str[pos])); + const __m256i block_last = + _mm256_loadu_si256(reinterpret_cast(&str[pos + 5])); + + // For i in 0..31: + // eq_X[i] = 0xFF if X[i] == block_X[i] else 0 + const __m256i eq_first = _mm256_cmpeq_epi8(first, block_first); + const __m256i eq_last = _mm256_cmpeq_epi8(last, block_last); + + // Set bit i in mask if byte i in both eq_first and eq_last has the most + // significant bit set. + uint32_t mask = _mm256_movemask_epi8(_mm256_and_si256(eq_first, eq_last)); + + // A bit set in mask now indicates a possible location for a temporal macro. + while (mask != 0) { + // The start position + 1 (as we know the first char is _). + const auto start = pos + __builtin_ctz(mask) + 1; + + // Clear the least significant bit set. + mask = mask & (mask - 1); + + result |= check_for_temporal_macros_helper(str, start); + } + } + + result |= check_for_temporal_macros_bmh(str.substr(pos)); + + return result; +} +#endif + +int +hash_source_code_file_nocache(const Context& ctx, + Hash& hash, + const std::string& path, + size_t size_hint, + bool is_precompiled) +{ + if (is_precompiled) { + if (hash.hash_file(path)) { + return HASH_SOURCE_CODE_OK; + } else { + return HASH_SOURCE_CODE_ERROR; + } + } else { + std::string data; + try { + data = Util::read_file(path, size_hint); + } catch (Error&) { + return HASH_SOURCE_CODE_ERROR; + } + int result = hash_source_code_string(ctx, hash, data, path); + return result; + } +} + +#ifdef INODE_CACHE_SUPPORTED +InodeCache::ContentType +get_content_type(const Config& config, const std::string& path) +{ + if (Util::is_precompiled_header(path)) { + return InodeCache::ContentType::precompiled_header; + } + if (config.sloppiness() & SLOPPY_TIME_MACROS) { + return InodeCache::ContentType::code_with_sloppy_time_macros; + } + return InodeCache::ContentType::code; +} +#endif + +} // namespace + +int +check_for_temporal_macros(string_view str) +{ +#ifdef HAVE_AVX2 + if (__builtin_cpu_supports("avx2")) { + return check_for_temporal_macros_avx2(str); + } +#endif + return check_for_temporal_macros_bmh(str); +} + +int +hash_source_code_string(const Context& ctx, + Hash& hash, + string_view str, + const std::string& path) +{ + int result = HASH_SOURCE_CODE_OK; + + // Check for __DATE__, __TIME__ and __TIMESTAMP__if the sloppiness + // configuration tells us we should. + if (!(ctx.config.sloppiness() & SLOPPY_TIME_MACROS)) { + result |= check_for_temporal_macros(str); + } + + // Hash the source string. + hash.hash(str); + + if (result & HASH_SOURCE_CODE_FOUND_DATE) { + log("Found __DATE__ in {}", path); + + // Make sure that the hash sum changes if the (potential) expansion of + // __DATE__ changes. + hash.hash_delimiter("date"); + auto now = Util::localtime(); + if (!now) { + return HASH_SOURCE_CODE_ERROR; + } + hash.hash(now->tm_year); + hash.hash(now->tm_mon); + hash.hash(now->tm_mday); + } + if (result & HASH_SOURCE_CODE_FOUND_TIME) { + // We don't know for sure that the program actually uses the __TIME__ macro, + // but we have to assume it anyway and hash the time stamp. However, that's + // not very useful since the chance that we get a cache hit later the same + // second should be quite slim... So, just signal back to the caller that + // __TIME__ has been found so that the direct mode can be disabled. + log("Found __TIME__ in {}", path); + } + if (result & HASH_SOURCE_CODE_FOUND_TIMESTAMP) { + log("Found __TIMESTAMP__ in {}", path); + + // Make sure that the hash sum changes if the (potential) expansion of + // __TIMESTAMP__ changes. + const auto stat = Stat::stat(path); + if (!stat) { + return HASH_SOURCE_CODE_ERROR; + } + + auto modified_time = Util::localtime(stat.mtime()); + if (!modified_time) { + return HASH_SOURCE_CODE_ERROR; + } + hash.hash_delimiter("timestamp"); +#ifdef HAVE_ASCTIME_R + char buffer[26]; + auto timestamp = asctime_r(&*modified_time, buffer); +#else + auto timestamp = asctime(&*modified_time); +#endif + if (!timestamp) { + return HASH_SOURCE_CODE_ERROR; + } + hash.hash(timestamp); + } + + return result; +} + +int +hash_source_code_file(const Context& ctx, + Hash& hash, + const std::string& path, + size_t size_hint) +{ +#ifdef INODE_CACHE_SUPPORTED + if (!ctx.config.inode_cache()) { +#endif + return hash_source_code_file_nocache( + ctx, hash, path, size_hint, Util::is_precompiled_header(path)); + +#ifdef INODE_CACHE_SUPPORTED + } + + // Reusable file hashes must be independent of the outer context. Thus hash + // files separately so that digests based on file contents can be reused. Then + // add the digest into the outer hash instead. + InodeCache::ContentType content_type = get_content_type(ctx.config, path); + Digest digest; + int return_value; + if (!ctx.inode_cache.get(path, content_type, digest, &return_value)) { + Hash file_hash; + return_value = hash_source_code_file_nocache( + ctx, + file_hash, + path, + size_hint, + content_type == InodeCache::ContentType::precompiled_header); + if (return_value == HASH_SOURCE_CODE_ERROR) { + return HASH_SOURCE_CODE_ERROR; + } + digest = file_hash.digest(); + ctx.inode_cache.put(path, content_type, digest, return_value); + } + hash.hash(digest.bytes(), Digest::size(), Hash::HashType::binary); + return return_value; +#endif +} + +bool +hash_binary_file(const Context& ctx, Hash& hash, const std::string& path) +{ + if (!ctx.config.inode_cache()) { + return hash.hash_file(path); + } + +#ifdef INODE_CACHE_SUPPORTED + // Reusable file hashes must be independent of the outer context. Thus hash + // files separately so that digests based on file contents can be reused. Then + // add the digest into the outer hash instead. + Digest digest; + if (!ctx.inode_cache.get(path, InodeCache::ContentType::binary, digest)) { + Hash file_hash; + if (!file_hash.hash_file(path)) { + return false; + } + digest = file_hash.digest(); + ctx.inode_cache.put(path, InodeCache::ContentType::binary, digest); + } + hash.hash(digest.bytes(), Digest::size(), Hash::HashType::binary); + return true; +#else + return hash.hash_file(path); +#endif +} + +bool +hash_command_output(Hash& hash, + const std::string& command, + const std::string& compiler) +{ +#ifdef _WIN32 + std::string adjusted_command = Util::strip_whitespace(command); + + // Add "echo" command. + bool using_cmd_exe; + if (Util::starts_with(adjusted_command, "echo")) { + adjusted_command = fmt::format("cmd.exe /c \"{}\"", adjusted_command); + using_cmd_exe = true; + } else if (Util::starts_with(adjusted_command, "%compiler%") + && compiler == "echo") { + adjusted_command = + fmt::format("cmd.exe /c \"{}{}\"", compiler, adjusted_command.substr(10)); + using_cmd_exe = true; + } else { + using_cmd_exe = false; + } + Args args = Args::from_string(adjusted_command); +#else + Args args = Args::from_string(command); +#endif + + for (size_t i = 0; i < args.size(); i++) { + if (args[i] == "%compiler%") { + args[i] = compiler; + } + } + + auto argv = args.to_argv(); + log("Executing compiler check command {}", + Util::format_argv_for_logging(argv.data())); + +#ifdef _WIN32 + PROCESS_INFORMATION pi; + memset(&pi, 0x00, sizeof(pi)); + STARTUPINFO si; + memset(&si, 0x00, sizeof(si)); + + std::string path = find_executable_in_path(args[0], "", getenv("PATH")); + if (path.empty()) { + path = args[0]; + } + std::string sh = win32getshell(path); + if (!sh.empty()) { + path = sh; + } + + si.cb = sizeof(STARTUPINFO); + + HANDLE pipe_out[2]; + SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), nullptr, TRUE}; + CreatePipe(&pipe_out[0], &pipe_out[1], &sa, 0); + SetHandleInformation(pipe_out[0], HANDLE_FLAG_INHERIT, 0); + si.hStdOutput = pipe_out[1]; + si.hStdError = pipe_out[1]; + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.dwFlags = STARTF_USESTDHANDLES; + + std::string win32args; + if (using_cmd_exe) { + win32args = adjusted_command; // quoted + } else { + win32args = Win32Util::argv_to_string(argv.data(), sh); + } + BOOL ret = CreateProcess(path.c_str(), + const_cast(win32args.c_str()), + nullptr, + nullptr, + 1, + 0, + nullptr, + nullptr, + &si, + &pi); + CloseHandle(pipe_out[1]); + if (ret == 0) { + return false; + } + int fd = _open_osfhandle((intptr_t)pipe_out[0], O_BINARY); + bool ok = hash.hash_fd(fd); + if (!ok) { + log("Error hashing compiler check command output: {}", strerror(errno)); + } + WaitForSingleObject(pi.hProcess, INFINITE); + DWORD exitcode; + GetExitCodeProcess(pi.hProcess, &exitcode); + CloseHandle(pipe_out[0]); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + if (exitcode != 0) { + log("Compiler check command returned {}", exitcode); + return false; + } + return ok; +#else + int pipefd[2]; + if (pipe(pipefd) == -1) { + throw Fatal("pipe failed: {}", strerror(errno)); + } + + pid_t pid = fork(); + if (pid == -1) { + throw Fatal("fork failed: {}", strerror(errno)); + } + + if (pid == 0) { + // Child. + close(pipefd[0]); + close(0); + dup2(pipefd[1], 1); + dup2(pipefd[1], 2); + _exit(execvp(argv[0], const_cast(argv.data()))); + // Never reached. + } else { + // Parent. + close(pipefd[1]); + bool ok = hash.hash_fd(pipefd[0]); + if (!ok) { + log("Error hashing compiler check command output: {}", strerror(errno)); + } + close(pipefd[0]); + + int status; + int result; + while ((result = waitpid(pid, &status, 0)) != pid) { + if (result == -1 && errno == EINTR) { + continue; + } + log("waitpid failed: {}", strerror(errno)); + return false; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + log("Compiler check command returned {}", WEXITSTATUS(status)); + return false; + } + return ok; + } +#endif +} + +bool +hash_multicommand_output(Hash& hash, + const std::string& command, + const std::string& compiler) +{ + for (const std::string& cmd : Util::split_into_strings(command, ";")) { + if (!hash_command_output(hash, cmd, compiler)) { + return false; + } + } + return true; +} diff --git a/src/hashutil.h b/src/hashutil.h deleted file mode 100644 index b6b916e..0000000 --- a/src/hashutil.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2009-2018 Joel Rosdahl -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -#ifndef HASHUTIL_H -#define HASHUTIL_H - -#include "conf.h" -#include "hash.h" -#include - -struct file_hash -{ - uint8_t hash[16]; - uint32_t size; -}; - -unsigned hash_from_string(void *str); -unsigned hash_from_int(int i); -int strings_equal(void *str1, void *str2); -int file_hashes_equal(struct file_hash *fh1, struct file_hash *fh2); - -#define HASH_SOURCE_CODE_OK 0 -#define HASH_SOURCE_CODE_ERROR 1 -#define HASH_SOURCE_CODE_FOUND_DATE 2 -#define HASH_SOURCE_CODE_FOUND_TIME 4 - -int check_for_temporal_macros(const char *str, size_t len); -int hash_source_code_string( - struct conf *conf, struct hash *hash, const char *str, size_t len, - const char *path); -int hash_source_code_file( - struct conf *conf, struct hash *hash, const char *path); -bool hash_command_output(struct hash *hash, const char *command, - const char *compiler); -bool hash_multicommand_output(struct hash *hash, const char *command, - const char *compiler); - -#endif diff --git a/src/hashutil.hpp b/src/hashutil.hpp new file mode 100644 index 0000000..ae7bcb0 --- /dev/null +++ b/src/hashutil.hpp @@ -0,0 +1,73 @@ +// Copyright (C) 2009-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include "third_party/nonstd/string_view.hpp" + +#include + +class Config; +class Context; +class Hash; + +const int HASH_SOURCE_CODE_OK = 0; +const int HASH_SOURCE_CODE_ERROR = (1 << 0); +const int HASH_SOURCE_CODE_FOUND_DATE = (1 << 1); +const int HASH_SOURCE_CODE_FOUND_TIME = (1 << 2); +const int HASH_SOURCE_CODE_FOUND_TIMESTAMP = (1 << 3); + +// Search for the strings "DATE", "TIME" and "TIMESTAMP" with two surrounding +// underscores in `str`. +// +// Returns a bitmask with HASH_SOURCE_CODE_FOUND_DATE, +// HASH_SOURCE_CODE_FOUND_TIME and HASH_SOURCE_CODE_FOUND_TIMESTAMP set +// appropriately. +int check_for_temporal_macros(nonstd::string_view str); + +// Hash a string. Returns a bitmask of HASH_SOURCE_CODE_* results. +int hash_source_code_string(const Context& ctx, + Hash& hash, + nonstd::string_view str, + const std::string& path); + +// Hash a file ignoring comments. Returns a bitmask of HASH_SOURCE_CODE_* +// results. +int hash_source_code_file(const Context& ctx, + Hash& hash, + const std::string& path, + size_t size_hint = 0); + +// Hash a binary file using the inode cache if enabled. +// +// Returns true on success, otherwise false. +bool hash_binary_file(const Context& ctx, Hash& hash, const std::string& path); + +// Hash the output of `command` (not executed via a shell). A "%compiler%" +// string in `command` will be replaced with `compiler`. +bool hash_command_output(Hash& hash, + const std::string& command, + const std::string& compiler); + +// Like `hash_command_output` but for each semicolon-separated command in +// `command`. +bool hash_multicommand_output(Hash& hash, + const std::string& command, + const std::string& compiler); diff --git a/src/language.c b/src/language.c deleted file mode 100644 index f97ca44..0000000 --- a/src/language.c +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright (C) 2010-2019 Joel Rosdahl -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -#include "ccache.h" - -#include "language.h" - -// Supported file extensions and corresponding languages (as in parameter to -// the -x option). -static const struct { - const char *extension; - const char *language; -} extensions[] = { - {".c", "c"}, - {".C", "c++"}, - {".cc", "c++"}, - {".CC", "c++"}, - {".cp", "c++"}, - {".CP", "c++"}, - {".cpp", "c++"}, - {".CPP", "c++"}, - {".cxx", "c++"}, - {".CXX", "c++"}, - {".c++", "c++"}, - {".C++", "c++"}, - {".m", "objective-c"}, - {".M", "objective-c++"}, - {".mm", "objective-c++"}, - {".sx", "assembler-with-cpp"}, - {".S", "assembler-with-cpp"}, - // Preprocessed: - {".i", "cpp-output"}, - {".ii", "c++-cpp-output"}, - {".mi", "objective-c-cpp-output"}, - {".mii", "objective-c++-cpp-output"}, - {".s", "assembler"}, - // Header file (for precompilation): - {".h", "c-header"}, - {".H", "c++-header"}, - {".h++", "c++-header"}, - {".H++", "c++-header"}, - {".hh", "c++-header"}, - {".HH", "c++-header"}, - {".hp", "c++-header"}, - {".HP", "c++-header"}, - {".hpp", "c++-header"}, - {".HPP", "c++-header"}, - {".hxx", "c++-header"}, - {".HXX", "c++-header"}, - {".tcc", "c++-header"}, - {".TCC", "c++-header"}, - {".cu", "cu"}, - {NULL, NULL} -}; - -// Supported languages and corresponding preprocessed languages. -static const struct { - const char *language; - const char *p_language; -} languages[] = { - {"c", "cpp-output"}, - {"cpp-output", "cpp-output"}, - {"c-header", "cpp-output"}, - {"c++", "c++-cpp-output"}, - {"c++-cpp-output", "c++-cpp-output"}, - {"c++-header", "c++-cpp-output"}, - {"cu", "cpp-output"}, - {"objective-c", "objective-c-cpp-output"}, - {"objective-c-header", "objective-c-cpp-output"}, - {"objc-cpp-output", "objective-c-cpp-output"}, - {"objective-c-cpp-output", "objective-c-cpp-output"}, - {"objective-c++", "objective-c++-cpp-output"}, - {"objc++-cpp-output", "objective-c++-cpp-output"}, - {"objective-c++-header", "objective-c++-cpp-output"}, - {"objective-c++-cpp-output", "objective-c++-cpp-output"}, - {"assembler-with-cpp", "assembler"}, - {"assembler", "assembler"}, - {NULL, NULL} -}; - -// Guess the language of a file based on its extension. Returns NULL if the -// extension is unknown. -const char * -language_for_file(const char *fname) -{ - const char *p = get_extension(fname); - for (int i = 0; extensions[i].extension; i++) { - if (str_eq(p, extensions[i].extension)) { - return extensions[i].language; - } - } - return NULL; -} - -// Return the preprocessed language for a given language, or NULL if unknown. -const char * -p_language_for_language(const char *language) -{ - if (!language) { - return NULL; - } - for (int i = 0; languages[i].language; ++i) { - if (str_eq(language, languages[i].language)) { - return languages[i].p_language; - } - } - return NULL; -} - -// Return the default file extension (including dot) for a language, or NULL if -// unknown. -const char * -extension_for_language(const char *language) -{ - if (!language) { - return NULL; - } - for (int i = 0; extensions[i].extension; i++) { - if (str_eq(language, extensions[i].language)) { - return extensions[i].extension; - } - } - return NULL; -} - -bool -language_is_supported(const char *language) -{ - return p_language_for_language(language) != NULL; -} - -bool -language_is_preprocessed(const char *language) -{ - const char *p_language = p_language_for_language(language); - assert(p_language); - return str_eq(language, p_language); -} diff --git a/src/language.cpp b/src/language.cpp new file mode 100644 index 0000000..70325ea --- /dev/null +++ b/src/language.cpp @@ -0,0 +1,148 @@ +// Copyright (C) 2010-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "language.hpp" + +#include "Util.hpp" + +namespace { + +// Supported file extensions and corresponding languages (as in parameter to +// the -x option). +const struct +{ + const char* extension; + const char* language; +} k_ext_lang_table[] = { + {".c", "c"}, + {".C", "c++"}, + {".cc", "c++"}, + {".CC", "c++"}, + {".cp", "c++"}, + {".CP", "c++"}, + {".cpp", "c++"}, + {".CPP", "c++"}, + {".cxx", "c++"}, + {".CXX", "c++"}, + {".c++", "c++"}, + {".C++", "c++"}, + {".m", "objective-c"}, + {".M", "objective-c++"}, + {".mm", "objective-c++"}, + {".sx", "assembler-with-cpp"}, + {".S", "assembler-with-cpp"}, + // Preprocessed: + {".i", "cpp-output"}, + {".ii", "c++-cpp-output"}, + {".mi", "objective-c-cpp-output"}, + {".mii", "objective-c++-cpp-output"}, + {".s", "assembler"}, + // Header file (for precompilation): + {".h", "c-header"}, + {".H", "c++-header"}, + {".h++", "c++-header"}, + {".H++", "c++-header"}, + {".hh", "c++-header"}, + {".HH", "c++-header"}, + {".hp", "c++-header"}, + {".HP", "c++-header"}, + {".hpp", "c++-header"}, + {".HPP", "c++-header"}, + {".hxx", "c++-header"}, + {".HXX", "c++-header"}, + {".tcc", "c++-header"}, + {".TCC", "c++-header"}, + {".cu", "cu"}, + {".hip", "hip"}, + {nullptr, nullptr}, +}; + +// Supported languages and corresponding preprocessed languages. +const struct +{ + const char* language; + const char* p_language; +} k_lang_p_lang_table[] = { + {"c", "cpp-output"}, + {"cpp-output", "cpp-output"}, + {"c-header", "cpp-output"}, + {"c++", "c++-cpp-output"}, + {"c++-cpp-output", "c++-cpp-output"}, + {"c++-header", "c++-cpp-output"}, + {"cu", "cpp-output"}, + {"hip", "cpp-output"}, + {"objective-c", "objective-c-cpp-output"}, + {"objective-c-header", "objective-c-cpp-output"}, + {"objc-cpp-output", "objective-c-cpp-output"}, + {"objective-c-cpp-output", "objective-c-cpp-output"}, + {"objective-c++", "objective-c++-cpp-output"}, + {"objc++-cpp-output", "objective-c++-cpp-output"}, + {"objective-c++-header", "objective-c++-cpp-output"}, + {"objective-c++-cpp-output", "objective-c++-cpp-output"}, + {"assembler-with-cpp", "assembler"}, + {"assembler", "assembler"}, + {nullptr, nullptr}, +}; + +} // namespace + +std::string +language_for_file(const std::string& fname) +{ + auto ext = Util::get_extension(fname); + for (size_t i = 0; k_ext_lang_table[i].extension; ++i) { + if (k_ext_lang_table[i].extension == ext) { + return k_ext_lang_table[i].language; + } + } + return {}; +} + +std::string +p_language_for_language(const std::string& language) +{ + for (size_t i = 0; k_lang_p_lang_table[i].language; ++i) { + if (language == k_lang_p_lang_table[i].language) { + return k_lang_p_lang_table[i].p_language; + } + } + return {}; +} + +std::string +extension_for_language(const std::string& language) +{ + for (size_t i = 0; k_ext_lang_table[i].extension; i++) { + if (language == k_ext_lang_table[i].language) { + return k_ext_lang_table[i].extension; + } + } + return {}; +} + +bool +language_is_supported(const std::string& language) +{ + return !p_language_for_language(language).empty(); +} + +bool +language_is_preprocessed(const std::string& language) +{ + return language == p_language_for_language(language); +} diff --git a/src/language.h b/src/language.h deleted file mode 100644 index ebfb8bb..0000000 --- a/src/language.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef CCACHE_LANGUAGE_H -#define CCACHE_LANGUAGE_H - -#include - -const char *language_for_file(const char *fname); -const char *p_language_for_language(const char *language); -const char *extension_for_language(const char *language); -bool language_is_supported(const char *language); -bool language_is_preprocessed(const char *language); - -#endif // CCACHE_LANGUAGE_H diff --git a/src/language.hpp b/src/language.hpp new file mode 100644 index 0000000..69f7f26 --- /dev/null +++ b/src/language.hpp @@ -0,0 +1,41 @@ +// Copyright (C) 2010-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +#include + +// Guess the language of `fname` based on its extension. Returns the empty +// string if the extension is unknown. +std::string language_for_file(const std::string& fname); + +// Return the preprocessed language for `language`, or the empty string if +// unknown. +std::string p_language_for_language(const std::string& language); + +// Return the default file extension (including dot) for `language`, or the +// empty string if unknown. +std::string extension_for_language(const std::string& language); + +// Return whether `language` is a supported language. +bool language_is_supported(const std::string& language); + +// Return whether `language` is supported preprocessed language. +bool language_is_preprocessed(const std::string& language); diff --git a/src/lockfile.c b/src/lockfile.c deleted file mode 100644 index bbf4c95..0000000 --- a/src/lockfile.c +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright (C) 2010-2020 Joel Rosdahl -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -#include "ccache.h" - -#ifndef _WIN32 - -// This function acquires a lockfile for the given path. Returns true if the -// lock was acquired, otherwise false. If the lock has been considered stale -// for the number of microseconds specified by staleness_limit, the function -// will (if possible) break the lock and then try to acquire it again. The -// staleness limit should be reasonably larger than the longest time the lock -// can be expected to be held, and the updates of the locked path should -// probably be made with an atomic rename(2) to avoid corruption in the rare -// case that the lock is broken by another process. -bool -lockfile_acquire(const char *path, unsigned staleness_limit) -{ - char *lockfile = format("%s.lock", path); - char *my_content = NULL; - char *content = NULL; - char *initial_content = NULL; - const char *hostname = get_hostname(); - bool acquired = false; - unsigned to_sleep = 1000; // Microseconds. - unsigned max_to_sleep = 10000; // Microseconds. - unsigned slept = 0; // Microseconds. - - while (true) { - free(my_content); - my_content = format("%s:%d:%d", hostname, (int)getpid(), (int)time(NULL)); - - if (symlink(my_content, lockfile) == 0) { - // We got the lock. - acquired = true; - goto out; - } - int saved_errno = errno; - cc_log("lockfile_acquire: symlink %s: %s", lockfile, strerror(saved_errno)); - if (saved_errno == ENOENT) { - // Directory doesn't exist? - if (create_parent_dirs(lockfile) == 0) { - // OK. Retry. - continue; - } - } - if (saved_errno == EPERM) { - // The file system does not support symbolic links. We have no choice but - // to grant the lock anyway. - acquired = true; - goto out; - } - if (saved_errno != EEXIST) { - // Directory doesn't exist or isn't writable? - goto out; - } - free(content); - content = x_readlink(lockfile); - if (!content) { - if (errno == ENOENT) { - // The symlink was removed after the symlink() call above, so retry - // acquiring it. - continue; - } else { - cc_log("lockfile_acquire: readlink %s: %s", lockfile, strerror(errno)); - goto out; - } - } - - if (str_eq(content, my_content)) { - // Lost NFS reply? - cc_log("lockfile_acquire: symlink %s failed but we got the lock anyway", - lockfile); - acquired = true; - goto out; - } - // A possible improvement here would be to check if the process holding the - // lock is still alive and break the lock early if it isn't. - cc_log("lockfile_acquire: lock info for %s: %s", lockfile, content); - if (!initial_content) { - initial_content = x_strdup(content); - } - if (slept > staleness_limit) { - if (str_eq(content, initial_content)) { - // The lock seems to be stale -- break it. - cc_log("lockfile_acquire: breaking %s", lockfile); - // Try to acquire path.lock.lock: - if (lockfile_acquire(lockfile, staleness_limit)) { - lockfile_release(path); // Remove path.lock - lockfile_release(lockfile); // Remove path.lock.lock - to_sleep = 1000; - slept = 0; - continue; - } - } - cc_log("lockfile_acquire: gave up acquiring %s", lockfile); - goto out; - } - cc_log("lockfile_acquire: failed to acquire %s; sleeping %u microseconds", - lockfile, to_sleep); - usleep(to_sleep); - slept += to_sleep; - to_sleep = MIN(max_to_sleep, 2 * to_sleep); - } - -out: - if (acquired) { - cc_log("Acquired lock %s", lockfile); - } else { - cc_log("Failed to acquire lock %s", lockfile); - } - free(lockfile); - free(my_content); - free(initial_content); - free(content); - return acquired; -} - -// Release the lockfile for the given path. Assumes that we are the legitimate -// owner. -void -lockfile_release(const char *path) -{ - char *lockfile = format("%s.lock", path); - cc_log("Releasing lock %s", lockfile); - tmp_unlink(lockfile); - free(lockfile); -} - -#else - -HANDLE lockfile_handle = NULL; - -// This function acquires a lockfile for the given path. Returns true if the -// lock was acquired, otherwise false. If the lock has been acquired within the -// limit (in microseconds) the function will give up and return false. The time -// limit should be reasonably larger than the longest time the lock can be -// expected to be held. -bool -lockfile_acquire(const char *path, unsigned time_limit) -{ - char *lockfile = format("%s.lock", path); - unsigned to_sleep = 1000; // Microseconds. - unsigned max_to_sleep = 10000; // Microseconds. - unsigned slept = 0; // Microseconds. - bool acquired = false; - - while (true) { - DWORD flags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE; - lockfile_handle = CreateFile( - lockfile, - GENERIC_WRITE, // desired access - 0, // shared mode (0 = not shared) - NULL, // security attributes - CREATE_ALWAYS, // creation disposition, - flags, // flags and attributes - NULL // template file - ); - if (lockfile_handle != INVALID_HANDLE_VALUE) { - acquired = true; - break; - } - - DWORD error = GetLastError(); - cc_log("lockfile_acquire: CreateFile %s: error code %lu", lockfile, error); - if (error == ERROR_PATH_NOT_FOUND) { - // Directory doesn't exist? - if (create_parent_dirs(lockfile) == 0) { - // OK. Retry. - continue; - } - } - - // ERROR_SHARING_VIOLATION: lock already held. - // ERROR_ACCESS_DENIED: maybe pending delete. - if (error != ERROR_SHARING_VIOLATION && error != ERROR_ACCESS_DENIED) { - // Fatal error, give up. - break; - } - - if (slept > time_limit) { - cc_log("lockfile_acquire: gave up acquiring %s", lockfile); - break; - } - - cc_log("lockfile_acquire: failed to acquire %s; sleeping %u microseconds", - lockfile, to_sleep); - usleep(to_sleep); - slept += to_sleep; - to_sleep = MIN(max_to_sleep, 2 * to_sleep); - } - - if (acquired) { - cc_log("Acquired lock %s", lockfile); - } else { - cc_log("Failed to acquire lock %s", lockfile); - } - free(lockfile); - return acquired; -} - -// Release the lockfile for the given path. Assumes that we are the legitimate -// owner. -void -lockfile_release(const char *path) -{ - assert(lockfile_handle != INVALID_HANDLE_VALUE); - cc_log("Releasing lock %s.lock", path); - CloseHandle(lockfile_handle); - lockfile_handle = NULL; -} - -#endif - -#ifdef TEST_LOCKFILE - -int -main(int argc, char **argv) -{ - extern struct conf *conf; - conf = conf_create(); - if (argc == 3) { - unsigned staleness_limit = atoi(argv[1]); - printf("Acquiring\n"); - bool acquired = lockfile_acquire(argv[2], staleness_limit); - if (acquired) { - printf("Sleeping 2 seconds\n"); - sleep(2); - lockfile_release(argv[2]); - printf("Released\n"); - } else { - printf("Failed to acquire\n"); - } - } else { - fprintf(stderr, - "Usage: testlockfile \n"); - } - return 1; -} -#endif diff --git a/src/macroskip.h b/src/macroskip.h deleted file mode 100644 index 75243fe..0000000 --- a/src/macroskip.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef CCACHE_MACROSKIP_H -#define CCACHE_MACROSKIP_H - -#include - -// A Boyer-Moore-Horspool skip table used for searching for the strings -// "__TIME__" and "__DATE__". -// -// macro_skip[c] = 8 for all c not in "__TIME__" and "__DATE__". -// -// The other characters map as follows: -// -// _ -> 1 -// A -> 4 -// D -> 5 -// E -> 2 -// I -> 4 -// M -> 3 -// T -> 3 -// -// -// This was generated with the following Python script: -// -// m = {'_': 1, -// 'A': 4, -// 'D': 5, -// 'E': 2, -// 'I': 4, -// 'M': 3, -// 'T': 3} -// -// for i in range(0, 256): -// if chr(i) in m: -// num = m[chr(i)] -// else: -// num = 8 -// print ("%d, " % num), -// -// if i % 16 == 15: -// print "" - -static const uint32_t macro_skip[] = { - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 4, 8, 8, 5, 2, 8, 8, 8, 4, 8, 8, 8, 3, 8, 8, - 8, 8, 8, 8, 3, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, -}; - -#endif diff --git a/src/macroskip.hpp b/src/macroskip.hpp new file mode 100644 index 0000000..f159323 --- /dev/null +++ b/src/macroskip.hpp @@ -0,0 +1,72 @@ +// Copyright (C) 2010-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#include "system.hpp" + +// A Boyer-Moore-Horspool skip table used for searching for the strings +// "__TIME__", "__DATE__" and "__TIMESTAMP__". +// +// macro_skip[c] = 8 for all c not in "__TIME__", "__DATE__" and "__TIMEST". +// +// The other characters map as follows: +// +// _ -> 1 +// A -> 4 +// D -> 5 +// E -> 2 +// I -> 4 +// M -> 3 +// T -> 3 +// S -> 1 +// +// +// This was generated with the following Python script: +// +// m = {'_': 1, +// 'A': 4, +// 'D': 5, +// 'E': 2, +// 'I': 4, +// 'M': 3, +// 'S': 1, +// 'T': 3} +// +// for i in range(0, 256): +// if chr(i) in m: +// num = m[chr(i)] +// else: +// num = 8 +// print ("%d, " % num), +// +// if i % 16 == 15: +// print "" + +const uint32_t macro_skip[] = { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, 8, 5, 2, 8, 8, 8, 4, 8, 8, 8, 3, + 8, 8, 8, 8, 8, 1, 3, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, +}; diff --git a/src/main.c b/src/main.cpp similarity index 74% rename from src/main.c rename to src/main.cpp index dda0b2e..9b4e5fa 100644 --- a/src/main.c +++ b/src/main.cpp @@ -1,6 +1,6 @@ -// ccache -- a fast C/C++ compiler cache +// Copyright (C) 2010-2020 Joel Rosdahl and other contributors // -// Copyright (C) 2010-2016 Joel Rosdahl +// See doc/AUTHORS.adoc for a complete list of contributors. // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the Free @@ -16,11 +16,10 @@ // this program; if not, write to the Free Software Foundation, Inc., 51 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -int -ccache_main(int argc, char *argv[]); +int ccache_main(int argc, const char* const* argv); int -main(int argc, char *argv[]) +main(int argc, char* const* argv) { - return ccache_main(argc, argv); + return ccache_main(argc, argv); } diff --git a/src/manifest.c b/src/manifest.c deleted file mode 100644 index fcc5287..0000000 --- a/src/manifest.c +++ /dev/null @@ -1,821 +0,0 @@ -// Copyright (C) 2009-2019 Joel Rosdahl -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -#include "ccache.h" -#include "hashtable_itr.h" -#include "hashutil.h" -#include "manifest.h" -#include "murmurhashneutral2.h" - -#include - -// Sketchy specification of the manifest disk format: -// -// magic number (4 bytes) -// file format version (1 byte unsigned int) -// size of the hash fields (in bytes) (1 byte unsigned int) -// reserved for future use (2 bytes) -// ---------------------------------------------------------------------------- -// number of include file paths (4 bytes unsigned int) -// path to include file (NUL-terminated string, -// ... at most 1024 bytes) -// -// ---------------------------------------------------------------------------- -// number of include file hash entries (4 bytes unsigned int) -// index of include file path (4 bytes unsigned int) -// hash of include file ( bytes) -// size of include file (4 bytes unsigned int) -// mtime of include file (8 bytes signed int) -// ctime of include file (8 bytes signed int) -// ... -// -// -// -// -// -// ---------------------------------------------------------------------------- -// number of object name entries (4 bytes unsigned int) -// number of include file hash indexes (4 bytes unsigned int) -// include file hash index (4 bytes unsigned int) -// ... -// -// hash part of object name ( bytes) -// size part of object name (4 bytes unsigned int) -// ... -// number of include file hash indexes -// include file hash index -// ... -// -// -// - -static const uint32_t MAGIC = 0x63436d46U; -static const uint32_t MAX_MANIFEST_ENTRIES = 100; -static const uint32_t MAX_MANIFEST_FILE_INFO_ENTRIES = 10000; - -#define ccache_static_assert(e) \ - do { enum { ccache_static_assert__ = 1/(e) }; } while (false) - -struct file_info { - // Index to n_files. - uint32_t index; - // Hash of referenced file. - uint8_t hash[16]; - // Size of referenced file. - uint32_t size; - // mtime of referenced file. - int64_t mtime; - // ctime of referenced file. - int64_t ctime; -}; - -struct object { - // Number of entries in file_info_indexes. - uint32_t n_file_info_indexes; - // Indexes to file_infos. - uint32_t *file_info_indexes; - // Hash of the object itself. - struct file_hash hash; -}; - -struct manifest { - // Version of decoded file. - uint8_t version; - - // Reserved for future use. - uint16_t reserved; - - // Size of hash fields (in bytes). - uint8_t hash_size; - - // Referenced include files. - uint32_t n_files; - char **files; - - // Information about referenced include files. - uint32_t n_file_infos; - struct file_info *file_infos; - - // Object names plus references to include file hashes. - uint32_t n_objects; - struct object *objects; -}; - -struct file_stats { - uint32_t size; - int64_t mtime; - int64_t ctime; -}; - -static unsigned int -hash_from_file_info(void *key) -{ - ccache_static_assert(sizeof(struct file_info) == 40); // No padding. - return murmurhashneutral2(key, sizeof(struct file_info), 0); -} - -static int -file_infos_equal(void *key1, void *key2) -{ - struct file_info *fi1 = (struct file_info *)key1; - struct file_info *fi2 = (struct file_info *)key2; - return fi1->index == fi2->index - && memcmp(fi1->hash, fi2->hash, 16) == 0 - && fi1->size == fi2->size - && fi1->mtime == fi2->mtime - && fi1->ctime == fi2->ctime; -} - -static void -free_manifest(struct manifest *mf) -{ - for (uint32_t i = 0; i < mf->n_files; i++) { - free(mf->files[i]); - } - free(mf->files); - free(mf->file_infos); - for (uint32_t i = 0; i < mf->n_objects; i++) { - free(mf->objects[i].file_info_indexes); - } - free(mf->objects); - free(mf); -} - -#define READ_BYTE(var) \ - do { \ - int ch_ = gzgetc(f); \ - if (ch_ == EOF) { \ - goto error; \ - } \ - (var) = ch_ & 0xFF; \ - } while (false) - -#define READ_INT(size, var) \ - do { \ - uint64_t u_ = 0; \ - for (size_t i_ = 0; i_ < (size); i_++) { \ - int ch_ = gzgetc(f); \ - if (ch_ == EOF) { \ - goto error; \ - } \ - u_ <<= 8; \ - u_ |= ch_ & 0xFF; \ - } \ - (var) = u_; \ - } while (false) - -#define READ_STR(var) \ - do { \ - char buf_[1024]; \ - size_t i_; \ - for (i_ = 0; i_ < sizeof(buf_); i_++) { \ - int ch_ = gzgetc(f); \ - if (ch_ == EOF) { \ - goto error; \ - } \ - buf_[i_] = ch_; \ - if (ch_ == '\0') { \ - break; \ - } \ - } \ - if (i_ == sizeof(buf_)) { \ - goto error; \ - } \ - (var) = x_strdup(buf_); \ - } while (false) - -#define READ_BYTES(n, var) \ - do { \ - for (size_t i_ = 0; i_ < (n); i_++) { \ - int ch_ = gzgetc(f); \ - if (ch_ == EOF) { \ - goto error; \ - } \ - (var)[i_] = ch_; \ - } \ - } while (false) - -static struct manifest * -create_empty_manifest(void) -{ - struct manifest *mf = x_malloc(sizeof(*mf)); - mf->hash_size = 16; - mf->n_files = 0; - mf->files = NULL; - mf->n_file_infos = 0; - mf->file_infos = NULL; - mf->n_objects = 0; - mf->objects = NULL; - - return mf; -} - -static struct manifest * -read_manifest(gzFile f, char **errmsg) -{ - *errmsg = NULL; - struct manifest *mf = create_empty_manifest(); - - uint32_t magic; - READ_INT(4, magic); - if (magic != MAGIC) { - *errmsg = format("Manifest file has bad magic number %u", magic); - goto error; - } - - READ_BYTE(mf->version); - if (mf->version != MANIFEST_VERSION) { - *errmsg = format( - "Unknown manifest version (actual %u, expected %u)", - mf->version, - MANIFEST_VERSION); - goto error; - } - - READ_BYTE(mf->hash_size); - if (mf->hash_size != 16) { - // Temporary measure until we support different hash algorithms. - *errmsg = - format("Manifest file has unsupported hash size %u", mf->hash_size); - goto error; - } - - READ_INT(2, mf->reserved); - - READ_INT(4, mf->n_files); - mf->files = x_calloc(mf->n_files, sizeof(*mf->files)); - for (uint32_t i = 0; i < mf->n_files; i++) { - READ_STR(mf->files[i]); - } - - READ_INT(4, mf->n_file_infos); - mf->file_infos = x_calloc(mf->n_file_infos, sizeof(*mf->file_infos)); - for (uint32_t i = 0; i < mf->n_file_infos; i++) { - READ_INT(4, mf->file_infos[i].index); - READ_BYTES(mf->hash_size, mf->file_infos[i].hash); - READ_INT(4, mf->file_infos[i].size); - READ_INT(8, mf->file_infos[i].mtime); - READ_INT(8, mf->file_infos[i].ctime); - } - - READ_INT(4, mf->n_objects); - mf->objects = x_calloc(mf->n_objects, sizeof(*mf->objects)); - for (uint32_t i = 0; i < mf->n_objects; i++) { - READ_INT(4, mf->objects[i].n_file_info_indexes); - mf->objects[i].file_info_indexes = - x_calloc(mf->objects[i].n_file_info_indexes, - sizeof(*mf->objects[i].file_info_indexes)); - for (uint32_t j = 0; j < mf->objects[i].n_file_info_indexes; j++) { - READ_INT(4, mf->objects[i].file_info_indexes[j]); - } - READ_BYTES(mf->hash_size, mf->objects[i].hash.hash); - READ_INT(4, mf->objects[i].hash.size); - } - - return mf; - -error: - if (!*errmsg) { - *errmsg = x_strdup("Corrupt manifest file"); - } - free_manifest(mf); - return NULL; -} - -#define WRITE_INT(size, var) \ - do { \ - uint64_t u_ = (var); \ - uint8_t ch_; \ - size_t i_; \ - for (i_ = 0; i_ < (size); i_++) { \ - ch_ = (u_ >> (8 * ((size) - i_ - 1))); \ - if (gzputc(f, ch_) == EOF) { \ - goto error; \ - } \ - } \ - } while (false) - -#define WRITE_STR(var) \ - do { \ - if (gzputs(f, var) == EOF || gzputc(f, '\0') == EOF) { \ - goto error; \ - } \ - } while (false) - -#define WRITE_BYTES(n, var) \ - do { \ - size_t i_; \ - for (i_ = 0; i_ < (n); i_++) { \ - if (gzputc(f, (var)[i_]) == EOF) { \ - goto error; \ - } \ - } \ - } while (false) - -static int -write_manifest(gzFile f, const struct manifest *mf) -{ - WRITE_INT(4, MAGIC); - WRITE_INT(1, MANIFEST_VERSION); - WRITE_INT(1, 16); - WRITE_INT(2, 0); - - WRITE_INT(4, mf->n_files); - for (uint32_t i = 0; i < mf->n_files; i++) { - WRITE_STR(mf->files[i]); - } - - WRITE_INT(4, mf->n_file_infos); - for (uint32_t i = 0; i < mf->n_file_infos; i++) { - WRITE_INT(4, mf->file_infos[i].index); - WRITE_BYTES(mf->hash_size, mf->file_infos[i].hash); - WRITE_INT(4, mf->file_infos[i].size); - WRITE_INT(8, mf->file_infos[i].mtime); - WRITE_INT(8, mf->file_infos[i].ctime); - } - - WRITE_INT(4, mf->n_objects); - for (uint32_t i = 0; i < mf->n_objects; i++) { - WRITE_INT(4, mf->objects[i].n_file_info_indexes); - for (uint32_t j = 0; j < mf->objects[i].n_file_info_indexes; j++) { - WRITE_INT(4, mf->objects[i].file_info_indexes[j]); - } - WRITE_BYTES(mf->hash_size, mf->objects[i].hash.hash); - WRITE_INT(4, mf->objects[i].hash.size); - } - - return 1; - -error: - cc_log("Error writing to manifest file"); - return 0; -} - -static int -verify_object(struct conf *conf, struct manifest *mf, struct object *obj, - struct hashtable *stated_files, struct hashtable *hashed_files) -{ - for (uint32_t i = 0; i < obj->n_file_info_indexes; i++) { - struct file_info *fi = &mf->file_infos[obj->file_info_indexes[i]]; - char *path = mf->files[fi->index]; - struct file_stats *st = hashtable_search(stated_files, path); - if (!st) { - struct stat file_stat; - if (x_stat(path, &file_stat) != 0) { - return 0; - } - st = x_malloc(sizeof(*st)); - st->size = file_stat.st_size; - st->mtime = file_stat.st_mtime; - st->ctime = file_stat.st_ctime; - hashtable_insert(stated_files, x_strdup(path), st); - } - - if (fi->size != st->size) { - return 0; - } - - // Clang stores the mtime of the included files in the precompiled header, - // and will error out if that header is later used without rebuilding. - if ((guessed_compiler == GUESSED_CLANG - || guessed_compiler == GUESSED_UNKNOWN) - && output_is_precompiled_header - && fi->mtime != st->mtime) { - cc_log("Precompiled header includes %s, which has a new mtime", path); - return 0; - } - - if (conf->sloppiness & SLOPPY_FILE_STAT_MATCHES) { - if (!(conf->sloppiness & SLOPPY_FILE_STAT_MATCHES_CTIME)) { - if (fi->mtime == st->mtime && fi->ctime == st->ctime) { - cc_log("mtime/ctime hit for %s", path); - continue; - } else { - cc_log("mtime/ctime miss for %s", path); - } - } else { - if (fi->mtime == st->mtime) { - cc_log("mtime hit for %s", path); - continue; - } else { - cc_log("mtime miss for %s", path); - } - } - } - - struct file_hash *actual = hashtable_search(hashed_files, path); - if (!actual) { - struct hash *hash = hash_init(); - int result = hash_source_code_file(conf, hash, path); - if (result & HASH_SOURCE_CODE_ERROR) { - cc_log("Failed hashing %s", path); - hash_free(hash); - return 0; - } - if (result & HASH_SOURCE_CODE_FOUND_TIME) { - hash_free(hash); - return 0; - } - actual = x_malloc(sizeof(*actual)); - hash_result_as_bytes(hash, actual->hash); - actual->size = hash_input_size(hash); - hashtable_insert(hashed_files, x_strdup(path), actual); - hash_free(hash); - } - if (memcmp(fi->hash, actual->hash, mf->hash_size) != 0 - || fi->size != actual->size) { - return 0; - } - } - - return 1; -} - -static struct hashtable * -create_string_index_map(char **strings, uint32_t len) -{ - struct hashtable *h = - create_hashtable(1000, hash_from_string, strings_equal); - for (uint32_t i = 0; i < len; i++) { - uint32_t *index = x_malloc(sizeof(*index)); - *index = i; - hashtable_insert(h, x_strdup(strings[i]), index); - } - return h; -} - -static struct hashtable * -create_file_info_index_map(struct file_info *infos, uint32_t len) -{ - struct hashtable *h = - create_hashtable(1000, hash_from_file_info, file_infos_equal); - for (uint32_t i = 0; i < len; i++) { - struct file_info *fi = x_malloc(sizeof(*fi)); - *fi = infos[i]; - uint32_t *index = x_malloc(sizeof(*index)); - *index = i; - hashtable_insert(h, fi, index); - } - return h; -} - -static uint32_t -get_include_file_index(struct manifest *mf, char *path, - struct hashtable *mf_files) -{ - uint32_t *index = hashtable_search(mf_files, path); - if (index) { - return *index; - } - - uint32_t n = mf->n_files; - mf->files = x_realloc(mf->files, (n + 1) * sizeof(*mf->files)); - mf->n_files++; - mf->files[n] = x_strdup(path); - return n; -} - -static uint32_t -get_file_hash_index(struct manifest *mf, - char *path, - struct file_hash *file_hash, - struct hashtable *mf_files, - struct hashtable *mf_file_infos, - bool save_timestamp) -{ - struct file_info fi; - fi.index = get_include_file_index(mf, path, mf_files); - memcpy(fi.hash, file_hash->hash, sizeof(fi.hash)); - fi.size = file_hash->size; - - // file_stat.st_{m,c}time has a resolution of 1 second, so we can cache the - // file's mtime and ctime only if they're at least one second older than - // time_of_compilation. - // - // st->ctime may be 0, so we have to check time_of_compilation against - // MAX(mtime, ctime). - // - // ccache only reads mtime/ctime if file_stat_match sloppiness is enabled, so - // mtimes/ctimes are stored as a dummy value (-1) if not enabled. This reduces - // the number of file_info entries for the common case. - - struct stat file_stat; - if (save_timestamp && stat(path, &file_stat) != -1 - && time_of_compilation > MAX(file_stat.st_mtime, file_stat.st_ctime)) { - fi.mtime = file_stat.st_mtime; - fi.ctime = file_stat.st_ctime; - } else { - fi.mtime = -1; - fi.ctime = -1; - } - - uint32_t *fi_index = hashtable_search(mf_file_infos, &fi); - if (fi_index) { - return *fi_index; - } - - uint32_t n = mf->n_file_infos; - mf->file_infos = x_realloc(mf->file_infos, (n + 1) * sizeof(*mf->file_infos)); - mf->n_file_infos++; - mf->file_infos[n] = fi; - return n; -} - -static void -add_file_info_indexes(uint32_t *indexes, uint32_t size, - struct manifest *mf, struct hashtable *included_files, - bool save_timestamp) -{ - if (size == 0) { - return; - } - - // path --> index - struct hashtable *mf_files = - create_string_index_map(mf->files, mf->n_files); - // struct file_info --> index - struct hashtable *mf_file_infos = - create_file_info_index_map(mf->file_infos, mf->n_file_infos); - struct hashtable_itr *iter = hashtable_iterator(included_files); - uint32_t i = 0; - do { - char *path = hashtable_iterator_key(iter); - struct file_hash *file_hash = hashtable_iterator_value(iter); - indexes[i] = get_file_hash_index(mf, path, file_hash, mf_files, - mf_file_infos, save_timestamp); - i++; - } while (hashtable_iterator_advance(iter)); - assert(i == size); - - hashtable_destroy(mf_file_infos, 1); - hashtable_destroy(mf_files, 1); -} - -static void -add_object_entry(struct manifest *mf, - struct file_hash *object_hash, - struct hashtable *included_files, - bool save_timestamp) -{ - uint32_t n_objs = mf->n_objects; - mf->objects = x_realloc(mf->objects, (n_objs + 1) * sizeof(*mf->objects)); - mf->n_objects++; - struct object *obj = &mf->objects[n_objs]; - - uint32_t n_fii = hashtable_count(included_files); - obj->n_file_info_indexes = n_fii; - obj->file_info_indexes = x_malloc(n_fii * sizeof(*obj->file_info_indexes)); - add_file_info_indexes(obj->file_info_indexes, n_fii, mf, included_files, - save_timestamp); - memcpy(obj->hash.hash, object_hash->hash, mf->hash_size); - obj->hash.size = object_hash->size; -} - -// Try to get the object hash from a manifest file. Caller frees. Returns NULL -// on failure. -struct file_hash * -manifest_get(struct conf *conf, const char *manifest_path) -{ - gzFile f = NULL; - struct manifest *mf = NULL; - struct hashtable *hashed_files = NULL; // path --> struct file_hash - struct hashtable *stated_files = NULL; // path --> struct file_stats - struct file_hash *fh = NULL; - - int fd = open(manifest_path, O_RDONLY | O_BINARY); - if (fd == -1) { - // Cache miss. - cc_log("No such manifest file"); - goto out; - } - f = gzdopen(fd, "rb"); - if (!f) { - close(fd); - cc_log("Failed to gzdopen manifest file"); - goto out; - } - - char *errmsg; - mf = read_manifest(f, &errmsg); - if (!mf) { - cc_log("%s", errmsg); - goto out; - } - - hashed_files = create_hashtable(1000, hash_from_string, strings_equal); - stated_files = create_hashtable(1000, hash_from_string, strings_equal); - - // Check newest object first since it's a bit more likely to match. - for (uint32_t i = mf->n_objects; i > 0; i--) { - if (verify_object(conf, mf, &mf->objects[i - 1], - stated_files, hashed_files)) { - fh = x_malloc(sizeof(*fh)); - *fh = mf->objects[i - 1].hash; - goto out; - } - } - -out: - if (hashed_files) { - hashtable_destroy(hashed_files, 1); - } - if (stated_files) { - hashtable_destroy(stated_files, 1); - } - if (f) { - gzclose(f); - } - if (mf) { - free_manifest(mf); - } - return fh; -} - -// Put the object name into a manifest file given a set of included files. -// Returns true on success, otherwise false. -bool -manifest_put(const char *manifest_path, struct file_hash *object_hash, - struct hashtable *included_files, bool save_timestamp) -{ - int ret = 0; - gzFile f2 = NULL; - struct manifest *mf = NULL; - char *tmp_file = NULL; - - // We don't bother to acquire a lock when writing the manifest to disk. A - // race between two processes will only result in one lost entry, which is - // not a big deal, and it's also very unlikely. - - int fd1 = open(manifest_path, O_RDONLY | O_BINARY); - if (fd1 == -1) { - // New file. - mf = create_empty_manifest(); - } else { - gzFile f1 = gzdopen(fd1, "rb"); - if (!f1) { - cc_log("Failed to gzdopen manifest file"); - close(fd1); - goto out; - } - char *errmsg; - mf = read_manifest(f1, &errmsg); - gzclose(f1); - if (!mf) { - cc_log("%s", errmsg); - free(errmsg); - cc_log("Failed to read manifest file; deleting it"); - x_unlink(manifest_path); - mf = create_empty_manifest(); - } - } - - if (mf->n_objects > MAX_MANIFEST_ENTRIES) { - // Normally, there shouldn't be many object entries in the manifest since - // new entries are added only if an include file has changed but not the - // source file, and you typically change source files more often than - // header files. However, it's certainly possible to imagine cases where - // the manifest will grow large (for instance, a generated header file that - // changes for every build), and this must be taken care of since - // processing an ever growing manifest eventually will take too much time. - // A good way of solving this would be to maintain the object entries in - // LRU order and discarding the old ones. An easy way is to throw away all - // entries when there are too many. Let's do that for now. - cc_log("More than %u entries in manifest file; discarding", - MAX_MANIFEST_ENTRIES); - free_manifest(mf); - mf = create_empty_manifest(); - } else if (mf->n_file_infos > MAX_MANIFEST_FILE_INFO_ENTRIES) { - // Rarely, file_info entries can grow large in pathological cases where - // many included files change, but the main file does not. This also puts - // an upper bound on the number of file_info entries. - cc_log("More than %u file_info entries in manifest file; discarding", - MAX_MANIFEST_FILE_INFO_ENTRIES); - free_manifest(mf); - mf = create_empty_manifest(); - } - - tmp_file = format("%s.tmp", manifest_path); - int fd2 = create_tmp_fd(&tmp_file); - f2 = gzdopen(fd2, "wb"); - if (!f2) { - cc_log("Failed to gzdopen %s", tmp_file); - goto out; - } - - add_object_entry(mf, object_hash, included_files, save_timestamp); - if (write_manifest(f2, mf)) { - gzclose(f2); - f2 = NULL; - if (x_rename(tmp_file, manifest_path) == 0) { - ret = 1; - } else { - cc_log("Failed to rename %s to %s", tmp_file, manifest_path); - goto out; - } - } else { - cc_log("Failed to write manifest file"); - goto out; - } - -out: - if (mf) { - free_manifest(mf); - } - if (tmp_file) { - free(tmp_file); - } - if (f2) { - gzclose(f2); - } - return ret; -} - -bool -manifest_dump(const char *manifest_path, FILE *stream) -{ - struct manifest *mf = NULL; - gzFile f = NULL; - bool ret = false; - - int fd = open(manifest_path, O_RDONLY | O_BINARY); - if (fd == -1) { - fprintf(stderr, "No such manifest file: %s\n", manifest_path); - goto out; - } - f = gzdopen(fd, "rb"); - if (!f) { - fprintf(stderr, "Failed to dzopen manifest file\n"); - close(fd); - goto out; - } - char *errmsg; - mf = read_manifest(f, &errmsg); - if (!mf) { - fprintf(stderr, "%s\n", errmsg); - free(errmsg); - goto out; - } - - fprintf(stream, "Magic: %c%c%c%c\n", - (MAGIC >> 24) & 0xFF, - (MAGIC >> 16) & 0xFF, - (MAGIC >> 8) & 0xFF, - MAGIC & 0xFF); - fprintf(stream, "Version: %u\n", mf->version); - fprintf(stream, "Hash size: %u\n", (unsigned)mf->hash_size); - fprintf(stream, "Reserved field: %u\n", (unsigned)mf->reserved); - fprintf(stream, "File paths (%u):\n", (unsigned)mf->n_files); - for (unsigned i = 0; i < mf->n_files; ++i) { - fprintf(stream, " %u: %s\n", i, mf->files[i]); - } - fprintf(stream, "File infos (%u):\n", (unsigned)mf->n_file_infos); - for (unsigned i = 0; i < mf->n_file_infos; ++i) { - char *hash; - fprintf(stream, " %u:\n", i); - fprintf(stream, " Path index: %u\n", mf->file_infos[i].index); - hash = format_hash_as_string(mf->file_infos[i].hash, -1); - fprintf(stream, " Hash: %s\n", hash); - free(hash); - fprintf(stream, " Size: %u\n", mf->file_infos[i].size); - fprintf(stream, " Mtime: %lld\n", (long long)mf->file_infos[i].mtime); - fprintf(stream, " Ctime: %lld\n", (long long)mf->file_infos[i].ctime); - } - fprintf(stream, "Results (%u):\n", (unsigned)mf->n_objects); - for (unsigned i = 0; i < mf->n_objects; ++i) { - char *hash; - fprintf(stream, " %u:\n", i); - fprintf(stream, " File info indexes:"); - for (unsigned j = 0; j < mf->objects[i].n_file_info_indexes; ++j) { - fprintf(stream, " %u", mf->objects[i].file_info_indexes[j]); - } - fprintf(stream, "\n"); - hash = format_hash_as_string(mf->objects[i].hash.hash, -1); - fprintf(stream, " Hash: %s\n", hash); - free(hash); - fprintf(stream, " Size: %u\n", (unsigned)mf->objects[i].hash.size); - } - - ret = true; - -out: - if (mf) { - free_manifest(mf); - } - if (f) { - gzclose(f); - } - return ret; -} diff --git a/src/manifest.h b/src/manifest.h deleted file mode 100644 index 8878384..0000000 --- a/src/manifest.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef MANIFEST_H -#define MANIFEST_H - -#include "conf.h" -#include "hashutil.h" -#include "hashtable.h" - -#define MANIFEST_VERSION 1 - -struct file_hash *manifest_get(struct conf *conf, const char *manifest_path); -bool manifest_put(const char *manifest_path, struct file_hash *object_hash, - struct hashtable *included_files, bool save_timestamp); -bool manifest_dump(const char *manifest_path, FILE *stream); - -#endif diff --git a/src/mdfour.c b/src/mdfour.c deleted file mode 100644 index c0aec6b..0000000 --- a/src/mdfour.c +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright (C) 1997-1998 Andrew Tridgell -// Copyright (C) 2009-2019 Joel Rosdahl -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -#include "ccache.h" -#include "mdfour.h" - -// NOTE: This code makes no attempt to be fast! - -#define MASK32 (0xffffffff) - -#define F(X, Y, Z) ((((X)&(Y)) | ((~(X))&(Z)))) -#define G(X, Y, Z) ((((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z)))) -#define H(X, Y, Z) (((X)^(Y)^(Z))) -#define lshift(x, s) (((((x)<<(s))&MASK32) | (((x)>>(32-(s)))&MASK32))) - -#define ROUND1(a, b, c, d, k, s) \ - a = lshift((a + F(b, c, d) + M[k])&MASK32, s) -#define ROUND2(a, b, c, d, k, s) \ - a = lshift((a + G(b, c, d) + M[k] + 0x5A827999)&MASK32, s) -#define ROUND3(a, b, c, d, k, s) \ - a = lshift((a + H(b, c, d) + M[k] + 0x6ED9EBA1)&MASK32, s) - -// This applies md4 to 64 byte chunks. -static void -mdfour64(struct mdfour *md, uint32_t *M) -{ - uint32_t AA, BB, CC, DD; - uint32_t A, B, C, D; - - A = md->A; - B = md->B; - C = md->C; - D = md->D; - AA = A; - BB = B; - CC = C; - DD = D; - - ROUND1(A, B, C, D, 0, 3); ROUND1(D, A, B, C, 1, 7); - ROUND1(C, D, A, B, 2, 11); ROUND1(B, C, D, A, 3, 19); - ROUND1(A, B, C, D, 4, 3); ROUND1(D, A, B, C, 5, 7); - ROUND1(C, D, A, B, 6, 11); ROUND1(B, C, D, A, 7, 19); - ROUND1(A, B, C, D, 8, 3); ROUND1(D, A, B, C, 9, 7); - ROUND1(C, D, A, B, 10, 11); ROUND1(B, C, D, A, 11, 19); - ROUND1(A, B, C, D, 12, 3); ROUND1(D, A, B, C, 13, 7); - ROUND1(C, D, A, B, 14, 11); ROUND1(B, C, D, A, 15, 19); - - - ROUND2(A, B, C, D, 0, 3); ROUND2(D, A, B, C, 4, 5); - ROUND2(C, D, A, B, 8, 9); ROUND2(B, C, D, A, 12, 13); - ROUND2(A, B, C, D, 1, 3); ROUND2(D, A, B, C, 5, 5); - ROUND2(C, D, A, B, 9, 9); ROUND2(B, C, D, A, 13, 13); - ROUND2(A, B, C, D, 2, 3); ROUND2(D, A, B, C, 6, 5); - ROUND2(C, D, A, B, 10, 9); ROUND2(B, C, D, A, 14, 13); - ROUND2(A, B, C, D, 3, 3); ROUND2(D, A, B, C, 7, 5); - ROUND2(C, D, A, B, 11, 9); ROUND2(B, C, D, A, 15, 13); - - ROUND3(A, B, C, D, 0, 3); ROUND3(D, A, B, C, 8, 9); - ROUND3(C, D, A, B, 4, 11); ROUND3(B, C, D, A, 12, 15); - ROUND3(A, B, C, D, 2, 3); ROUND3(D, A, B, C, 10, 9); - ROUND3(C, D, A, B, 6, 11); ROUND3(B, C, D, A, 14, 15); - ROUND3(A, B, C, D, 1, 3); ROUND3(D, A, B, C, 9, 9); - ROUND3(C, D, A, B, 5, 11); ROUND3(B, C, D, A, 13, 15); - ROUND3(A, B, C, D, 3, 3); ROUND3(D, A, B, C, 11, 9); - ROUND3(C, D, A, B, 7, 11); ROUND3(B, C, D, A, 15, 15); - - A += AA; - B += BB; - C += CC; - D += DD; - - A &= MASK32; - B &= MASK32; - C &= MASK32; - D &= MASK32; - - md->A = A; - md->B = B; - md->C = C; - md->D = D; -} - -static void -copy64(uint32_t *M, const unsigned char *in) -{ -#ifdef WORDS_BIGENDIAN - for (int i = 0; i < 16; i++) { - M[i] = (in[i*4+3]<<24) | (in[i*4+2]<<16) | - (in[i*4+1]<<8) | (in[i*4+0]<<0); - } -#else - memcpy(M, in, 16*4); -#endif -} - -static void -copy4(unsigned char *out, uint32_t x) -{ -#ifdef WORDS_BIGENDIAN - out[0] = x&0xFF; - out[1] = (x>>8)&0xFF; - out[2] = (x>>16)&0xFF; - out[3] = (x>>24)&0xFF; -#else - memcpy(out, &x, 4); -#endif -} - -void -mdfour_begin(struct mdfour *md) -{ - md->A = 0x67452301; - md->B = 0xefcdab89; - md->C = 0x98badcfe; - md->D = 0x10325476; - md->totalN = 0; - md->tail_len = 0; -} - -static -void mdfour_tail(struct mdfour *md, const unsigned char *in, size_t n) -{ - md->totalN += n; - uint32_t b = md->totalN * 8; - unsigned char buf[128] = { 0 }; - uint32_t M[16]; - if (n) { - memcpy(buf, in, n); - } - buf[n] = 0x80; - - if (n <= 55) { - copy4(buf+56, b); - copy64(M, buf); - mdfour64(md, M); - } else { - copy4(buf+120, b); - copy64(M, buf); - mdfour64(md, M); - copy64(M, buf+64); - mdfour64(md, M); - } -} - -void -mdfour_update(struct mdfour *md, const unsigned char *in, size_t n) -{ - assert(in); - - uint32_t M[16]; - if (md->tail_len) { - size_t len = 64 - md->tail_len; - if (len > n) { - len = n; - } - memcpy(md->tail+md->tail_len, in, len); - md->tail_len += len; - n -= len; - in += len; - if (md->tail_len == 64) { - copy64(M, md->tail); - mdfour64(md, M); - md->totalN += 64; - md->tail_len = 0; - } - } - - while (n >= 64) { - copy64(M, in); - mdfour64(md, M); - in += 64; - n -= 64; - md->totalN += 64; - } - - if (n) { - memcpy(md->tail, in, n); - md->tail_len = n; - } -} - -void -mdfour_result(struct mdfour *md, unsigned char *out) -{ - struct mdfour result; - result.A = md->A; - result.B = md->B; - result.C = md->C; - result.D = md->D; - result.totalN = md->totalN; - result.tail_len = md->tail_len; - memcpy(result.tail, md->tail, result.tail_len); - - mdfour_tail(&result, result.tail, result.tail_len); - copy4(out, result.A); - copy4(out+4, result.B); - copy4(out+8, result.C); - copy4(out+12, result.D); -} diff --git a/src/mdfour.h b/src/mdfour.h deleted file mode 100644 index 761a19e..0000000 --- a/src/mdfour.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef MDFOUR_H -#define MDFOUR_H - -#include -#include - -struct mdfour { - uint32_t A, B, C, D; - size_t totalN; - size_t tail_len; - unsigned char tail[64]; -}; - -void mdfour_begin(struct mdfour *md); -void mdfour_update(struct mdfour *md, const unsigned char *in, size_t n); -void mdfour_result(struct mdfour *md, unsigned char *out); - -#endif diff --git a/src/murmurhashneutral2.c b/src/murmurhashneutral2.c deleted file mode 100644 index d63e0a1..0000000 --- a/src/murmurhashneutral2.c +++ /dev/null @@ -1,44 +0,0 @@ -// MurmurHashNeutral2, by Austin Appleby. Released to the public domain. See -// . - -#include "murmurhashneutral2.h" - -unsigned int -murmurhashneutral2(const void *key, int len, unsigned int seed) -{ - const unsigned int m = 0x5bd1e995; - const int r = 24; - unsigned int h = seed ^ len; - const unsigned char *data = (const unsigned char *)key; - - while (len >= 4) { - unsigned int k = data[0]; - k |= ((unsigned int) data[1]) << 8; - k |= ((unsigned int) data[2]) << 16; - k |= ((unsigned int) data[3]) << 24; - - k *= m; - k ^= k >> r; - k *= m; - - h *= m; - h ^= k; - - data += 4; - len -= 4; - } - - switch (len) - { - case 3: h ^= ((unsigned int) data[2]) << 16; // Fallthrough. - case 2: h ^= ((unsigned int) data[1]) << 8; // Fallthrough. - case 1: h ^= ((unsigned int) data[0]); - h *= m; - }; - - h ^= h >> 13; - h *= m; - h ^= h >> 15; - - return h; -} diff --git a/src/murmurhashneutral2.h b/src/murmurhashneutral2.h deleted file mode 100644 index fe056fd..0000000 --- a/src/murmurhashneutral2.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef MURMURHASHNEUTRAL2_H -#define MURMURHASHNEUTRAL2_H - -unsigned int murmurhashneutral2(const void *key, int len, unsigned int seed); - -#endif diff --git a/src/snprintf.c b/src/snprintf.c deleted file mode 100644 index e4cdade..0000000 --- a/src/snprintf.c +++ /dev/null @@ -1,2116 +0,0 @@ -/* $Id: snprintf.c,v 1.9 2008/01/20 14:02:00 holger Exp $ */ - -/* - * Copyright (c) 1995 Patrick Powell. - * - * This code is based on code written by Patrick Powell . - * It may be used for any purpose as long as this notice remains intact on all - * source code distributions. - */ - -/* - * Copyright (c) 2008 Holger Weiss. - * - * This version of the code is maintained by Holger Weiss . - * My changes to the code may freely be used, modified and/or redistributed for - * any purpose. It would be nice if additions and fixes to this file (including - * trivial code cleanups) would be sent back in order to let me include them in - * the version available at . - * However, this is not a requirement for using or redistributing (possibly - * modified) versions of this file, nor is leaving this notice intact mandatory. - */ - -/* - * History - * - * 2008-01-20 Holger Weiss for C99-snprintf 1.1: - * - * Fixed the detection of infinite floating point values on IRIX (and - * possibly other systems) and applied another few minor cleanups. - * - * 2008-01-06 Holger Weiss for C99-snprintf 1.0: - * - * Added a lot of new features, fixed many bugs, and incorporated various - * improvements done by Andrew Tridgell , Russ Allbery - * , Hrvoje Niksic , Damien Miller - * , and others for the Samba, INN, Wget, and OpenSSH - * projects. The additions include: support the "e", "E", "g", "G", and - * "F" conversion specifiers (and use conversion style "f" or "F" for the - * still unsupported "a" and "A" specifiers); support the "hh", "ll", "j", - * "t", and "z" length modifiers; support the "#" flag and the (non-C99) - * "'" flag; use localeconv(3) (if available) to get both the current - * locale's decimal point character and the separator between groups of - * digits; fix the handling of various corner cases of field width and - * precision specifications; fix various floating point conversion bugs; - * handle infinite and NaN floating point values; don't attempt to write to - * the output buffer (which may be NULL) if a size of zero was specified; - * check for integer overflow of the field width, precision, and return - * values and during the floating point conversion; use the OUTCHAR() macro - * instead of a function for better performance; provide asprintf(3) and - * vasprintf(3) functions; add new test cases. The replacement functions - * have been renamed to use an "rpl_" prefix, the function calls in the - * main project (and in this file) must be redefined accordingly for each - * replacement function which is needed (by using Autoconf or other means). - * Various other minor improvements have been applied and the coding style - * was cleaned up for consistency. - * - * 2007-07-23 Holger Weiss for Mutt 1.5.13: - * - * C99 compliant snprintf(3) and vsnprintf(3) functions return the number - * of characters that would have been written to a sufficiently sized - * buffer (excluding the '\0'). The original code simply returned the - * length of the resulting output string, so that's been fixed. - * - * 1998-03-05 Michael Elkins for Mutt 0.90.8: - * - * The original code assumed that both snprintf(3) and vsnprintf(3) were - * missing. Some systems only have snprintf(3) but not vsnprintf(3), so - * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. - * - * 1998-01-27 Thomas Roessler for Mutt 0.89i: - * - * The PGP code was using unsigned hexadecimal formats. Unfortunately, - * unsigned formats simply didn't work. - * - * 1997-10-22 Brandon Long for Mutt 0.87.1: - * - * Ok, added some minimal floating point support, which means this probably - * requires libm on most operating systems. Don't yet support the exponent - * (e,E) and sigfig (g,G). Also, fmtint() was pretty badly broken, it just - * wasn't being exercised in ways which showed it, so that's been fixed. - * Also, formatted the code to Mutt conventions, and removed dead code left - * over from the original. Also, there is now a builtin-test, run with: - * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf - * - * 2996-09-15 Brandon Long for Mutt 0.43: - * - * This was ugly. It is still ugly. I opted out of floating point - * numbers, but the formatter understands just about everything from the - * normal C string format, at least as far as I can tell from the Solaris - * 2.5 printf(3S) man page. - */ - -/* - * ToDo - * - * - Add wide character support. - * - Add support for "%a" and "%A" conversions. - * - Create test routines which predefine the expected results. Our test cases - * usually expose bugs in system implementations rather than in ours :-) - */ - -/* - * Usage - * - * 1) The following preprocessor macros should be defined to 1 if the feature or - * file in question is available on the target system (by using Autoconf or - * other means), though basic functionality should be available as long as - * HAVE_STDARG_H and HAVE_STDLIB_H are defined correctly: - * - * HAVE_VSNPRINTF - * HAVE_SNPRINTF - * HAVE_VASPRINTF - * HAVE_ASPRINTF - * HAVE_STDARG_H - * HAVE_STDDEF_H - * HAVE_STDINT_H - * HAVE_STDLIB_H - * HAVE_INTTYPES_H - * HAVE_LOCALE_H - * HAVE_LOCALECONV - * HAVE_LCONV_DECIMAL_POINT - * HAVE_LCONV_THOUSANDS_SEP - * HAVE_LONG_DOUBLE - * HAVE_LONG_LONG_INT - * HAVE_UNSIGNED_LONG_LONG_INT - * HAVE_INTMAX_T - * HAVE_UINTMAX_T - * HAVE_UINTPTR_T - * HAVE_PTRDIFF_T - * HAVE_VA_COPY - * HAVE___VA_COPY - * - * 2) The calls to the functions which should be replaced must be redefined - * throughout the project files (by using Autoconf or other means): - * - * #define vsnprintf rpl_vsnprintf - * #define snprintf rpl_snprintf - * #define vasprintf rpl_vasprintf - * #define asprintf rpl_asprintf - * - * 3) The required replacement functions should be declared in some header file - * included throughout the project files: - * - * #if HAVE_CONFIG_H - * #include - * #endif - * #if HAVE_STDARG_H - * #include - * #if !HAVE_VSNPRINTF - * int rpl_vsnprintf(char *, size_t, const char *, va_list); - * #endif - * #if !HAVE_SNPRINTF - * int rpl_snprintf(char *, size_t, const char *, ...); - * #endif - * #if !HAVE_VASPRINTF - * int rpl_vasprintf(char **, const char *, va_list); - * #endif - * #if !HAVE_ASPRINTF - * int rpl_asprintf(char **, const char *, ...); - * #endif - * #endif - * - * Autoconf macros for handling step 1 and step 2 are available at - * . - */ - -#if HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#ifdef TEST_SNPRINTF -#include /* For pow(3), NAN, and INFINITY. */ -#include /* For strcmp(3). */ -#if defined(__NetBSD__) || \ - defined(__FreeBSD__) || \ - defined(__OpenBSD__) || \ - defined(__NeXT__) || \ - defined(__bsd__) -#define OS_BSD 1 -#elif defined(sgi) || defined(__sgi) -#ifndef __c99 -#define __c99 /* Force C99 mode to get included on IRIX 6.5.30. */ -#endif /* !defined(__c99) */ -#define OS_IRIX 1 -#define OS_SYSV 1 -#elif defined(__svr4__) -#define OS_SYSV 1 -#elif defined(__linux__) -#define OS_LINUX 1 -#endif /* defined(__NetBSD__) || defined(__FreeBSD__) || [...] */ -#if HAVE_CONFIG_H /* Undefine definitions possibly done in config.h. */ -#ifdef HAVE_SNPRINTF -#undef HAVE_SNPRINTF -#endif /* defined(HAVE_SNPRINTF) */ -#ifdef HAVE_VSNPRINTF -#undef HAVE_VSNPRINTF -#endif /* defined(HAVE_VSNPRINTF) */ -#ifdef HAVE_ASPRINTF -#undef HAVE_ASPRINTF -#endif /* defined(HAVE_ASPRINTF) */ -#ifdef HAVE_VASPRINTF -#undef HAVE_VASPRINTF -#endif /* defined(HAVE_VASPRINTF) */ -#ifdef snprintf -#undef snprintf -#endif /* defined(snprintf) */ -#ifdef vsnprintf -#undef vsnprintf -#endif /* defined(vsnprintf) */ -#ifdef asprintf -#undef asprintf -#endif /* defined(asprintf) */ -#ifdef vasprintf -#undef vasprintf -#endif /* defined(vasprintf) */ -#else /* By default, we assume a modern system for testing. */ -#ifndef HAVE_STDARG_H -#define HAVE_STDARG_H 1 -#endif /* HAVE_STDARG_H */ -#ifndef HAVE_STDDEF_H -#define HAVE_STDDEF_H 1 -#endif /* HAVE_STDDEF_H */ -#ifndef HAVE_STDINT_H -#define HAVE_STDINT_H 1 -#endif /* HAVE_STDINT_H */ -#ifndef HAVE_STDLIB_H -#define HAVE_STDLIB_H 1 -#endif /* HAVE_STDLIB_H */ -#ifndef HAVE_INTTYPES_H -#define HAVE_INTTYPES_H 1 -#endif /* HAVE_INTTYPES_H */ -#ifndef HAVE_LOCALE_H -#define HAVE_LOCALE_H 1 -#endif /* HAVE_LOCALE_H */ -#ifndef HAVE_LOCALECONV -#define HAVE_LOCALECONV 1 -#endif /* !defined(HAVE_LOCALECONV) */ -#ifndef HAVE_LCONV_DECIMAL_POINT -#define HAVE_LCONV_DECIMAL_POINT 1 -#endif /* HAVE_LCONV_DECIMAL_POINT */ -#ifndef HAVE_LCONV_THOUSANDS_SEP -#define HAVE_LCONV_THOUSANDS_SEP 1 -#endif /* HAVE_LCONV_THOUSANDS_SEP */ -#ifndef HAVE_LONG_DOUBLE -#define HAVE_LONG_DOUBLE 1 -#endif /* !defined(HAVE_LONG_DOUBLE) */ -#ifndef HAVE_LONG_LONG_INT -#define HAVE_LONG_LONG_INT 1 -#endif /* !defined(HAVE_LONG_LONG_INT) */ -#ifndef HAVE_UNSIGNED_LONG_LONG_INT -#define HAVE_UNSIGNED_LONG_LONG_INT 1 -#endif /* !defined(HAVE_UNSIGNED_LONG_LONG_INT) */ -#ifndef HAVE_INTMAX_T -#define HAVE_INTMAX_T 1 -#endif /* !defined(HAVE_INTMAX_T) */ -#ifndef HAVE_UINTMAX_T -#define HAVE_UINTMAX_T 1 -#endif /* !defined(HAVE_UINTMAX_T) */ -#ifndef HAVE_UINTPTR_T -#define HAVE_UINTPTR_T 1 -#endif /* !defined(HAVE_UINTPTR_T) */ -#ifndef HAVE_PTRDIFF_T -#define HAVE_PTRDIFF_T 1 -#endif /* !defined(HAVE_PTRDIFF_T) */ -#ifndef HAVE_VA_COPY -#define HAVE_VA_COPY 1 -#endif /* !defined(HAVE_VA_COPY) */ -#ifndef HAVE___VA_COPY -#define HAVE___VA_COPY 1 -#endif /* !defined(HAVE___VA_COPY) */ -#endif /* HAVE_CONFIG_H */ -#define snprintf rpl_snprintf -#define vsnprintf rpl_vsnprintf -#define asprintf rpl_asprintf -#define vasprintf rpl_vasprintf -#endif /* TEST_SNPRINTF */ - -#if !HAVE_SNPRINTF || !HAVE_VSNPRINTF || !HAVE_ASPRINTF || !HAVE_VASPRINTF -#include /* For NULL, size_t, vsnprintf(3), and vasprintf(3). */ -#ifdef VA_START -#undef VA_START -#endif /* defined(VA_START) */ -#ifdef VA_SHIFT -#undef VA_SHIFT -#endif /* defined(VA_SHIFT) */ -#if HAVE_STDARG_H -#include -#define VA_START(ap, last) va_start(ap, last) -#define VA_SHIFT(ap, value, type) /* No-op for ANSI C. */ -#else /* Assume is available. */ -#include -#define VA_START(ap, last) va_start(ap) /* "last" is ignored. */ -#define VA_SHIFT(ap, value, type) value = va_arg(ap, type) -#endif /* HAVE_STDARG_H */ - -#if !HAVE_VSNPRINTF -int rpl_vsnprintf(char *, size_t, const char *, va_list); -#define vsnprintf rpl_vsnprintf -#endif -#if !HAVE_SNPRINTF -int rpl_snprintf(char *, size_t, const char *, ...); -#define snprintf rpl_snprintf -#endif -#if !HAVE_VASPRINTF -int rpl_vasprintf(char **, const char *, va_list); -#define vasprintf rpl_vasprintf -#endif -#if !HAVE_ASPRINTF -int rpl_asprintf(char **, const char *, ...); -#define asprintf rpl_asprintf -#endif - -#if !HAVE_VASPRINTF -#if HAVE_STDLIB_H -#include /* For malloc(3). */ -#endif /* HAVE_STDLIB_H */ -#ifdef VA_COPY -#undef VA_COPY -#endif /* defined(VA_COPY) */ -#ifdef VA_END_COPY -#undef VA_END_COPY -#endif /* defined(VA_END_COPY) */ -#if HAVE_VA_COPY -#define VA_COPY(dest, src) va_copy(dest, src) -#define VA_END_COPY(ap) va_end(ap) -#elif HAVE___VA_COPY -#define VA_COPY(dest, src) __va_copy(dest, src) -#define VA_END_COPY(ap) va_end(ap) -#else -#define VA_COPY(dest, src) (void)mymemcpy(&dest, &src, sizeof(va_list)) -#define VA_END_COPY(ap) /* No-op. */ -#define NEED_MYMEMCPY 1 -static void *mymemcpy(void *, void *, size_t); -#endif /* HAVE_VA_COPY */ -#endif /* !HAVE_VASPRINTF */ - -#if !HAVE_VSNPRINTF -#include /* For ERANGE and errno. */ -#include /* For *_MAX. */ -#if HAVE_INTTYPES_H -#include /* For intmax_t (if not defined in ). */ -#endif /* HAVE_INTTYPES_H */ -#if HAVE_LOCALE_H -#include /* For localeconv(3). */ -#endif /* HAVE_LOCALE_H */ -#if HAVE_STDDEF_H -#include /* For ptrdiff_t. */ -#endif /* HAVE_STDDEF_H */ -#if HAVE_STDINT_H -#include /* For intmax_t. */ -#endif /* HAVE_STDINT_H */ - -/* Support for unsigned long long int. We may also need ULLONG_MAX. */ -#ifndef ULONG_MAX /* We may need ULONG_MAX as a fallback. */ -#ifdef UINT_MAX -#define ULONG_MAX UINT_MAX -#else -#define ULONG_MAX INT_MAX -#endif /* defined(UINT_MAX) */ -#endif /* !defined(ULONG_MAX) */ -#ifdef ULLONG -#undef ULLONG -#endif /* defined(ULLONG) */ -#if HAVE_UNSIGNED_LONG_LONG_INT -#define ULLONG unsigned long long int -#ifndef ULLONG_MAX -#define ULLONG_MAX ULONG_MAX -#endif /* !defined(ULLONG_MAX) */ -#else -#define ULLONG unsigned long int -#ifdef ULLONG_MAX -#undef ULLONG_MAX -#endif /* defined(ULLONG_MAX) */ -#define ULLONG_MAX ULONG_MAX -#endif /* HAVE_LONG_LONG_INT */ - -/* Support for uintmax_t. We also need UINTMAX_MAX. */ -#ifdef UINTMAX_T -#undef UINTMAX_T -#endif /* defined(UINTMAX_T) */ -#if HAVE_UINTMAX_T || defined(uintmax_t) -#define UINTMAX_T uintmax_t -#ifndef UINTMAX_MAX -#define UINTMAX_MAX ULLONG_MAX -#endif /* !defined(UINTMAX_MAX) */ -#else -#define UINTMAX_T ULLONG -#ifdef UINTMAX_MAX -#undef UINTMAX_MAX -#endif /* defined(UINTMAX_MAX) */ -#define UINTMAX_MAX ULLONG_MAX -#endif /* HAVE_UINTMAX_T || defined(uintmax_t) */ - -/* Support for long double. */ -#ifndef LDOUBLE -#if HAVE_LONG_DOUBLE -#define LDOUBLE long double -#else -#define LDOUBLE double -#endif /* HAVE_LONG_DOUBLE */ -#endif /* !defined(LDOUBLE) */ - -/* Support for long long int. */ -#ifndef LLONG -#if HAVE_LONG_LONG_INT -#define LLONG long long int -#else -#define LLONG long int -#endif /* HAVE_LONG_LONG_INT */ -#endif /* !defined(LLONG) */ - -/* Support for intmax_t. */ -#ifndef INTMAX_T -#if HAVE_INTMAX_T || defined(intmax_t) -#define INTMAX_T intmax_t -#else -#define INTMAX_T LLONG -#endif /* HAVE_INTMAX_T || defined(intmax_t) */ -#endif /* !defined(INTMAX_T) */ - -/* Support for uintptr_t. */ -#ifndef UINTPTR_T -#if HAVE_UINTPTR_T || defined(uintptr_t) -#define UINTPTR_T uintptr_t -#else -#define UINTPTR_T unsigned long int -#endif /* HAVE_UINTPTR_T || defined(uintptr_t) */ -#endif /* !defined(UINTPTR_T) */ - -/* Support for ptrdiff_t. */ -#ifndef PTRDIFF_T -#if HAVE_PTRDIFF_T || defined(ptrdiff_t) -#define PTRDIFF_T ptrdiff_t -#else -#define PTRDIFF_T long int -#endif /* HAVE_PTRDIFF_T || defined(ptrdiff_t) */ -#endif /* !defined(PTRDIFF_T) */ - -/* - * We need an unsigned integer type corresponding to ptrdiff_t (cf. C99: - * 7.19.6.1, 7). However, we'll simply use PTRDIFF_T and convert it to an - * unsigned type if necessary. This should work just fine in practice. - */ -#ifndef UPTRDIFF_T -#define UPTRDIFF_T PTRDIFF_T -#endif /* !defined(UPTRDIFF_T) */ - -/* - * We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7). - * However, we'll simply use size_t and convert it to a signed type if - * necessary. This should work just fine in practice. - */ -#ifndef SSIZE_T -#define SSIZE_T size_t -#endif /* !defined(SSIZE_T) */ - -/* Either ERANGE or E2BIG should be available everywhere. */ -#ifndef ERANGE -#define ERANGE E2BIG -#endif /* !defined(ERANGE) */ -#ifndef EOVERFLOW -#define EOVERFLOW ERANGE -#endif /* !defined(EOVERFLOW) */ - -/* - * Buffer size to hold the octal string representation of UINT128_MAX without - * nul-termination ("3777777777777777777777777777777777777777777"). - */ -#ifdef MAX_CONVERT_LENGTH -#undef MAX_CONVERT_LENGTH -#endif /* defined(MAX_CONVERT_LENGTH) */ -#define MAX_CONVERT_LENGTH 43 - -/* Format read states. */ -#define PRINT_S_DEFAULT 0 -#define PRINT_S_FLAGS 1 -#define PRINT_S_WIDTH 2 -#define PRINT_S_DOT 3 -#define PRINT_S_PRECISION 4 -#define PRINT_S_MOD 5 -#define PRINT_S_CONV 6 - -/* Format flags. */ -#define PRINT_F_MINUS (1 << 0) -#define PRINT_F_PLUS (1 << 1) -#define PRINT_F_SPACE (1 << 2) -#define PRINT_F_NUM (1 << 3) -#define PRINT_F_ZERO (1 << 4) -#define PRINT_F_QUOTE (1 << 5) -#define PRINT_F_UP (1 << 6) -#define PRINT_F_UNSIGNED (1 << 7) -#define PRINT_F_TYPE_G (1 << 8) -#define PRINT_F_TYPE_E (1 << 9) - -/* Conversion flags. */ -#define PRINT_C_CHAR 1 -#define PRINT_C_SHORT 2 -#define PRINT_C_LONG 3 -#define PRINT_C_LLONG 4 -#define PRINT_C_LDOUBLE 5 -#define PRINT_C_SIZE 6 -#define PRINT_C_PTRDIFF 7 -#define PRINT_C_INTMAX 8 - -#ifndef MAX -#define MAX(x, y) ((x >= y) ? x : y) -#endif /* !defined(MAX) */ -#ifndef CHARTOINT -#define CHARTOINT(ch) (ch - '0') -#endif /* !defined(CHARTOINT) */ -#ifndef ISDIGIT -#define ISDIGIT(ch) ('0' <= (unsigned char)ch && (unsigned char)ch <= '9') -#endif /* !defined(ISDIGIT) */ -#ifndef ISNAN -#define ISNAN(x) (x != x) -#endif /* !defined(ISNAN) */ -#ifndef ISINF -#define ISINF(x) (x != 0.0 && x + x == x) -#endif /* !defined(ISINF) */ - -#ifdef OUTCHAR -#undef OUTCHAR -#endif /* defined(OUTCHAR) */ -#define OUTCHAR(str, len, size, ch) \ -do { \ - if (len + 1 < size) \ - str[len] = ch; \ - (len)++; \ -} while (/* CONSTCOND */ 0) - -static void fmtstr(char *, size_t *, size_t, const char *, int, int, int); -static void fmtint(char *, size_t *, size_t, INTMAX_T, int, int, int, int); -static void fmtflt(char *, size_t *, size_t, LDOUBLE, int, int, int, int *); -static void printsep(char *, size_t *, size_t); -static int getnumsep(int); -static int getexponent(LDOUBLE); -static int convert(UINTMAX_T, char *, size_t, int, int); -static UINTMAX_T cast(LDOUBLE); -static UINTMAX_T myround(LDOUBLE); -static LDOUBLE mypow10(int); - -#ifndef __MINGW32__ -extern int errno; -#endif - -int -rpl_vsnprintf(char *str, size_t size, const char *format, va_list args) -{ - LDOUBLE fvalue; - INTMAX_T value; - unsigned char cvalue; - const char *strvalue; - INTMAX_T *intmaxptr; - PTRDIFF_T *ptrdiffptr; - SSIZE_T *sizeptr; - LLONG *llongptr; - long int *longptr; - int *intptr; - short int *shortptr; - signed char *charptr; - size_t len = 0; - int overflow = 0; - int base = 0; - int cflags = 0; - int flags = 0; - int width = 0; - int precision = -1; - int state = PRINT_S_DEFAULT; - char ch = *format++; - - /* - * C99 says: "If `n' is zero, nothing is written, and `s' may be a null - * pointer." (7.19.6.5, 2) We're forgiving and allow a NULL pointer - * even if a size larger than zero was specified. At least NetBSD's - * snprintf(3) does the same, as well as other versions of this file. - * (Though some of these versions will write to a non-NULL buffer even - * if a size of zero was specified, which violates the standard.) - */ - if (str == NULL && size != 0) - size = 0; - - while (ch != '\0') - switch (state) { - case PRINT_S_DEFAULT: - if (ch == '%') - state = PRINT_S_FLAGS; - else - OUTCHAR(str, len, size, ch); - ch = *format++; - break; - case PRINT_S_FLAGS: - switch (ch) { - case '-': - flags |= PRINT_F_MINUS; - ch = *format++; - break; - case '+': - flags |= PRINT_F_PLUS; - ch = *format++; - break; - case ' ': - flags |= PRINT_F_SPACE; - ch = *format++; - break; - case '#': - flags |= PRINT_F_NUM; - ch = *format++; - break; - case '0': - flags |= PRINT_F_ZERO; - ch = *format++; - break; - case '\'': /* SUSv2 flag (not in C99). */ - flags |= PRINT_F_QUOTE; - ch = *format++; - break; - default: - state = PRINT_S_WIDTH; - break; - } - break; - case PRINT_S_WIDTH: - if (ISDIGIT(ch)) { - ch = CHARTOINT(ch); - if (width > (INT_MAX - ch) / 10) { - overflow = 1; - goto out; - } - width = 10 * width + ch; - ch = *format++; - } else if (ch == '*') { - /* - * C99 says: "A negative field width argument is - * taken as a `-' flag followed by a positive - * field width." (7.19.6.1, 5) - */ - if ((width = va_arg(args, int)) < 0) { - flags |= PRINT_F_MINUS; - width = -width; - } - ch = *format++; - state = PRINT_S_DOT; - } else - state = PRINT_S_DOT; - break; - case PRINT_S_DOT: - if (ch == '.') { - state = PRINT_S_PRECISION; - ch = *format++; - } else - state = PRINT_S_MOD; - break; - case PRINT_S_PRECISION: - if (precision == -1) - precision = 0; - if (ISDIGIT(ch)) { - ch = CHARTOINT(ch); - if (precision > (INT_MAX - ch) / 10) { - overflow = 1; - goto out; - } - precision = 10 * precision + ch; - ch = *format++; - } else if (ch == '*') { - /* - * C99 says: "A negative precision argument is - * taken as if the precision were omitted." - * (7.19.6.1, 5) - */ - if ((precision = va_arg(args, int)) < 0) - precision = -1; - ch = *format++; - state = PRINT_S_MOD; - } else - state = PRINT_S_MOD; - break; - case PRINT_S_MOD: - switch (ch) { - case 'h': - ch = *format++; - if (ch == 'h') { /* It's a char. */ - ch = *format++; - cflags = PRINT_C_CHAR; - } else - cflags = PRINT_C_SHORT; - break; - case 'l': - ch = *format++; - if (ch == 'l') { /* It's a long long. */ - ch = *format++; - cflags = PRINT_C_LLONG; - } else - cflags = PRINT_C_LONG; - break; - case 'L': - cflags = PRINT_C_LDOUBLE; - ch = *format++; - break; - case 'j': - cflags = PRINT_C_INTMAX; - ch = *format++; - break; - case 't': - cflags = PRINT_C_PTRDIFF; - ch = *format++; - break; - case 'z': - cflags = PRINT_C_SIZE; - ch = *format++; - break; - } - state = PRINT_S_CONV; - break; - case PRINT_S_CONV: - switch (ch) { - case 'd': - /* FALLTHROUGH */ - case 'i': - switch (cflags) { - case PRINT_C_CHAR: - value = (signed char)va_arg(args, int); - break; - case PRINT_C_SHORT: - value = (short int)va_arg(args, int); - break; - case PRINT_C_LONG: - value = va_arg(args, long int); - break; - case PRINT_C_LLONG: - value = va_arg(args, LLONG); - break; - case PRINT_C_SIZE: - value = va_arg(args, SSIZE_T); - break; - case PRINT_C_INTMAX: - value = va_arg(args, INTMAX_T); - break; - case PRINT_C_PTRDIFF: - value = va_arg(args, PTRDIFF_T); - break; - default: - value = va_arg(args, int); - break; - } - fmtint(str, &len, size, value, 10, width, - precision, flags); - break; - case 'X': - flags |= PRINT_F_UP; - /* FALLTHROUGH */ - case 'x': - base = 16; - /* FALLTHROUGH */ - case 'o': - if (base == 0) - base = 8; - /* FALLTHROUGH */ - case 'u': - if (base == 0) - base = 10; - flags |= PRINT_F_UNSIGNED; - switch (cflags) { - case PRINT_C_CHAR: - value = (unsigned char)va_arg(args, - unsigned int); - break; - case PRINT_C_SHORT: - value = (unsigned short int)va_arg(args, - unsigned int); - break; - case PRINT_C_LONG: - value = va_arg(args, unsigned long int); - break; - case PRINT_C_LLONG: - value = va_arg(args, ULLONG); - break; - case PRINT_C_SIZE: - value = va_arg(args, size_t); - break; - case PRINT_C_INTMAX: - value = va_arg(args, UINTMAX_T); - break; - case PRINT_C_PTRDIFF: - value = va_arg(args, UPTRDIFF_T); - break; - default: - value = va_arg(args, unsigned int); - break; - } - fmtint(str, &len, size, value, base, width, - precision, flags); - break; - case 'A': - /* Not yet supported, we'll use "%F". */ - /* FALLTHROUGH */ - case 'F': - flags |= PRINT_F_UP; - case 'a': - /* Not yet supported, we'll use "%f". */ - /* FALLTHROUGH */ - case 'f': - if (cflags == PRINT_C_LDOUBLE) - fvalue = va_arg(args, LDOUBLE); - else - fvalue = va_arg(args, double); - fmtflt(str, &len, size, fvalue, width, - precision, flags, &overflow); - if (overflow) - goto out; - break; - case 'E': - flags |= PRINT_F_UP; - /* FALLTHROUGH */ - case 'e': - flags |= PRINT_F_TYPE_E; - if (cflags == PRINT_C_LDOUBLE) - fvalue = va_arg(args, LDOUBLE); - else - fvalue = va_arg(args, double); - fmtflt(str, &len, size, fvalue, width, - precision, flags, &overflow); - if (overflow) - goto out; - break; - case 'G': - flags |= PRINT_F_UP; - /* FALLTHROUGH */ - case 'g': - flags |= PRINT_F_TYPE_G; - if (cflags == PRINT_C_LDOUBLE) - fvalue = va_arg(args, LDOUBLE); - else - fvalue = va_arg(args, double); - /* - * If the precision is zero, it is treated as - * one (cf. C99: 7.19.6.1, 8). - */ - if (precision == 0) - precision = 1; - fmtflt(str, &len, size, fvalue, width, - precision, flags, &overflow); - if (overflow) - goto out; - break; - case 'c': - cvalue = va_arg(args, int); - OUTCHAR(str, len, size, cvalue); - break; - case 's': - strvalue = va_arg(args, char *); - fmtstr(str, &len, size, strvalue, width, - precision, flags); - break; - case 'p': - /* - * C99 says: "The value of the pointer is - * converted to a sequence of printing - * characters, in an implementation-defined - * manner." (C99: 7.19.6.1, 8) - */ - if ((strvalue = va_arg(args, void *)) == NULL) - /* - * We use the glibc format. BSD prints - * "0x0", SysV "0". - */ - fmtstr(str, &len, size, "(nil)", width, - -1, flags); - else { - /* - * We use the BSD/glibc format. SysV - * omits the "0x" prefix (which we emit - * using the PRINT_F_NUM flag). - */ - flags |= PRINT_F_NUM; - flags |= PRINT_F_UNSIGNED; - fmtint(str, &len, size, - (UINTPTR_T)strvalue, 16, width, - precision, flags); - } - break; - case 'n': - switch (cflags) { - case PRINT_C_CHAR: - charptr = va_arg(args, signed char *); - *charptr = len; - break; - case PRINT_C_SHORT: - shortptr = va_arg(args, short int *); - *shortptr = len; - break; - case PRINT_C_LONG: - longptr = va_arg(args, long int *); - *longptr = len; - break; - case PRINT_C_LLONG: - llongptr = va_arg(args, LLONG *); - *llongptr = len; - break; - case PRINT_C_SIZE: - /* - * C99 says that with the "z" length - * modifier, "a following `n' conversion - * specifier applies to a pointer to a - * signed integer type corresponding to - * size_t argument." (7.19.6.1, 7) - */ - sizeptr = va_arg(args, SSIZE_T *); - *sizeptr = len; - break; - case PRINT_C_INTMAX: - intmaxptr = va_arg(args, INTMAX_T *); - *intmaxptr = len; - break; - case PRINT_C_PTRDIFF: - ptrdiffptr = va_arg(args, PTRDIFF_T *); - *ptrdiffptr = len; - break; - default: - intptr = va_arg(args, int *); - *intptr = len; - break; - } - break; - case '%': /* Print a "%" character verbatim. */ - OUTCHAR(str, len, size, ch); - break; - default: /* Skip other characters. */ - break; - } - ch = *format++; - state = PRINT_S_DEFAULT; - base = cflags = flags = width = 0; - precision = -1; - break; - } -out: - if (len < size) - str[len] = '\0'; - else if (size > 0) - str[size - 1] = '\0'; - - if (overflow || len >= INT_MAX) { - errno = overflow ? EOVERFLOW : ERANGE; - return -1; - } - return (int)len; -} - -static void -fmtstr(char *str, size_t *len, size_t size, const char *value, int width, - int precision, int flags) -{ - int padlen, strln; /* Amount to pad. */ - int noprecision = (precision == -1); - - if (value == NULL) /* We're forgiving. */ - value = "(null)"; - - /* If a precision was specified, don't read the string past it. */ - for (strln = 0; value[strln] != '\0' && - (noprecision || strln < precision); strln++) - continue; - - if ((padlen = width - strln) < 0) - padlen = 0; - if (flags & PRINT_F_MINUS) /* Left justify. */ - padlen = -padlen; - - while (padlen > 0) { /* Leading spaces. */ - OUTCHAR(str, *len, size, ' '); - padlen--; - } - while (*value != '\0' && (noprecision || precision-- > 0)) { - OUTCHAR(str, *len, size, *value); - value++; - } - while (padlen < 0) { /* Trailing spaces. */ - OUTCHAR(str, *len, size, ' '); - padlen++; - } -} - -static void -fmtint(char *str, size_t *len, size_t size, INTMAX_T value, int base, int width, - int precision, int flags) -{ - UINTMAX_T uvalue; - char iconvert[MAX_CONVERT_LENGTH]; - char sign = 0; - char hexprefix = 0; - int spadlen = 0; /* Amount to space pad. */ - int zpadlen = 0; /* Amount to zero pad. */ - int pos; - int separators = (flags & PRINT_F_QUOTE); - int noprecision = (precision == -1); - - if (flags & PRINT_F_UNSIGNED) - uvalue = value; - else { - uvalue = (value >= 0) ? value : -value; - if (value < 0) - sign = '-'; - else if (flags & PRINT_F_PLUS) /* Do a sign. */ - sign = '+'; - else if (flags & PRINT_F_SPACE) - sign = ' '; - } - - pos = convert(uvalue, iconvert, sizeof(iconvert), base, - flags & PRINT_F_UP); - - if (flags & PRINT_F_NUM && uvalue != 0) { - /* - * C99 says: "The result is converted to an `alternative form'. - * For `o' conversion, it increases the precision, if and only - * if necessary, to force the first digit of the result to be a - * zero (if the value and precision are both 0, a single 0 is - * printed). For `x' (or `X') conversion, a nonzero result has - * `0x' (or `0X') prefixed to it." (7.19.6.1, 6) - */ - switch (base) { - case 8: - if (precision <= pos) - precision = pos + 1; - break; - case 16: - hexprefix = (flags & PRINT_F_UP) ? 'X' : 'x'; - break; - } - } - - if (separators) /* Get the number of group separators we'll print. */ - separators = getnumsep(pos); - - zpadlen = precision - pos - separators; - spadlen = width /* Minimum field width. */ - - separators /* Number of separators. */ - - MAX(precision, pos) /* Number of integer digits. */ - - ((sign != 0) ? 1 : 0) /* Will we print a sign? */ - - ((hexprefix != 0) ? 2 : 0); /* Will we print a prefix? */ - - if (zpadlen < 0) - zpadlen = 0; - if (spadlen < 0) - spadlen = 0; - - /* - * C99 says: "If the `0' and `-' flags both appear, the `0' flag is - * ignored. For `d', `i', `o', `u', `x', and `X' conversions, if a - * precision is specified, the `0' flag is ignored." (7.19.6.1, 6) - */ - if (flags & PRINT_F_MINUS) /* Left justify. */ - spadlen = -spadlen; - else if (flags & PRINT_F_ZERO && noprecision) { - zpadlen += spadlen; - spadlen = 0; - } - while (spadlen > 0) { /* Leading spaces. */ - OUTCHAR(str, *len, size, ' '); - spadlen--; - } - if (sign != 0) /* Sign. */ - OUTCHAR(str, *len, size, sign); - if (hexprefix != 0) { /* A "0x" or "0X" prefix. */ - OUTCHAR(str, *len, size, '0'); - OUTCHAR(str, *len, size, hexprefix); - } - while (zpadlen > 0) { /* Leading zeros. */ - OUTCHAR(str, *len, size, '0'); - zpadlen--; - } - while (pos > 0) { /* The actual digits. */ - pos--; - OUTCHAR(str, *len, size, iconvert[pos]); - if (separators > 0 && pos > 0 && pos % 3 == 0) - printsep(str, len, size); - } - while (spadlen < 0) { /* Trailing spaces. */ - OUTCHAR(str, *len, size, ' '); - spadlen++; - } -} - -static void -fmtflt(char *str, size_t *len, size_t size, LDOUBLE fvalue, int width, - int precision, int flags, int *overflow) -{ - LDOUBLE ufvalue; - UINTMAX_T intpart; - UINTMAX_T fracpart; - UINTMAX_T mask; - const char *infnan = NULL; - char iconvert[MAX_CONVERT_LENGTH]; - char fconvert[MAX_CONVERT_LENGTH]; - char econvert[4]; /* "e-12" (without nul-termination). */ - char esign = 0; - char sign = 0; - int leadfraczeros = 0; - int exponent = 0; - int emitpoint = 0; - int omitzeros = 0; - int omitcount = 0; - int padlen = 0; - int epos = 0; - int fpos = 0; - int ipos = 0; - int separators = (flags & PRINT_F_QUOTE); - int estyle = (flags & PRINT_F_TYPE_E); -#if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT - struct lconv *lc = localeconv(); -#endif /* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */ - - /* - * AIX' man page says the default is 0, but C99 and at least Solaris' - * and NetBSD's man pages say the default is 6, and sprintf(3) on AIX - * defaults to 6. - */ - if (precision == -1) - precision = 6; - - if (fvalue < 0.0) - sign = '-'; - else if (flags & PRINT_F_PLUS) /* Do a sign. */ - sign = '+'; - else if (flags & PRINT_F_SPACE) - sign = ' '; - - if (ISNAN(fvalue)) - infnan = (flags & PRINT_F_UP) ? "NAN" : "nan"; - else if (ISINF(fvalue)) - infnan = (flags & PRINT_F_UP) ? "INF" : "inf"; - - if (infnan != NULL) { - if (sign != 0) - iconvert[ipos++] = sign; - while (*infnan != '\0') - iconvert[ipos++] = *infnan++; - fmtstr(str, len, size, iconvert, width, ipos, flags); - return; - } - - /* "%e" (or "%E") or "%g" (or "%G") conversion. */ - if (flags & PRINT_F_TYPE_E || flags & PRINT_F_TYPE_G) { - if (flags & PRINT_F_TYPE_G) { - /* - * For "%g" (and "%G") conversions, the precision - * specifies the number of significant digits, which - * includes the digits in the integer part. The - * conversion will or will not be using "e-style" (like - * "%e" or "%E" conversions) depending on the precision - * and on the exponent. However, the exponent can be - * affected by rounding the converted value, so we'll - * leave this decision for later. Until then, we'll - * assume that we're going to do an "e-style" conversion - * (in order to get the exponent calculated). For - * "e-style", the precision must be decremented by one. - */ - precision--; - /* - * For "%g" (and "%G") conversions, trailing zeros are - * removed from the fractional portion of the result - * unless the "#" flag was specified. - */ - if (!(flags & PRINT_F_NUM)) - omitzeros = 1; - } - exponent = getexponent(fvalue); - estyle = 1; - } - -again: - /* - * Sorry, we only support 9, 19, or 38 digits (that is, the number of - * digits of the 32-bit, the 64-bit, or the 128-bit UINTMAX_MAX value - * minus one) past the decimal point due to our conversion method. - */ - switch (sizeof(UINTMAX_T)) { - case 16: - if (precision > 38) - precision = 38; - break; - case 8: - if (precision > 19) - precision = 19; - break; - default: - if (precision > 9) - precision = 9; - break; - } - - ufvalue = (fvalue >= 0.0) ? fvalue : -fvalue; - if (estyle) /* We want exactly one integer digit. */ - ufvalue /= mypow10(exponent); - - if ((intpart = cast(ufvalue)) == UINTMAX_MAX) { - *overflow = 1; - return; - } - - /* - * Factor of ten with the number of digits needed for the fractional - * part. For example, if the precision is 3, the mask will be 1000. - */ - mask = (UINTMAX_T)mypow10(precision); - /* - * We "cheat" by converting the fractional part to integer by - * multiplying by a factor of ten. - */ - if ((fracpart = myround(mask * (ufvalue - intpart))) >= mask) { - /* - * For example, ufvalue = 2.99962, intpart = 2, and mask = 1000 - * (because precision = 3). Now, myround(1000 * 0.99962) will - * return 1000. So, the integer part must be incremented by one - * and the fractional part must be set to zero. - */ - intpart++; - fracpart = 0; - if (estyle && intpart == 10) { - /* - * The value was rounded up to ten, but we only want one - * integer digit if using "e-style". So, the integer - * part must be set to one and the exponent must be - * incremented by one. - */ - intpart = 1; - exponent++; - } - } - - /* - * Now that we know the real exponent, we can check whether or not to - * use "e-style" for "%g" (and "%G") conversions. If we don't need - * "e-style", the precision must be adjusted and the integer and - * fractional parts must be recalculated from the original value. - * - * C99 says: "Let P equal the precision if nonzero, 6 if the precision - * is omitted, or 1 if the precision is zero. Then, if a conversion - * with style `E' would have an exponent of X: - * - * - if P > X >= -4, the conversion is with style `f' (or `F') and - * precision P - (X + 1). - * - * - otherwise, the conversion is with style `e' (or `E') and precision - * P - 1." (7.19.6.1, 8) - * - * Note that we had decremented the precision by one. - */ - if (flags & PRINT_F_TYPE_G && estyle && - precision + 1 > exponent && exponent >= -4) { - precision -= exponent; - estyle = 0; - goto again; - } - - if (estyle) { - if (exponent < 0) { - exponent = -exponent; - esign = '-'; - } else - esign = '+'; - - /* - * Convert the exponent. The sizeof(econvert) is 4. So, the - * econvert buffer can hold e.g. "e+99" and "e-99". We don't - * support an exponent which contains more than two digits. - * Therefore, the following stores are safe. - */ - epos = convert(exponent, econvert, 2, 10, 0); - /* - * C99 says: "The exponent always contains at least two digits, - * and only as many more digits as necessary to represent the - * exponent." (7.19.6.1, 8) - */ - if (epos == 1) - econvert[epos++] = '0'; - econvert[epos++] = esign; - econvert[epos++] = (flags & PRINT_F_UP) ? 'E' : 'e'; - } - - /* Convert the integer part and the fractional part. */ - ipos = convert(intpart, iconvert, sizeof(iconvert), 10, 0); - if (fracpart != 0) /* convert() would return 1 if fracpart == 0. */ - fpos = convert(fracpart, fconvert, sizeof(fconvert), 10, 0); - - leadfraczeros = precision - fpos; - - if (omitzeros) { - if (fpos > 0) /* Omit trailing fractional part zeros. */ - while (omitcount < fpos && fconvert[omitcount] == '0') - omitcount++; - else { /* The fractional part is zero, omit it completely. */ - omitcount = precision; - leadfraczeros = 0; - } - precision -= omitcount; - } - - /* - * Print a decimal point if either the fractional part is non-zero - * and/or the "#" flag was specified. - */ - if (precision > 0 || flags & PRINT_F_NUM) - emitpoint = 1; - if (separators) /* Get the number of group separators we'll print. */ - separators = getnumsep(ipos); - - padlen = width /* Minimum field width. */ - - ipos /* Number of integer digits. */ - - epos /* Number of exponent characters. */ - - precision /* Number of fractional digits. */ - - separators /* Number of group separators. */ - - (emitpoint ? 1 : 0) /* Will we print a decimal point? */ - - ((sign != 0) ? 1 : 0); /* Will we print a sign character? */ - - if (padlen < 0) - padlen = 0; - - /* - * C99 says: "If the `0' and `-' flags both appear, the `0' flag is - * ignored." (7.19.6.1, 6) - */ - if (flags & PRINT_F_MINUS) /* Left justifty. */ - padlen = -padlen; - else if (flags & PRINT_F_ZERO && padlen > 0) { - if (sign != 0) { /* Sign. */ - OUTCHAR(str, *len, size, sign); - sign = 0; - } - while (padlen > 0) { /* Leading zeros. */ - OUTCHAR(str, *len, size, '0'); - padlen--; - } - } - while (padlen > 0) { /* Leading spaces. */ - OUTCHAR(str, *len, size, ' '); - padlen--; - } - if (sign != 0) /* Sign. */ - OUTCHAR(str, *len, size, sign); - while (ipos > 0) { /* Integer part. */ - ipos--; - OUTCHAR(str, *len, size, iconvert[ipos]); - if (separators > 0 && ipos > 0 && ipos % 3 == 0) - printsep(str, len, size); - } - if (emitpoint) { /* Decimal point. */ -#if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT - if (lc->decimal_point != NULL && *lc->decimal_point != '\0') - OUTCHAR(str, *len, size, *lc->decimal_point); - else /* We'll always print some decimal point character. */ -#endif /* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */ - OUTCHAR(str, *len, size, '.'); - } - while (leadfraczeros > 0) { /* Leading fractional part zeros. */ - OUTCHAR(str, *len, size, '0'); - leadfraczeros--; - } - while (fpos > omitcount) { /* The remaining fractional part. */ - fpos--; - OUTCHAR(str, *len, size, fconvert[fpos]); - } - while (epos > 0) { /* Exponent. */ - epos--; - OUTCHAR(str, *len, size, econvert[epos]); - } - while (padlen < 0) { /* Trailing spaces. */ - OUTCHAR(str, *len, size, ' '); - padlen++; - } -} - -static void -printsep(char *str, size_t *len, size_t size) -{ -#if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP - struct lconv *lc = localeconv(); - int i; - - if (lc->thousands_sep != NULL) - for (i = 0; lc->thousands_sep[i] != '\0'; i++) - OUTCHAR(str, *len, size, lc->thousands_sep[i]); - else -#endif /* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */ - OUTCHAR(str, *len, size, ','); -} - -static int -getnumsep(int digits) -{ - int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3; -#if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP - int strln; - struct lconv *lc = localeconv(); - - /* We support an arbitrary separator length (including zero). */ - if (lc->thousands_sep != NULL) { - for (strln = 0; lc->thousands_sep[strln] != '\0'; strln++) - continue; - separators *= strln; - } -#endif /* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */ - return separators; -} - -static int -getexponent(LDOUBLE value) -{ - LDOUBLE tmp = (value >= 0.0) ? value : -value; - int exponent = 0; - - /* - * We check for 99 > exponent > -99 in order to work around possible - * endless loops which could happen (at least) in the second loop (at - * least) if we're called with an infinite value. However, we checked - * for infinity before calling this function using our ISINF() macro, so - * this might be somewhat paranoid. - */ - while (tmp < 1.0 && tmp > 0.0 && --exponent > -99) - tmp *= 10; - while (tmp >= 10.0 && ++exponent < 99) - tmp /= 10; - - return exponent; -} - -static int -convert(UINTMAX_T value, char *buf, size_t size, int base, int caps) -{ - const char *digits = caps ? "0123456789ABCDEF" : "0123456789abcdef"; - size_t pos = 0; - - /* We return an unterminated buffer with the digits in reverse order. */ - do { - buf[pos++] = digits[value % base]; - value /= base; - } while (value != 0 && pos < size); - - return (int)pos; -} - -static UINTMAX_T -cast(LDOUBLE value) -{ - UINTMAX_T result; - - /* - * We check for ">=" and not for ">" because if UINTMAX_MAX cannot be - * represented exactly as an LDOUBLE value (but is less than LDBL_MAX), - * it may be increased to the nearest higher representable value for the - * comparison (cf. C99: 6.3.1.4, 2). It might then equal the LDOUBLE - * value although converting the latter to UINTMAX_T would overflow. - */ - if (value >= UINTMAX_MAX) - return UINTMAX_MAX; - - result = (UINTMAX_T)value; - /* - * At least on NetBSD/sparc64 3.0.2 and 4.99.30, casting long double to - * an integer type converts e.g. 1.9 to 2 instead of 1 (which violates - * the standard). Sigh. - */ - return (result <= value) ? result : result - 1; -} - -static UINTMAX_T -myround(LDOUBLE value) -{ - UINTMAX_T intpart = cast(value); - - return ((value -= intpart) < 0.5) ? intpart : intpart + 1; -} - -static LDOUBLE -mypow10(int exponent) -{ - LDOUBLE result = 1; - - while (exponent > 0) { - result *= 10; - exponent--; - } - while (exponent < 0) { - result /= 10; - exponent++; - } - return result; -} -#endif /* !HAVE_VSNPRINTF */ - -#if !HAVE_VASPRINTF -#if NEED_MYMEMCPY -void * -mymemcpy(void *dst, void *src, size_t len) -{ - const char *from = src; - char *to = dst; - - /* No need for optimization, we use this only to replace va_copy(3). */ - while (len-- > 0) - *to++ = *from++; - return dst; -} -#endif /* NEED_MYMEMCPY */ - -int -rpl_vasprintf(char **ret, const char *format, va_list ap) -{ - size_t size; - int len; - va_list aq; - - VA_COPY(aq, ap); - len = vsnprintf(NULL, 0, format, aq); - VA_END_COPY(aq); - if (len < 0 || (*ret = malloc(size = len + 1)) == NULL) - return -1; - return vsnprintf(*ret, size, format, ap); -} -#endif /* !HAVE_VASPRINTF */ - -#if !HAVE_SNPRINTF -#if HAVE_STDARG_H -int -rpl_snprintf(char *str, size_t size, const char *format, ...) -#else -int -rpl_snprintf(va_alist) va_dcl -#endif /* HAVE_STDARG_H */ -{ -#if !HAVE_STDARG_H - char *str; - size_t size; - char *format; -#endif /* HAVE_STDARG_H */ - va_list ap; - int len; - - VA_START(ap, format); - VA_SHIFT(ap, str, char *); - VA_SHIFT(ap, size, size_t); - VA_SHIFT(ap, format, const char *); - len = vsnprintf(str, size, format, ap); - va_end(ap); - return len; -} -#endif /* !HAVE_SNPRINTF */ - -#if !HAVE_ASPRINTF -#if HAVE_STDARG_H -int -rpl_asprintf(char **ret, const char *format, ...) -#else -int -rpl_asprintf(va_alist) va_dcl -#endif /* HAVE_STDARG_H */ -{ -#if !HAVE_STDARG_H - char **ret; - char *format; -#endif /* HAVE_STDARG_H */ - va_list ap; - int len; - - VA_START(ap, format); - VA_SHIFT(ap, ret, char **); - VA_SHIFT(ap, format, const char *); - len = vasprintf(ret, format, ap); - va_end(ap); - return len; -} -#endif /* !HAVE_ASPRINTF */ -#else /* Dummy declaration to avoid empty translation unit warnings. */ -int main(void); -#endif /* !HAVE_SNPRINTF || !HAVE_VSNPRINTF || !HAVE_ASPRINTF || [...] */ - -#ifdef TEST_SNPRINTF -int -main(void) -{ - const char *float_fmt[] = { - /* "%E" and "%e" formats. */ -#if HAVE_LONG_LONG_INT && !OS_BSD && !OS_IRIX - "%.16e", - "%22.16e", - "%022.16e", - "%-22.16e", - "%#+'022.16e", -#endif /* HAVE_LONG_LONG_INT && !OS_BSD && !OS_IRIX */ - "foo|%#+0123.9E|bar", - "%-123.9e", - "%123.9e", - "%+23.9e", - "%+05.8e", - "%-05.8e", - "%05.8e", - "%+5.8e", - "%-5.8e", - "% 5.8e", - "%5.8e", - "%+4.9e", -#if !OS_LINUX /* glibc sometimes gets these wrong. */ - "%+#010.0e", - "%#10.1e", - "%10.5e", - "% 10.5e", - "%5.0e", - "%5.e", - "%#5.0e", - "%#5.e", - "%3.2e", - "%3.1e", - "%-1.5e", - "%1.5e", - "%01.3e", - "%1.e", - "%.1e", - "%#.0e", - "%+.0e", - "% .0e", - "%.0e", - "%#.e", - "%+.e", - "% .e", - "%.e", - "%4e", - "%e", - "%E", -#endif /* !OS_LINUX */ - /* "%F" and "%f" formats. */ -#if !OS_BSD && !OS_IRIX - "% '022f", - "%+'022f", - "%-'22f", - "%'22f", -#if HAVE_LONG_LONG_INT - "%.16f", - "%22.16f", - "%022.16f", - "%-22.16f", - "%#+'022.16f", -#endif /* HAVE_LONG_LONG_INT */ -#endif /* !OS_BSD && !OS_IRIX */ - "foo|%#+0123.9F|bar", - "%-123.9f", - "%123.9f", - "%+23.9f", - "%+#010.0f", - "%#10.1f", - "%10.5f", - "% 10.5f", - "%+05.8f", - "%-05.8f", - "%05.8f", - "%+5.8f", - "%-5.8f", - "% 5.8f", - "%5.8f", - "%5.0f", - "%5.f", - "%#5.0f", - "%#5.f", - "%+4.9f", - "%3.2f", - "%3.1f", - "%-1.5f", - "%1.5f", - "%01.3f", - "%1.f", - "%.1f", - "%#.0f", - "%+.0f", - "% .0f", - "%.0f", - "%#.f", - "%+.f", - "% .f", - "%.f", - "%4f", - "%f", - "%F", - /* "%G" and "%g" formats. */ -#if !OS_BSD && !OS_IRIX && !OS_LINUX - "% '022g", - "%+'022g", - "%-'22g", - "%'22g", -#if HAVE_LONG_LONG_INT - "%.16g", - "%22.16g", - "%022.16g", - "%-22.16g", - "%#+'022.16g", -#endif /* HAVE_LONG_LONG_INT */ -#endif /* !OS_BSD && !OS_IRIX && !OS_LINUX */ - "foo|%#+0123.9G|bar", - "%-123.9g", - "%123.9g", - "%+23.9g", - "%+05.8g", - "%-05.8g", - "%05.8g", - "%+5.8g", - "%-5.8g", - "% 5.8g", - "%5.8g", - "%+4.9g", -#if !OS_LINUX /* glibc sometimes gets these wrong. */ - "%+#010.0g", - "%#10.1g", - "%10.5g", - "% 10.5g", - "%5.0g", - "%5.g", - "%#5.0g", - "%#5.g", - "%3.2g", - "%3.1g", - "%-1.5g", - "%1.5g", - "%01.3g", - "%1.g", - "%.1g", - "%#.0g", - "%+.0g", - "% .0g", - "%.0g", - "%#.g", - "%+.g", - "% .g", - "%.g", - "%4g", - "%g", - "%G", -#endif /* !OS_LINUX */ - NULL - }; - double float_val[] = { - -4.136, - -134.52, - -5.04030201, - -3410.01234, - -999999.999999, - -913450.29876, - -913450.2, - -91345.2, - -9134.2, - -913.2, - -91.2, - -9.2, - -9.9, - 4.136, - 134.52, - 5.04030201, - 3410.01234, - 999999.999999, - 913450.29876, - 913450.2, - 91345.2, - 9134.2, - 913.2, - 91.2, - 9.2, - 9.9, - 9.96, - 9.996, - 9.9996, - 9.99996, - 9.999996, - 9.9999996, - 9.99999996, - 0.99999996, - 0.99999999, - 0.09999999, - 0.00999999, - 0.00099999, - 0.00009999, - 0.00000999, - 0.00000099, - 0.00000009, - 0.00000001, - 0.0000001, - 0.000001, - 0.00001, - 0.0001, - 0.001, - 0.01, - 0.1, - 1.0, - 1.5, - -1.5, - -1.0, - -0.1, -#if !OS_BSD /* BSD sometimes gets these wrong. */ -#ifdef INFINITY - INFINITY, - -INFINITY, -#endif /* defined(INFINITY) */ -#ifdef NAN - NAN, -#endif /* defined(NAN) */ -#endif /* !OS_BSD */ - 0 - }; - const char *long_fmt[] = { - "foo|%0123ld|bar", -#if !OS_IRIX - "% '0123ld", - "%+'0123ld", - "%-'123ld", - "%'123ld", -#endif /* !OS_IRiX */ - "%123.9ld", - "% 123.9ld", - "%+123.9ld", - "%-123.9ld", - "%0123ld", - "% 0123ld", - "%+0123ld", - "%-0123ld", - "%10.5ld", - "% 10.5ld", - "%+10.5ld", - "%-10.5ld", - "%010ld", - "% 010ld", - "%+010ld", - "%-010ld", - "%4.2ld", - "% 4.2ld", - "%+4.2ld", - "%-4.2ld", - "%04ld", - "% 04ld", - "%+04ld", - "%-04ld", - "%5.5ld", - "%+22.33ld", - "%01.3ld", - "%1.5ld", - "%-1.5ld", - "%44ld", - "%4ld", - "%4.0ld", - "%4.ld", - "%.44ld", - "%.4ld", - "%.0ld", - "%.ld", - "%ld", - NULL - }; - long int long_val[] = { -#ifdef LONG_MAX - LONG_MAX, -#endif /* LONG_MAX */ -#ifdef LONG_MIN - LONG_MIN, -#endif /* LONG_MIN */ - -91340, - 91340, - 341, - 134, - 0203, - -1, - 1, - 0 - }; - const char *ulong_fmt[] = { - /* "%u" formats. */ - "foo|%0123lu|bar", -#if !OS_IRIX - "% '0123lu", - "%+'0123lu", - "%-'123lu", - "%'123lu", -#endif /* !OS_IRiX */ - "%123.9lu", - "% 123.9lu", - "%+123.9lu", - "%-123.9lu", - "%0123lu", - "% 0123lu", - "%+0123lu", - "%-0123lu", - "%5.5lu", - "%+22.33lu", - "%01.3lu", - "%1.5lu", - "%-1.5lu", - "%44lu", - "%lu", - /* "%o" formats. */ - "foo|%#0123lo|bar", - "%#123.9lo", - "%# 123.9lo", - "%#+123.9lo", - "%#-123.9lo", - "%#0123lo", - "%# 0123lo", - "%#+0123lo", - "%#-0123lo", - "%#5.5lo", - "%#+22.33lo", - "%#01.3lo", - "%#1.5lo", - "%#-1.5lo", - "%#44lo", - "%#lo", - "%123.9lo", - "% 123.9lo", - "%+123.9lo", - "%-123.9lo", - "%0123lo", - "% 0123lo", - "%+0123lo", - "%-0123lo", - "%5.5lo", - "%+22.33lo", - "%01.3lo", - "%1.5lo", - "%-1.5lo", - "%44lo", - "%lo", - /* "%X" and "%x" formats. */ - "foo|%#0123lX|bar", - "%#123.9lx", - "%# 123.9lx", - "%#+123.9lx", - "%#-123.9lx", - "%#0123lx", - "%# 0123lx", - "%#+0123lx", - "%#-0123lx", - "%#5.5lx", - "%#+22.33lx", - "%#01.3lx", - "%#1.5lx", - "%#-1.5lx", - "%#44lx", - "%#lx", - "%#lX", - "%123.9lx", - "% 123.9lx", - "%+123.9lx", - "%-123.9lx", - "%0123lx", - "% 0123lx", - "%+0123lx", - "%-0123lx", - "%5.5lx", - "%+22.33lx", - "%01.3lx", - "%1.5lx", - "%-1.5lx", - "%44lx", - "%lx", - "%lX", - NULL - }; - unsigned long int ulong_val[] = { -#ifdef ULONG_MAX - ULONG_MAX, -#endif /* ULONG_MAX */ - 91340, - 341, - 134, - 0203, - 1, - 0 - }; - const char *llong_fmt[] = { - "foo|%0123lld|bar", - "%123.9lld", - "% 123.9lld", - "%+123.9lld", - "%-123.9lld", - "%0123lld", - "% 0123lld", - "%+0123lld", - "%-0123lld", - "%5.5lld", - "%+22.33lld", - "%01.3lld", - "%1.5lld", - "%-1.5lld", - "%44lld", - "%lld", - NULL - }; - LLONG llong_val[] = { -#ifdef LLONG_MAX - LLONG_MAX, -#endif /* LLONG_MAX */ -#ifdef LLONG_MIN - LLONG_MIN, -#endif /* LLONG_MIN */ - -91340, - 91340, - 341, - 134, - 0203, - -1, - 1, - 0 - }; - const char *string_fmt[] = { - "foo|%10.10s|bar", - "%-10.10s", - "%10.10s", - "%10.5s", - "%5.10s", - "%10.1s", - "%1.10s", - "%10.0s", - "%0.10s", - "%-42.5s", - "%2.s", - "%.10s", - "%.1s", - "%.0s", - "%.s", - "%4s", - "%s", - NULL - }; - const char *string_val[] = { - "Hello", - "Hello, world!", - "Sound check: One, two, three.", - "This string is a little longer than the other strings.", - "1", - "", - NULL - }; -#if !OS_SYSV /* SysV uses a different format than we do. */ - const char *pointer_fmt[] = { - "foo|%p|bar", - "%42p", - "%p", - NULL - }; - const char *pointer_val[] = { - *pointer_fmt, - *string_fmt, - *string_val, - NULL - }; -#endif /* !OS_SYSV */ - char buf1[1024], buf2[1024]; - double value, digits = 9.123456789012345678901234567890123456789; - int i, j, r1, r2, failed = 0, num = 0; - -/* - * Use -DTEST_NILS in order to also test the conversion of nil values. Might - * segfault on systems which don't support converting a NULL pointer with "%s" - * and lets some test cases fail against BSD and glibc due to bugs in their - * implementations. - */ -#ifndef TEST_NILS -#define TEST_NILS 0 -#elif TEST_NILS -#undef TEST_NILS -#define TEST_NILS 1 -#endif /* !defined(TEST_NILS) */ -#ifdef TEST -#undef TEST -#endif /* defined(TEST) */ -#define TEST(fmt, val) \ -do { \ - for (i = 0; fmt[i] != NULL; i++) \ - for (j = 0; j == 0 || val[j - TEST_NILS] != 0; j++) { \ - r1 = sprintf(buf1, fmt[i], val[j]); \ - r2 = snprintf(buf2, sizeof(buf2), fmt[i], val[j]); \ - if (strcmp(buf1, buf2) != 0 || r1 != r2) { \ - (void)printf("Results don't match, " \ - "format string: %s\n" \ - "\t sprintf(3): [%s] (%d)\n" \ - "\tsnprintf(3): [%s] (%d)\n", \ - fmt[i], buf1, r1, buf2, r2); \ - failed++; \ - } \ - num++; \ - } \ -} while (/* CONSTCOND */ 0) - -#if HAVE_LOCALE_H - (void)setlocale(LC_ALL, ""); -#endif /* HAVE_LOCALE_H */ - - (void)puts("Testing our snprintf(3) against your system's sprintf(3)."); - TEST(float_fmt, float_val); - TEST(long_fmt, long_val); - TEST(ulong_fmt, ulong_val); - TEST(llong_fmt, llong_val); - TEST(string_fmt, string_val); -#if !OS_SYSV /* SysV uses a different format than we do. */ - TEST(pointer_fmt, pointer_val); -#endif /* !OS_SYSV */ - (void)printf("Result: %d out of %d tests failed.\n", failed, num); - - (void)fputs("Checking how many digits we support: ", stdout); - for (i = 0; i < 100; i++) { - value = pow(10, i) * digits; - (void)sprintf(buf1, "%.1f", value); - (void)snprintf(buf2, sizeof(buf2), "%.1f", value); - if (strcmp(buf1, buf2) != 0) { - (void)printf("apparently %d.\n", i); - break; - } - } - return (failed == 0) ? 0 : 1; -} -#endif /* TEST_SNPRINTF */ - -/* vim: set joinspaces textwidth=80: */ diff --git a/src/stats.c b/src/stats.c deleted file mode 100644 index c06c44b..0000000 --- a/src/stats.c +++ /dev/null @@ -1,721 +0,0 @@ -// Copyright (C) 2002-2004 Andrew Tridgell -// Copyright (C) 2009-2020 Joel Rosdahl -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -// Routines to handle the stats files. The stats file is stored one per cache -// subdirectory to make this more scalable. - -#include "ccache.h" -#include "hashutil.h" - -#include -#include -#include -#include -#include -#include -#include - -extern char *stats_file; -extern struct conf *conf; -extern unsigned lock_staleness_limit; -extern char *primary_config_path; -extern char *secondary_config_path; - -static struct counters *counter_updates; - -#define FLAG_NOZERO 1 // don't zero with the -z option -#define FLAG_ALWAYS 2 // always show, even if zero -#define FLAG_NEVER 4 // never show - -// Returns a formatted version of a statistics value, or NULL if the statistics -// line shouldn't be printed. Caller frees. -typedef char *(*format_fn)(uint64_t value); - -static char *format_size_times_1024(uint64_t size); -static char *format_timestamp(uint64_t timestamp); -static void stats_flush_to_file(const char *sfile, struct counters *updates); - -// Statistics fields in display order. -static struct { - enum stats stat; - const char *id; // for --print-stats - const char *message; // for --show-stats - format_fn format; // NULL -> use plain integer format - unsigned flags; -} stats_info[] = { - { - STATS_ZEROTIMESTAMP, - "stats_zeroed_timestamp", - "stats zeroed", - format_timestamp, - FLAG_ALWAYS - }, - { - STATS_CACHEHIT_DIR, - "direct_cache_hit", - "cache hit (direct)", - NULL, - FLAG_ALWAYS - }, - { - STATS_CACHEHIT_CPP, - "preprocessed_cache_hit", - "cache hit (preprocessed)", - NULL, - FLAG_ALWAYS - }, - { - STATS_CACHEMISS, - "cache_miss", - "cache miss", - NULL, - FLAG_ALWAYS - }, - { - STATS_LINK, - "called_for_link", - "called for link", - NULL, - 0 - }, - { - STATS_PREPROCESSING, - "called_for_preprocessing", - "called for preprocessing", - NULL, - 0 - }, - { - STATS_MULTIPLE, - "multiple_source_files", - "multiple source files", - NULL, - 0 - }, - { - STATS_STDOUT, - "compiler_produced_stdout", - "compiler produced stdout", - NULL, - 0 - }, - { - STATS_NOOUTPUT, - "compiler_produced_no_output", - "compiler produced no output", - NULL, - 0 - }, - { - STATS_EMPTYOUTPUT, - "compiler_produced_empty_output", - "compiler produced empty output", - NULL, - 0 - }, - { - STATS_STATUS, - "compile_failed", - "compile failed", - NULL, - 0 - }, - { - STATS_ERROR, - "internal_error", - "ccache internal error", - NULL, - 0 - }, - { - STATS_PREPROCESSOR, - "preprocessor_error", - "preprocessor error", - NULL, - 0 - }, - { - STATS_CANTUSEPCH, - "could_not_use_precompiled_header", - "can't use precompiled header", - NULL, - 0 - }, - { - STATS_COMPILER, - "could_not_find_compiler", - "couldn't find the compiler", - NULL, - 0 - }, - { - STATS_MISSING, - "missing_cache_file", - "cache file missing", - NULL, - 0 - }, - { - STATS_ARGS, - "bad_compiler_arguments", - "bad compiler arguments", - NULL, - 0 - }, - { - STATS_SOURCELANG, - "unsupported_source_language", - "unsupported source language", - NULL, - 0 - }, - { - STATS_COMPCHECK, - "compiler_check_failed", - "compiler check failed", - NULL, - 0 - }, - { - STATS_CONFTEST, - "autoconf_test", - "autoconf compile/link", - NULL, - 0 - }, - { - STATS_UNSUPPORTED_OPTION, - "unsupported_compiler_option", - "unsupported compiler option", - NULL, - 0 - }, - { - STATS_UNSUPPORTED_DIRECTIVE, - "unsupported_code_directive", - "unsupported code directive", - NULL, - 0 - }, - { - STATS_OUTSTDOUT, - "output_to_stdout", - "output to stdout", - NULL, - 0 - }, - { - STATS_BADOUTPUTFILE, - "bad_output_file", - "could not write to output file", - NULL, - 0 - }, - { - STATS_NOINPUT, - "no_input_file", - "no input file", - NULL, - 0 - }, - { - STATS_BADEXTRAFILE, - "error_hashing_extra_file", - "error hashing extra file", - NULL, - 0 - }, - { - STATS_NUMCLEANUPS, - "cleanups_performed", - "cleanups performed", - NULL, - FLAG_ALWAYS - }, - { - STATS_NUMFILES, - "files_in_cache", - "files in cache", - NULL, - FLAG_NOZERO|FLAG_ALWAYS - }, - { - STATS_TOTALSIZE, - "cache_size_kibibyte", - "cache size", - format_size_times_1024, - FLAG_NOZERO|FLAG_ALWAYS - }, - { - STATS_OBSOLETE_MAXFILES, - "OBSOLETE", - "OBSOLETE", - NULL, - FLAG_NOZERO|FLAG_NEVER - }, - { - STATS_OBSOLETE_MAXSIZE, - "OBSOLETE", - "OBSOLETE", - NULL, - FLAG_NOZERO|FLAG_NEVER - }, - { - STATS_NONE, - NULL, - NULL, - NULL, - 0 - } -}; - -static char * -format_size(uint64_t size) -{ - char *s = format_human_readable_size(size); - reformat(&s, "%11s", s); - return s; -} - -static char * -format_size_times_1024(uint64_t size) -{ - return format_size(size * 1024); -} - -static char * -format_timestamp(uint64_t timestamp) -{ - if (timestamp > 0) { - struct tm tm; - localtime_r((time_t *)×tamp, &tm); - char buffer[100]; - strftime(buffer, sizeof(buffer), "%c", &tm); - return format(" %s", buffer); - } else { - return NULL; - } -} - -// Parse a stats file from a buffer, adding to the counters. -static void -parse_stats(struct counters *counters, const char *buf) -{ - size_t i = 0; - const char *p = buf; - while (true) { - char *p2; - long val = strtol(p, &p2, 10); - if (p2 == p) { - break; - } - if (counters->size < i + 1) { - counters_resize(counters, i + 1); - } - counters->data[i] += val; - i++; - p = p2; - } -} - -// Write out a stats file. -void -stats_write(const char *path, struct counters *counters) -{ - char *tmp_file = format("%s.tmp", path); - FILE *f = create_tmp_file(&tmp_file, "wb"); - for (size_t i = 0; i < counters->size; i++) { - if (fprintf(f, "%u\n", counters->data[i]) < 0) { - fclose(f); - goto error; - } - } - if (fclose(f) == EOF) { - goto error; - } - x_rename(tmp_file, path); - free(tmp_file); - return; - -error: - tmp_unlink(tmp_file); - fatal("Failed to write to %s", tmp_file); -} - -static void -init_counter_updates(void) -{ - if (!counter_updates) { - counter_updates = counters_init(STATS_END); - } -} - -static double -stats_hit_rate(struct counters *counters) -{ - unsigned direct = counters->data[STATS_CACHEHIT_DIR]; - unsigned preprocessed = counters->data[STATS_CACHEHIT_CPP]; - unsigned hit = direct + preprocessed; - unsigned miss = counters->data[STATS_CACHEMISS]; - unsigned total = hit + miss; - return total > 0 ? (100.0 * hit) / total : 0.0; -} - -static void -stats_collect(struct counters *counters, time_t *last_updated) -{ - struct stat st; - unsigned zero_timestamp = 0; - - *last_updated = 0; - - // Add up the stats in each directory. - for (int dir = -1; dir <= 0xF; dir++) { - char *fname; - - if (dir == -1) { - fname = format("%s/stats", conf->cache_dir); - } else { - fname = format("%s/%1x/stats", conf->cache_dir, dir); - } - - counters->data[STATS_ZEROTIMESTAMP] = 0; // Don't add - stats_read(fname, counters); - zero_timestamp = MAX(counters->data[STATS_ZEROTIMESTAMP], zero_timestamp); - if (stat(fname, &st) == 0 && st.st_mtime > *last_updated) { - *last_updated = st.st_mtime; - } - free(fname); - } - - counters->data[STATS_ZEROTIMESTAMP] = zero_timestamp; -} - -// Record that a number of bytes and files have been added to the cache. Size -// is in bytes. -void -stats_update_size(const char *sfile, int64_t size, int files) -{ - struct counters *updates; - if (sfile == stats_file) { - init_counter_updates(); - updates = counter_updates; - } else { - updates = counters_init(STATS_END); - } - updates->data[STATS_NUMFILES] += files; - updates->data[STATS_TOTALSIZE] += size / 1024; - if (sfile != stats_file) { - stats_flush_to_file(sfile, updates); - counters_free(updates); - } -} - -// Read in the stats from one directory and add to the counters. -void -stats_read(const char *sfile, struct counters *counters) -{ - char *data = read_text_file(sfile, 1024); - if (data) { - parse_stats(counters, data); - } - free(data); -} - -// Write counter updates in updates to sfile. -static void -stats_flush_to_file(const char *sfile, struct counters *updates) -{ - assert(conf); - - if (!updates) { - return; - } - - if (conf->disable) { - // Just log result, don't update statistics. - cc_log("Result: disabled"); - return; - } - - if (!str_eq(conf->log_file, "") || conf->debug) { - for (int i = 0; i < STATS_END; ++i) { - if (updates->data[stats_info[i].stat] != 0 - && !(stats_info[i].flags & FLAG_NOZERO)) { - cc_log("Result: %s", stats_info[i].message); - } - } - } - - if (!conf->stats) { - return; - } - - bool should_flush = false; - for (int i = 0; i < STATS_END; ++i) { - if (updates->data[i] > 0) { - should_flush = true; - break; - } - } - if (!should_flush) { - return; - } - - if (!sfile) { - char *stats_dir; - - // A NULL sfile means that we didn't get past calculate_object_hash(), so - // we just choose one of stats files in the 16 subdirectories. - stats_dir = format("%s/%x", conf->cache_dir, hash_from_int(getpid()) % 16); - sfile = format("%s/stats", stats_dir); - free(stats_dir); - } - - if (!lockfile_acquire(sfile, lock_staleness_limit)) { - return; - } - - struct counters *counters = counters_init(STATS_END); - stats_read(sfile, counters); - for (int i = 0; i < STATS_END; ++i) { - counters->data[i] += updates->data[i]; - } - stats_write(sfile, counters); - lockfile_release(sfile); - - char *subdir = dirname(sfile); - bool need_cleanup = false; - - if (conf->max_files != 0 - && counters->data[STATS_NUMFILES] > conf->max_files / 16) { - cc_log("Need to clean up %s since it holds %u files (limit: %u files)", - subdir, - counters->data[STATS_NUMFILES], - conf->max_files / 16); - need_cleanup = true; - } - if (conf->max_size != 0 - && counters->data[STATS_TOTALSIZE] > conf->max_size / 1024 / 16) { - cc_log("Need to clean up %s since it holds %u KiB (limit: %lu KiB)", - subdir, - counters->data[STATS_TOTALSIZE], - (unsigned long)conf->max_size / 1024 / 16); - need_cleanup = true; - } - - if (need_cleanup) { - clean_up_dir(conf, subdir, conf->limit_multiple); - } - - free(subdir); - counters_free(counters); -} - -// Write counter updates in counter_updates to disk. -void -stats_flush(void) -{ - stats_flush_to_file(stats_file, counter_updates); - counters_free(counter_updates); - counter_updates = NULL; -} - -// Update a normal stat. -void -stats_update(enum stats stat) -{ - assert(stat > STATS_NONE && stat < STATS_END); - init_counter_updates(); - counter_updates->data[stat]++; -} - -// Get the pending update of a counter value. -unsigned -stats_get_pending(enum stats stat) -{ - init_counter_updates(); - return counter_updates->data[stat]; -} - -// Sum and display the total stats for all cache dirs. -void -stats_summary(void) -{ - assert(conf); - - struct counters *counters = counters_init(STATS_END); - time_t last_updated; - stats_collect(counters, &last_updated); - - printf("cache directory %s\n", conf->cache_dir); - printf("primary config %s\n", - primary_config_path ? primary_config_path : ""); - printf("secondary config (readonly) %s\n", - secondary_config_path ? secondary_config_path : ""); - if (last_updated > 0) { - struct tm tm; - localtime_r(&last_updated, &tm); - char timestamp[100]; - strftime(timestamp, sizeof(timestamp), "%c", &tm); - printf("stats updated %s\n", timestamp); - } - - // ...and display them. - for (int i = 0; stats_info[i].message; i++) { - enum stats stat = stats_info[i].stat; - - if (stats_info[i].flags & FLAG_NEVER) { - continue; - } - if (counters->data[stat] == 0 && !(stats_info[i].flags & FLAG_ALWAYS)) { - continue; - } - - char *value; - if (stats_info[i].format) { - value = stats_info[i].format(counters->data[stat]); - } else { - value = format("%8u", counters->data[stat]); - } - if (value) { - printf("%-31s %s\n", stats_info[i].message, value); - free(value); - } - - if (stat == STATS_CACHEMISS) { - double percent = stats_hit_rate(counters); - printf("cache hit rate %6.2f %%\n", percent); - } - } - - if (conf->max_files != 0) { - printf("max files %8u\n", conf->max_files); - } - if (conf->max_size != 0) { - char *value = format_size(conf->max_size); - printf("max cache size %s\n", value); - free(value); - } - - counters_free(counters); -} - -// Print machine-parsable (tab-separated) statistics counters. -void -stats_print(void) -{ - assert(conf); - - struct counters *counters = counters_init(STATS_END); - time_t last_updated; - stats_collect(counters, &last_updated); - - printf("stats_updated_timestamp\t%llu\n", (unsigned long long)last_updated); - - for (int i = 0; stats_info[i].message; i++) { - if (!(stats_info[i].flags & FLAG_NEVER)) { - printf("%s\t%u\n", stats_info[i].id, counters->data[stats_info[i].stat]); - } - } - - counters_free(counters); -} - -// Zero all the stats structures. -void -stats_zero(void) -{ - assert(conf); - - char *fname = format("%s/stats", conf->cache_dir); - x_unlink(fname); - free(fname); - - time_t timestamp = time(NULL); - - for (int dir = 0; dir <= 0xF; dir++) { - struct counters *counters = counters_init(STATS_END); - struct stat st; - fname = format("%s/%1x/stats", conf->cache_dir, dir); - if (stat(fname, &st) != 0) { - // No point in trying to reset the stats file if it doesn't exist. - free(fname); - continue; - } - if (lockfile_acquire(fname, lock_staleness_limit)) { - stats_read(fname, counters); - for (unsigned i = 0; stats_info[i].message; i++) { - if (!(stats_info[i].flags & FLAG_NOZERO)) { - counters->data[stats_info[i].stat] = 0; - } - } - counters->data[STATS_ZEROTIMESTAMP] = timestamp; - stats_write(fname, counters); - lockfile_release(fname); - } - counters_free(counters); - free(fname); - } -} - -// Get the per-directory limits. -void -stats_get_obsolete_limits(const char *dir, unsigned *maxfiles, - uint64_t *maxsize) -{ - struct counters *counters = counters_init(STATS_END); - char *sname = format("%s/stats", dir); - stats_read(sname, counters); - *maxfiles = counters->data[STATS_OBSOLETE_MAXFILES]; - *maxsize = (uint64_t)counters->data[STATS_OBSOLETE_MAXSIZE] * 1024; - free(sname); - counters_free(counters); -} - -// Set the per-directory sizes. -void -stats_set_sizes(const char *dir, unsigned num_files, uint64_t total_size) -{ - struct counters *counters = counters_init(STATS_END); - char *statsfile = format("%s/stats", dir); - if (lockfile_acquire(statsfile, lock_staleness_limit)) { - stats_read(statsfile, counters); - counters->data[STATS_NUMFILES] = num_files; - counters->data[STATS_TOTALSIZE] = total_size / 1024; - stats_write(statsfile, counters); - lockfile_release(statsfile); - } - free(statsfile); - counters_free(counters); -} - -// Count directory cleanup run. -void -stats_add_cleanup(const char *dir, unsigned count) -{ - struct counters *counters = counters_init(STATS_END); - char *statsfile = format("%s/stats", dir); - if (lockfile_acquire(statsfile, lock_staleness_limit)) { - stats_read(statsfile, counters); - counters->data[STATS_NUMCLEANUPS] += count; - stats_write(statsfile, counters); - lockfile_release(statsfile); - } - free(statsfile); - counters_free(counters); -} diff --git a/src/system.h b/src/system.h deleted file mode 100644 index 86d7f30..0000000 --- a/src/system.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (C) 2010-2020 Joel Rosdahl -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3 of the License, or (at your option) -// any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -// more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., 51 -// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -#ifndef CCACHE_SYSTEM_H -#define CCACHE_SYSTEM_H - -#include "config.h" - -#ifdef __clang__ -#pragma clang diagnostic push -#if __has_warning("-Wreserved-id-macro") -#pragma clang diagnostic ignored "-Wreserved-id-macro" -#endif -#endif -#ifndef _FILE_OFFSET_BITS -#define _FILE_OFFSET_BITS 64 -#endif -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#include -#ifdef HAVE_SYS_MMAN_H -#include -#endif -#include -#include -#ifdef HAVE_SYS_WAIT_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// AIX/PASE does not properly define usleep within its headers. However, the -// function is available in libc.a. This extern define ensures that it is -// usable within the ccache code base. -#ifdef _AIX -extern int usleep(useconds_t); -#endif - -extern char **environ; - -#ifndef ESTALE -#define ESTALE -1 -#endif - -#if !HAVE_VSNPRINTF - int rpl_vsnprintf(char *, size_t, const char *, va_list); - #define vsnprintf rpl_vsnprintf -#endif -#if !HAVE_SNPRINTF - int rpl_snprintf(char *, size_t, const char *, ...); - #define snprintf rpl_snprintf -#endif -#if !HAVE_VASPRINTF - int rpl_vasprintf(char **, const char *, va_list); - #define vasprintf rpl_vasprintf -#endif -#if !HAVE_ASPRINTF - int rpl_asprintf(char **, const char *, ...); - #define asprintf rpl_asprintf -#endif - -#endif // CCACHE_SYSTEM_H diff --git a/src/system.hpp b/src/system.hpp new file mode 100644 index 0000000..79d07ef --- /dev/null +++ b/src/system.hpp @@ -0,0 +1,190 @@ +// Copyright (C) 2010-2020 Joel Rosdahl and other contributors +// +// See doc/AUTHORS.adoc for a complete list of contributors. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., 51 +// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#pragma once + +#ifdef __MINGW32__ +# define __USE_MINGW_ANSI_STDIO 1 +# define __STDC_FORMAT_MACROS 1 +#endif + +#include "config.h" + +#ifdef HAVE_SYS_FILE_H +# include +#endif + +#ifdef HAVE_SYS_MMAN_H +# include +#endif +#include +#include +#ifdef HAVE_SYS_WAIT_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_DIRENT_H +# include +#endif + +#include + +#ifdef HAVE_STRINGS_H +# include +#endif + +#ifdef HAVE_UNISTD_H +# include +#endif + +#ifdef HAVE_UTIME_H +# include +#elif defined(HAVE_SYS_UTIME_H) +# include +#endif + +#ifdef HAVE_VARARGS_H +# include +#endif + +// AIX/PASE does not properly define usleep within its headers. However, the +// function is available in libc.a. This extern define ensures that it is +// usable within the ccache code base. +#ifdef _AIX +extern int usleep(useconds_t); +#endif + +#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) + +// Buffer size for I/O operations. Should be a multiple of 4 KiB. +const size_t READ_BUFFER_SIZE = 65536; + +#ifndef ESTALE +# define ESTALE -1 +#endif + +#ifdef _WIN32 +# ifndef _WIN32_WINNT +// _WIN32_WINNT is set in the generated header config.h +# error _WIN32_WINNT is undefined +# endif + +# ifdef _MSC_VER +typedef int mode_t; +typedef int pid_t; +# endif + +# ifndef __MINGW32__ +typedef int64_t ssize_t; +# endif + +// Defined in Win32Util.cpp +void usleep(int64_t usec); +struct tm* localtime_r(time_t* _clock, struct tm* _result); + +# ifdef _MSC_VER +int gettimeofday(struct timeval* tp, struct timezone* tzp); +int asprintf(char** strp, const char* fmt, ...); +# endif + +// From: +// http://mesos.apache.org/api/latest/c++/3rdparty_2stout_2include_2stout_2windows_8hpp_source.html +# ifdef _MSC_VER +const mode_t S_IRUSR = mode_t(_S_IREAD); +const mode_t S_IWUSR = mode_t(_S_IWRITE); +# endif + +// From https://stackoverflow.com/a/62371749/262458 +# define _CRT_INTERNAL_NONSTDC_NAMES 1 +# include +# if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG) +# define S_ISREG(m) (((m)&S_IFMT) == S_IFREG) +# endif +# if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR) +# define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) +# endif + +# include +# include +# include +# define NOMINMAX 1 +# include +# define mkdir(a, b) _mkdir(a) +# define link(src, dst) (CreateHardLink(dst, src, nullptr) ? 0 : -1) +# define execv(a, b) win32execute(a, b, 0, -1, -1) +# define strncasecmp _strnicmp +# define strcasecmp _stricmp + +# ifdef _MSC_VER +# define PATH_MAX MAX_PATH +# endif + +# ifdef _MSC_VER +# define DLLIMPORT __declspec(dllimport) +# else +# define DLLIMPORT +# endif + +# define STDIN_FILENO 0 +# define STDOUT_FILENO 1 +# define STDERR_FILENO 2 +# define DIR_DELIM_CH '\\' +# define PATH_DELIM ";" +#else +# define DLLIMPORT +# define DIR_DELIM_CH '/' +# define PATH_DELIM ":" +#endif + +DLLIMPORT extern char** environ; + +// Work with silly DOS binary open. +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +#ifdef HAVE_SYS_MMAN_H +# define INODE_CACHE_SUPPORTED +#endif + +// Workaround for missing std::is_trivially_copyable in GCC < 5. +#if __GNUG__ && __GNUC__ < 5 +# define IS_TRIVIALLY_COPYABLE(T) __has_trivial_copy(T) +#else +# define IS_TRIVIALLY_COPYABLE(T) std::is_trivially_copyable::value +#endif + +// GCC version of a couple of standard C++ attributes +#ifdef __GNUC__ +# define nodiscard gnu::warn_unused_result +# define maybe_unused gnu::unused +#endif diff --git a/src/third_party/.clang-tidy b/src/third_party/.clang-tidy new file mode 100644 index 0000000..d2f78e1 --- /dev/null +++ b/src/third_party/.clang-tidy @@ -0,0 +1,9 @@ +# It's currently not possible to disable all checkers within a subdirectory via +# config file... So just pick a fast check that will never fail. +--- +Checks: '-*,readability-function-size' +CheckOptions: + - key: readability-function-size.LineThreshold + value: 99999999 +... + diff --git a/src/third_party/CMakeLists.txt b/src/third_party/CMakeLists.txt new file mode 100644 index 0000000..3ed7158 --- /dev/null +++ b/src/third_party/CMakeLists.txt @@ -0,0 +1,76 @@ +set(third_party_source_files base32hex.c format.cpp xxhash.c) + +if(NOT MSVC) + list(APPEND third_party_source_files getopt_long.c) +else() + list(APPEND third_party_source_files win32/getopt.c) + target_compile_definitions(third_party_lib PUBLIC -DSTATIC_GETOPT) +endif() + +add_library(third_party_lib STATIC ${third_party_source_files}) + +if(ENABLE_TRACING) + target_sources(third_party_lib PRIVATE minitrace.c) +endif() + +set(xxhdispatchtest [=[ +#include "xxh_x86dispatch.c" + +int main() +{ + XXH3_64bits_dispatch("foo", 3); + return 1; +} +]=]) + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/xxhdispatchtest.c" "${xxhdispatchtest}") + +try_compile(USE_XXH_DISPATCH ${CMAKE_CURRENT_BINARY_DIR} + "${CMAKE_CURRENT_BINARY_DIR}/xxhdispatchtest.c" + CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${CMAKE_CURRENT_SOURCE_DIR}" + COMPILE_DEFINITIONS "-DXXH_STATIC_LINKING_ONLY") + +target_compile_definitions(third_party_lib INTERFACE "-DXXH_STATIC_LINKING_ONLY") +if(USE_XXH_DISPATCH) + target_sources(third_party_lib PRIVATE xxh_x86dispatch.c) + target_compile_definitions(third_party_lib INTERFACE "-DUSE_XXH_DISPATCH") +endif() + +# Treat third party headers as system files (no warning for those headers). +target_include_directories( + third_party_lib + PRIVATE ${CMAKE_BINARY_DIR} . SYSTEM) + +target_link_libraries(third_party_lib PRIVATE standard_settings) +target_link_libraries(third_party_lib INTERFACE blake3) + +# These warnings are enabled by default even without e.g. -Wall, but we don't +# want them in third_party. +if(CMAKE_CXX_COMPILER_ID MATCHES "^GNU|Clang$") + target_compile_options( + third_party_lib + PRIVATE + $<$:-Wno-implicit-function-declaration + -Wno-int-conversion>) +endif() + +if(CMAKE_C_COMPILER_ID STREQUAL "GNU") + target_compile_options( + third_party_lib + PRIVATE $<$:-Wno-attributes>) +endif() + +# Silence warning from winbase.h due to /Zc:preprocessor. +if(MSVC) + target_compile_options( + third_party_lib + PRIVATE /wd5105) +endif() + +# The headers are included from the rest of the project, so turn off warnings as +# required. +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(third_party_lib INTERFACE -Wno-shadow) +endif() + +add_subdirectory(blake3) diff --git a/src/third_party/base32hex.c b/src/third_party/base32hex.c new file mode 100644 index 0000000..4c2275d --- /dev/null +++ b/src/third_party/base32hex.c @@ -0,0 +1,79 @@ +/* (C) 2012 Peter Conrad + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "base32hex.h" + +#define to_32hex(c) ((c) < 10 ? (c) + '0' : (c) + 'a' - 10) + +/* out must point to a buffer of at least (len * 8 / 5) + 1 bytes. + * Encoded string is *not* padded. + * See RFC-4648. This implementation produces lowercase hex characters. + * Returns length of encoded string. + */ +unsigned int base32hex(char *out, const uint8_t *in, unsigned int len) { +unsigned int buf = 0, bits = 0; +char *x = out; + + while (len-- > 0) { + buf <<= 8; + buf |= *in++; + bits += 8; + while (bits >= 5) { + char c = (buf >> (bits - 5)) & 0x1f; + *x++ = to_32hex(c); + bits -= 5; + } + } + if (bits > 0) { + char c = (buf << (5 - bits)) & 0x1f; + *x++ = to_32hex(c); + } + return x - out; +} + +#ifdef TEST +#include +#include +#include + +static void test(char *in, char *expected, int explen) { +char buf[255]; +int r; + + if ((r = base32hex(buf, in, strlen(in))) != explen) { + printf("Failed: b32h('%s') yields %d chars (expected %d)\n", + in, r, explen); + exit(1); + } + if (strncmp(buf, expected, r)) { + buf[r] = 0; + printf("Failed: b32h('%s') = '%s' (expected %s)\n", + in, buf, expected); + exit(1); + } +} + +int main(int argc, char **argv) { + test("", "", 0); + test("f", "co", 2); + test("fo", "cpng", 4); + test("foo", "cpnmu", 5); + test("foob", "cpnmuog", 7); + test("fooba", "cpnmuoj1", 8); + test("foobar", "cpnmuoj1e8", 10); + printf("Success!\n"); +} + +#endif diff --git a/src/third_party/base32hex.h b/src/third_party/base32hex.h new file mode 100644 index 0000000..8386719 --- /dev/null +++ b/src/third_party/base32hex.h @@ -0,0 +1,23 @@ +/* (C) 2012 Peter Conrad + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef BASE32_HEX_H +#define BASE32_HEX_H + +#include + +extern unsigned int base32hex(char *out, const uint8_t *in, unsigned int len); + +#endif diff --git a/src/third_party/blake3/CMakeLists.txt b/src/third_party/blake3/CMakeLists.txt new file mode 100644 index 0000000..ed09d58 --- /dev/null +++ b/src/third_party/blake3/CMakeLists.txt @@ -0,0 +1,41 @@ +add_library(blake3 STATIC blake3.c blake3_dispatch.c blake3_portable.c) + +target_link_libraries(blake3 PRIVATE standard_settings) + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SIZEOF_VOID_P EQUAL 8) + set(blake_source_type asm) + set(blake_suffix "_x86-64_unix.S") +else() + set(blake_source_type c) + set(blake_suffix ".c") +endif() + +include(CheckAsmCompilerFlag) +include(CheckCCompilerFlag) + +function(add_source_if_enabled feature compile_flags) + string(TOUPPER "have_${blake_source_type}_${feature}" have_feature) + if(${blake_source_type} STREQUAL "asm") + check_asm_compiler_flag(${compile_flags} ${have_feature}) + else() + check_c_compiler_flag(${compile_flags} ${have_feature}) + endif() + + if(${have_feature}) + target_sources(blake3 PRIVATE blake3_${feature}${blake_suffix}) + set_property( + SOURCE blake3_${feature}${blake_suffix} + APPEND PROPERTY COMPILE_FLAGS ${compile_flags}) + else() + string(TOUPPER "blake3_no_${feature}" no_feature) + target_compile_definitions(blake3 PRIVATE ${no_feature}) + endif() +endfunction() + +add_source_if_enabled(sse2 "-msse2") +add_source_if_enabled(sse41 "-msse4.1") +add_source_if_enabled(avx2 "-mavx2") +add_source_if_enabled(avx512 "-mavx512f -mavx512vl") + +# TODO: how to detect ARM NEON support? +# If NEON, define BLAKE3_USE_NEON and build blake3_neon.c diff --git a/src/third_party/blake3/blake3.c b/src/third_party/blake3/blake3.c new file mode 100644 index 0000000..741a76d --- /dev/null +++ b/src/third_party/blake3/blake3.c @@ -0,0 +1,603 @@ +#include +#include +#include + +#include "blake3.h" +#include "blake3_impl.h" + +INLINE void chunk_state_init(blake3_chunk_state *self, const uint32_t key[8], + uint8_t flags) { + memcpy(self->cv, key, BLAKE3_KEY_LEN); + self->chunk_counter = 0; + memset(self->buf, 0, BLAKE3_BLOCK_LEN); + self->buf_len = 0; + self->blocks_compressed = 0; + self->flags = flags; +} + +INLINE void chunk_state_reset(blake3_chunk_state *self, const uint32_t key[8], + uint64_t chunk_counter) { + memcpy(self->cv, key, BLAKE3_KEY_LEN); + self->chunk_counter = chunk_counter; + self->blocks_compressed = 0; + memset(self->buf, 0, BLAKE3_BLOCK_LEN); + self->buf_len = 0; +} + +INLINE size_t chunk_state_len(const blake3_chunk_state *self) { + return (BLAKE3_BLOCK_LEN * (size_t)self->blocks_compressed) + + ((size_t)self->buf_len); +} + +INLINE size_t chunk_state_fill_buf(blake3_chunk_state *self, + const uint8_t *input, size_t input_len) { + size_t take = BLAKE3_BLOCK_LEN - ((size_t)self->buf_len); + if (take > input_len) { + take = input_len; + } + uint8_t *dest = self->buf + ((size_t)self->buf_len); + memcpy(dest, input, take); + self->buf_len += (uint8_t)take; + return take; +} + +INLINE uint8_t chunk_state_maybe_start_flag(const blake3_chunk_state *self) { + if (self->blocks_compressed == 0) { + return CHUNK_START; + } else { + return 0; + } +} + +typedef struct { + uint32_t input_cv[8]; + uint64_t counter; + uint8_t block[BLAKE3_BLOCK_LEN]; + uint8_t block_len; + uint8_t flags; +} output_t; + +INLINE output_t make_output(const uint32_t input_cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, + uint8_t flags) { + output_t ret; + memcpy(ret.input_cv, input_cv, 32); + memcpy(ret.block, block, BLAKE3_BLOCK_LEN); + ret.block_len = block_len; + ret.counter = counter; + ret.flags = flags; + return ret; +} + +// Chaining values within a given chunk (specifically the compress_in_place +// interface) are represented as words. This avoids unnecessary bytes<->words +// conversion overhead in the portable implementation. However, the hash_many +// interface handles both user input and parent node blocks, so it accepts +// bytes. For that reason, chaining values in the CV stack are represented as +// bytes. +INLINE void output_chaining_value(const output_t *self, uint8_t cv[32]) { + uint32_t cv_words[8]; + memcpy(cv_words, self->input_cv, 32); + blake3_compress_in_place(cv_words, self->block, self->block_len, + self->counter, self->flags); + store_cv_words(cv, cv_words); +} + +INLINE void output_root_bytes(const output_t *self, uint64_t seek, uint8_t *out, + size_t out_len) { + uint64_t output_block_counter = seek / 64; + size_t offset_within_block = seek % 64; + uint8_t wide_buf[64]; + while (out_len > 0) { + blake3_compress_xof(self->input_cv, self->block, self->block_len, + output_block_counter, self->flags | ROOT, wide_buf); + size_t available_bytes = 64 - offset_within_block; + size_t memcpy_len; + if (out_len > available_bytes) { + memcpy_len = available_bytes; + } else { + memcpy_len = out_len; + } + memcpy(out, wide_buf + offset_within_block, memcpy_len); + out += memcpy_len; + out_len -= memcpy_len; + output_block_counter += 1; + offset_within_block = 0; + } +} + +INLINE void chunk_state_update(blake3_chunk_state *self, const uint8_t *input, + size_t input_len) { + if (self->buf_len > 0) { + size_t take = chunk_state_fill_buf(self, input, input_len); + input += take; + input_len -= take; + if (input_len > 0) { + blake3_compress_in_place( + self->cv, self->buf, BLAKE3_BLOCK_LEN, self->chunk_counter, + self->flags | chunk_state_maybe_start_flag(self)); + self->blocks_compressed += 1; + self->buf_len = 0; + memset(self->buf, 0, BLAKE3_BLOCK_LEN); + } + } + + while (input_len > BLAKE3_BLOCK_LEN) { + blake3_compress_in_place(self->cv, input, BLAKE3_BLOCK_LEN, + self->chunk_counter, + self->flags | chunk_state_maybe_start_flag(self)); + self->blocks_compressed += 1; + input += BLAKE3_BLOCK_LEN; + input_len -= BLAKE3_BLOCK_LEN; + } + + size_t take = chunk_state_fill_buf(self, input, input_len); + input += take; + input_len -= take; +} + +INLINE output_t chunk_state_output(const blake3_chunk_state *self) { + uint8_t block_flags = + self->flags | chunk_state_maybe_start_flag(self) | CHUNK_END; + return make_output(self->cv, self->buf, self->buf_len, self->chunk_counter, + block_flags); +} + +INLINE output_t parent_output(const uint8_t block[BLAKE3_BLOCK_LEN], + const uint32_t key[8], uint8_t flags) { + return make_output(key, block, BLAKE3_BLOCK_LEN, 0, flags | PARENT); +} + +// Given some input larger than one chunk, return the number of bytes that +// should go in the left subtree. This is the largest power-of-2 number of +// chunks that leaves at least 1 byte for the right subtree. +INLINE size_t left_len(size_t content_len) { + // Subtract 1 to reserve at least one byte for the right side. content_len + // should always be greater than BLAKE3_CHUNK_LEN. + size_t full_chunks = (content_len - 1) / BLAKE3_CHUNK_LEN; + return round_down_to_power_of_2(full_chunks) * BLAKE3_CHUNK_LEN; +} + +// Use SIMD parallelism to hash up to MAX_SIMD_DEGREE chunks at the same time +// on a single thread. Write out the chunk chaining values and return the +// number of chunks hashed. These chunks are never the root and never empty; +// those cases use a different codepath. +INLINE size_t compress_chunks_parallel(const uint8_t *input, size_t input_len, + const uint32_t key[8], + uint64_t chunk_counter, uint8_t flags, + uint8_t *out) { +#if defined(BLAKE3_TESTING) + assert(0 < input_len); + assert(input_len <= MAX_SIMD_DEGREE * BLAKE3_CHUNK_LEN); +#endif + + const uint8_t *chunks_array[MAX_SIMD_DEGREE]; + size_t input_position = 0; + size_t chunks_array_len = 0; + while (input_len - input_position >= BLAKE3_CHUNK_LEN) { + chunks_array[chunks_array_len] = &input[input_position]; + input_position += BLAKE3_CHUNK_LEN; + chunks_array_len += 1; + } + + blake3_hash_many(chunks_array, chunks_array_len, + BLAKE3_CHUNK_LEN / BLAKE3_BLOCK_LEN, key, chunk_counter, + true, flags, CHUNK_START, CHUNK_END, out); + + // Hash the remaining partial chunk, if there is one. Note that the empty + // chunk (meaning the empty message) is a different codepath. + if (input_len > input_position) { + uint64_t counter = chunk_counter + (uint64_t)chunks_array_len; + blake3_chunk_state chunk_state; + chunk_state_init(&chunk_state, key, flags); + chunk_state.chunk_counter = counter; + chunk_state_update(&chunk_state, &input[input_position], + input_len - input_position); + output_t output = chunk_state_output(&chunk_state); + output_chaining_value(&output, &out[chunks_array_len * BLAKE3_OUT_LEN]); + return chunks_array_len + 1; + } else { + return chunks_array_len; + } +} + +// Use SIMD parallelism to hash up to MAX_SIMD_DEGREE parents at the same time +// on a single thread. Write out the parent chaining values and return the +// number of parents hashed. (If there's an odd input chaining value left over, +// return it as an additional output.) These parents are never the root and +// never empty; those cases use a different codepath. +INLINE size_t compress_parents_parallel(const uint8_t *child_chaining_values, + size_t num_chaining_values, + const uint32_t key[8], uint8_t flags, + uint8_t *out) { +#if defined(BLAKE3_TESTING) + assert(2 <= num_chaining_values); + assert(num_chaining_values <= 2 * MAX_SIMD_DEGREE_OR_2); +#endif + + const uint8_t *parents_array[MAX_SIMD_DEGREE_OR_2]; + size_t parents_array_len = 0; + while (num_chaining_values - (2 * parents_array_len) >= 2) { + parents_array[parents_array_len] = + &child_chaining_values[2 * parents_array_len * BLAKE3_OUT_LEN]; + parents_array_len += 1; + } + + blake3_hash_many(parents_array, parents_array_len, 1, key, + 0, // Parents always use counter 0. + false, flags | PARENT, + 0, // Parents have no start flags. + 0, // Parents have no end flags. + out); + + // If there's an odd child left over, it becomes an output. + if (num_chaining_values > 2 * parents_array_len) { + memcpy(&out[parents_array_len * BLAKE3_OUT_LEN], + &child_chaining_values[2 * parents_array_len * BLAKE3_OUT_LEN], + BLAKE3_OUT_LEN); + return parents_array_len + 1; + } else { + return parents_array_len; + } +} + +// The wide helper function returns (writes out) an array of chaining values +// and returns the length of that array. The number of chaining values returned +// is the dyanmically detected SIMD degree, at most MAX_SIMD_DEGREE. Or fewer, +// if the input is shorter than that many chunks. The reason for maintaining a +// wide array of chaining values going back up the tree, is to allow the +// implementation to hash as many parents in parallel as possible. +// +// As a special case when the SIMD degree is 1, this function will still return +// at least 2 outputs. This guarantees that this function doesn't perform the +// root compression. (If it did, it would use the wrong flags, and also we +// wouldn't be able to implement exendable ouput.) Note that this function is +// not used when the whole input is only 1 chunk long; that's a different +// codepath. +// +// Why not just have the caller split the input on the first update(), instead +// of implementing this special rule? Because we don't want to limit SIMD or +// multi-threading parallelism for that update(). +static size_t blake3_compress_subtree_wide(const uint8_t *input, + size_t input_len, + const uint32_t key[8], + uint64_t chunk_counter, + uint8_t flags, uint8_t *out) { + // Note that the single chunk case does *not* bump the SIMD degree up to 2 + // when it is 1. If this implementation adds multi-threading in the future, + // this gives us the option of multi-threading even the 2-chunk case, which + // can help performance on smaller platforms. + if (input_len <= blake3_simd_degree() * BLAKE3_CHUNK_LEN) { + return compress_chunks_parallel(input, input_len, key, chunk_counter, flags, + out); + } + + // With more than simd_degree chunks, we need to recurse. Start by dividing + // the input into left and right subtrees. (Note that this is only optimal + // as long as the SIMD degree is a power of 2. If we ever get a SIMD degree + // of 3 or something, we'll need a more complicated strategy.) + size_t left_input_len = left_len(input_len); + size_t right_input_len = input_len - left_input_len; + const uint8_t *right_input = &input[left_input_len]; + uint64_t right_chunk_counter = + chunk_counter + (uint64_t)(left_input_len / BLAKE3_CHUNK_LEN); + + // Make space for the child outputs. Here we use MAX_SIMD_DEGREE_OR_2 to + // account for the special case of returning 2 outputs when the SIMD degree + // is 1. + uint8_t cv_array[2 * MAX_SIMD_DEGREE_OR_2 * BLAKE3_OUT_LEN]; + size_t degree = blake3_simd_degree(); + if (left_input_len > BLAKE3_CHUNK_LEN && degree == 1) { + // The special case: We always use a degree of at least two, to make + // sure there are two outputs. Except, as noted above, at the chunk + // level, where we allow degree=1. (Note that the 1-chunk-input case is + // a different codepath.) + degree = 2; + } + uint8_t *right_cvs = &cv_array[degree * BLAKE3_OUT_LEN]; + + // Recurse! If this implementation adds multi-threading support in the + // future, this is where it will go. + size_t left_n = blake3_compress_subtree_wide(input, left_input_len, key, + chunk_counter, flags, cv_array); + size_t right_n = blake3_compress_subtree_wide( + right_input, right_input_len, key, right_chunk_counter, flags, right_cvs); + + // The special case again. If simd_degree=1, then we'll have left_n=1 and + // right_n=1. Rather than compressing them into a single output, return + // them directly, to make sure we always have at least two outputs. + if (left_n == 1) { + memcpy(out, cv_array, 2 * BLAKE3_OUT_LEN); + return 2; + } + + // Otherwise, do one layer of parent node compression. + size_t num_chaining_values = left_n + right_n; + return compress_parents_parallel(cv_array, num_chaining_values, key, flags, + out); +} + +// Hash a subtree with compress_subtree_wide(), and then condense the resulting +// list of chaining values down to a single parent node. Don't compress that +// last parent node, however. Instead, return its message bytes (the +// concatenated chaining values of its children). This is necessary when the +// first call to update() supplies a complete subtree, because the topmost +// parent node of that subtree could end up being the root. It's also necessary +// for extended output in the general case. +// +// As with compress_subtree_wide(), this function is not used on inputs of 1 +// chunk or less. That's a different codepath. +INLINE void compress_subtree_to_parent_node( + const uint8_t *input, size_t input_len, const uint32_t key[8], + uint64_t chunk_counter, uint8_t flags, uint8_t out[2 * BLAKE3_OUT_LEN]) { +#if defined(BLAKE3_TESTING) + assert(input_len > BLAKE3_CHUNK_LEN); +#endif + + uint8_t cv_array[MAX_SIMD_DEGREE_OR_2 * BLAKE3_OUT_LEN]; + size_t num_cvs = blake3_compress_subtree_wide(input, input_len, key, + chunk_counter, flags, cv_array); + + // If MAX_SIMD_DEGREE is greater than 2 and there's enough input, + // compress_subtree_wide() returns more than 2 chaining values. Condense + // them into 2 by forming parent nodes repeatedly. + uint8_t out_array[MAX_SIMD_DEGREE_OR_2 * BLAKE3_OUT_LEN / 2]; + while (num_cvs > 2) { + num_cvs = + compress_parents_parallel(cv_array, num_cvs, key, flags, out_array); + memcpy(cv_array, out_array, num_cvs * BLAKE3_OUT_LEN); + } + memcpy(out, cv_array, 2 * BLAKE3_OUT_LEN); +} + +INLINE void hasher_init_base(blake3_hasher *self, const uint32_t key[8], + uint8_t flags) { + memcpy(self->key, key, BLAKE3_KEY_LEN); + chunk_state_init(&self->chunk, key, flags); + self->cv_stack_len = 0; +} + +void blake3_hasher_init(blake3_hasher *self) { hasher_init_base(self, IV, 0); } + +void blake3_hasher_init_keyed(blake3_hasher *self, + const uint8_t key[BLAKE3_KEY_LEN]) { + uint32_t key_words[8]; + load_key_words(key, key_words); + hasher_init_base(self, key_words, KEYED_HASH); +} + +void blake3_hasher_init_derive_key_raw(blake3_hasher *self, const void *context, + size_t context_len) { + blake3_hasher context_hasher; + hasher_init_base(&context_hasher, IV, DERIVE_KEY_CONTEXT); + blake3_hasher_update(&context_hasher, context, context_len); + uint8_t context_key[BLAKE3_KEY_LEN]; + blake3_hasher_finalize(&context_hasher, context_key, BLAKE3_KEY_LEN); + uint32_t context_key_words[8]; + load_key_words(context_key, context_key_words); + hasher_init_base(self, context_key_words, DERIVE_KEY_MATERIAL); +} + +void blake3_hasher_init_derive_key(blake3_hasher *self, const char *context) { + blake3_hasher_init_derive_key_raw(self, context, strlen(context)); +} + +// As described in hasher_push_cv() below, we do "lazy merging", delaying +// merges until right before the next CV is about to be added. This is +// different from the reference implementation. Another difference is that we +// aren't always merging 1 chunk at a time. Instead, each CV might represent +// any power-of-two number of chunks, as long as the smaller-above-larger stack +// order is maintained. Instead of the "count the trailing 0-bits" algorithm +// described in the spec, we use a "count the total number of 1-bits" variant +// that doesn't require us to retain the subtree size of the CV on top of the +// stack. The principle is the same: each CV that should remain in the stack is +// represented by a 1-bit in the total number of chunks (or bytes) so far. +INLINE void hasher_merge_cv_stack(blake3_hasher *self, uint64_t total_len) { + size_t post_merge_stack_len = (size_t)popcnt(total_len); + while (self->cv_stack_len > post_merge_stack_len) { + uint8_t *parent_node = + &self->cv_stack[(self->cv_stack_len - 2) * BLAKE3_OUT_LEN]; + output_t output = parent_output(parent_node, self->key, self->chunk.flags); + output_chaining_value(&output, parent_node); + self->cv_stack_len -= 1; + } +} + +// In reference_impl.rs, we merge the new CV with existing CVs from the stack +// before pushing it. We can do that because we know more input is coming, so +// we know none of the merges are root. +// +// This setting is different. We want to feed as much input as possible to +// compress_subtree_wide(), without setting aside anything for the chunk_state. +// If the user gives us 64 KiB, we want to parallelize over all 64 KiB at once +// as a single subtree, if at all possible. +// +// This leads to two problems: +// 1) This 64 KiB input might be the only call that ever gets made to update. +// In this case, the root node of the 64 KiB subtree would be the root node +// of the whole tree, and it would need to be ROOT finalized. We can't +// compress it until we know. +// 2) This 64 KiB input might complete a larger tree, whose root node is +// similarly going to be the the root of the whole tree. For example, maybe +// we have 196 KiB (that is, 128 + 64) hashed so far. We can't compress the +// node at the root of the 256 KiB subtree until we know how to finalize it. +// +// The second problem is solved with "lazy merging". That is, when we're about +// to add a CV to the stack, we don't merge it with anything first, as the +// reference impl does. Instead we do merges using the *previous* CV that was +// added, which is sitting on top of the stack, and we put the new CV +// (unmerged) on top of the stack afterwards. This guarantees that we never +// merge the root node until finalize(). +// +// Solving the first problem requires an additional tool, +// compress_subtree_to_parent_node(). That function always returns the top +// *two* chaining values of the subtree it's compressing. We then do lazy +// merging with each of them separately, so that the second CV will always +// remain unmerged. (That also helps us support extendable output when we're +// hashing an input all-at-once.) +INLINE void hasher_push_cv(blake3_hasher *self, uint8_t new_cv[BLAKE3_OUT_LEN], + uint64_t chunk_counter) { + hasher_merge_cv_stack(self, chunk_counter); + memcpy(&self->cv_stack[self->cv_stack_len * BLAKE3_OUT_LEN], new_cv, + BLAKE3_OUT_LEN); + self->cv_stack_len += 1; +} + +void blake3_hasher_update(blake3_hasher *self, const void *input, + size_t input_len) { + // Explicitly checking for zero avoids causing UB by passing a null pointer + // to memcpy. This comes up in practice with things like: + // std::vector v; + // blake3_hasher_update(&hasher, v.data(), v.size()); + if (input_len == 0) { + return; + } + + const uint8_t *input_bytes = (const uint8_t *)input; + + // If we have some partial chunk bytes in the internal chunk_state, we need + // to finish that chunk first. + if (chunk_state_len(&self->chunk) > 0) { + size_t take = BLAKE3_CHUNK_LEN - chunk_state_len(&self->chunk); + if (take > input_len) { + take = input_len; + } + chunk_state_update(&self->chunk, input_bytes, take); + input_bytes += take; + input_len -= take; + // If we've filled the current chunk and there's more coming, finalize this + // chunk and proceed. In this case we know it's not the root. + if (input_len > 0) { + output_t output = chunk_state_output(&self->chunk); + uint8_t chunk_cv[32]; + output_chaining_value(&output, chunk_cv); + hasher_push_cv(self, chunk_cv, self->chunk.chunk_counter); + chunk_state_reset(&self->chunk, self->key, self->chunk.chunk_counter + 1); + } else { + return; + } + } + + // Now the chunk_state is clear, and we have more input. If there's more than + // a single chunk (so, definitely not the root chunk), hash the largest whole + // subtree we can, with the full benefits of SIMD (and maybe in the future, + // multi-threading) parallelism. Two restrictions: + // - The subtree has to be a power-of-2 number of chunks. Only subtrees along + // the right edge can be incomplete, and we don't know where the right edge + // is going to be until we get to finalize(). + // - The subtree must evenly divide the total number of chunks up until this + // point (if total is not 0). If the current incomplete subtree is only + // waiting for 1 more chunk, we can't hash a subtree of 4 chunks. We have + // to complete the current subtree first. + // Because we might need to break up the input to form powers of 2, or to + // evenly divide what we already have, this part runs in a loop. + while (input_len > BLAKE3_CHUNK_LEN) { + size_t subtree_len = round_down_to_power_of_2(input_len); + uint64_t count_so_far = self->chunk.chunk_counter * BLAKE3_CHUNK_LEN; + // Shrink the subtree_len until it evenly divides the count so far. We know + // that subtree_len itself is a power of 2, so we can use a bitmasking + // trick instead of an actual remainder operation. (Note that if the caller + // consistently passes power-of-2 inputs of the same size, as is hopefully + // typical, this loop condition will always fail, and subtree_len will + // always be the full length of the input.) + // + // An aside: We don't have to shrink subtree_len quite this much. For + // example, if count_so_far is 1, we could pass 2 chunks to + // compress_subtree_to_parent_node. Since we'll get 2 CVs back, we'll still + // get the right answer in the end, and we might get to use 2-way SIMD + // parallelism. The problem with this optimization, is that it gets us + // stuck always hashing 2 chunks. The total number of chunks will remain + // odd, and we'll never graduate to higher degrees of parallelism. See + // https://github.com/BLAKE3-team/BLAKE3/issues/69. + while ((((uint64_t)(subtree_len - 1)) & count_so_far) != 0) { + subtree_len /= 2; + } + // The shrunken subtree_len might now be 1 chunk long. If so, hash that one + // chunk by itself. Otherwise, compress the subtree into a pair of CVs. + uint64_t subtree_chunks = subtree_len / BLAKE3_CHUNK_LEN; + if (subtree_len <= BLAKE3_CHUNK_LEN) { + blake3_chunk_state chunk_state; + chunk_state_init(&chunk_state, self->key, self->chunk.flags); + chunk_state.chunk_counter = self->chunk.chunk_counter; + chunk_state_update(&chunk_state, input_bytes, subtree_len); + output_t output = chunk_state_output(&chunk_state); + uint8_t cv[BLAKE3_OUT_LEN]; + output_chaining_value(&output, cv); + hasher_push_cv(self, cv, chunk_state.chunk_counter); + } else { + // This is the high-performance happy path, though getting here depends + // on the caller giving us a long enough input. + uint8_t cv_pair[2 * BLAKE3_OUT_LEN]; + compress_subtree_to_parent_node(input_bytes, subtree_len, self->key, + self->chunk.chunk_counter, + self->chunk.flags, cv_pair); + hasher_push_cv(self, cv_pair, self->chunk.chunk_counter); + hasher_push_cv(self, &cv_pair[BLAKE3_OUT_LEN], + self->chunk.chunk_counter + (subtree_chunks / 2)); + } + self->chunk.chunk_counter += subtree_chunks; + input_bytes += subtree_len; + input_len -= subtree_len; + } + + // If there's any remaining input less than a full chunk, add it to the chunk + // state. In that case, also do a final merge loop to make sure the subtree + // stack doesn't contain any unmerged pairs. The remaining input means we + // know these merges are non-root. This merge loop isn't strictly necessary + // here, because hasher_push_chunk_cv already does its own merge loop, but it + // simplifies blake3_hasher_finalize below. + if (input_len > 0) { + chunk_state_update(&self->chunk, input_bytes, input_len); + hasher_merge_cv_stack(self, self->chunk.chunk_counter); + } +} + +void blake3_hasher_finalize(const blake3_hasher *self, uint8_t *out, + size_t out_len) { + blake3_hasher_finalize_seek(self, 0, out, out_len); +} + +void blake3_hasher_finalize_seek(const blake3_hasher *self, uint64_t seek, + uint8_t *out, size_t out_len) { + // Explicitly checking for zero avoids causing UB by passing a null pointer + // to memcpy. This comes up in practice with things like: + // std::vector v; + // blake3_hasher_finalize(&hasher, v.data(), v.size()); + if (out_len == 0) { + return; + } + + // If the subtree stack is empty, then the current chunk is the root. + if (self->cv_stack_len == 0) { + output_t output = chunk_state_output(&self->chunk); + output_root_bytes(&output, seek, out, out_len); + return; + } + // If there are any bytes in the chunk state, finalize that chunk and do a + // roll-up merge between that chunk hash and every subtree in the stack. In + // this case, the extra merge loop at the end of blake3_hasher_update + // guarantees that none of the subtrees in the stack need to be merged with + // each other first. Otherwise, if there are no bytes in the chunk state, + // then the top of the stack is a chunk hash, and we start the merge from + // that. + output_t output; + size_t cvs_remaining; + if (chunk_state_len(&self->chunk) > 0) { + cvs_remaining = self->cv_stack_len; + output = chunk_state_output(&self->chunk); + } else { + // There are always at least 2 CVs in the stack in this case. + cvs_remaining = self->cv_stack_len - 2; + output = parent_output(&self->cv_stack[cvs_remaining * 32], self->key, + self->chunk.flags); + } + while (cvs_remaining > 0) { + cvs_remaining -= 1; + uint8_t parent_block[BLAKE3_BLOCK_LEN]; + memcpy(parent_block, &self->cv_stack[cvs_remaining * 32], 32); + output_chaining_value(&output, &parent_block[32]); + output = parent_output(parent_block, self->key, self->chunk.flags); + } + output_root_bytes(&output, seek, out, out_len); +} diff --git a/src/third_party/blake3/blake3.h b/src/third_party/blake3/blake3.h new file mode 100644 index 0000000..51f1d2a --- /dev/null +++ b/src/third_party/blake3/blake3.h @@ -0,0 +1,58 @@ +#ifndef BLAKE3_H +#define BLAKE3_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLAKE3_KEY_LEN 32 +#define BLAKE3_OUT_LEN 32 +#define BLAKE3_BLOCK_LEN 64 +#define BLAKE3_CHUNK_LEN 1024 +#define BLAKE3_MAX_DEPTH 54 +#define BLAKE3_MAX_SIMD_DEGREE 16 + +// This struct is a private implementation detail. It has to be here because +// it's part of blake3_hasher below. +typedef struct { + uint32_t cv[8]; + uint64_t chunk_counter; + uint8_t buf[BLAKE3_BLOCK_LEN]; + uint8_t buf_len; + uint8_t blocks_compressed; + uint8_t flags; +} blake3_chunk_state; + +typedef struct { + uint32_t key[8]; + blake3_chunk_state chunk; + uint8_t cv_stack_len; + // The stack size is MAX_DEPTH + 1 because we do lazy merging. For example, + // with 7 chunks, we have 3 entries in the stack. Adding an 8th chunk + // requires a 4th entry, rather than merging everything down to 1, because we + // don't know whether more input is coming. This is different from how the + // reference implementation does things. + uint8_t cv_stack[(BLAKE3_MAX_DEPTH + 1) * BLAKE3_OUT_LEN]; +} blake3_hasher; + +void blake3_hasher_init(blake3_hasher *self); +void blake3_hasher_init_keyed(blake3_hasher *self, + const uint8_t key[BLAKE3_KEY_LEN]); +void blake3_hasher_init_derive_key(blake3_hasher *self, const char *context); +void blake3_hasher_init_derive_key_raw(blake3_hasher *self, const void *context, + size_t context_len); +void blake3_hasher_update(blake3_hasher *self, const void *input, + size_t input_len); +void blake3_hasher_finalize(const blake3_hasher *self, uint8_t *out, + size_t out_len); +void blake3_hasher_finalize_seek(const blake3_hasher *self, uint64_t seek, + uint8_t *out, size_t out_len); + +#ifdef __cplusplus +} +#endif + +#endif /* BLAKE3_H */ diff --git a/src/third_party/blake3/blake3_avx2.c b/src/third_party/blake3/blake3_avx2.c new file mode 100644 index 0000000..c5a2ce9 --- /dev/null +++ b/src/third_party/blake3/blake3_avx2.c @@ -0,0 +1,325 @@ +#include "blake3_impl.h" + +#include + +#define DEGREE 8 + +INLINE __m256i loadu(const uint8_t src[32]) { + return _mm256_loadu_si256((const __m256i *)src); +} + +INLINE void storeu(__m256i src, uint8_t dest[16]) { + _mm256_storeu_si256((__m256i *)dest, src); +} + +INLINE __m256i addv(__m256i a, __m256i b) { return _mm256_add_epi32(a, b); } + +// Note that clang-format doesn't like the name "xor" for some reason. +INLINE __m256i xorv(__m256i a, __m256i b) { return _mm256_xor_si256(a, b); } + +INLINE __m256i set1(uint32_t x) { return _mm256_set1_epi32((int32_t)x); } + +INLINE __m256i rot16(__m256i x) { + return _mm256_shuffle_epi8( + x, _mm256_set_epi8(13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2, + 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2)); +} + +INLINE __m256i rot12(__m256i x) { + return _mm256_or_si256(_mm256_srli_epi32(x, 12), _mm256_slli_epi32(x, 32 - 12)); +} + +INLINE __m256i rot8(__m256i x) { + return _mm256_shuffle_epi8( + x, _mm256_set_epi8(12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1, + 12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1)); +} + +INLINE __m256i rot7(__m256i x) { + return _mm256_or_si256(_mm256_srli_epi32(x, 7), _mm256_slli_epi32(x, 32 - 7)); +} + +INLINE void round_fn(__m256i v[16], __m256i m[16], size_t r) { + v[0] = addv(v[0], m[(size_t)MSG_SCHEDULE[r][0]]); + v[1] = addv(v[1], m[(size_t)MSG_SCHEDULE[r][2]]); + v[2] = addv(v[2], m[(size_t)MSG_SCHEDULE[r][4]]); + v[3] = addv(v[3], m[(size_t)MSG_SCHEDULE[r][6]]); + v[0] = addv(v[0], v[4]); + v[1] = addv(v[1], v[5]); + v[2] = addv(v[2], v[6]); + v[3] = addv(v[3], v[7]); + v[12] = xorv(v[12], v[0]); + v[13] = xorv(v[13], v[1]); + v[14] = xorv(v[14], v[2]); + v[15] = xorv(v[15], v[3]); + v[12] = rot16(v[12]); + v[13] = rot16(v[13]); + v[14] = rot16(v[14]); + v[15] = rot16(v[15]); + v[8] = addv(v[8], v[12]); + v[9] = addv(v[9], v[13]); + v[10] = addv(v[10], v[14]); + v[11] = addv(v[11], v[15]); + v[4] = xorv(v[4], v[8]); + v[5] = xorv(v[5], v[9]); + v[6] = xorv(v[6], v[10]); + v[7] = xorv(v[7], v[11]); + v[4] = rot12(v[4]); + v[5] = rot12(v[5]); + v[6] = rot12(v[6]); + v[7] = rot12(v[7]); + v[0] = addv(v[0], m[(size_t)MSG_SCHEDULE[r][1]]); + v[1] = addv(v[1], m[(size_t)MSG_SCHEDULE[r][3]]); + v[2] = addv(v[2], m[(size_t)MSG_SCHEDULE[r][5]]); + v[3] = addv(v[3], m[(size_t)MSG_SCHEDULE[r][7]]); + v[0] = addv(v[0], v[4]); + v[1] = addv(v[1], v[5]); + v[2] = addv(v[2], v[6]); + v[3] = addv(v[3], v[7]); + v[12] = xorv(v[12], v[0]); + v[13] = xorv(v[13], v[1]); + v[14] = xorv(v[14], v[2]); + v[15] = xorv(v[15], v[3]); + v[12] = rot8(v[12]); + v[13] = rot8(v[13]); + v[14] = rot8(v[14]); + v[15] = rot8(v[15]); + v[8] = addv(v[8], v[12]); + v[9] = addv(v[9], v[13]); + v[10] = addv(v[10], v[14]); + v[11] = addv(v[11], v[15]); + v[4] = xorv(v[4], v[8]); + v[5] = xorv(v[5], v[9]); + v[6] = xorv(v[6], v[10]); + v[7] = xorv(v[7], v[11]); + v[4] = rot7(v[4]); + v[5] = rot7(v[5]); + v[6] = rot7(v[6]); + v[7] = rot7(v[7]); + + v[0] = addv(v[0], m[(size_t)MSG_SCHEDULE[r][8]]); + v[1] = addv(v[1], m[(size_t)MSG_SCHEDULE[r][10]]); + v[2] = addv(v[2], m[(size_t)MSG_SCHEDULE[r][12]]); + v[3] = addv(v[3], m[(size_t)MSG_SCHEDULE[r][14]]); + v[0] = addv(v[0], v[5]); + v[1] = addv(v[1], v[6]); + v[2] = addv(v[2], v[7]); + v[3] = addv(v[3], v[4]); + v[15] = xorv(v[15], v[0]); + v[12] = xorv(v[12], v[1]); + v[13] = xorv(v[13], v[2]); + v[14] = xorv(v[14], v[3]); + v[15] = rot16(v[15]); + v[12] = rot16(v[12]); + v[13] = rot16(v[13]); + v[14] = rot16(v[14]); + v[10] = addv(v[10], v[15]); + v[11] = addv(v[11], v[12]); + v[8] = addv(v[8], v[13]); + v[9] = addv(v[9], v[14]); + v[5] = xorv(v[5], v[10]); + v[6] = xorv(v[6], v[11]); + v[7] = xorv(v[7], v[8]); + v[4] = xorv(v[4], v[9]); + v[5] = rot12(v[5]); + v[6] = rot12(v[6]); + v[7] = rot12(v[7]); + v[4] = rot12(v[4]); + v[0] = addv(v[0], m[(size_t)MSG_SCHEDULE[r][9]]); + v[1] = addv(v[1], m[(size_t)MSG_SCHEDULE[r][11]]); + v[2] = addv(v[2], m[(size_t)MSG_SCHEDULE[r][13]]); + v[3] = addv(v[3], m[(size_t)MSG_SCHEDULE[r][15]]); + v[0] = addv(v[0], v[5]); + v[1] = addv(v[1], v[6]); + v[2] = addv(v[2], v[7]); + v[3] = addv(v[3], v[4]); + v[15] = xorv(v[15], v[0]); + v[12] = xorv(v[12], v[1]); + v[13] = xorv(v[13], v[2]); + v[14] = xorv(v[14], v[3]); + v[15] = rot8(v[15]); + v[12] = rot8(v[12]); + v[13] = rot8(v[13]); + v[14] = rot8(v[14]); + v[10] = addv(v[10], v[15]); + v[11] = addv(v[11], v[12]); + v[8] = addv(v[8], v[13]); + v[9] = addv(v[9], v[14]); + v[5] = xorv(v[5], v[10]); + v[6] = xorv(v[6], v[11]); + v[7] = xorv(v[7], v[8]); + v[4] = xorv(v[4], v[9]); + v[5] = rot7(v[5]); + v[6] = rot7(v[6]); + v[7] = rot7(v[7]); + v[4] = rot7(v[4]); +} + +INLINE void transpose_vecs(__m256i vecs[DEGREE]) { + // Interleave 32-bit lanes. The low unpack is lanes 00/11/44/55, and the high + // is 22/33/66/77. + __m256i ab_0145 = _mm256_unpacklo_epi32(vecs[0], vecs[1]); + __m256i ab_2367 = _mm256_unpackhi_epi32(vecs[0], vecs[1]); + __m256i cd_0145 = _mm256_unpacklo_epi32(vecs[2], vecs[3]); + __m256i cd_2367 = _mm256_unpackhi_epi32(vecs[2], vecs[3]); + __m256i ef_0145 = _mm256_unpacklo_epi32(vecs[4], vecs[5]); + __m256i ef_2367 = _mm256_unpackhi_epi32(vecs[4], vecs[5]); + __m256i gh_0145 = _mm256_unpacklo_epi32(vecs[6], vecs[7]); + __m256i gh_2367 = _mm256_unpackhi_epi32(vecs[6], vecs[7]); + + // Interleave 64-bit lates. The low unpack is lanes 00/22 and the high is + // 11/33. + __m256i abcd_04 = _mm256_unpacklo_epi64(ab_0145, cd_0145); + __m256i abcd_15 = _mm256_unpackhi_epi64(ab_0145, cd_0145); + __m256i abcd_26 = _mm256_unpacklo_epi64(ab_2367, cd_2367); + __m256i abcd_37 = _mm256_unpackhi_epi64(ab_2367, cd_2367); + __m256i efgh_04 = _mm256_unpacklo_epi64(ef_0145, gh_0145); + __m256i efgh_15 = _mm256_unpackhi_epi64(ef_0145, gh_0145); + __m256i efgh_26 = _mm256_unpacklo_epi64(ef_2367, gh_2367); + __m256i efgh_37 = _mm256_unpackhi_epi64(ef_2367, gh_2367); + + // Interleave 128-bit lanes. + vecs[0] = _mm256_permute2x128_si256(abcd_04, efgh_04, 0x20); + vecs[1] = _mm256_permute2x128_si256(abcd_15, efgh_15, 0x20); + vecs[2] = _mm256_permute2x128_si256(abcd_26, efgh_26, 0x20); + vecs[3] = _mm256_permute2x128_si256(abcd_37, efgh_37, 0x20); + vecs[4] = _mm256_permute2x128_si256(abcd_04, efgh_04, 0x31); + vecs[5] = _mm256_permute2x128_si256(abcd_15, efgh_15, 0x31); + vecs[6] = _mm256_permute2x128_si256(abcd_26, efgh_26, 0x31); + vecs[7] = _mm256_permute2x128_si256(abcd_37, efgh_37, 0x31); +} + +INLINE void transpose_msg_vecs(const uint8_t *const *inputs, + size_t block_offset, __m256i out[16]) { + out[0] = loadu(&inputs[0][block_offset + 0 * sizeof(__m256i)]); + out[1] = loadu(&inputs[1][block_offset + 0 * sizeof(__m256i)]); + out[2] = loadu(&inputs[2][block_offset + 0 * sizeof(__m256i)]); + out[3] = loadu(&inputs[3][block_offset + 0 * sizeof(__m256i)]); + out[4] = loadu(&inputs[4][block_offset + 0 * sizeof(__m256i)]); + out[5] = loadu(&inputs[5][block_offset + 0 * sizeof(__m256i)]); + out[6] = loadu(&inputs[6][block_offset + 0 * sizeof(__m256i)]); + out[7] = loadu(&inputs[7][block_offset + 0 * sizeof(__m256i)]); + out[8] = loadu(&inputs[0][block_offset + 1 * sizeof(__m256i)]); + out[9] = loadu(&inputs[1][block_offset + 1 * sizeof(__m256i)]); + out[10] = loadu(&inputs[2][block_offset + 1 * sizeof(__m256i)]); + out[11] = loadu(&inputs[3][block_offset + 1 * sizeof(__m256i)]); + out[12] = loadu(&inputs[4][block_offset + 1 * sizeof(__m256i)]); + out[13] = loadu(&inputs[5][block_offset + 1 * sizeof(__m256i)]); + out[14] = loadu(&inputs[6][block_offset + 1 * sizeof(__m256i)]); + out[15] = loadu(&inputs[7][block_offset + 1 * sizeof(__m256i)]); + for (size_t i = 0; i < 8; ++i) { + _mm_prefetch(&inputs[i][block_offset + 256], _MM_HINT_T0); + } + transpose_vecs(&out[0]); + transpose_vecs(&out[8]); +} + +INLINE void load_counters(uint64_t counter, bool increment_counter, + __m256i *out_lo, __m256i *out_hi) { + const __m256i mask = _mm256_set1_epi32(-(int32_t)increment_counter); + const __m256i add0 = _mm256_set_epi32(7, 6, 5, 4, 3, 2, 1, 0); + const __m256i add1 = _mm256_and_si256(mask, add0); + __m256i l = _mm256_add_epi32(_mm256_set1_epi32(counter), add1); + __m256i carry = _mm256_cmpgt_epi32(_mm256_xor_si256(add1, _mm256_set1_epi32(0x80000000)), + _mm256_xor_si256( l, _mm256_set1_epi32(0x80000000))); + __m256i h = _mm256_sub_epi32(_mm256_set1_epi32(counter >> 32), carry); + *out_lo = l; + *out_hi = h; +} + +void blake3_hash8_avx2(const uint8_t *const *inputs, size_t blocks, + const uint32_t key[8], uint64_t counter, + bool increment_counter, uint8_t flags, + uint8_t flags_start, uint8_t flags_end, uint8_t *out) { + __m256i h_vecs[8] = { + set1(key[0]), set1(key[1]), set1(key[2]), set1(key[3]), + set1(key[4]), set1(key[5]), set1(key[6]), set1(key[7]), + }; + __m256i counter_low_vec, counter_high_vec; + load_counters(counter, increment_counter, &counter_low_vec, + &counter_high_vec); + uint8_t block_flags = flags | flags_start; + + for (size_t block = 0; block < blocks; block++) { + if (block + 1 == blocks) { + block_flags |= flags_end; + } + __m256i block_len_vec = set1(BLAKE3_BLOCK_LEN); + __m256i block_flags_vec = set1(block_flags); + __m256i msg_vecs[16]; + transpose_msg_vecs(inputs, block * BLAKE3_BLOCK_LEN, msg_vecs); + + __m256i v[16] = { + h_vecs[0], h_vecs[1], h_vecs[2], h_vecs[3], + h_vecs[4], h_vecs[5], h_vecs[6], h_vecs[7], + set1(IV[0]), set1(IV[1]), set1(IV[2]), set1(IV[3]), + counter_low_vec, counter_high_vec, block_len_vec, block_flags_vec, + }; + round_fn(v, msg_vecs, 0); + round_fn(v, msg_vecs, 1); + round_fn(v, msg_vecs, 2); + round_fn(v, msg_vecs, 3); + round_fn(v, msg_vecs, 4); + round_fn(v, msg_vecs, 5); + round_fn(v, msg_vecs, 6); + h_vecs[0] = xorv(v[0], v[8]); + h_vecs[1] = xorv(v[1], v[9]); + h_vecs[2] = xorv(v[2], v[10]); + h_vecs[3] = xorv(v[3], v[11]); + h_vecs[4] = xorv(v[4], v[12]); + h_vecs[5] = xorv(v[5], v[13]); + h_vecs[6] = xorv(v[6], v[14]); + h_vecs[7] = xorv(v[7], v[15]); + + block_flags = flags; + } + + transpose_vecs(h_vecs); + storeu(h_vecs[0], &out[0 * sizeof(__m256i)]); + storeu(h_vecs[1], &out[1 * sizeof(__m256i)]); + storeu(h_vecs[2], &out[2 * sizeof(__m256i)]); + storeu(h_vecs[3], &out[3 * sizeof(__m256i)]); + storeu(h_vecs[4], &out[4 * sizeof(__m256i)]); + storeu(h_vecs[5], &out[5 * sizeof(__m256i)]); + storeu(h_vecs[6], &out[6 * sizeof(__m256i)]); + storeu(h_vecs[7], &out[7 * sizeof(__m256i)]); +} + +#if !defined(BLAKE3_NO_SSE41) +void blake3_hash_many_sse41(const uint8_t *const *inputs, size_t num_inputs, + size_t blocks, const uint32_t key[8], + uint64_t counter, bool increment_counter, + uint8_t flags, uint8_t flags_start, + uint8_t flags_end, uint8_t *out); +#else +void blake3_hash_many_portable(const uint8_t *const *inputs, size_t num_inputs, + size_t blocks, const uint32_t key[8], + uint64_t counter, bool increment_counter, + uint8_t flags, uint8_t flags_start, + uint8_t flags_end, uint8_t *out); +#endif + +void blake3_hash_many_avx2(const uint8_t *const *inputs, size_t num_inputs, + size_t blocks, const uint32_t key[8], + uint64_t counter, bool increment_counter, + uint8_t flags, uint8_t flags_start, + uint8_t flags_end, uint8_t *out) { + while (num_inputs >= DEGREE) { + blake3_hash8_avx2(inputs, blocks, key, counter, increment_counter, flags, + flags_start, flags_end, out); + if (increment_counter) { + counter += DEGREE; + } + inputs += DEGREE; + num_inputs -= DEGREE; + out = &out[DEGREE * BLAKE3_OUT_LEN]; + } +#if !defined(BLAKE3_NO_SSE41) + blake3_hash_many_sse41(inputs, num_inputs, blocks, key, counter, + increment_counter, flags, flags_start, flags_end, out); +#else + blake3_hash_many_portable(inputs, num_inputs, blocks, key, counter, + increment_counter, flags, flags_start, flags_end, + out); +#endif +} diff --git a/src/third_party/blake3/blake3_avx2_x86-64_unix.S b/src/third_party/blake3/blake3_avx2_x86-64_unix.S new file mode 100644 index 0000000..812bb85 --- /dev/null +++ b/src/third_party/blake3/blake3_avx2_x86-64_unix.S @@ -0,0 +1,1815 @@ +#if defined(__ELF__) && defined(__linux__) +.section .note.GNU-stack,"",%progbits +#endif + +#if defined(__ELF__) && defined(__CET__) && defined(__has_include) +#if __has_include() +#include +#endif +#endif + +#if !defined(_CET_ENDBR) +#define _CET_ENDBR +#endif + +.intel_syntax noprefix +.global _blake3_hash_many_avx2 +.global blake3_hash_many_avx2 +#ifdef __APPLE__ +.text +#else +.section .text +#endif + .p2align 6 +_blake3_hash_many_avx2: +blake3_hash_many_avx2: + _CET_ENDBR + push r15 + push r14 + push r13 + push r12 + push rbx + push rbp + mov rbp, rsp + sub rsp, 680 + and rsp, 0xFFFFFFFFFFFFFFC0 + neg r9d + vmovd xmm0, r9d + vpbroadcastd ymm0, xmm0 + vmovdqa ymmword ptr [rsp+0x280], ymm0 + vpand ymm1, ymm0, ymmword ptr [ADD0+rip] + vpand ymm2, ymm0, ymmword ptr [ADD1+rip] + vmovdqa ymmword ptr [rsp+0x220], ymm2 + vmovd xmm2, r8d + vpbroadcastd ymm2, xmm2 + vpaddd ymm2, ymm2, ymm1 + vmovdqa ymmword ptr [rsp+0x240], ymm2 + vpxor ymm1, ymm1, ymmword ptr [CMP_MSB_MASK+rip] + vpxor ymm2, ymm2, ymmword ptr [CMP_MSB_MASK+rip] + vpcmpgtd ymm2, ymm1, ymm2 + shr r8, 32 + vmovd xmm3, r8d + vpbroadcastd ymm3, xmm3 + vpsubd ymm3, ymm3, ymm2 + vmovdqa ymmword ptr [rsp+0x260], ymm3 + shl rdx, 6 + mov qword ptr [rsp+0x2A0], rdx + cmp rsi, 8 + jc 3f +2: + vpbroadcastd ymm0, dword ptr [rcx] + vpbroadcastd ymm1, dword ptr [rcx+0x4] + vpbroadcastd ymm2, dword ptr [rcx+0x8] + vpbroadcastd ymm3, dword ptr [rcx+0xC] + vpbroadcastd ymm4, dword ptr [rcx+0x10] + vpbroadcastd ymm5, dword ptr [rcx+0x14] + vpbroadcastd ymm6, dword ptr [rcx+0x18] + vpbroadcastd ymm7, dword ptr [rcx+0x1C] + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + mov r10, qword ptr [rdi+0x10] + mov r11, qword ptr [rdi+0x18] + mov r12, qword ptr [rdi+0x20] + mov r13, qword ptr [rdi+0x28] + mov r14, qword ptr [rdi+0x30] + mov r15, qword ptr [rdi+0x38] + movzx eax, byte ptr [rbp+0x38] + movzx ebx, byte ptr [rbp+0x40] + or eax, ebx + xor edx, edx +.p2align 5 +9: + movzx ebx, byte ptr [rbp+0x48] + or ebx, eax + add rdx, 64 + cmp rdx, qword ptr [rsp+0x2A0] + cmove eax, ebx + mov dword ptr [rsp+0x200], eax + vmovups xmm8, xmmword ptr [r8+rdx-0x40] + vinsertf128 ymm8, ymm8, xmmword ptr [r12+rdx-0x40], 0x01 + vmovups xmm9, xmmword ptr [r9+rdx-0x40] + vinsertf128 ymm9, ymm9, xmmword ptr [r13+rdx-0x40], 0x01 + vunpcklpd ymm12, ymm8, ymm9 + vunpckhpd ymm13, ymm8, ymm9 + vmovups xmm10, xmmword ptr [r10+rdx-0x40] + vinsertf128 ymm10, ymm10, xmmword ptr [r14+rdx-0x40], 0x01 + vmovups xmm11, xmmword ptr [r11+rdx-0x40] + vinsertf128 ymm11, ymm11, xmmword ptr [r15+rdx-0x40], 0x01 + vunpcklpd ymm14, ymm10, ymm11 + vunpckhpd ymm15, ymm10, ymm11 + vshufps ymm8, ymm12, ymm14, 136 + vmovaps ymmword ptr [rsp], ymm8 + vshufps ymm9, ymm12, ymm14, 221 + vmovaps ymmword ptr [rsp+0x20], ymm9 + vshufps ymm10, ymm13, ymm15, 136 + vmovaps ymmword ptr [rsp+0x40], ymm10 + vshufps ymm11, ymm13, ymm15, 221 + vmovaps ymmword ptr [rsp+0x60], ymm11 + vmovups xmm8, xmmword ptr [r8+rdx-0x30] + vinsertf128 ymm8, ymm8, xmmword ptr [r12+rdx-0x30], 0x01 + vmovups xmm9, xmmword ptr [r9+rdx-0x30] + vinsertf128 ymm9, ymm9, xmmword ptr [r13+rdx-0x30], 0x01 + vunpcklpd ymm12, ymm8, ymm9 + vunpckhpd ymm13, ymm8, ymm9 + vmovups xmm10, xmmword ptr [r10+rdx-0x30] + vinsertf128 ymm10, ymm10, xmmword ptr [r14+rdx-0x30], 0x01 + vmovups xmm11, xmmword ptr [r11+rdx-0x30] + vinsertf128 ymm11, ymm11, xmmword ptr [r15+rdx-0x30], 0x01 + vunpcklpd ymm14, ymm10, ymm11 + vunpckhpd ymm15, ymm10, ymm11 + vshufps ymm8, ymm12, ymm14, 136 + vmovaps ymmword ptr [rsp+0x80], ymm8 + vshufps ymm9, ymm12, ymm14, 221 + vmovaps ymmword ptr [rsp+0xA0], ymm9 + vshufps ymm10, ymm13, ymm15, 136 + vmovaps ymmword ptr [rsp+0xC0], ymm10 + vshufps ymm11, ymm13, ymm15, 221 + vmovaps ymmword ptr [rsp+0xE0], ymm11 + vmovups xmm8, xmmword ptr [r8+rdx-0x20] + vinsertf128 ymm8, ymm8, xmmword ptr [r12+rdx-0x20], 0x01 + vmovups xmm9, xmmword ptr [r9+rdx-0x20] + vinsertf128 ymm9, ymm9, xmmword ptr [r13+rdx-0x20], 0x01 + vunpcklpd ymm12, ymm8, ymm9 + vunpckhpd ymm13, ymm8, ymm9 + vmovups xmm10, xmmword ptr [r10+rdx-0x20] + vinsertf128 ymm10, ymm10, xmmword ptr [r14+rdx-0x20], 0x01 + vmovups xmm11, xmmword ptr [r11+rdx-0x20] + vinsertf128 ymm11, ymm11, xmmword ptr [r15+rdx-0x20], 0x01 + vunpcklpd ymm14, ymm10, ymm11 + vunpckhpd ymm15, ymm10, ymm11 + vshufps ymm8, ymm12, ymm14, 136 + vmovaps ymmword ptr [rsp+0x100], ymm8 + vshufps ymm9, ymm12, ymm14, 221 + vmovaps ymmword ptr [rsp+0x120], ymm9 + vshufps ymm10, ymm13, ymm15, 136 + vmovaps ymmword ptr [rsp+0x140], ymm10 + vshufps ymm11, ymm13, ymm15, 221 + vmovaps ymmword ptr [rsp+0x160], ymm11 + vmovups xmm8, xmmword ptr [r8+rdx-0x10] + vinsertf128 ymm8, ymm8, xmmword ptr [r12+rdx-0x10], 0x01 + vmovups xmm9, xmmword ptr [r9+rdx-0x10] + vinsertf128 ymm9, ymm9, xmmword ptr [r13+rdx-0x10], 0x01 + vunpcklpd ymm12, ymm8, ymm9 + vunpckhpd ymm13, ymm8, ymm9 + vmovups xmm10, xmmword ptr [r10+rdx-0x10] + vinsertf128 ymm10, ymm10, xmmword ptr [r14+rdx-0x10], 0x01 + vmovups xmm11, xmmword ptr [r11+rdx-0x10] + vinsertf128 ymm11, ymm11, xmmword ptr [r15+rdx-0x10], 0x01 + vunpcklpd ymm14, ymm10, ymm11 + vunpckhpd ymm15, ymm10, ymm11 + vshufps ymm8, ymm12, ymm14, 136 + vmovaps ymmword ptr [rsp+0x180], ymm8 + vshufps ymm9, ymm12, ymm14, 221 + vmovaps ymmword ptr [rsp+0x1A0], ymm9 + vshufps ymm10, ymm13, ymm15, 136 + vmovaps ymmword ptr [rsp+0x1C0], ymm10 + vshufps ymm11, ymm13, ymm15, 221 + vmovaps ymmword ptr [rsp+0x1E0], ymm11 + vpbroadcastd ymm15, dword ptr [rsp+0x200] + prefetcht0 [r8+rdx+0x80] + prefetcht0 [r12+rdx+0x80] + prefetcht0 [r9+rdx+0x80] + prefetcht0 [r13+rdx+0x80] + prefetcht0 [r10+rdx+0x80] + prefetcht0 [r14+rdx+0x80] + prefetcht0 [r11+rdx+0x80] + prefetcht0 [r15+rdx+0x80] + vpaddd ymm0, ymm0, ymmword ptr [rsp] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x40] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x80] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0xC0] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm0, ymmword ptr [rsp+0x240] + vpxor ymm13, ymm1, ymmword ptr [rsp+0x260] + vpxor ymm14, ymm2, ymmword ptr [BLAKE3_BLOCK_LEN+rip] + vpxor ymm15, ymm3, ymm15 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [BLAKE3_IV_0+rip] + vpaddd ymm9, ymm13, ymmword ptr [BLAKE3_IV_1+rip] + vpaddd ymm10, ymm14, ymmword ptr [BLAKE3_IV_2+rip] + vpaddd ymm11, ymm15, ymmword ptr [BLAKE3_IV_3+rip] + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x20] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x60] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0xA0] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0xE0] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x100] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x140] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x180] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x1C0] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x120] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x160] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x1A0] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x1E0] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x40] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x60] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0xE0] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x80] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0xC0] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x140] + vpaddd ymm2, ymm2, ymmword ptr [rsp] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x1A0] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x20] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x180] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x120] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x1E0] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x160] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0xA0] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x1C0] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x100] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x60] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x140] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x1A0] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0xE0] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x80] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x180] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x40] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x1C0] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0xC0] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x120] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x160] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x100] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0xA0] + vpaddd ymm1, ymm1, ymmword ptr [rsp] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x1E0] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x20] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x140] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x180] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x1C0] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x1A0] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0xE0] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x120] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x60] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x1E0] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x80] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x160] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0xA0] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x20] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x40] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x100] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0xC0] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x180] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x120] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x1E0] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x1C0] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x1A0] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x160] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x140] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x100] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0xE0] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0xA0] + vpaddd ymm2, ymm2, ymmword ptr [rsp] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0xC0] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x40] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x60] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x20] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x80] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x120] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x160] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x100] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x1E0] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x1C0] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0xA0] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x180] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x20] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x1A0] + vpaddd ymm1, ymm1, ymmword ptr [rsp] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x40] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x80] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x60] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x140] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0xC0] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0xE0] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x160] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0xA0] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x20] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x100] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x1E0] + vpaddd ymm1, ymm1, ymmword ptr [rsp] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x120] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0xC0] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x1C0] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x40] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x60] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0xE0] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x140] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x180] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x80] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x1A0] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vpxor ymm0, ymm0, ymm8 + vpxor ymm1, ymm1, ymm9 + vpxor ymm2, ymm2, ymm10 + vpxor ymm3, ymm3, ymm11 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpxor ymm4, ymm4, ymm12 + vpxor ymm5, ymm5, ymm13 + vpxor ymm6, ymm6, ymm14 + vpxor ymm7, ymm7, ymm15 + movzx eax, byte ptr [rbp+0x38] + jne 9b + mov rbx, qword ptr [rbp+0x50] + vunpcklps ymm8, ymm0, ymm1 + vunpcklps ymm9, ymm2, ymm3 + vunpckhps ymm10, ymm0, ymm1 + vunpcklps ymm11, ymm4, ymm5 + vunpcklps ymm0, ymm6, ymm7 + vshufps ymm12, ymm8, ymm9, 78 + vblendps ymm1, ymm8, ymm12, 0xCC + vshufps ymm8, ymm11, ymm0, 78 + vunpckhps ymm13, ymm2, ymm3 + vblendps ymm2, ymm11, ymm8, 0xCC + vblendps ymm3, ymm12, ymm9, 0xCC + vperm2f128 ymm12, ymm1, ymm2, 0x20 + vmovups ymmword ptr [rbx], ymm12 + vunpckhps ymm14, ymm4, ymm5 + vblendps ymm4, ymm8, ymm0, 0xCC + vunpckhps ymm15, ymm6, ymm7 + vperm2f128 ymm7, ymm3, ymm4, 0x20 + vmovups ymmword ptr [rbx+0x20], ymm7 + vshufps ymm5, ymm10, ymm13, 78 + vblendps ymm6, ymm5, ymm13, 0xCC + vshufps ymm13, ymm14, ymm15, 78 + vblendps ymm10, ymm10, ymm5, 0xCC + vblendps ymm14, ymm14, ymm13, 0xCC + vperm2f128 ymm8, ymm10, ymm14, 0x20 + vmovups ymmword ptr [rbx+0x40], ymm8 + vblendps ymm15, ymm13, ymm15, 0xCC + vperm2f128 ymm13, ymm6, ymm15, 0x20 + vmovups ymmword ptr [rbx+0x60], ymm13 + vperm2f128 ymm9, ymm1, ymm2, 0x31 + vperm2f128 ymm11, ymm3, ymm4, 0x31 + vmovups ymmword ptr [rbx+0x80], ymm9 + vperm2f128 ymm14, ymm10, ymm14, 0x31 + vperm2f128 ymm15, ymm6, ymm15, 0x31 + vmovups ymmword ptr [rbx+0xA0], ymm11 + vmovups ymmword ptr [rbx+0xC0], ymm14 + vmovups ymmword ptr [rbx+0xE0], ymm15 + vmovdqa ymm0, ymmword ptr [rsp+0x220] + vpaddd ymm1, ymm0, ymmword ptr [rsp+0x240] + vmovdqa ymmword ptr [rsp+0x240], ymm1 + vpxor ymm0, ymm0, ymmword ptr [CMP_MSB_MASK+rip] + vpxor ymm2, ymm1, ymmword ptr [CMP_MSB_MASK+rip] + vpcmpgtd ymm2, ymm0, ymm2 + vmovdqa ymm0, ymmword ptr [rsp+0x260] + vpsubd ymm2, ymm0, ymm2 + vmovdqa ymmword ptr [rsp+0x260], ymm2 + add rdi, 64 + add rbx, 256 + mov qword ptr [rbp+0x50], rbx + sub rsi, 8 + cmp rsi, 8 + jnc 2b + test rsi, rsi + jnz 3f +4: + vzeroupper + mov rsp, rbp + pop rbp + pop rbx + pop r12 + pop r13 + pop r14 + pop r15 + ret +.p2align 5 +3: + mov rbx, qword ptr [rbp+0x50] + mov r15, qword ptr [rsp+0x2A0] + movzx r13d, byte ptr [rbp+0x38] + movzx r12d, byte ptr [rbp+0x48] + test rsi, 0x4 + je 3f + vbroadcasti128 ymm0, xmmword ptr [rcx] + vbroadcasti128 ymm1, xmmword ptr [rcx+0x10] + vmovdqa ymm8, ymm0 + vmovdqa ymm9, ymm1 + vbroadcasti128 ymm12, xmmword ptr [rsp+0x240] + vbroadcasti128 ymm13, xmmword ptr [rsp+0x260] + vpunpckldq ymm14, ymm12, ymm13 + vpunpckhdq ymm15, ymm12, ymm13 + vpermq ymm14, ymm14, 0x50 + vpermq ymm15, ymm15, 0x50 + vbroadcasti128 ymm12, xmmword ptr [BLAKE3_BLOCK_LEN+rip] + vpblendd ymm14, ymm14, ymm12, 0x44 + vpblendd ymm15, ymm15, ymm12, 0x44 + vmovdqa ymmword ptr [rsp], ymm14 + vmovdqa ymmword ptr [rsp+0x20], ymm15 + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + mov r10, qword ptr [rdi+0x10] + mov r11, qword ptr [rdi+0x18] + movzx eax, byte ptr [rbp+0x40] + or eax, r13d + xor edx, edx +.p2align 5 +2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + mov dword ptr [rsp+0x200], eax + vmovups ymm2, ymmword ptr [r8+rdx-0x40] + vinsertf128 ymm2, ymm2, xmmword ptr [r9+rdx-0x40], 0x01 + vmovups ymm3, ymmword ptr [r8+rdx-0x30] + vinsertf128 ymm3, ymm3, xmmword ptr [r9+rdx-0x30], 0x01 + vshufps ymm4, ymm2, ymm3, 136 + vshufps ymm5, ymm2, ymm3, 221 + vmovups ymm2, ymmword ptr [r8+rdx-0x20] + vinsertf128 ymm2, ymm2, xmmword ptr [r9+rdx-0x20], 0x01 + vmovups ymm3, ymmword ptr [r8+rdx-0x10] + vinsertf128 ymm3, ymm3, xmmword ptr [r9+rdx-0x10], 0x01 + vshufps ymm6, ymm2, ymm3, 136 + vshufps ymm7, ymm2, ymm3, 221 + vpshufd ymm6, ymm6, 0x93 + vpshufd ymm7, ymm7, 0x93 + vmovups ymm10, ymmword ptr [r10+rdx-0x40] + vinsertf128 ymm10, ymm10, xmmword ptr [r11+rdx-0x40], 0x01 + vmovups ymm11, ymmword ptr [r10+rdx-0x30] + vinsertf128 ymm11, ymm11, xmmword ptr [r11+rdx-0x30], 0x01 + vshufps ymm12, ymm10, ymm11, 136 + vshufps ymm13, ymm10, ymm11, 221 + vmovups ymm10, ymmword ptr [r10+rdx-0x20] + vinsertf128 ymm10, ymm10, xmmword ptr [r11+rdx-0x20], 0x01 + vmovups ymm11, ymmword ptr [r10+rdx-0x10] + vinsertf128 ymm11, ymm11, xmmword ptr [r11+rdx-0x10], 0x01 + vshufps ymm14, ymm10, ymm11, 136 + vshufps ymm15, ymm10, ymm11, 221 + vpshufd ymm14, ymm14, 0x93 + vpshufd ymm15, ymm15, 0x93 + prefetcht0 [r8+rdx+0x80] + prefetcht0 [r9+rdx+0x80] + prefetcht0 [r10+rdx+0x80] + prefetcht0 [r11+rdx+0x80] + vpbroadcastd ymm2, dword ptr [rsp+0x200] + vmovdqa ymm3, ymmword ptr [rsp] + vmovdqa ymm11, ymmword ptr [rsp+0x20] + vpblendd ymm3, ymm3, ymm2, 0x88 + vpblendd ymm11, ymm11, ymm2, 0x88 + vbroadcasti128 ymm2, xmmword ptr [BLAKE3_IV+rip] + vmovdqa ymm10, ymm2 + mov al, 7 +9: + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm8, ymm8, ymm12 + vmovdqa ymmword ptr [rsp+0x40], ymm4 + nop + vmovdqa ymmword ptr [rsp+0x60], ymm12 + nop + vpaddd ymm0, ymm0, ymm1 + vpaddd ymm8, ymm8, ymm9 + vpxor ymm3, ymm3, ymm0 + vpxor ymm11, ymm11, ymm8 + vbroadcasti128 ymm4, xmmword ptr [ROT16+rip] + vpshufb ymm3, ymm3, ymm4 + vpshufb ymm11, ymm11, ymm4 + vpaddd ymm2, ymm2, ymm3 + vpaddd ymm10, ymm10, ymm11 + vpxor ymm1, ymm1, ymm2 + vpxor ymm9, ymm9, ymm10 + vpsrld ymm4, ymm1, 12 + vpslld ymm1, ymm1, 20 + vpor ymm1, ymm1, ymm4 + vpsrld ymm4, ymm9, 12 + vpslld ymm9, ymm9, 20 + vpor ymm9, ymm9, ymm4 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm0, ymm0, ymm1 + vpaddd ymm8, ymm8, ymm9 + vmovdqa ymmword ptr [rsp+0x80], ymm5 + vmovdqa ymmword ptr [rsp+0xA0], ymm13 + vpxor ymm3, ymm3, ymm0 + vpxor ymm11, ymm11, ymm8 + vbroadcasti128 ymm4, xmmword ptr [ROT8+rip] + vpshufb ymm3, ymm3, ymm4 + vpshufb ymm11, ymm11, ymm4 + vpaddd ymm2, ymm2, ymm3 + vpaddd ymm10, ymm10, ymm11 + vpxor ymm1, ymm1, ymm2 + vpxor ymm9, ymm9, ymm10 + vpsrld ymm4, ymm1, 7 + vpslld ymm1, ymm1, 25 + vpor ymm1, ymm1, ymm4 + vpsrld ymm4, ymm9, 7 + vpslld ymm9, ymm9, 25 + vpor ymm9, ymm9, ymm4 + vpshufd ymm0, ymm0, 0x93 + vpshufd ymm8, ymm8, 0x93 + vpshufd ymm3, ymm3, 0x4E + vpshufd ymm11, ymm11, 0x4E + vpshufd ymm2, ymm2, 0x39 + vpshufd ymm10, ymm10, 0x39 + vpaddd ymm0, ymm0, ymm6 + vpaddd ymm8, ymm8, ymm14 + vpaddd ymm0, ymm0, ymm1 + vpaddd ymm8, ymm8, ymm9 + vpxor ymm3, ymm3, ymm0 + vpxor ymm11, ymm11, ymm8 + vbroadcasti128 ymm4, xmmword ptr [ROT16+rip] + vpshufb ymm3, ymm3, ymm4 + vpshufb ymm11, ymm11, ymm4 + vpaddd ymm2, ymm2, ymm3 + vpaddd ymm10, ymm10, ymm11 + vpxor ymm1, ymm1, ymm2 + vpxor ymm9, ymm9, ymm10 + vpsrld ymm4, ymm1, 12 + vpslld ymm1, ymm1, 20 + vpor ymm1, ymm1, ymm4 + vpsrld ymm4, ymm9, 12 + vpslld ymm9, ymm9, 20 + vpor ymm9, ymm9, ymm4 + vpaddd ymm0, ymm0, ymm7 + vpaddd ymm8, ymm8, ymm15 + vpaddd ymm0, ymm0, ymm1 + vpaddd ymm8, ymm8, ymm9 + vpxor ymm3, ymm3, ymm0 + vpxor ymm11, ymm11, ymm8 + vbroadcasti128 ymm4, xmmword ptr [ROT8+rip] + vpshufb ymm3, ymm3, ymm4 + vpshufb ymm11, ymm11, ymm4 + vpaddd ymm2, ymm2, ymm3 + vpaddd ymm10, ymm10, ymm11 + vpxor ymm1, ymm1, ymm2 + vpxor ymm9, ymm9, ymm10 + vpsrld ymm4, ymm1, 7 + vpslld ymm1, ymm1, 25 + vpor ymm1, ymm1, ymm4 + vpsrld ymm4, ymm9, 7 + vpslld ymm9, ymm9, 25 + vpor ymm9, ymm9, ymm4 + vpshufd ymm0, ymm0, 0x39 + vpshufd ymm8, ymm8, 0x39 + vpshufd ymm3, ymm3, 0x4E + vpshufd ymm11, ymm11, 0x4E + vpshufd ymm2, ymm2, 0x93 + vpshufd ymm10, ymm10, 0x93 + dec al + je 9f + vmovdqa ymm4, ymmword ptr [rsp+0x40] + vmovdqa ymm5, ymmword ptr [rsp+0x80] + vshufps ymm12, ymm4, ymm5, 214 + vpshufd ymm13, ymm4, 0x0F + vpshufd ymm4, ymm12, 0x39 + vshufps ymm12, ymm6, ymm7, 250 + vpblendd ymm13, ymm13, ymm12, 0xAA + vpunpcklqdq ymm12, ymm7, ymm5 + vpblendd ymm12, ymm12, ymm6, 0x88 + vpshufd ymm12, ymm12, 0x78 + vpunpckhdq ymm5, ymm5, ymm7 + vpunpckldq ymm6, ymm6, ymm5 + vpshufd ymm7, ymm6, 0x1E + vmovdqa ymmword ptr [rsp+0x40], ymm13 + vmovdqa ymmword ptr [rsp+0x80], ymm12 + vmovdqa ymm12, ymmword ptr [rsp+0x60] + vmovdqa ymm13, ymmword ptr [rsp+0xA0] + vshufps ymm5, ymm12, ymm13, 214 + vpshufd ymm6, ymm12, 0x0F + vpshufd ymm12, ymm5, 0x39 + vshufps ymm5, ymm14, ymm15, 250 + vpblendd ymm6, ymm6, ymm5, 0xAA + vpunpcklqdq ymm5, ymm15, ymm13 + vpblendd ymm5, ymm5, ymm14, 0x88 + vpshufd ymm5, ymm5, 0x78 + vpunpckhdq ymm13, ymm13, ymm15 + vpunpckldq ymm14, ymm14, ymm13 + vpshufd ymm15, ymm14, 0x1E + vmovdqa ymm13, ymm6 + vmovdqa ymm14, ymm5 + vmovdqa ymm5, ymmword ptr [rsp+0x40] + vmovdqa ymm6, ymmword ptr [rsp+0x80] + jmp 9b +9: + vpxor ymm0, ymm0, ymm2 + vpxor ymm1, ymm1, ymm3 + vpxor ymm8, ymm8, ymm10 + vpxor ymm9, ymm9, ymm11 + mov eax, r13d + cmp rdx, r15 + jne 2b + vmovdqu xmmword ptr [rbx], xmm0 + vmovdqu xmmword ptr [rbx+0x10], xmm1 + vextracti128 xmmword ptr [rbx+0x20], ymm0, 0x01 + vextracti128 xmmword ptr [rbx+0x30], ymm1, 0x01 + vmovdqu xmmword ptr [rbx+0x40], xmm8 + vmovdqu xmmword ptr [rbx+0x50], xmm9 + vextracti128 xmmword ptr [rbx+0x60], ymm8, 0x01 + vextracti128 xmmword ptr [rbx+0x70], ymm9, 0x01 + vmovaps xmm8, xmmword ptr [rsp+0x280] + vmovaps xmm0, xmmword ptr [rsp+0x240] + vmovaps xmm1, xmmword ptr [rsp+0x250] + vmovaps xmm2, xmmword ptr [rsp+0x260] + vmovaps xmm3, xmmword ptr [rsp+0x270] + vblendvps xmm0, xmm0, xmm1, xmm8 + vblendvps xmm2, xmm2, xmm3, xmm8 + vmovaps xmmword ptr [rsp+0x240], xmm0 + vmovaps xmmword ptr [rsp+0x260], xmm2 + add rbx, 128 + add rdi, 32 + sub rsi, 4 +3: + test rsi, 0x2 + je 3f + vbroadcasti128 ymm0, xmmword ptr [rcx] + vbroadcasti128 ymm1, xmmword ptr [rcx+0x10] + vmovd xmm13, dword ptr [rsp+0x240] + vpinsrd xmm13, xmm13, dword ptr [rsp+0x260], 1 + vpinsrd xmm13, xmm13, dword ptr [BLAKE3_BLOCK_LEN+rip], 2 + vmovd xmm14, dword ptr [rsp+0x244] + vpinsrd xmm14, xmm14, dword ptr [rsp+0x264], 1 + vpinsrd xmm14, xmm14, dword ptr [BLAKE3_BLOCK_LEN+rip], 2 + vinserti128 ymm13, ymm13, xmm14, 0x01 + vbroadcasti128 ymm14, xmmword ptr [ROT16+rip] + vbroadcasti128 ymm15, xmmword ptr [ROT8+rip] + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + movzx eax, byte ptr [rbp+0x40] + or eax, r13d + xor edx, edx +.p2align 5 +2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + mov dword ptr [rsp+0x200], eax + vbroadcasti128 ymm2, xmmword ptr [BLAKE3_IV+rip] + vpbroadcastd ymm8, dword ptr [rsp+0x200] + vpblendd ymm3, ymm13, ymm8, 0x88 + vmovups ymm8, ymmword ptr [r8+rdx-0x40] + vinsertf128 ymm8, ymm8, xmmword ptr [r9+rdx-0x40], 0x01 + vmovups ymm9, ymmword ptr [r8+rdx-0x30] + vinsertf128 ymm9, ymm9, xmmword ptr [r9+rdx-0x30], 0x01 + vshufps ymm4, ymm8, ymm9, 136 + vshufps ymm5, ymm8, ymm9, 221 + vmovups ymm8, ymmword ptr [r8+rdx-0x20] + vinsertf128 ymm8, ymm8, xmmword ptr [r9+rdx-0x20], 0x01 + vmovups ymm9, ymmword ptr [r8+rdx-0x10] + vinsertf128 ymm9, ymm9, xmmword ptr [r9+rdx-0x10], 0x01 + vshufps ymm6, ymm8, ymm9, 136 + vshufps ymm7, ymm8, ymm9, 221 + vpshufd ymm6, ymm6, 0x93 + vpshufd ymm7, ymm7, 0x93 + mov al, 7 +9: + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm0, ymm0, ymm1 + vpxor ymm3, ymm3, ymm0 + vpshufb ymm3, ymm3, ymm14 + vpaddd ymm2, ymm2, ymm3 + vpxor ymm1, ymm1, ymm2 + vpsrld ymm8, ymm1, 12 + vpslld ymm1, ymm1, 20 + vpor ymm1, ymm1, ymm8 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm0, ymm0, ymm1 + vpxor ymm3, ymm3, ymm0 + vpshufb ymm3, ymm3, ymm15 + vpaddd ymm2, ymm2, ymm3 + vpxor ymm1, ymm1, ymm2 + vpsrld ymm8, ymm1, 7 + vpslld ymm1, ymm1, 25 + vpor ymm1, ymm1, ymm8 + vpshufd ymm0, ymm0, 0x93 + vpshufd ymm3, ymm3, 0x4E + vpshufd ymm2, ymm2, 0x39 + vpaddd ymm0, ymm0, ymm6 + vpaddd ymm0, ymm0, ymm1 + vpxor ymm3, ymm3, ymm0 + vpshufb ymm3, ymm3, ymm14 + vpaddd ymm2, ymm2, ymm3 + vpxor ymm1, ymm1, ymm2 + vpsrld ymm8, ymm1, 12 + vpslld ymm1, ymm1, 20 + vpor ymm1, ymm1, ymm8 + vpaddd ymm0, ymm0, ymm7 + vpaddd ymm0, ymm0, ymm1 + vpxor ymm3, ymm3, ymm0 + vpshufb ymm3, ymm3, ymm15 + vpaddd ymm2, ymm2, ymm3 + vpxor ymm1, ymm1, ymm2 + vpsrld ymm8, ymm1, 7 + vpslld ymm1, ymm1, 25 + vpor ymm1, ymm1, ymm8 + vpshufd ymm0, ymm0, 0x39 + vpshufd ymm3, ymm3, 0x4E + vpshufd ymm2, ymm2, 0x93 + dec al + jz 9f + vshufps ymm8, ymm4, ymm5, 214 + vpshufd ymm9, ymm4, 0x0F + vpshufd ymm4, ymm8, 0x39 + vshufps ymm8, ymm6, ymm7, 250 + vpblendd ymm9, ymm9, ymm8, 0xAA + vpunpcklqdq ymm8, ymm7, ymm5 + vpblendd ymm8, ymm8, ymm6, 0x88 + vpshufd ymm8, ymm8, 0x78 + vpunpckhdq ymm5, ymm5, ymm7 + vpunpckldq ymm6, ymm6, ymm5 + vpshufd ymm7, ymm6, 0x1E + vmovdqa ymm5, ymm9 + vmovdqa ymm6, ymm8 + jmp 9b +9: + vpxor ymm0, ymm0, ymm2 + vpxor ymm1, ymm1, ymm3 + mov eax, r13d + cmp rdx, r15 + jne 2b + vmovdqu xmmword ptr [rbx], xmm0 + vmovdqu xmmword ptr [rbx+0x10], xmm1 + vextracti128 xmmword ptr [rbx+0x20], ymm0, 0x01 + vextracti128 xmmword ptr [rbx+0x30], ymm1, 0x01 + vmovaps ymm8, ymmword ptr [rsp+0x280] + vmovaps ymm0, ymmword ptr [rsp+0x240] + vmovups ymm1, ymmword ptr [rsp+0x248] + vmovaps ymm2, ymmword ptr [rsp+0x260] + vmovups ymm3, ymmword ptr [rsp+0x268] + vblendvps ymm0, ymm0, ymm1, ymm8 + vblendvps ymm2, ymm2, ymm3, ymm8 + vmovaps ymmword ptr [rsp+0x240], ymm0 + vmovaps ymmword ptr [rsp+0x260], ymm2 + add rbx, 64 + add rdi, 16 + sub rsi, 2 +3: + test rsi, 0x1 + je 4b + vmovdqu xmm0, xmmword ptr [rcx] + vmovdqu xmm1, xmmword ptr [rcx+0x10] + vmovd xmm3, dword ptr [rsp+0x240] + vpinsrd xmm3, xmm3, dword ptr [rsp+0x260], 1 + vpinsrd xmm13, xmm3, dword ptr [BLAKE3_BLOCK_LEN+rip], 2 + vmovdqa xmm14, xmmword ptr [ROT16+rip] + vmovdqa xmm15, xmmword ptr [ROT8+rip] + mov r8, qword ptr [rdi] + movzx eax, byte ptr [rbp+0x40] + or eax, r13d + xor edx, edx +.p2align 5 +2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + vmovdqa xmm2, xmmword ptr [BLAKE3_IV+rip] + vmovdqa xmm3, xmm13 + vpinsrd xmm3, xmm3, eax, 3 + vmovups xmm8, xmmword ptr [r8+rdx-0x40] + vmovups xmm9, xmmword ptr [r8+rdx-0x30] + vshufps xmm4, xmm8, xmm9, 136 + vshufps xmm5, xmm8, xmm9, 221 + vmovups xmm8, xmmword ptr [r8+rdx-0x20] + vmovups xmm9, xmmword ptr [r8+rdx-0x10] + vshufps xmm6, xmm8, xmm9, 136 + vshufps xmm7, xmm8, xmm9, 221 + vpshufd xmm6, xmm6, 0x93 + vpshufd xmm7, xmm7, 0x93 + mov al, 7 +9: + vpaddd xmm0, xmm0, xmm4 + vpaddd xmm0, xmm0, xmm1 + vpxor xmm3, xmm3, xmm0 + vpshufb xmm3, xmm3, xmm14 + vpaddd xmm2, xmm2, xmm3 + vpxor xmm1, xmm1, xmm2 + vpsrld xmm8, xmm1, 12 + vpslld xmm1, xmm1, 20 + vpor xmm1, xmm1, xmm8 + vpaddd xmm0, xmm0, xmm5 + vpaddd xmm0, xmm0, xmm1 + vpxor xmm3, xmm3, xmm0 + vpshufb xmm3, xmm3, xmm15 + vpaddd xmm2, xmm2, xmm3 + vpxor xmm1, xmm1, xmm2 + vpsrld xmm8, xmm1, 7 + vpslld xmm1, xmm1, 25 + vpor xmm1, xmm1, xmm8 + vpshufd xmm0, xmm0, 0x93 + vpshufd xmm3, xmm3, 0x4E + vpshufd xmm2, xmm2, 0x39 + vpaddd xmm0, xmm0, xmm6 + vpaddd xmm0, xmm0, xmm1 + vpxor xmm3, xmm3, xmm0 + vpshufb xmm3, xmm3, xmm14 + vpaddd xmm2, xmm2, xmm3 + vpxor xmm1, xmm1, xmm2 + vpsrld xmm8, xmm1, 12 + vpslld xmm1, xmm1, 20 + vpor xmm1, xmm1, xmm8 + vpaddd xmm0, xmm0, xmm7 + vpaddd xmm0, xmm0, xmm1 + vpxor xmm3, xmm3, xmm0 + vpshufb xmm3, xmm3, xmm15 + vpaddd xmm2, xmm2, xmm3 + vpxor xmm1, xmm1, xmm2 + vpsrld xmm8, xmm1, 7 + vpslld xmm1, xmm1, 25 + vpor xmm1, xmm1, xmm8 + vpshufd xmm0, xmm0, 0x39 + vpshufd xmm3, xmm3, 0x4E + vpshufd xmm2, xmm2, 0x93 + dec al + jz 9f + vshufps xmm8, xmm4, xmm5, 214 + vpshufd xmm9, xmm4, 0x0F + vpshufd xmm4, xmm8, 0x39 + vshufps xmm8, xmm6, xmm7, 250 + vpblendd xmm9, xmm9, xmm8, 0xAA + vpunpcklqdq xmm8, xmm7, xmm5 + vpblendd xmm8, xmm8, xmm6, 0x88 + vpshufd xmm8, xmm8, 0x78 + vpunpckhdq xmm5, xmm5, xmm7 + vpunpckldq xmm6, xmm6, xmm5 + vpshufd xmm7, xmm6, 0x1E + vmovdqa xmm5, xmm9 + vmovdqa xmm6, xmm8 + jmp 9b +9: + vpxor xmm0, xmm0, xmm2 + vpxor xmm1, xmm1, xmm3 + mov eax, r13d + cmp rdx, r15 + jne 2b + vmovdqu xmmword ptr [rbx], xmm0 + vmovdqu xmmword ptr [rbx+0x10], xmm1 + jmp 4b + + +#ifdef __APPLE__ +.static_data +#else +.section .rodata +#endif +.p2align 6 +ADD0: + .long 0, 1, 2, 3, 4, 5, 6, 7 +ADD1: + .long 8, 8, 8, 8, 8, 8, 8, 8 +BLAKE3_IV_0: + .long 0x6A09E667, 0x6A09E667, 0x6A09E667, 0x6A09E667 + .long 0x6A09E667, 0x6A09E667, 0x6A09E667, 0x6A09E667 +BLAKE3_IV_1: + .long 0xBB67AE85, 0xBB67AE85, 0xBB67AE85, 0xBB67AE85 + .long 0xBB67AE85, 0xBB67AE85, 0xBB67AE85, 0xBB67AE85 +BLAKE3_IV_2: + .long 0x3C6EF372, 0x3C6EF372, 0x3C6EF372, 0x3C6EF372 + .long 0x3C6EF372, 0x3C6EF372, 0x3C6EF372, 0x3C6EF372 +BLAKE3_IV_3: + .long 0xA54FF53A, 0xA54FF53A, 0xA54FF53A, 0xA54FF53A + .long 0xA54FF53A, 0xA54FF53A, 0xA54FF53A, 0xA54FF53A +BLAKE3_BLOCK_LEN: + .long 0x00000040, 0x00000040, 0x00000040, 0x00000040 + .long 0x00000040, 0x00000040, 0x00000040, 0x00000040 +ROT16: + .byte 2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13 +ROT8: + .byte 1, 2, 3, 0, 5, 6, 7, 4, 9, 10, 11, 8, 13, 14, 15, 12 +CMP_MSB_MASK: + .long 0x80000000, 0x80000000, 0x80000000, 0x80000000 + .long 0x80000000, 0x80000000, 0x80000000, 0x80000000 +BLAKE3_IV: + .long 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A + diff --git a/src/third_party/blake3/blake3_avx2_x86-64_windows_gnu.S b/src/third_party/blake3/blake3_avx2_x86-64_windows_gnu.S new file mode 100644 index 0000000..bb58d2a --- /dev/null +++ b/src/third_party/blake3/blake3_avx2_x86-64_windows_gnu.S @@ -0,0 +1,1817 @@ +.intel_syntax noprefix +.global _blake3_hash_many_avx2 +.global blake3_hash_many_avx2 +.section .text + .p2align 6 +_blake3_hash_many_avx2: +blake3_hash_many_avx2: + push r15 + push r14 + push r13 + push r12 + push rsi + push rdi + push rbx + push rbp + mov rbp, rsp + sub rsp, 880 + and rsp, 0xFFFFFFFFFFFFFFC0 + vmovdqa xmmword ptr [rsp+0x2D0], xmm6 + vmovdqa xmmword ptr [rsp+0x2E0], xmm7 + vmovdqa xmmword ptr [rsp+0x2F0], xmm8 + vmovdqa xmmword ptr [rsp+0x300], xmm9 + vmovdqa xmmword ptr [rsp+0x310], xmm10 + vmovdqa xmmword ptr [rsp+0x320], xmm11 + vmovdqa xmmword ptr [rsp+0x330], xmm12 + vmovdqa xmmword ptr [rsp+0x340], xmm13 + vmovdqa xmmword ptr [rsp+0x350], xmm14 + vmovdqa xmmword ptr [rsp+0x360], xmm15 + mov rdi, rcx + mov rsi, rdx + mov rdx, r8 + mov rcx, r9 + mov r8, qword ptr [rbp+0x68] + movzx r9, byte ptr [rbp+0x70] + neg r9d + vmovd xmm0, r9d + vpbroadcastd ymm0, xmm0 + vmovdqa ymmword ptr [rsp+0x260], ymm0 + vpand ymm1, ymm0, ymmword ptr [ADD0+rip] + vpand ymm2, ymm0, ymmword ptr [ADD1+rip] + vmovdqa ymmword ptr [rsp+0x2A0], ymm2 + vmovd xmm2, r8d + vpbroadcastd ymm2, xmm2 + vpaddd ymm2, ymm2, ymm1 + vmovdqa ymmword ptr [rsp+0x220], ymm2 + vpxor ymm1, ymm1, ymmword ptr [CMP_MSB_MASK+rip] + vpxor ymm2, ymm2, ymmword ptr [CMP_MSB_MASK+rip] + vpcmpgtd ymm2, ymm1, ymm2 + shr r8, 32 + vmovd xmm3, r8d + vpbroadcastd ymm3, xmm3 + vpsubd ymm3, ymm3, ymm2 + vmovdqa ymmword ptr [rsp+0x240], ymm3 + shl rdx, 6 + mov qword ptr [rsp+0x2C0], rdx + cmp rsi, 8 + jc 3f +2: + vpbroadcastd ymm0, dword ptr [rcx] + vpbroadcastd ymm1, dword ptr [rcx+0x4] + vpbroadcastd ymm2, dword ptr [rcx+0x8] + vpbroadcastd ymm3, dword ptr [rcx+0xC] + vpbroadcastd ymm4, dword ptr [rcx+0x10] + vpbroadcastd ymm5, dword ptr [rcx+0x14] + vpbroadcastd ymm6, dword ptr [rcx+0x18] + vpbroadcastd ymm7, dword ptr [rcx+0x1C] + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + mov r10, qword ptr [rdi+0x10] + mov r11, qword ptr [rdi+0x18] + mov r12, qword ptr [rdi+0x20] + mov r13, qword ptr [rdi+0x28] + mov r14, qword ptr [rdi+0x30] + mov r15, qword ptr [rdi+0x38] + movzx eax, byte ptr [rbp+0x78] + movzx ebx, byte ptr [rbp+0x80] + or eax, ebx + xor edx, edx +.p2align 5 +9: + movzx ebx, byte ptr [rbp+0x88] + or ebx, eax + add rdx, 64 + cmp rdx, qword ptr [rsp+0x2C0] + cmove eax, ebx + mov dword ptr [rsp+0x200], eax + vmovups xmm8, xmmword ptr [r8+rdx-0x40] + vinsertf128 ymm8, ymm8, xmmword ptr [r12+rdx-0x40], 0x01 + vmovups xmm9, xmmword ptr [r9+rdx-0x40] + vinsertf128 ymm9, ymm9, xmmword ptr [r13+rdx-0x40], 0x01 + vunpcklpd ymm12, ymm8, ymm9 + vunpckhpd ymm13, ymm8, ymm9 + vmovups xmm10, xmmword ptr [r10+rdx-0x40] + vinsertf128 ymm10, ymm10, xmmword ptr [r14+rdx-0x40], 0x01 + vmovups xmm11, xmmword ptr [r11+rdx-0x40] + vinsertf128 ymm11, ymm11, xmmword ptr [r15+rdx-0x40], 0x01 + vunpcklpd ymm14, ymm10, ymm11 + vunpckhpd ymm15, ymm10, ymm11 + vshufps ymm8, ymm12, ymm14, 136 + vmovaps ymmword ptr [rsp], ymm8 + vshufps ymm9, ymm12, ymm14, 221 + vmovaps ymmword ptr [rsp+0x20], ymm9 + vshufps ymm10, ymm13, ymm15, 136 + vmovaps ymmword ptr [rsp+0x40], ymm10 + vshufps ymm11, ymm13, ymm15, 221 + vmovaps ymmword ptr [rsp+0x60], ymm11 + vmovups xmm8, xmmword ptr [r8+rdx-0x30] + vinsertf128 ymm8, ymm8, xmmword ptr [r12+rdx-0x30], 0x01 + vmovups xmm9, xmmword ptr [r9+rdx-0x30] + vinsertf128 ymm9, ymm9, xmmword ptr [r13+rdx-0x30], 0x01 + vunpcklpd ymm12, ymm8, ymm9 + vunpckhpd ymm13, ymm8, ymm9 + vmovups xmm10, xmmword ptr [r10+rdx-0x30] + vinsertf128 ymm10, ymm10, xmmword ptr [r14+rdx-0x30], 0x01 + vmovups xmm11, xmmword ptr [r11+rdx-0x30] + vinsertf128 ymm11, ymm11, xmmword ptr [r15+rdx-0x30], 0x01 + vunpcklpd ymm14, ymm10, ymm11 + vunpckhpd ymm15, ymm10, ymm11 + vshufps ymm8, ymm12, ymm14, 136 + vmovaps ymmword ptr [rsp+0x80], ymm8 + vshufps ymm9, ymm12, ymm14, 221 + vmovaps ymmword ptr [rsp+0xA0], ymm9 + vshufps ymm10, ymm13, ymm15, 136 + vmovaps ymmword ptr [rsp+0xC0], ymm10 + vshufps ymm11, ymm13, ymm15, 221 + vmovaps ymmword ptr [rsp+0xE0], ymm11 + vmovups xmm8, xmmword ptr [r8+rdx-0x20] + vinsertf128 ymm8, ymm8, xmmword ptr [r12+rdx-0x20], 0x01 + vmovups xmm9, xmmword ptr [r9+rdx-0x20] + vinsertf128 ymm9, ymm9, xmmword ptr [r13+rdx-0x20], 0x01 + vunpcklpd ymm12, ymm8, ymm9 + vunpckhpd ymm13, ymm8, ymm9 + vmovups xmm10, xmmword ptr [r10+rdx-0x20] + vinsertf128 ymm10, ymm10, xmmword ptr [r14+rdx-0x20], 0x01 + vmovups xmm11, xmmword ptr [r11+rdx-0x20] + vinsertf128 ymm11, ymm11, xmmword ptr [r15+rdx-0x20], 0x01 + vunpcklpd ymm14, ymm10, ymm11 + vunpckhpd ymm15, ymm10, ymm11 + vshufps ymm8, ymm12, ymm14, 136 + vmovaps ymmword ptr [rsp+0x100], ymm8 + vshufps ymm9, ymm12, ymm14, 221 + vmovaps ymmword ptr [rsp+0x120], ymm9 + vshufps ymm10, ymm13, ymm15, 136 + vmovaps ymmword ptr [rsp+0x140], ymm10 + vshufps ymm11, ymm13, ymm15, 221 + vmovaps ymmword ptr [rsp+0x160], ymm11 + vmovups xmm8, xmmword ptr [r8+rdx-0x10] + vinsertf128 ymm8, ymm8, xmmword ptr [r12+rdx-0x10], 0x01 + vmovups xmm9, xmmword ptr [r9+rdx-0x10] + vinsertf128 ymm9, ymm9, xmmword ptr [r13+rdx-0x10], 0x01 + vunpcklpd ymm12, ymm8, ymm9 + vunpckhpd ymm13, ymm8, ymm9 + vmovups xmm10, xmmword ptr [r10+rdx-0x10] + vinsertf128 ymm10, ymm10, xmmword ptr [r14+rdx-0x10], 0x01 + vmovups xmm11, xmmword ptr [r11+rdx-0x10] + vinsertf128 ymm11, ymm11, xmmword ptr [r15+rdx-0x10], 0x01 + vunpcklpd ymm14, ymm10, ymm11 + vunpckhpd ymm15, ymm10, ymm11 + vshufps ymm8, ymm12, ymm14, 136 + vmovaps ymmword ptr [rsp+0x180], ymm8 + vshufps ymm9, ymm12, ymm14, 221 + vmovaps ymmword ptr [rsp+0x1A0], ymm9 + vshufps ymm10, ymm13, ymm15, 136 + vmovaps ymmword ptr [rsp+0x1C0], ymm10 + vshufps ymm11, ymm13, ymm15, 221 + vmovaps ymmword ptr [rsp+0x1E0], ymm11 + vpbroadcastd ymm15, dword ptr [rsp+0x200] + prefetcht0 [r8+rdx+0x80] + prefetcht0 [r12+rdx+0x80] + prefetcht0 [r9+rdx+0x80] + prefetcht0 [r13+rdx+0x80] + prefetcht0 [r10+rdx+0x80] + prefetcht0 [r14+rdx+0x80] + prefetcht0 [r11+rdx+0x80] + prefetcht0 [r15+rdx+0x80] + vpaddd ymm0, ymm0, ymmword ptr [rsp] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x40] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x80] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0xC0] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm0, ymmword ptr [rsp+0x220] + vpxor ymm13, ymm1, ymmword ptr [rsp+0x240] + vpxor ymm14, ymm2, ymmword ptr [BLAKE3_BLOCK_LEN+rip] + vpxor ymm15, ymm3, ymm15 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [BLAKE3_IV_0+rip] + vpaddd ymm9, ymm13, ymmword ptr [BLAKE3_IV_1+rip] + vpaddd ymm10, ymm14, ymmword ptr [BLAKE3_IV_2+rip] + vpaddd ymm11, ymm15, ymmword ptr [BLAKE3_IV_3+rip] + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x20] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x60] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0xA0] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0xE0] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x100] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x140] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x180] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x1C0] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x120] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x160] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x1A0] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x1E0] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x40] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x60] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0xE0] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x80] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0xC0] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x140] + vpaddd ymm2, ymm2, ymmword ptr [rsp] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x1A0] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x20] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x180] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x120] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x1E0] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x160] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0xA0] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x1C0] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x100] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x60] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x140] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x1A0] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0xE0] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x80] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x180] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x40] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x1C0] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0xC0] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x120] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x160] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x100] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0xA0] + vpaddd ymm1, ymm1, ymmword ptr [rsp] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x1E0] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x20] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x140] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x180] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x1C0] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x1A0] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0xE0] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x120] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x60] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x1E0] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x80] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x160] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0xA0] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x20] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x40] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x100] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0xC0] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x180] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x120] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x1E0] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x1C0] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x1A0] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x160] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x140] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x100] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0xE0] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0xA0] + vpaddd ymm2, ymm2, ymmword ptr [rsp] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0xC0] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x40] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x60] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x20] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x80] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x120] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x160] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x100] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x1E0] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x1C0] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0xA0] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x180] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x20] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x1A0] + vpaddd ymm1, ymm1, ymmword ptr [rsp] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x40] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x80] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x60] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x140] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0xC0] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0xE0] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x160] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0xA0] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x20] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x100] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x1E0] + vpaddd ymm1, ymm1, ymmword ptr [rsp] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x120] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0xC0] + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxor ymm12, ymm12, ymm0 + vpxor ymm13, ymm13, ymm1 + vpxor ymm14, ymm14, ymm2 + vpxor ymm15, ymm15, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpshufb ymm15, ymm15, ymm8 + vpaddd ymm8, ymm12, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxor ymm4, ymm4, ymm8 + vpxor ymm5, ymm5, ymm9 + vpxor ymm6, ymm6, ymm10 + vpxor ymm7, ymm7, ymm11 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x1C0] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x40] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x60] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0xE0] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT16+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vmovdqa ymmword ptr [rsp+0x200], ymm8 + vpsrld ymm8, ymm5, 12 + vpslld ymm5, ymm5, 20 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 12 + vpslld ymm6, ymm6, 20 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 12 + vpslld ymm7, ymm7, 20 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 12 + vpslld ymm4, ymm4, 20 + vpor ymm4, ymm4, ymm8 + vpaddd ymm0, ymm0, ymmword ptr [rsp+0x140] + vpaddd ymm1, ymm1, ymmword ptr [rsp+0x180] + vpaddd ymm2, ymm2, ymmword ptr [rsp+0x80] + vpaddd ymm3, ymm3, ymmword ptr [rsp+0x1A0] + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxor ymm15, ymm15, ymm0 + vpxor ymm12, ymm12, ymm1 + vpxor ymm13, ymm13, ymm2 + vpxor ymm14, ymm14, ymm3 + vbroadcasti128 ymm8, xmmword ptr [ROT8+rip] + vpshufb ymm15, ymm15, ymm8 + vpshufb ymm12, ymm12, ymm8 + vpshufb ymm13, ymm13, ymm8 + vpshufb ymm14, ymm14, ymm8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm13, ymmword ptr [rsp+0x200] + vpaddd ymm9, ymm9, ymm14 + vpxor ymm5, ymm5, ymm10 + vpxor ymm6, ymm6, ymm11 + vpxor ymm7, ymm7, ymm8 + vpxor ymm4, ymm4, ymm9 + vpxor ymm0, ymm0, ymm8 + vpxor ymm1, ymm1, ymm9 + vpxor ymm2, ymm2, ymm10 + vpxor ymm3, ymm3, ymm11 + vpsrld ymm8, ymm5, 7 + vpslld ymm5, ymm5, 25 + vpor ymm5, ymm5, ymm8 + vpsrld ymm8, ymm6, 7 + vpslld ymm6, ymm6, 25 + vpor ymm6, ymm6, ymm8 + vpsrld ymm8, ymm7, 7 + vpslld ymm7, ymm7, 25 + vpor ymm7, ymm7, ymm8 + vpsrld ymm8, ymm4, 7 + vpslld ymm4, ymm4, 25 + vpor ymm4, ymm4, ymm8 + vpxor ymm4, ymm4, ymm12 + vpxor ymm5, ymm5, ymm13 + vpxor ymm6, ymm6, ymm14 + vpxor ymm7, ymm7, ymm15 + movzx eax, byte ptr [rbp+0x78] + jne 9b + mov rbx, qword ptr [rbp+0x90] + vunpcklps ymm8, ymm0, ymm1 + vunpcklps ymm9, ymm2, ymm3 + vunpckhps ymm10, ymm0, ymm1 + vunpcklps ymm11, ymm4, ymm5 + vunpcklps ymm0, ymm6, ymm7 + vshufps ymm12, ymm8, ymm9, 78 + vblendps ymm1, ymm8, ymm12, 0xCC + vshufps ymm8, ymm11, ymm0, 78 + vunpckhps ymm13, ymm2, ymm3 + vblendps ymm2, ymm11, ymm8, 0xCC + vblendps ymm3, ymm12, ymm9, 0xCC + vperm2f128 ymm12, ymm1, ymm2, 0x20 + vmovups ymmword ptr [rbx], ymm12 + vunpckhps ymm14, ymm4, ymm5 + vblendps ymm4, ymm8, ymm0, 0xCC + vunpckhps ymm15, ymm6, ymm7 + vperm2f128 ymm7, ymm3, ymm4, 0x20 + vmovups ymmword ptr [rbx+0x20], ymm7 + vshufps ymm5, ymm10, ymm13, 78 + vblendps ymm6, ymm5, ymm13, 0xCC + vshufps ymm13, ymm14, ymm15, 78 + vblendps ymm10, ymm10, ymm5, 0xCC + vblendps ymm14, ymm14, ymm13, 0xCC + vperm2f128 ymm8, ymm10, ymm14, 0x20 + vmovups ymmword ptr [rbx+0x40], ymm8 + vblendps ymm15, ymm13, ymm15, 0xCC + vperm2f128 ymm13, ymm6, ymm15, 0x20 + vmovups ymmword ptr [rbx+0x60], ymm13 + vperm2f128 ymm9, ymm1, ymm2, 0x31 + vperm2f128 ymm11, ymm3, ymm4, 0x31 + vmovups ymmword ptr [rbx+0x80], ymm9 + vperm2f128 ymm14, ymm10, ymm14, 0x31 + vperm2f128 ymm15, ymm6, ymm15, 0x31 + vmovups ymmword ptr [rbx+0xA0], ymm11 + vmovups ymmword ptr [rbx+0xC0], ymm14 + vmovups ymmword ptr [rbx+0xE0], ymm15 + vmovdqa ymm0, ymmword ptr [rsp+0x2A0] + vpaddd ymm1, ymm0, ymmword ptr [rsp+0x220] + vmovdqa ymmword ptr [rsp+0x220], ymm1 + vpxor ymm0, ymm0, ymmword ptr [CMP_MSB_MASK+rip] + vpxor ymm2, ymm1, ymmword ptr [CMP_MSB_MASK+rip] + vpcmpgtd ymm2, ymm0, ymm2 + vmovdqa ymm0, ymmword ptr [rsp+0x240] + vpsubd ymm2, ymm0, ymm2 + vmovdqa ymmword ptr [rsp+0x240], ymm2 + add rdi, 64 + add rbx, 256 + mov qword ptr [rbp+0x90], rbx + sub rsi, 8 + cmp rsi, 8 + jnc 2b + test rsi, rsi + jnz 3f +4: + vzeroupper + vmovdqa xmm6, xmmword ptr [rsp+0x2D0] + vmovdqa xmm7, xmmword ptr [rsp+0x2E0] + vmovdqa xmm8, xmmword ptr [rsp+0x2F0] + vmovdqa xmm9, xmmword ptr [rsp+0x300] + vmovdqa xmm10, xmmword ptr [rsp+0x310] + vmovdqa xmm11, xmmword ptr [rsp+0x320] + vmovdqa xmm12, xmmword ptr [rsp+0x330] + vmovdqa xmm13, xmmword ptr [rsp+0x340] + vmovdqa xmm14, xmmword ptr [rsp+0x350] + vmovdqa xmm15, xmmword ptr [rsp+0x360] + mov rsp, rbp + pop rbp + pop rbx + pop rdi + pop rsi + pop r12 + pop r13 + pop r14 + pop r15 + ret +.p2align 5 +3: + mov rbx, qword ptr [rbp+0x90] + mov r15, qword ptr [rsp+0x2C0] + movzx r13d, byte ptr [rbp+0x78] + movzx r12d, byte ptr [rbp+0x88] + test rsi, 0x4 + je 3f + vbroadcasti128 ymm0, xmmword ptr [rcx] + vbroadcasti128 ymm1, xmmword ptr [rcx+0x10] + vmovdqa ymm8, ymm0 + vmovdqa ymm9, ymm1 + vbroadcasti128 ymm12, xmmword ptr [rsp+0x220] + vbroadcasti128 ymm13, xmmword ptr [rsp+0x240] + vpunpckldq ymm14, ymm12, ymm13 + vpunpckhdq ymm15, ymm12, ymm13 + vpermq ymm14, ymm14, 0x50 + vpermq ymm15, ymm15, 0x50 + vbroadcasti128 ymm12, xmmword ptr [BLAKE3_BLOCK_LEN+rip] + vpblendd ymm14, ymm14, ymm12, 0x44 + vpblendd ymm15, ymm15, ymm12, 0x44 + vmovdqa ymmword ptr [rsp], ymm14 + vmovdqa ymmword ptr [rsp+0x20], ymm15 + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + mov r10, qword ptr [rdi+0x10] + mov r11, qword ptr [rdi+0x18] + movzx eax, byte ptr [rbp+0x80] + or eax, r13d + xor edx, edx +.p2align 5 +2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + mov dword ptr [rsp+0x200], eax + vmovups ymm2, ymmword ptr [r8+rdx-0x40] + vinsertf128 ymm2, ymm2, xmmword ptr [r9+rdx-0x40], 0x01 + vmovups ymm3, ymmword ptr [r8+rdx-0x30] + vinsertf128 ymm3, ymm3, xmmword ptr [r9+rdx-0x30], 0x01 + vshufps ymm4, ymm2, ymm3, 136 + vshufps ymm5, ymm2, ymm3, 221 + vmovups ymm2, ymmword ptr [r8+rdx-0x20] + vinsertf128 ymm2, ymm2, xmmword ptr [r9+rdx-0x20], 0x01 + vmovups ymm3, ymmword ptr [r8+rdx-0x10] + vinsertf128 ymm3, ymm3, xmmword ptr [r9+rdx-0x10], 0x01 + vshufps ymm6, ymm2, ymm3, 136 + vshufps ymm7, ymm2, ymm3, 221 + vpshufd ymm6, ymm6, 0x93 + vpshufd ymm7, ymm7, 0x93 + vmovups ymm10, ymmword ptr [r10+rdx-0x40] + vinsertf128 ymm10, ymm10, xmmword ptr [r11+rdx-0x40], 0x01 + vmovups ymm11, ymmword ptr [r10+rdx-0x30] + vinsertf128 ymm11, ymm11, xmmword ptr [r11+rdx-0x30], 0x01 + vshufps ymm12, ymm10, ymm11, 136 + vshufps ymm13, ymm10, ymm11, 221 + vmovups ymm10, ymmword ptr [r10+rdx-0x20] + vinsertf128 ymm10, ymm10, xmmword ptr [r11+rdx-0x20], 0x01 + vmovups ymm11, ymmword ptr [r10+rdx-0x10] + vinsertf128 ymm11, ymm11, xmmword ptr [r11+rdx-0x10], 0x01 + vshufps ymm14, ymm10, ymm11, 136 + vshufps ymm15, ymm10, ymm11, 221 + vpshufd ymm14, ymm14, 0x93 + vpshufd ymm15, ymm15, 0x93 + vpbroadcastd ymm2, dword ptr [rsp+0x200] + vmovdqa ymm3, ymmword ptr [rsp] + vmovdqa ymm11, ymmword ptr [rsp+0x20] + vpblendd ymm3, ymm3, ymm2, 0x88 + vpblendd ymm11, ymm11, ymm2, 0x88 + vbroadcasti128 ymm2, xmmword ptr [BLAKE3_IV+rip] + vmovdqa ymm10, ymm2 + mov al, 7 +9: + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm8, ymm8, ymm12 + vmovdqa ymmword ptr [rsp+0x40], ymm4 + nop + vmovdqa ymmword ptr [rsp+0x60], ymm12 + nop + vpaddd ymm0, ymm0, ymm1 + vpaddd ymm8, ymm8, ymm9 + vpxor ymm3, ymm3, ymm0 + vpxor ymm11, ymm11, ymm8 + vbroadcasti128 ymm4, xmmword ptr [ROT16+rip] + vpshufb ymm3, ymm3, ymm4 + vpshufb ymm11, ymm11, ymm4 + vpaddd ymm2, ymm2, ymm3 + vpaddd ymm10, ymm10, ymm11 + vpxor ymm1, ymm1, ymm2 + vpxor ymm9, ymm9, ymm10 + vpsrld ymm4, ymm1, 12 + vpslld ymm1, ymm1, 20 + vpor ymm1, ymm1, ymm4 + vpsrld ymm4, ymm9, 12 + vpslld ymm9, ymm9, 20 + vpor ymm9, ymm9, ymm4 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm0, ymm0, ymm1 + vpaddd ymm8, ymm8, ymm9 + vmovdqa ymmword ptr [rsp+0x80], ymm5 + vmovdqa ymmword ptr [rsp+0xA0], ymm13 + vpxor ymm3, ymm3, ymm0 + vpxor ymm11, ymm11, ymm8 + vbroadcasti128 ymm4, xmmword ptr [ROT8+rip] + vpshufb ymm3, ymm3, ymm4 + vpshufb ymm11, ymm11, ymm4 + vpaddd ymm2, ymm2, ymm3 + vpaddd ymm10, ymm10, ymm11 + vpxor ymm1, ymm1, ymm2 + vpxor ymm9, ymm9, ymm10 + vpsrld ymm4, ymm1, 7 + vpslld ymm1, ymm1, 25 + vpor ymm1, ymm1, ymm4 + vpsrld ymm4, ymm9, 7 + vpslld ymm9, ymm9, 25 + vpor ymm9, ymm9, ymm4 + vpshufd ymm0, ymm0, 0x93 + vpshufd ymm8, ymm8, 0x93 + vpshufd ymm3, ymm3, 0x4E + vpshufd ymm11, ymm11, 0x4E + vpshufd ymm2, ymm2, 0x39 + vpshufd ymm10, ymm10, 0x39 + vpaddd ymm0, ymm0, ymm6 + vpaddd ymm8, ymm8, ymm14 + vpaddd ymm0, ymm0, ymm1 + vpaddd ymm8, ymm8, ymm9 + vpxor ymm3, ymm3, ymm0 + vpxor ymm11, ymm11, ymm8 + vbroadcasti128 ymm4, xmmword ptr [ROT16+rip] + vpshufb ymm3, ymm3, ymm4 + vpshufb ymm11, ymm11, ymm4 + vpaddd ymm2, ymm2, ymm3 + vpaddd ymm10, ymm10, ymm11 + vpxor ymm1, ymm1, ymm2 + vpxor ymm9, ymm9, ymm10 + vpsrld ymm4, ymm1, 12 + vpslld ymm1, ymm1, 20 + vpor ymm1, ymm1, ymm4 + vpsrld ymm4, ymm9, 12 + vpslld ymm9, ymm9, 20 + vpor ymm9, ymm9, ymm4 + vpaddd ymm0, ymm0, ymm7 + vpaddd ymm8, ymm8, ymm15 + vpaddd ymm0, ymm0, ymm1 + vpaddd ymm8, ymm8, ymm9 + vpxor ymm3, ymm3, ymm0 + vpxor ymm11, ymm11, ymm8 + vbroadcasti128 ymm4, xmmword ptr [ROT8+rip] + vpshufb ymm3, ymm3, ymm4 + vpshufb ymm11, ymm11, ymm4 + vpaddd ymm2, ymm2, ymm3 + vpaddd ymm10, ymm10, ymm11 + vpxor ymm1, ymm1, ymm2 + vpxor ymm9, ymm9, ymm10 + vpsrld ymm4, ymm1, 7 + vpslld ymm1, ymm1, 25 + vpor ymm1, ymm1, ymm4 + vpsrld ymm4, ymm9, 7 + vpslld ymm9, ymm9, 25 + vpor ymm9, ymm9, ymm4 + vpshufd ymm0, ymm0, 0x39 + vpshufd ymm8, ymm8, 0x39 + vpshufd ymm3, ymm3, 0x4E + vpshufd ymm11, ymm11, 0x4E + vpshufd ymm2, ymm2, 0x93 + vpshufd ymm10, ymm10, 0x93 + dec al + je 9f + vmovdqa ymm4, ymmword ptr [rsp+0x40] + vmovdqa ymm5, ymmword ptr [rsp+0x80] + vshufps ymm12, ymm4, ymm5, 214 + vpshufd ymm13, ymm4, 0x0F + vpshufd ymm4, ymm12, 0x39 + vshufps ymm12, ymm6, ymm7, 250 + vpblendd ymm13, ymm13, ymm12, 0xAA + vpunpcklqdq ymm12, ymm7, ymm5 + vpblendd ymm12, ymm12, ymm6, 0x88 + vpshufd ymm12, ymm12, 0x78 + vpunpckhdq ymm5, ymm5, ymm7 + vpunpckldq ymm6, ymm6, ymm5 + vpshufd ymm7, ymm6, 0x1E + vmovdqa ymmword ptr [rsp+0x40], ymm13 + vmovdqa ymmword ptr [rsp+0x80], ymm12 + vmovdqa ymm12, ymmword ptr [rsp+0x60] + vmovdqa ymm13, ymmword ptr [rsp+0xA0] + vshufps ymm5, ymm12, ymm13, 214 + vpshufd ymm6, ymm12, 0x0F + vpshufd ymm12, ymm5, 0x39 + vshufps ymm5, ymm14, ymm15, 250 + vpblendd ymm6, ymm6, ymm5, 0xAA + vpunpcklqdq ymm5, ymm15, ymm13 + vpblendd ymm5, ymm5, ymm14, 0x88 + vpshufd ymm5, ymm5, 0x78 + vpunpckhdq ymm13, ymm13, ymm15 + vpunpckldq ymm14, ymm14, ymm13 + vpshufd ymm15, ymm14, 0x1E + vmovdqa ymm13, ymm6 + vmovdqa ymm14, ymm5 + vmovdqa ymm5, ymmword ptr [rsp+0x40] + vmovdqa ymm6, ymmword ptr [rsp+0x80] + jmp 9b +9: + vpxor ymm0, ymm0, ymm2 + vpxor ymm1, ymm1, ymm3 + vpxor ymm8, ymm8, ymm10 + vpxor ymm9, ymm9, ymm11 + mov eax, r13d + cmp rdx, r15 + jne 2b + vmovdqu xmmword ptr [rbx], xmm0 + vmovdqu xmmword ptr [rbx+0x10], xmm1 + vextracti128 xmmword ptr [rbx+0x20], ymm0, 0x01 + vextracti128 xmmword ptr [rbx+0x30], ymm1, 0x01 + vmovdqu xmmword ptr [rbx+0x40], xmm8 + vmovdqu xmmword ptr [rbx+0x50], xmm9 + vextracti128 xmmword ptr [rbx+0x60], ymm8, 0x01 + vextracti128 xmmword ptr [rbx+0x70], ymm9, 0x01 + vmovaps xmm8, xmmword ptr [rsp+0x260] + vmovaps xmm0, xmmword ptr [rsp+0x220] + vmovaps xmm1, xmmword ptr [rsp+0x230] + vmovaps xmm2, xmmword ptr [rsp+0x240] + vmovaps xmm3, xmmword ptr [rsp+0x250] + vblendvps xmm0, xmm0, xmm1, xmm8 + vblendvps xmm2, xmm2, xmm3, xmm8 + vmovaps xmmword ptr [rsp+0x220], xmm0 + vmovaps xmmword ptr [rsp+0x240], xmm2 + add rbx, 128 + add rdi, 32 + sub rsi, 4 +3: + test rsi, 0x2 + je 3f + vbroadcasti128 ymm0, xmmword ptr [rcx] + vbroadcasti128 ymm1, xmmword ptr [rcx+0x10] + vmovd xmm13, dword ptr [rsp+0x220] + vpinsrd xmm13, xmm13, dword ptr [rsp+0x240], 1 + vpinsrd xmm13, xmm13, dword ptr [BLAKE3_BLOCK_LEN+rip], 2 + vmovd xmm14, dword ptr [rsp+0x224] + vpinsrd xmm14, xmm14, dword ptr [rsp+0x244], 1 + vpinsrd xmm14, xmm14, dword ptr [BLAKE3_BLOCK_LEN+rip], 2 + vinserti128 ymm13, ymm13, xmm14, 0x01 + vbroadcasti128 ymm14, xmmword ptr [ROT16+rip] + vbroadcasti128 ymm15, xmmword ptr [ROT8+rip] + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + movzx eax, byte ptr [rbp+0x80] + or eax, r13d + xor edx, edx +.p2align 5 +2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + mov dword ptr [rsp+0x200], eax + vbroadcasti128 ymm2, xmmword ptr [BLAKE3_IV+rip] + vpbroadcastd ymm8, dword ptr [rsp+0x200] + vpblendd ymm3, ymm13, ymm8, 0x88 + vmovups ymm8, ymmword ptr [r8+rdx-0x40] + vinsertf128 ymm8, ymm8, xmmword ptr [r9+rdx-0x40], 0x01 + vmovups ymm9, ymmword ptr [r8+rdx-0x30] + vinsertf128 ymm9, ymm9, xmmword ptr [r9+rdx-0x30], 0x01 + vshufps ymm4, ymm8, ymm9, 136 + vshufps ymm5, ymm8, ymm9, 221 + vmovups ymm8, ymmword ptr [r8+rdx-0x20] + vinsertf128 ymm8, ymm8, xmmword ptr [r9+rdx-0x20], 0x01 + vmovups ymm9, ymmword ptr [r8+rdx-0x10] + vinsertf128 ymm9, ymm9, xmmword ptr [r9+rdx-0x10], 0x01 + vshufps ymm6, ymm8, ymm9, 136 + vshufps ymm7, ymm8, ymm9, 221 + vpshufd ymm6, ymm6, 0x93 + vpshufd ymm7, ymm7, 0x93 + mov al, 7 +9: + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm0, ymm0, ymm1 + vpxor ymm3, ymm3, ymm0 + vpshufb ymm3, ymm3, ymm14 + vpaddd ymm2, ymm2, ymm3 + vpxor ymm1, ymm1, ymm2 + vpsrld ymm8, ymm1, 12 + vpslld ymm1, ymm1, 20 + vpor ymm1, ymm1, ymm8 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm0, ymm0, ymm1 + vpxor ymm3, ymm3, ymm0 + vpshufb ymm3, ymm3, ymm15 + vpaddd ymm2, ymm2, ymm3 + vpxor ymm1, ymm1, ymm2 + vpsrld ymm8, ymm1, 7 + vpslld ymm1, ymm1, 25 + vpor ymm1, ymm1, ymm8 + vpshufd ymm0, ymm0, 0x93 + vpshufd ymm3, ymm3, 0x4E + vpshufd ymm2, ymm2, 0x39 + vpaddd ymm0, ymm0, ymm6 + vpaddd ymm0, ymm0, ymm1 + vpxor ymm3, ymm3, ymm0 + vpshufb ymm3, ymm3, ymm14 + vpaddd ymm2, ymm2, ymm3 + vpxor ymm1, ymm1, ymm2 + vpsrld ymm8, ymm1, 12 + vpslld ymm1, ymm1, 20 + vpor ymm1, ymm1, ymm8 + vpaddd ymm0, ymm0, ymm7 + vpaddd ymm0, ymm0, ymm1 + vpxor ymm3, ymm3, ymm0 + vpshufb ymm3, ymm3, ymm15 + vpaddd ymm2, ymm2, ymm3 + vpxor ymm1, ymm1, ymm2 + vpsrld ymm8, ymm1, 7 + vpslld ymm1, ymm1, 25 + vpor ymm1, ymm1, ymm8 + vpshufd ymm0, ymm0, 0x39 + vpshufd ymm3, ymm3, 0x4E + vpshufd ymm2, ymm2, 0x93 + dec al + jz 9f + vshufps ymm8, ymm4, ymm5, 214 + vpshufd ymm9, ymm4, 0x0F + vpshufd ymm4, ymm8, 0x39 + vshufps ymm8, ymm6, ymm7, 250 + vpblendd ymm9, ymm9, ymm8, 0xAA + vpunpcklqdq ymm8, ymm7, ymm5 + vpblendd ymm8, ymm8, ymm6, 0x88 + vpshufd ymm8, ymm8, 0x78 + vpunpckhdq ymm5, ymm5, ymm7 + vpunpckldq ymm6, ymm6, ymm5 + vpshufd ymm7, ymm6, 0x1E + vmovdqa ymm5, ymm9 + vmovdqa ymm6, ymm8 + jmp 9b +9: + vpxor ymm0, ymm0, ymm2 + vpxor ymm1, ymm1, ymm3 + mov eax, r13d + cmp rdx, r15 + jne 2b + vmovdqu xmmword ptr [rbx], xmm0 + vmovdqu xmmword ptr [rbx+0x10], xmm1 + vextracti128 xmmword ptr [rbx+0x20], ymm0, 0x01 + vextracti128 xmmword ptr [rbx+0x30], ymm1, 0x01 + vmovaps ymm8, ymmword ptr [rsp+0x260] + vmovaps ymm0, ymmword ptr [rsp+0x220] + vmovups ymm1, ymmword ptr [rsp+0x228] + vmovaps ymm2, ymmword ptr [rsp+0x240] + vmovups ymm3, ymmword ptr [rsp+0x248] + vblendvps ymm0, ymm0, ymm1, ymm8 + vblendvps ymm2, ymm2, ymm3, ymm8 + vmovaps ymmword ptr [rsp+0x220], ymm0 + vmovaps ymmword ptr [rsp+0x240], ymm2 + add rbx, 64 + add rdi, 16 + sub rsi, 2 +3: + test rsi, 0x1 + je 4b + vmovdqu xmm0, xmmword ptr [rcx] + vmovdqu xmm1, xmmword ptr [rcx+0x10] + vmovd xmm3, dword ptr [rsp+0x220] + vpinsrd xmm3, xmm3, dword ptr [rsp+0x240], 1 + vpinsrd xmm13, xmm3, dword ptr [BLAKE3_BLOCK_LEN+rip], 2 + vmovdqa xmm14, xmmword ptr [ROT16+rip] + vmovdqa xmm15, xmmword ptr [ROT8+rip] + mov r8, qword ptr [rdi] + movzx eax, byte ptr [rbp+0x80] + or eax, r13d + xor edx, edx +.p2align 5 +2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + vmovdqa xmm2, xmmword ptr [BLAKE3_IV+rip] + vmovdqa xmm3, xmm13 + vpinsrd xmm3, xmm3, eax, 3 + vmovups xmm8, xmmword ptr [r8+rdx-0x40] + vmovups xmm9, xmmword ptr [r8+rdx-0x30] + vshufps xmm4, xmm8, xmm9, 136 + vshufps xmm5, xmm8, xmm9, 221 + vmovups xmm8, xmmword ptr [r8+rdx-0x20] + vmovups xmm9, xmmword ptr [r8+rdx-0x10] + vshufps xmm6, xmm8, xmm9, 136 + vshufps xmm7, xmm8, xmm9, 221 + vpshufd xmm6, xmm6, 0x93 + vpshufd xmm7, xmm7, 0x93 + mov al, 7 +9: + vpaddd xmm0, xmm0, xmm4 + vpaddd xmm0, xmm0, xmm1 + vpxor xmm3, xmm3, xmm0 + vpshufb xmm3, xmm3, xmm14 + vpaddd xmm2, xmm2, xmm3 + vpxor xmm1, xmm1, xmm2 + vpsrld xmm8, xmm1, 12 + vpslld xmm1, xmm1, 20 + vpor xmm1, xmm1, xmm8 + vpaddd xmm0, xmm0, xmm5 + vpaddd xmm0, xmm0, xmm1 + vpxor xmm3, xmm3, xmm0 + vpshufb xmm3, xmm3, xmm15 + vpaddd xmm2, xmm2, xmm3 + vpxor xmm1, xmm1, xmm2 + vpsrld xmm8, xmm1, 7 + vpslld xmm1, xmm1, 25 + vpor xmm1, xmm1, xmm8 + vpshufd xmm0, xmm0, 0x93 + vpshufd xmm3, xmm3, 0x4E + vpshufd xmm2, xmm2, 0x39 + vpaddd xmm0, xmm0, xmm6 + vpaddd xmm0, xmm0, xmm1 + vpxor xmm3, xmm3, xmm0 + vpshufb xmm3, xmm3, xmm14 + vpaddd xmm2, xmm2, xmm3 + vpxor xmm1, xmm1, xmm2 + vpsrld xmm8, xmm1, 12 + vpslld xmm1, xmm1, 20 + vpor xmm1, xmm1, xmm8 + vpaddd xmm0, xmm0, xmm7 + vpaddd xmm0, xmm0, xmm1 + vpxor xmm3, xmm3, xmm0 + vpshufb xmm3, xmm3, xmm15 + vpaddd xmm2, xmm2, xmm3 + vpxor xmm1, xmm1, xmm2 + vpsrld xmm8, xmm1, 7 + vpslld xmm1, xmm1, 25 + vpor xmm1, xmm1, xmm8 + vpshufd xmm0, xmm0, 0x39 + vpshufd xmm3, xmm3, 0x4E + vpshufd xmm2, xmm2, 0x93 + dec al + jz 9f + vshufps xmm8, xmm4, xmm5, 214 + vpshufd xmm9, xmm4, 0x0F + vpshufd xmm4, xmm8, 0x39 + vshufps xmm8, xmm6, xmm7, 250 + vpblendd xmm9, xmm9, xmm8, 0xAA + vpunpcklqdq xmm8, xmm7, xmm5 + vpblendd xmm8, xmm8, xmm6, 0x88 + vpshufd xmm8, xmm8, 0x78 + vpunpckhdq xmm5, xmm5, xmm7 + vpunpckldq xmm6, xmm6, xmm5 + vpshufd xmm7, xmm6, 0x1E + vmovdqa xmm5, xmm9 + vmovdqa xmm6, xmm8 + jmp 9b +9: + vpxor xmm0, xmm0, xmm2 + vpxor xmm1, xmm1, xmm3 + mov eax, r13d + cmp rdx, r15 + jne 2b + vmovdqu xmmword ptr [rbx], xmm0 + vmovdqu xmmword ptr [rbx+0x10], xmm1 + jmp 4b + +.section .rodata +.p2align 6 +ADD0: + .long 0, 1, 2, 3, 4, 5, 6, 7 +ADD1: + .long 8, 8, 8, 8, 8, 8, 8, 8 +BLAKE3_IV_0: + .long 0x6A09E667, 0x6A09E667, 0x6A09E667, 0x6A09E667 + .long 0x6A09E667, 0x6A09E667, 0x6A09E667, 0x6A09E667 +BLAKE3_IV_1: + .long 0xBB67AE85, 0xBB67AE85, 0xBB67AE85, 0xBB67AE85 + .long 0xBB67AE85, 0xBB67AE85, 0xBB67AE85, 0xBB67AE85 +BLAKE3_IV_2: + .long 0x3C6EF372, 0x3C6EF372, 0x3C6EF372, 0x3C6EF372 + .long 0x3C6EF372, 0x3C6EF372, 0x3C6EF372, 0x3C6EF372 +BLAKE3_IV_3: + .long 0xA54FF53A, 0xA54FF53A, 0xA54FF53A, 0xA54FF53A + .long 0xA54FF53A, 0xA54FF53A, 0xA54FF53A, 0xA54FF53A +BLAKE3_BLOCK_LEN: + .long 0x00000040, 0x00000040, 0x00000040, 0x00000040 + .long 0x00000040, 0x00000040, 0x00000040, 0x00000040 +ROT16: + .byte 2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13 +ROT8: + .byte 1, 2, 3, 0, 5, 6, 7, 4, 9, 10, 11, 8, 13, 14, 15, 12 +CMP_MSB_MASK: + .long 0x80000000, 0x80000000, 0x80000000, 0x80000000 + .long 0x80000000, 0x80000000, 0x80000000, 0x80000000 +BLAKE3_IV: + .long 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A + diff --git a/src/third_party/blake3/blake3_avx512.c b/src/third_party/blake3/blake3_avx512.c new file mode 100644 index 0000000..77a5c38 --- /dev/null +++ b/src/third_party/blake3/blake3_avx512.c @@ -0,0 +1,1204 @@ +#include "blake3_impl.h" + +#include + +#define _mm_shuffle_ps2(a, b, c) \ + (_mm_castps_si128( \ + _mm_shuffle_ps(_mm_castsi128_ps(a), _mm_castsi128_ps(b), (c)))) + +INLINE __m128i loadu_128(const uint8_t src[16]) { + return _mm_loadu_si128((const __m128i *)src); +} + +INLINE __m256i loadu_256(const uint8_t src[32]) { + return _mm256_loadu_si256((const __m256i *)src); +} + +INLINE __m512i loadu_512(const uint8_t src[64]) { + return _mm512_loadu_si512((const __m512i *)src); +} + +INLINE void storeu_128(__m128i src, uint8_t dest[16]) { + _mm_storeu_si128((__m128i *)dest, src); +} + +INLINE void storeu_256(__m256i src, uint8_t dest[16]) { + _mm256_storeu_si256((__m256i *)dest, src); +} + +INLINE __m128i add_128(__m128i a, __m128i b) { return _mm_add_epi32(a, b); } + +INLINE __m256i add_256(__m256i a, __m256i b) { return _mm256_add_epi32(a, b); } + +INLINE __m512i add_512(__m512i a, __m512i b) { return _mm512_add_epi32(a, b); } + +INLINE __m128i xor_128(__m128i a, __m128i b) { return _mm_xor_si128(a, b); } + +INLINE __m256i xor_256(__m256i a, __m256i b) { return _mm256_xor_si256(a, b); } + +INLINE __m512i xor_512(__m512i a, __m512i b) { return _mm512_xor_si512(a, b); } + +INLINE __m128i set1_128(uint32_t x) { return _mm_set1_epi32((int32_t)x); } + +INLINE __m256i set1_256(uint32_t x) { return _mm256_set1_epi32((int32_t)x); } + +INLINE __m512i set1_512(uint32_t x) { return _mm512_set1_epi32((int32_t)x); } + +INLINE __m128i set4(uint32_t a, uint32_t b, uint32_t c, uint32_t d) { + return _mm_setr_epi32((int32_t)a, (int32_t)b, (int32_t)c, (int32_t)d); +} + +INLINE __m128i rot16_128(__m128i x) { return _mm_ror_epi32(x, 16); } + +INLINE __m256i rot16_256(__m256i x) { return _mm256_ror_epi32(x, 16); } + +INLINE __m512i rot16_512(__m512i x) { return _mm512_ror_epi32(x, 16); } + +INLINE __m128i rot12_128(__m128i x) { return _mm_ror_epi32(x, 12); } + +INLINE __m256i rot12_256(__m256i x) { return _mm256_ror_epi32(x, 12); } + +INLINE __m512i rot12_512(__m512i x) { return _mm512_ror_epi32(x, 12); } + +INLINE __m128i rot8_128(__m128i x) { return _mm_ror_epi32(x, 8); } + +INLINE __m256i rot8_256(__m256i x) { return _mm256_ror_epi32(x, 8); } + +INLINE __m512i rot8_512(__m512i x) { return _mm512_ror_epi32(x, 8); } + +INLINE __m128i rot7_128(__m128i x) { return _mm_ror_epi32(x, 7); } + +INLINE __m256i rot7_256(__m256i x) { return _mm256_ror_epi32(x, 7); } + +INLINE __m512i rot7_512(__m512i x) { return _mm512_ror_epi32(x, 7); } + +/* + * ---------------------------------------------------------------------------- + * compress_avx512 + * ---------------------------------------------------------------------------- + */ + +INLINE void g1(__m128i *row0, __m128i *row1, __m128i *row2, __m128i *row3, + __m128i m) { + *row0 = add_128(add_128(*row0, m), *row1); + *row3 = xor_128(*row3, *row0); + *row3 = rot16_128(*row3); + *row2 = add_128(*row2, *row3); + *row1 = xor_128(*row1, *row2); + *row1 = rot12_128(*row1); +} + +INLINE void g2(__m128i *row0, __m128i *row1, __m128i *row2, __m128i *row3, + __m128i m) { + *row0 = add_128(add_128(*row0, m), *row1); + *row3 = xor_128(*row3, *row0); + *row3 = rot8_128(*row3); + *row2 = add_128(*row2, *row3); + *row1 = xor_128(*row1, *row2); + *row1 = rot7_128(*row1); +} + +// Note the optimization here of leaving row1 as the unrotated row, rather than +// row0. All the message loads below are adjusted to compensate for this. See +// discussion at https://github.com/sneves/blake2-avx2/pull/4 +INLINE void diagonalize(__m128i *row0, __m128i *row2, __m128i *row3) { + *row0 = _mm_shuffle_epi32(*row0, _MM_SHUFFLE(2, 1, 0, 3)); + *row3 = _mm_shuffle_epi32(*row3, _MM_SHUFFLE(1, 0, 3, 2)); + *row2 = _mm_shuffle_epi32(*row2, _MM_SHUFFLE(0, 3, 2, 1)); +} + +INLINE void undiagonalize(__m128i *row0, __m128i *row2, __m128i *row3) { + *row0 = _mm_shuffle_epi32(*row0, _MM_SHUFFLE(0, 3, 2, 1)); + *row3 = _mm_shuffle_epi32(*row3, _MM_SHUFFLE(1, 0, 3, 2)); + *row2 = _mm_shuffle_epi32(*row2, _MM_SHUFFLE(2, 1, 0, 3)); +} + +INLINE void compress_pre(__m128i rows[4], const uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, uint8_t flags) { + rows[0] = loadu_128((uint8_t *)&cv[0]); + rows[1] = loadu_128((uint8_t *)&cv[4]); + rows[2] = set4(IV[0], IV[1], IV[2], IV[3]); + rows[3] = set4(counter_low(counter), counter_high(counter), + (uint32_t)block_len, (uint32_t)flags); + + __m128i m0 = loadu_128(&block[sizeof(__m128i) * 0]); + __m128i m1 = loadu_128(&block[sizeof(__m128i) * 1]); + __m128i m2 = loadu_128(&block[sizeof(__m128i) * 2]); + __m128i m3 = loadu_128(&block[sizeof(__m128i) * 3]); + + __m128i t0, t1, t2, t3, tt; + + // Round 1. The first round permutes the message words from the original + // input order, into the groups that get mixed in parallel. + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(2, 0, 2, 0)); // 6 4 2 0 + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 3, 1)); // 7 5 3 1 + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(2, 0, 2, 0)); // 14 12 10 8 + t2 = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2, 1, 0, 3)); // 12 10 8 14 + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 1, 3, 1)); // 15 13 11 9 + t3 = _mm_shuffle_epi32(t3, _MM_SHUFFLE(2, 1, 0, 3)); // 13 11 9 15 + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); + m0 = t0; + m1 = t1; + m2 = t2; + m3 = t3; + + // Round 2. This round and all following rounds apply a fixed permutation + // to the message words from the round before. + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 1, 2)); + t0 = _mm_shuffle_epi32(t0, _MM_SHUFFLE(0, 3, 2, 1)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 3, 2, 2)); + tt = _mm_shuffle_epi32(m0, _MM_SHUFFLE(0, 0, 3, 3)); + t1 = _mm_blend_epi16(tt, t1, 0xCC); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_unpacklo_epi64(m3, m1); + tt = _mm_blend_epi16(t2, m2, 0xC0); + t2 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(1, 3, 2, 0)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_unpackhi_epi32(m1, m3); + tt = _mm_unpacklo_epi32(m2, t3); + t3 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(0, 1, 3, 2)); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); + m0 = t0; + m1 = t1; + m2 = t2; + m3 = t3; + + // Round 3 + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 1, 2)); + t0 = _mm_shuffle_epi32(t0, _MM_SHUFFLE(0, 3, 2, 1)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 3, 2, 2)); + tt = _mm_shuffle_epi32(m0, _MM_SHUFFLE(0, 0, 3, 3)); + t1 = _mm_blend_epi16(tt, t1, 0xCC); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_unpacklo_epi64(m3, m1); + tt = _mm_blend_epi16(t2, m2, 0xC0); + t2 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(1, 3, 2, 0)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_unpackhi_epi32(m1, m3); + tt = _mm_unpacklo_epi32(m2, t3); + t3 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(0, 1, 3, 2)); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); + m0 = t0; + m1 = t1; + m2 = t2; + m3 = t3; + + // Round 4 + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 1, 2)); + t0 = _mm_shuffle_epi32(t0, _MM_SHUFFLE(0, 3, 2, 1)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 3, 2, 2)); + tt = _mm_shuffle_epi32(m0, _MM_SHUFFLE(0, 0, 3, 3)); + t1 = _mm_blend_epi16(tt, t1, 0xCC); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_unpacklo_epi64(m3, m1); + tt = _mm_blend_epi16(t2, m2, 0xC0); + t2 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(1, 3, 2, 0)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_unpackhi_epi32(m1, m3); + tt = _mm_unpacklo_epi32(m2, t3); + t3 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(0, 1, 3, 2)); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); + m0 = t0; + m1 = t1; + m2 = t2; + m3 = t3; + + // Round 5 + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 1, 2)); + t0 = _mm_shuffle_epi32(t0, _MM_SHUFFLE(0, 3, 2, 1)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 3, 2, 2)); + tt = _mm_shuffle_epi32(m0, _MM_SHUFFLE(0, 0, 3, 3)); + t1 = _mm_blend_epi16(tt, t1, 0xCC); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_unpacklo_epi64(m3, m1); + tt = _mm_blend_epi16(t2, m2, 0xC0); + t2 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(1, 3, 2, 0)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_unpackhi_epi32(m1, m3); + tt = _mm_unpacklo_epi32(m2, t3); + t3 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(0, 1, 3, 2)); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); + m0 = t0; + m1 = t1; + m2 = t2; + m3 = t3; + + // Round 6 + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 1, 2)); + t0 = _mm_shuffle_epi32(t0, _MM_SHUFFLE(0, 3, 2, 1)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 3, 2, 2)); + tt = _mm_shuffle_epi32(m0, _MM_SHUFFLE(0, 0, 3, 3)); + t1 = _mm_blend_epi16(tt, t1, 0xCC); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_unpacklo_epi64(m3, m1); + tt = _mm_blend_epi16(t2, m2, 0xC0); + t2 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(1, 3, 2, 0)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_unpackhi_epi32(m1, m3); + tt = _mm_unpacklo_epi32(m2, t3); + t3 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(0, 1, 3, 2)); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); + m0 = t0; + m1 = t1; + m2 = t2; + m3 = t3; + + // Round 7 + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 1, 2)); + t0 = _mm_shuffle_epi32(t0, _MM_SHUFFLE(0, 3, 2, 1)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 3, 2, 2)); + tt = _mm_shuffle_epi32(m0, _MM_SHUFFLE(0, 0, 3, 3)); + t1 = _mm_blend_epi16(tt, t1, 0xCC); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_unpacklo_epi64(m3, m1); + tt = _mm_blend_epi16(t2, m2, 0xC0); + t2 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(1, 3, 2, 0)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_unpackhi_epi32(m1, m3); + tt = _mm_unpacklo_epi32(m2, t3); + t3 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(0, 1, 3, 2)); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); +} + +void blake3_compress_xof_avx512(const uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, + uint8_t flags, uint8_t out[64]) { + __m128i rows[4]; + compress_pre(rows, cv, block, block_len, counter, flags); + storeu_128(xor_128(rows[0], rows[2]), &out[0]); + storeu_128(xor_128(rows[1], rows[3]), &out[16]); + storeu_128(xor_128(rows[2], loadu_128((uint8_t *)&cv[0])), &out[32]); + storeu_128(xor_128(rows[3], loadu_128((uint8_t *)&cv[4])), &out[48]); +} + +void blake3_compress_in_place_avx512(uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, + uint8_t flags) { + __m128i rows[4]; + compress_pre(rows, cv, block, block_len, counter, flags); + storeu_128(xor_128(rows[0], rows[2]), (uint8_t *)&cv[0]); + storeu_128(xor_128(rows[1], rows[3]), (uint8_t *)&cv[4]); +} + +/* + * ---------------------------------------------------------------------------- + * hash4_avx512 + * ---------------------------------------------------------------------------- + */ + +INLINE void round_fn4(__m128i v[16], __m128i m[16], size_t r) { + v[0] = add_128(v[0], m[(size_t)MSG_SCHEDULE[r][0]]); + v[1] = add_128(v[1], m[(size_t)MSG_SCHEDULE[r][2]]); + v[2] = add_128(v[2], m[(size_t)MSG_SCHEDULE[r][4]]); + v[3] = add_128(v[3], m[(size_t)MSG_SCHEDULE[r][6]]); + v[0] = add_128(v[0], v[4]); + v[1] = add_128(v[1], v[5]); + v[2] = add_128(v[2], v[6]); + v[3] = add_128(v[3], v[7]); + v[12] = xor_128(v[12], v[0]); + v[13] = xor_128(v[13], v[1]); + v[14] = xor_128(v[14], v[2]); + v[15] = xor_128(v[15], v[3]); + v[12] = rot16_128(v[12]); + v[13] = rot16_128(v[13]); + v[14] = rot16_128(v[14]); + v[15] = rot16_128(v[15]); + v[8] = add_128(v[8], v[12]); + v[9] = add_128(v[9], v[13]); + v[10] = add_128(v[10], v[14]); + v[11] = add_128(v[11], v[15]); + v[4] = xor_128(v[4], v[8]); + v[5] = xor_128(v[5], v[9]); + v[6] = xor_128(v[6], v[10]); + v[7] = xor_128(v[7], v[11]); + v[4] = rot12_128(v[4]); + v[5] = rot12_128(v[5]); + v[6] = rot12_128(v[6]); + v[7] = rot12_128(v[7]); + v[0] = add_128(v[0], m[(size_t)MSG_SCHEDULE[r][1]]); + v[1] = add_128(v[1], m[(size_t)MSG_SCHEDULE[r][3]]); + v[2] = add_128(v[2], m[(size_t)MSG_SCHEDULE[r][5]]); + v[3] = add_128(v[3], m[(size_t)MSG_SCHEDULE[r][7]]); + v[0] = add_128(v[0], v[4]); + v[1] = add_128(v[1], v[5]); + v[2] = add_128(v[2], v[6]); + v[3] = add_128(v[3], v[7]); + v[12] = xor_128(v[12], v[0]); + v[13] = xor_128(v[13], v[1]); + v[14] = xor_128(v[14], v[2]); + v[15] = xor_128(v[15], v[3]); + v[12] = rot8_128(v[12]); + v[13] = rot8_128(v[13]); + v[14] = rot8_128(v[14]); + v[15] = rot8_128(v[15]); + v[8] = add_128(v[8], v[12]); + v[9] = add_128(v[9], v[13]); + v[10] = add_128(v[10], v[14]); + v[11] = add_128(v[11], v[15]); + v[4] = xor_128(v[4], v[8]); + v[5] = xor_128(v[5], v[9]); + v[6] = xor_128(v[6], v[10]); + v[7] = xor_128(v[7], v[11]); + v[4] = rot7_128(v[4]); + v[5] = rot7_128(v[5]); + v[6] = rot7_128(v[6]); + v[7] = rot7_128(v[7]); + + v[0] = add_128(v[0], m[(size_t)MSG_SCHEDULE[r][8]]); + v[1] = add_128(v[1], m[(size_t)MSG_SCHEDULE[r][10]]); + v[2] = add_128(v[2], m[(size_t)MSG_SCHEDULE[r][12]]); + v[3] = add_128(v[3], m[(size_t)MSG_SCHEDULE[r][14]]); + v[0] = add_128(v[0], v[5]); + v[1] = add_128(v[1], v[6]); + v[2] = add_128(v[2], v[7]); + v[3] = add_128(v[3], v[4]); + v[15] = xor_128(v[15], v[0]); + v[12] = xor_128(v[12], v[1]); + v[13] = xor_128(v[13], v[2]); + v[14] = xor_128(v[14], v[3]); + v[15] = rot16_128(v[15]); + v[12] = rot16_128(v[12]); + v[13] = rot16_128(v[13]); + v[14] = rot16_128(v[14]); + v[10] = add_128(v[10], v[15]); + v[11] = add_128(v[11], v[12]); + v[8] = add_128(v[8], v[13]); + v[9] = add_128(v[9], v[14]); + v[5] = xor_128(v[5], v[10]); + v[6] = xor_128(v[6], v[11]); + v[7] = xor_128(v[7], v[8]); + v[4] = xor_128(v[4], v[9]); + v[5] = rot12_128(v[5]); + v[6] = rot12_128(v[6]); + v[7] = rot12_128(v[7]); + v[4] = rot12_128(v[4]); + v[0] = add_128(v[0], m[(size_t)MSG_SCHEDULE[r][9]]); + v[1] = add_128(v[1], m[(size_t)MSG_SCHEDULE[r][11]]); + v[2] = add_128(v[2], m[(size_t)MSG_SCHEDULE[r][13]]); + v[3] = add_128(v[3], m[(size_t)MSG_SCHEDULE[r][15]]); + v[0] = add_128(v[0], v[5]); + v[1] = add_128(v[1], v[6]); + v[2] = add_128(v[2], v[7]); + v[3] = add_128(v[3], v[4]); + v[15] = xor_128(v[15], v[0]); + v[12] = xor_128(v[12], v[1]); + v[13] = xor_128(v[13], v[2]); + v[14] = xor_128(v[14], v[3]); + v[15] = rot8_128(v[15]); + v[12] = rot8_128(v[12]); + v[13] = rot8_128(v[13]); + v[14] = rot8_128(v[14]); + v[10] = add_128(v[10], v[15]); + v[11] = add_128(v[11], v[12]); + v[8] = add_128(v[8], v[13]); + v[9] = add_128(v[9], v[14]); + v[5] = xor_128(v[5], v[10]); + v[6] = xor_128(v[6], v[11]); + v[7] = xor_128(v[7], v[8]); + v[4] = xor_128(v[4], v[9]); + v[5] = rot7_128(v[5]); + v[6] = rot7_128(v[6]); + v[7] = rot7_128(v[7]); + v[4] = rot7_128(v[4]); +} + +INLINE void transpose_vecs_128(__m128i vecs[4]) { + // Interleave 32-bit lates. The low unpack is lanes 00/11 and the high is + // 22/33. Note that this doesn't split the vector into two lanes, as the + // AVX2 counterparts do. + __m128i ab_01 = _mm_unpacklo_epi32(vecs[0], vecs[1]); + __m128i ab_23 = _mm_unpackhi_epi32(vecs[0], vecs[1]); + __m128i cd_01 = _mm_unpacklo_epi32(vecs[2], vecs[3]); + __m128i cd_23 = _mm_unpackhi_epi32(vecs[2], vecs[3]); + + // Interleave 64-bit lanes. + __m128i abcd_0 = _mm_unpacklo_epi64(ab_01, cd_01); + __m128i abcd_1 = _mm_unpackhi_epi64(ab_01, cd_01); + __m128i abcd_2 = _mm_unpacklo_epi64(ab_23, cd_23); + __m128i abcd_3 = _mm_unpackhi_epi64(ab_23, cd_23); + + vecs[0] = abcd_0; + vecs[1] = abcd_1; + vecs[2] = abcd_2; + vecs[3] = abcd_3; +} + +INLINE void transpose_msg_vecs4(const uint8_t *const *inputs, + size_t block_offset, __m128i out[16]) { + out[0] = loadu_128(&inputs[0][block_offset + 0 * sizeof(__m128i)]); + out[1] = loadu_128(&inputs[1][block_offset + 0 * sizeof(__m128i)]); + out[2] = loadu_128(&inputs[2][block_offset + 0 * sizeof(__m128i)]); + out[3] = loadu_128(&inputs[3][block_offset + 0 * sizeof(__m128i)]); + out[4] = loadu_128(&inputs[0][block_offset + 1 * sizeof(__m128i)]); + out[5] = loadu_128(&inputs[1][block_offset + 1 * sizeof(__m128i)]); + out[6] = loadu_128(&inputs[2][block_offset + 1 * sizeof(__m128i)]); + out[7] = loadu_128(&inputs[3][block_offset + 1 * sizeof(__m128i)]); + out[8] = loadu_128(&inputs[0][block_offset + 2 * sizeof(__m128i)]); + out[9] = loadu_128(&inputs[1][block_offset + 2 * sizeof(__m128i)]); + out[10] = loadu_128(&inputs[2][block_offset + 2 * sizeof(__m128i)]); + out[11] = loadu_128(&inputs[3][block_offset + 2 * sizeof(__m128i)]); + out[12] = loadu_128(&inputs[0][block_offset + 3 * sizeof(__m128i)]); + out[13] = loadu_128(&inputs[1][block_offset + 3 * sizeof(__m128i)]); + out[14] = loadu_128(&inputs[2][block_offset + 3 * sizeof(__m128i)]); + out[15] = loadu_128(&inputs[3][block_offset + 3 * sizeof(__m128i)]); + for (size_t i = 0; i < 4; ++i) { + _mm_prefetch(&inputs[i][block_offset + 256], _MM_HINT_T0); + } + transpose_vecs_128(&out[0]); + transpose_vecs_128(&out[4]); + transpose_vecs_128(&out[8]); + transpose_vecs_128(&out[12]); +} + +INLINE void load_counters4(uint64_t counter, bool increment_counter, + __m128i *out_lo, __m128i *out_hi) { + uint64_t mask = (increment_counter ? ~0 : 0); + __m256i mask_vec = _mm256_set1_epi64x(mask); + __m256i deltas = _mm256_setr_epi64x(0, 1, 2, 3); + deltas = _mm256_and_si256(mask_vec, deltas); + __m256i counters = + _mm256_add_epi64(_mm256_set1_epi64x((int64_t)counter), deltas); + *out_lo = _mm256_cvtepi64_epi32(counters); + *out_hi = _mm256_cvtepi64_epi32(_mm256_srli_epi64(counters, 32)); +} + +void blake3_hash4_avx512(const uint8_t *const *inputs, size_t blocks, + const uint32_t key[8], uint64_t counter, + bool increment_counter, uint8_t flags, + uint8_t flags_start, uint8_t flags_end, uint8_t *out) { + __m128i h_vecs[8] = { + set1_128(key[0]), set1_128(key[1]), set1_128(key[2]), set1_128(key[3]), + set1_128(key[4]), set1_128(key[5]), set1_128(key[6]), set1_128(key[7]), + }; + __m128i counter_low_vec, counter_high_vec; + load_counters4(counter, increment_counter, &counter_low_vec, + &counter_high_vec); + uint8_t block_flags = flags | flags_start; + + for (size_t block = 0; block < blocks; block++) { + if (block + 1 == blocks) { + block_flags |= flags_end; + } + __m128i block_len_vec = set1_128(BLAKE3_BLOCK_LEN); + __m128i block_flags_vec = set1_128(block_flags); + __m128i msg_vecs[16]; + transpose_msg_vecs4(inputs, block * BLAKE3_BLOCK_LEN, msg_vecs); + + __m128i v[16] = { + h_vecs[0], h_vecs[1], h_vecs[2], h_vecs[3], + h_vecs[4], h_vecs[5], h_vecs[6], h_vecs[7], + set1_128(IV[0]), set1_128(IV[1]), set1_128(IV[2]), set1_128(IV[3]), + counter_low_vec, counter_high_vec, block_len_vec, block_flags_vec, + }; + round_fn4(v, msg_vecs, 0); + round_fn4(v, msg_vecs, 1); + round_fn4(v, msg_vecs, 2); + round_fn4(v, msg_vecs, 3); + round_fn4(v, msg_vecs, 4); + round_fn4(v, msg_vecs, 5); + round_fn4(v, msg_vecs, 6); + h_vecs[0] = xor_128(v[0], v[8]); + h_vecs[1] = xor_128(v[1], v[9]); + h_vecs[2] = xor_128(v[2], v[10]); + h_vecs[3] = xor_128(v[3], v[11]); + h_vecs[4] = xor_128(v[4], v[12]); + h_vecs[5] = xor_128(v[5], v[13]); + h_vecs[6] = xor_128(v[6], v[14]); + h_vecs[7] = xor_128(v[7], v[15]); + + block_flags = flags; + } + + transpose_vecs_128(&h_vecs[0]); + transpose_vecs_128(&h_vecs[4]); + // The first four vecs now contain the first half of each output, and the + // second four vecs contain the second half of each output. + storeu_128(h_vecs[0], &out[0 * sizeof(__m128i)]); + storeu_128(h_vecs[4], &out[1 * sizeof(__m128i)]); + storeu_128(h_vecs[1], &out[2 * sizeof(__m128i)]); + storeu_128(h_vecs[5], &out[3 * sizeof(__m128i)]); + storeu_128(h_vecs[2], &out[4 * sizeof(__m128i)]); + storeu_128(h_vecs[6], &out[5 * sizeof(__m128i)]); + storeu_128(h_vecs[3], &out[6 * sizeof(__m128i)]); + storeu_128(h_vecs[7], &out[7 * sizeof(__m128i)]); +} + +/* + * ---------------------------------------------------------------------------- + * hash8_avx512 + * ---------------------------------------------------------------------------- + */ + +INLINE void round_fn8(__m256i v[16], __m256i m[16], size_t r) { + v[0] = add_256(v[0], m[(size_t)MSG_SCHEDULE[r][0]]); + v[1] = add_256(v[1], m[(size_t)MSG_SCHEDULE[r][2]]); + v[2] = add_256(v[2], m[(size_t)MSG_SCHEDULE[r][4]]); + v[3] = add_256(v[3], m[(size_t)MSG_SCHEDULE[r][6]]); + v[0] = add_256(v[0], v[4]); + v[1] = add_256(v[1], v[5]); + v[2] = add_256(v[2], v[6]); + v[3] = add_256(v[3], v[7]); + v[12] = xor_256(v[12], v[0]); + v[13] = xor_256(v[13], v[1]); + v[14] = xor_256(v[14], v[2]); + v[15] = xor_256(v[15], v[3]); + v[12] = rot16_256(v[12]); + v[13] = rot16_256(v[13]); + v[14] = rot16_256(v[14]); + v[15] = rot16_256(v[15]); + v[8] = add_256(v[8], v[12]); + v[9] = add_256(v[9], v[13]); + v[10] = add_256(v[10], v[14]); + v[11] = add_256(v[11], v[15]); + v[4] = xor_256(v[4], v[8]); + v[5] = xor_256(v[5], v[9]); + v[6] = xor_256(v[6], v[10]); + v[7] = xor_256(v[7], v[11]); + v[4] = rot12_256(v[4]); + v[5] = rot12_256(v[5]); + v[6] = rot12_256(v[6]); + v[7] = rot12_256(v[7]); + v[0] = add_256(v[0], m[(size_t)MSG_SCHEDULE[r][1]]); + v[1] = add_256(v[1], m[(size_t)MSG_SCHEDULE[r][3]]); + v[2] = add_256(v[2], m[(size_t)MSG_SCHEDULE[r][5]]); + v[3] = add_256(v[3], m[(size_t)MSG_SCHEDULE[r][7]]); + v[0] = add_256(v[0], v[4]); + v[1] = add_256(v[1], v[5]); + v[2] = add_256(v[2], v[6]); + v[3] = add_256(v[3], v[7]); + v[12] = xor_256(v[12], v[0]); + v[13] = xor_256(v[13], v[1]); + v[14] = xor_256(v[14], v[2]); + v[15] = xor_256(v[15], v[3]); + v[12] = rot8_256(v[12]); + v[13] = rot8_256(v[13]); + v[14] = rot8_256(v[14]); + v[15] = rot8_256(v[15]); + v[8] = add_256(v[8], v[12]); + v[9] = add_256(v[9], v[13]); + v[10] = add_256(v[10], v[14]); + v[11] = add_256(v[11], v[15]); + v[4] = xor_256(v[4], v[8]); + v[5] = xor_256(v[5], v[9]); + v[6] = xor_256(v[6], v[10]); + v[7] = xor_256(v[7], v[11]); + v[4] = rot7_256(v[4]); + v[5] = rot7_256(v[5]); + v[6] = rot7_256(v[6]); + v[7] = rot7_256(v[7]); + + v[0] = add_256(v[0], m[(size_t)MSG_SCHEDULE[r][8]]); + v[1] = add_256(v[1], m[(size_t)MSG_SCHEDULE[r][10]]); + v[2] = add_256(v[2], m[(size_t)MSG_SCHEDULE[r][12]]); + v[3] = add_256(v[3], m[(size_t)MSG_SCHEDULE[r][14]]); + v[0] = add_256(v[0], v[5]); + v[1] = add_256(v[1], v[6]); + v[2] = add_256(v[2], v[7]); + v[3] = add_256(v[3], v[4]); + v[15] = xor_256(v[15], v[0]); + v[12] = xor_256(v[12], v[1]); + v[13] = xor_256(v[13], v[2]); + v[14] = xor_256(v[14], v[3]); + v[15] = rot16_256(v[15]); + v[12] = rot16_256(v[12]); + v[13] = rot16_256(v[13]); + v[14] = rot16_256(v[14]); + v[10] = add_256(v[10], v[15]); + v[11] = add_256(v[11], v[12]); + v[8] = add_256(v[8], v[13]); + v[9] = add_256(v[9], v[14]); + v[5] = xor_256(v[5], v[10]); + v[6] = xor_256(v[6], v[11]); + v[7] = xor_256(v[7], v[8]); + v[4] = xor_256(v[4], v[9]); + v[5] = rot12_256(v[5]); + v[6] = rot12_256(v[6]); + v[7] = rot12_256(v[7]); + v[4] = rot12_256(v[4]); + v[0] = add_256(v[0], m[(size_t)MSG_SCHEDULE[r][9]]); + v[1] = add_256(v[1], m[(size_t)MSG_SCHEDULE[r][11]]); + v[2] = add_256(v[2], m[(size_t)MSG_SCHEDULE[r][13]]); + v[3] = add_256(v[3], m[(size_t)MSG_SCHEDULE[r][15]]); + v[0] = add_256(v[0], v[5]); + v[1] = add_256(v[1], v[6]); + v[2] = add_256(v[2], v[7]); + v[3] = add_256(v[3], v[4]); + v[15] = xor_256(v[15], v[0]); + v[12] = xor_256(v[12], v[1]); + v[13] = xor_256(v[13], v[2]); + v[14] = xor_256(v[14], v[3]); + v[15] = rot8_256(v[15]); + v[12] = rot8_256(v[12]); + v[13] = rot8_256(v[13]); + v[14] = rot8_256(v[14]); + v[10] = add_256(v[10], v[15]); + v[11] = add_256(v[11], v[12]); + v[8] = add_256(v[8], v[13]); + v[9] = add_256(v[9], v[14]); + v[5] = xor_256(v[5], v[10]); + v[6] = xor_256(v[6], v[11]); + v[7] = xor_256(v[7], v[8]); + v[4] = xor_256(v[4], v[9]); + v[5] = rot7_256(v[5]); + v[6] = rot7_256(v[6]); + v[7] = rot7_256(v[7]); + v[4] = rot7_256(v[4]); +} + +INLINE void transpose_vecs_256(__m256i vecs[8]) { + // Interleave 32-bit lanes. The low unpack is lanes 00/11/44/55, and the high + // is 22/33/66/77. + __m256i ab_0145 = _mm256_unpacklo_epi32(vecs[0], vecs[1]); + __m256i ab_2367 = _mm256_unpackhi_epi32(vecs[0], vecs[1]); + __m256i cd_0145 = _mm256_unpacklo_epi32(vecs[2], vecs[3]); + __m256i cd_2367 = _mm256_unpackhi_epi32(vecs[2], vecs[3]); + __m256i ef_0145 = _mm256_unpacklo_epi32(vecs[4], vecs[5]); + __m256i ef_2367 = _mm256_unpackhi_epi32(vecs[4], vecs[5]); + __m256i gh_0145 = _mm256_unpacklo_epi32(vecs[6], vecs[7]); + __m256i gh_2367 = _mm256_unpackhi_epi32(vecs[6], vecs[7]); + + // Interleave 64-bit lates. The low unpack is lanes 00/22 and the high is + // 11/33. + __m256i abcd_04 = _mm256_unpacklo_epi64(ab_0145, cd_0145); + __m256i abcd_15 = _mm256_unpackhi_epi64(ab_0145, cd_0145); + __m256i abcd_26 = _mm256_unpacklo_epi64(ab_2367, cd_2367); + __m256i abcd_37 = _mm256_unpackhi_epi64(ab_2367, cd_2367); + __m256i efgh_04 = _mm256_unpacklo_epi64(ef_0145, gh_0145); + __m256i efgh_15 = _mm256_unpackhi_epi64(ef_0145, gh_0145); + __m256i efgh_26 = _mm256_unpacklo_epi64(ef_2367, gh_2367); + __m256i efgh_37 = _mm256_unpackhi_epi64(ef_2367, gh_2367); + + // Interleave 128-bit lanes. + vecs[0] = _mm256_permute2x128_si256(abcd_04, efgh_04, 0x20); + vecs[1] = _mm256_permute2x128_si256(abcd_15, efgh_15, 0x20); + vecs[2] = _mm256_permute2x128_si256(abcd_26, efgh_26, 0x20); + vecs[3] = _mm256_permute2x128_si256(abcd_37, efgh_37, 0x20); + vecs[4] = _mm256_permute2x128_si256(abcd_04, efgh_04, 0x31); + vecs[5] = _mm256_permute2x128_si256(abcd_15, efgh_15, 0x31); + vecs[6] = _mm256_permute2x128_si256(abcd_26, efgh_26, 0x31); + vecs[7] = _mm256_permute2x128_si256(abcd_37, efgh_37, 0x31); +} + +INLINE void transpose_msg_vecs8(const uint8_t *const *inputs, + size_t block_offset, __m256i out[16]) { + out[0] = loadu_256(&inputs[0][block_offset + 0 * sizeof(__m256i)]); + out[1] = loadu_256(&inputs[1][block_offset + 0 * sizeof(__m256i)]); + out[2] = loadu_256(&inputs[2][block_offset + 0 * sizeof(__m256i)]); + out[3] = loadu_256(&inputs[3][block_offset + 0 * sizeof(__m256i)]); + out[4] = loadu_256(&inputs[4][block_offset + 0 * sizeof(__m256i)]); + out[5] = loadu_256(&inputs[5][block_offset + 0 * sizeof(__m256i)]); + out[6] = loadu_256(&inputs[6][block_offset + 0 * sizeof(__m256i)]); + out[7] = loadu_256(&inputs[7][block_offset + 0 * sizeof(__m256i)]); + out[8] = loadu_256(&inputs[0][block_offset + 1 * sizeof(__m256i)]); + out[9] = loadu_256(&inputs[1][block_offset + 1 * sizeof(__m256i)]); + out[10] = loadu_256(&inputs[2][block_offset + 1 * sizeof(__m256i)]); + out[11] = loadu_256(&inputs[3][block_offset + 1 * sizeof(__m256i)]); + out[12] = loadu_256(&inputs[4][block_offset + 1 * sizeof(__m256i)]); + out[13] = loadu_256(&inputs[5][block_offset + 1 * sizeof(__m256i)]); + out[14] = loadu_256(&inputs[6][block_offset + 1 * sizeof(__m256i)]); + out[15] = loadu_256(&inputs[7][block_offset + 1 * sizeof(__m256i)]); + for (size_t i = 0; i < 8; ++i) { + _mm_prefetch(&inputs[i][block_offset + 256], _MM_HINT_T0); + } + transpose_vecs_256(&out[0]); + transpose_vecs_256(&out[8]); +} + +INLINE void load_counters8(uint64_t counter, bool increment_counter, + __m256i *out_lo, __m256i *out_hi) { + uint64_t mask = (increment_counter ? ~0 : 0); + __m512i mask_vec = _mm512_set1_epi64(mask); + __m512i deltas = _mm512_setr_epi64(0, 1, 2, 3, 4, 5, 6, 7); + deltas = _mm512_and_si512(mask_vec, deltas); + __m512i counters = + _mm512_add_epi64(_mm512_set1_epi64((int64_t)counter), deltas); + *out_lo = _mm512_cvtepi64_epi32(counters); + *out_hi = _mm512_cvtepi64_epi32(_mm512_srli_epi64(counters, 32)); +} + +void blake3_hash8_avx512(const uint8_t *const *inputs, size_t blocks, + const uint32_t key[8], uint64_t counter, + bool increment_counter, uint8_t flags, + uint8_t flags_start, uint8_t flags_end, uint8_t *out) { + __m256i h_vecs[8] = { + set1_256(key[0]), set1_256(key[1]), set1_256(key[2]), set1_256(key[3]), + set1_256(key[4]), set1_256(key[5]), set1_256(key[6]), set1_256(key[7]), + }; + __m256i counter_low_vec, counter_high_vec; + load_counters8(counter, increment_counter, &counter_low_vec, + &counter_high_vec); + uint8_t block_flags = flags | flags_start; + + for (size_t block = 0; block < blocks; block++) { + if (block + 1 == blocks) { + block_flags |= flags_end; + } + __m256i block_len_vec = set1_256(BLAKE3_BLOCK_LEN); + __m256i block_flags_vec = set1_256(block_flags); + __m256i msg_vecs[16]; + transpose_msg_vecs8(inputs, block * BLAKE3_BLOCK_LEN, msg_vecs); + + __m256i v[16] = { + h_vecs[0], h_vecs[1], h_vecs[2], h_vecs[3], + h_vecs[4], h_vecs[5], h_vecs[6], h_vecs[7], + set1_256(IV[0]), set1_256(IV[1]), set1_256(IV[2]), set1_256(IV[3]), + counter_low_vec, counter_high_vec, block_len_vec, block_flags_vec, + }; + round_fn8(v, msg_vecs, 0); + round_fn8(v, msg_vecs, 1); + round_fn8(v, msg_vecs, 2); + round_fn8(v, msg_vecs, 3); + round_fn8(v, msg_vecs, 4); + round_fn8(v, msg_vecs, 5); + round_fn8(v, msg_vecs, 6); + h_vecs[0] = xor_256(v[0], v[8]); + h_vecs[1] = xor_256(v[1], v[9]); + h_vecs[2] = xor_256(v[2], v[10]); + h_vecs[3] = xor_256(v[3], v[11]); + h_vecs[4] = xor_256(v[4], v[12]); + h_vecs[5] = xor_256(v[5], v[13]); + h_vecs[6] = xor_256(v[6], v[14]); + h_vecs[7] = xor_256(v[7], v[15]); + + block_flags = flags; + } + + transpose_vecs_256(h_vecs); + storeu_256(h_vecs[0], &out[0 * sizeof(__m256i)]); + storeu_256(h_vecs[1], &out[1 * sizeof(__m256i)]); + storeu_256(h_vecs[2], &out[2 * sizeof(__m256i)]); + storeu_256(h_vecs[3], &out[3 * sizeof(__m256i)]); + storeu_256(h_vecs[4], &out[4 * sizeof(__m256i)]); + storeu_256(h_vecs[5], &out[5 * sizeof(__m256i)]); + storeu_256(h_vecs[6], &out[6 * sizeof(__m256i)]); + storeu_256(h_vecs[7], &out[7 * sizeof(__m256i)]); +} + +/* + * ---------------------------------------------------------------------------- + * hash16_avx512 + * ---------------------------------------------------------------------------- + */ + +INLINE void round_fn16(__m512i v[16], __m512i m[16], size_t r) { + v[0] = add_512(v[0], m[(size_t)MSG_SCHEDULE[r][0]]); + v[1] = add_512(v[1], m[(size_t)MSG_SCHEDULE[r][2]]); + v[2] = add_512(v[2], m[(size_t)MSG_SCHEDULE[r][4]]); + v[3] = add_512(v[3], m[(size_t)MSG_SCHEDULE[r][6]]); + v[0] = add_512(v[0], v[4]); + v[1] = add_512(v[1], v[5]); + v[2] = add_512(v[2], v[6]); + v[3] = add_512(v[3], v[7]); + v[12] = xor_512(v[12], v[0]); + v[13] = xor_512(v[13], v[1]); + v[14] = xor_512(v[14], v[2]); + v[15] = xor_512(v[15], v[3]); + v[12] = rot16_512(v[12]); + v[13] = rot16_512(v[13]); + v[14] = rot16_512(v[14]); + v[15] = rot16_512(v[15]); + v[8] = add_512(v[8], v[12]); + v[9] = add_512(v[9], v[13]); + v[10] = add_512(v[10], v[14]); + v[11] = add_512(v[11], v[15]); + v[4] = xor_512(v[4], v[8]); + v[5] = xor_512(v[5], v[9]); + v[6] = xor_512(v[6], v[10]); + v[7] = xor_512(v[7], v[11]); + v[4] = rot12_512(v[4]); + v[5] = rot12_512(v[5]); + v[6] = rot12_512(v[6]); + v[7] = rot12_512(v[7]); + v[0] = add_512(v[0], m[(size_t)MSG_SCHEDULE[r][1]]); + v[1] = add_512(v[1], m[(size_t)MSG_SCHEDULE[r][3]]); + v[2] = add_512(v[2], m[(size_t)MSG_SCHEDULE[r][5]]); + v[3] = add_512(v[3], m[(size_t)MSG_SCHEDULE[r][7]]); + v[0] = add_512(v[0], v[4]); + v[1] = add_512(v[1], v[5]); + v[2] = add_512(v[2], v[6]); + v[3] = add_512(v[3], v[7]); + v[12] = xor_512(v[12], v[0]); + v[13] = xor_512(v[13], v[1]); + v[14] = xor_512(v[14], v[2]); + v[15] = xor_512(v[15], v[3]); + v[12] = rot8_512(v[12]); + v[13] = rot8_512(v[13]); + v[14] = rot8_512(v[14]); + v[15] = rot8_512(v[15]); + v[8] = add_512(v[8], v[12]); + v[9] = add_512(v[9], v[13]); + v[10] = add_512(v[10], v[14]); + v[11] = add_512(v[11], v[15]); + v[4] = xor_512(v[4], v[8]); + v[5] = xor_512(v[5], v[9]); + v[6] = xor_512(v[6], v[10]); + v[7] = xor_512(v[7], v[11]); + v[4] = rot7_512(v[4]); + v[5] = rot7_512(v[5]); + v[6] = rot7_512(v[6]); + v[7] = rot7_512(v[7]); + + v[0] = add_512(v[0], m[(size_t)MSG_SCHEDULE[r][8]]); + v[1] = add_512(v[1], m[(size_t)MSG_SCHEDULE[r][10]]); + v[2] = add_512(v[2], m[(size_t)MSG_SCHEDULE[r][12]]); + v[3] = add_512(v[3], m[(size_t)MSG_SCHEDULE[r][14]]); + v[0] = add_512(v[0], v[5]); + v[1] = add_512(v[1], v[6]); + v[2] = add_512(v[2], v[7]); + v[3] = add_512(v[3], v[4]); + v[15] = xor_512(v[15], v[0]); + v[12] = xor_512(v[12], v[1]); + v[13] = xor_512(v[13], v[2]); + v[14] = xor_512(v[14], v[3]); + v[15] = rot16_512(v[15]); + v[12] = rot16_512(v[12]); + v[13] = rot16_512(v[13]); + v[14] = rot16_512(v[14]); + v[10] = add_512(v[10], v[15]); + v[11] = add_512(v[11], v[12]); + v[8] = add_512(v[8], v[13]); + v[9] = add_512(v[9], v[14]); + v[5] = xor_512(v[5], v[10]); + v[6] = xor_512(v[6], v[11]); + v[7] = xor_512(v[7], v[8]); + v[4] = xor_512(v[4], v[9]); + v[5] = rot12_512(v[5]); + v[6] = rot12_512(v[6]); + v[7] = rot12_512(v[7]); + v[4] = rot12_512(v[4]); + v[0] = add_512(v[0], m[(size_t)MSG_SCHEDULE[r][9]]); + v[1] = add_512(v[1], m[(size_t)MSG_SCHEDULE[r][11]]); + v[2] = add_512(v[2], m[(size_t)MSG_SCHEDULE[r][13]]); + v[3] = add_512(v[3], m[(size_t)MSG_SCHEDULE[r][15]]); + v[0] = add_512(v[0], v[5]); + v[1] = add_512(v[1], v[6]); + v[2] = add_512(v[2], v[7]); + v[3] = add_512(v[3], v[4]); + v[15] = xor_512(v[15], v[0]); + v[12] = xor_512(v[12], v[1]); + v[13] = xor_512(v[13], v[2]); + v[14] = xor_512(v[14], v[3]); + v[15] = rot8_512(v[15]); + v[12] = rot8_512(v[12]); + v[13] = rot8_512(v[13]); + v[14] = rot8_512(v[14]); + v[10] = add_512(v[10], v[15]); + v[11] = add_512(v[11], v[12]); + v[8] = add_512(v[8], v[13]); + v[9] = add_512(v[9], v[14]); + v[5] = xor_512(v[5], v[10]); + v[6] = xor_512(v[6], v[11]); + v[7] = xor_512(v[7], v[8]); + v[4] = xor_512(v[4], v[9]); + v[5] = rot7_512(v[5]); + v[6] = rot7_512(v[6]); + v[7] = rot7_512(v[7]); + v[4] = rot7_512(v[4]); +} + +// 0b10001000, or lanes a0/a2/b0/b2 in little-endian order +#define LO_IMM8 0x88 + +INLINE __m512i unpack_lo_128(__m512i a, __m512i b) { + return _mm512_shuffle_i32x4(a, b, LO_IMM8); +} + +// 0b11011101, or lanes a1/a3/b1/b3 in little-endian order +#define HI_IMM8 0xdd + +INLINE __m512i unpack_hi_128(__m512i a, __m512i b) { + return _mm512_shuffle_i32x4(a, b, HI_IMM8); +} + +INLINE void transpose_vecs_512(__m512i vecs[16]) { + // Interleave 32-bit lanes. The _0 unpack is lanes + // 0/0/1/1/4/4/5/5/8/8/9/9/12/12/13/13, and the _2 unpack is lanes + // 2/2/3/3/6/6/7/7/10/10/11/11/14/14/15/15. + __m512i ab_0 = _mm512_unpacklo_epi32(vecs[0], vecs[1]); + __m512i ab_2 = _mm512_unpackhi_epi32(vecs[0], vecs[1]); + __m512i cd_0 = _mm512_unpacklo_epi32(vecs[2], vecs[3]); + __m512i cd_2 = _mm512_unpackhi_epi32(vecs[2], vecs[3]); + __m512i ef_0 = _mm512_unpacklo_epi32(vecs[4], vecs[5]); + __m512i ef_2 = _mm512_unpackhi_epi32(vecs[4], vecs[5]); + __m512i gh_0 = _mm512_unpacklo_epi32(vecs[6], vecs[7]); + __m512i gh_2 = _mm512_unpackhi_epi32(vecs[6], vecs[7]); + __m512i ij_0 = _mm512_unpacklo_epi32(vecs[8], vecs[9]); + __m512i ij_2 = _mm512_unpackhi_epi32(vecs[8], vecs[9]); + __m512i kl_0 = _mm512_unpacklo_epi32(vecs[10], vecs[11]); + __m512i kl_2 = _mm512_unpackhi_epi32(vecs[10], vecs[11]); + __m512i mn_0 = _mm512_unpacklo_epi32(vecs[12], vecs[13]); + __m512i mn_2 = _mm512_unpackhi_epi32(vecs[12], vecs[13]); + __m512i op_0 = _mm512_unpacklo_epi32(vecs[14], vecs[15]); + __m512i op_2 = _mm512_unpackhi_epi32(vecs[14], vecs[15]); + + // Interleave 64-bit lates. The _0 unpack is lanes + // 0/0/0/0/4/4/4/4/8/8/8/8/12/12/12/12, the _1 unpack is lanes + // 1/1/1/1/5/5/5/5/9/9/9/9/13/13/13/13, the _2 unpack is lanes + // 2/2/2/2/6/6/6/6/10/10/10/10/14/14/14/14, and the _3 unpack is lanes + // 3/3/3/3/7/7/7/7/11/11/11/11/15/15/15/15. + __m512i abcd_0 = _mm512_unpacklo_epi64(ab_0, cd_0); + __m512i abcd_1 = _mm512_unpackhi_epi64(ab_0, cd_0); + __m512i abcd_2 = _mm512_unpacklo_epi64(ab_2, cd_2); + __m512i abcd_3 = _mm512_unpackhi_epi64(ab_2, cd_2); + __m512i efgh_0 = _mm512_unpacklo_epi64(ef_0, gh_0); + __m512i efgh_1 = _mm512_unpackhi_epi64(ef_0, gh_0); + __m512i efgh_2 = _mm512_unpacklo_epi64(ef_2, gh_2); + __m512i efgh_3 = _mm512_unpackhi_epi64(ef_2, gh_2); + __m512i ijkl_0 = _mm512_unpacklo_epi64(ij_0, kl_0); + __m512i ijkl_1 = _mm512_unpackhi_epi64(ij_0, kl_0); + __m512i ijkl_2 = _mm512_unpacklo_epi64(ij_2, kl_2); + __m512i ijkl_3 = _mm512_unpackhi_epi64(ij_2, kl_2); + __m512i mnop_0 = _mm512_unpacklo_epi64(mn_0, op_0); + __m512i mnop_1 = _mm512_unpackhi_epi64(mn_0, op_0); + __m512i mnop_2 = _mm512_unpacklo_epi64(mn_2, op_2); + __m512i mnop_3 = _mm512_unpackhi_epi64(mn_2, op_2); + + // Interleave 128-bit lanes. The _0 unpack is + // 0/0/0/0/8/8/8/8/0/0/0/0/8/8/8/8, the _1 unpack is + // 1/1/1/1/9/9/9/9/1/1/1/1/9/9/9/9, and so on. + __m512i abcdefgh_0 = unpack_lo_128(abcd_0, efgh_0); + __m512i abcdefgh_1 = unpack_lo_128(abcd_1, efgh_1); + __m512i abcdefgh_2 = unpack_lo_128(abcd_2, efgh_2); + __m512i abcdefgh_3 = unpack_lo_128(abcd_3, efgh_3); + __m512i abcdefgh_4 = unpack_hi_128(abcd_0, efgh_0); + __m512i abcdefgh_5 = unpack_hi_128(abcd_1, efgh_1); + __m512i abcdefgh_6 = unpack_hi_128(abcd_2, efgh_2); + __m512i abcdefgh_7 = unpack_hi_128(abcd_3, efgh_3); + __m512i ijklmnop_0 = unpack_lo_128(ijkl_0, mnop_0); + __m512i ijklmnop_1 = unpack_lo_128(ijkl_1, mnop_1); + __m512i ijklmnop_2 = unpack_lo_128(ijkl_2, mnop_2); + __m512i ijklmnop_3 = unpack_lo_128(ijkl_3, mnop_3); + __m512i ijklmnop_4 = unpack_hi_128(ijkl_0, mnop_0); + __m512i ijklmnop_5 = unpack_hi_128(ijkl_1, mnop_1); + __m512i ijklmnop_6 = unpack_hi_128(ijkl_2, mnop_2); + __m512i ijklmnop_7 = unpack_hi_128(ijkl_3, mnop_3); + + // Interleave 128-bit lanes again for the final outputs. + vecs[0] = unpack_lo_128(abcdefgh_0, ijklmnop_0); + vecs[1] = unpack_lo_128(abcdefgh_1, ijklmnop_1); + vecs[2] = unpack_lo_128(abcdefgh_2, ijklmnop_2); + vecs[3] = unpack_lo_128(abcdefgh_3, ijklmnop_3); + vecs[4] = unpack_lo_128(abcdefgh_4, ijklmnop_4); + vecs[5] = unpack_lo_128(abcdefgh_5, ijklmnop_5); + vecs[6] = unpack_lo_128(abcdefgh_6, ijklmnop_6); + vecs[7] = unpack_lo_128(abcdefgh_7, ijklmnop_7); + vecs[8] = unpack_hi_128(abcdefgh_0, ijklmnop_0); + vecs[9] = unpack_hi_128(abcdefgh_1, ijklmnop_1); + vecs[10] = unpack_hi_128(abcdefgh_2, ijklmnop_2); + vecs[11] = unpack_hi_128(abcdefgh_3, ijklmnop_3); + vecs[12] = unpack_hi_128(abcdefgh_4, ijklmnop_4); + vecs[13] = unpack_hi_128(abcdefgh_5, ijklmnop_5); + vecs[14] = unpack_hi_128(abcdefgh_6, ijklmnop_6); + vecs[15] = unpack_hi_128(abcdefgh_7, ijklmnop_7); +} + +INLINE void transpose_msg_vecs16(const uint8_t *const *inputs, + size_t block_offset, __m512i out[16]) { + out[0] = loadu_512(&inputs[0][block_offset]); + out[1] = loadu_512(&inputs[1][block_offset]); + out[2] = loadu_512(&inputs[2][block_offset]); + out[3] = loadu_512(&inputs[3][block_offset]); + out[4] = loadu_512(&inputs[4][block_offset]); + out[5] = loadu_512(&inputs[5][block_offset]); + out[6] = loadu_512(&inputs[6][block_offset]); + out[7] = loadu_512(&inputs[7][block_offset]); + out[8] = loadu_512(&inputs[8][block_offset]); + out[9] = loadu_512(&inputs[9][block_offset]); + out[10] = loadu_512(&inputs[10][block_offset]); + out[11] = loadu_512(&inputs[11][block_offset]); + out[12] = loadu_512(&inputs[12][block_offset]); + out[13] = loadu_512(&inputs[13][block_offset]); + out[14] = loadu_512(&inputs[14][block_offset]); + out[15] = loadu_512(&inputs[15][block_offset]); + for (size_t i = 0; i < 16; ++i) { + _mm_prefetch(&inputs[i][block_offset + 256], _MM_HINT_T0); + } + transpose_vecs_512(out); +} + +INLINE void load_counters16(uint64_t counter, bool increment_counter, + __m512i *out_lo, __m512i *out_hi) { + const __m512i mask = _mm512_set1_epi32(-(int32_t)increment_counter); + const __m512i add0 = _mm512_set_epi32(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); + const __m512i add1 = _mm512_and_si512(mask, add0); + __m512i l = _mm512_add_epi32(_mm512_set1_epi32(counter), add1); + __mmask16 carry = _mm512_cmp_epu32_mask(l, add1, _MM_CMPINT_LT); + __m512i h = _mm512_mask_add_epi32(_mm512_set1_epi32(counter >> 32), carry, _mm512_set1_epi32(counter >> 32), _mm512_set1_epi32(1)); + *out_lo = l; + *out_hi = h; +} + +void blake3_hash16_avx512(const uint8_t *const *inputs, size_t blocks, + const uint32_t key[8], uint64_t counter, + bool increment_counter, uint8_t flags, + uint8_t flags_start, uint8_t flags_end, + uint8_t *out) { + __m512i h_vecs[8] = { + set1_512(key[0]), set1_512(key[1]), set1_512(key[2]), set1_512(key[3]), + set1_512(key[4]), set1_512(key[5]), set1_512(key[6]), set1_512(key[7]), + }; + __m512i counter_low_vec, counter_high_vec; + load_counters16(counter, increment_counter, &counter_low_vec, + &counter_high_vec); + uint8_t block_flags = flags | flags_start; + + for (size_t block = 0; block < blocks; block++) { + if (block + 1 == blocks) { + block_flags |= flags_end; + } + __m512i block_len_vec = set1_512(BLAKE3_BLOCK_LEN); + __m512i block_flags_vec = set1_512(block_flags); + __m512i msg_vecs[16]; + transpose_msg_vecs16(inputs, block * BLAKE3_BLOCK_LEN, msg_vecs); + + __m512i v[16] = { + h_vecs[0], h_vecs[1], h_vecs[2], h_vecs[3], + h_vecs[4], h_vecs[5], h_vecs[6], h_vecs[7], + set1_512(IV[0]), set1_512(IV[1]), set1_512(IV[2]), set1_512(IV[3]), + counter_low_vec, counter_high_vec, block_len_vec, block_flags_vec, + }; + round_fn16(v, msg_vecs, 0); + round_fn16(v, msg_vecs, 1); + round_fn16(v, msg_vecs, 2); + round_fn16(v, msg_vecs, 3); + round_fn16(v, msg_vecs, 4); + round_fn16(v, msg_vecs, 5); + round_fn16(v, msg_vecs, 6); + h_vecs[0] = xor_512(v[0], v[8]); + h_vecs[1] = xor_512(v[1], v[9]); + h_vecs[2] = xor_512(v[2], v[10]); + h_vecs[3] = xor_512(v[3], v[11]); + h_vecs[4] = xor_512(v[4], v[12]); + h_vecs[5] = xor_512(v[5], v[13]); + h_vecs[6] = xor_512(v[6], v[14]); + h_vecs[7] = xor_512(v[7], v[15]); + + block_flags = flags; + } + + // transpose_vecs_512 operates on a 16x16 matrix of words, but we only have 8 + // state vectors. Pad the matrix with zeros. After transposition, store the + // lower half of each vector. + __m512i padded[16] = { + h_vecs[0], h_vecs[1], h_vecs[2], h_vecs[3], + h_vecs[4], h_vecs[5], h_vecs[6], h_vecs[7], + set1_512(0), set1_512(0), set1_512(0), set1_512(0), + set1_512(0), set1_512(0), set1_512(0), set1_512(0), + }; + transpose_vecs_512(padded); + _mm256_mask_storeu_epi32(&out[0 * sizeof(__m256i)], (__mmask8)-1, _mm512_castsi512_si256(padded[0])); + _mm256_mask_storeu_epi32(&out[1 * sizeof(__m256i)], (__mmask8)-1, _mm512_castsi512_si256(padded[1])); + _mm256_mask_storeu_epi32(&out[2 * sizeof(__m256i)], (__mmask8)-1, _mm512_castsi512_si256(padded[2])); + _mm256_mask_storeu_epi32(&out[3 * sizeof(__m256i)], (__mmask8)-1, _mm512_castsi512_si256(padded[3])); + _mm256_mask_storeu_epi32(&out[4 * sizeof(__m256i)], (__mmask8)-1, _mm512_castsi512_si256(padded[4])); + _mm256_mask_storeu_epi32(&out[5 * sizeof(__m256i)], (__mmask8)-1, _mm512_castsi512_si256(padded[5])); + _mm256_mask_storeu_epi32(&out[6 * sizeof(__m256i)], (__mmask8)-1, _mm512_castsi512_si256(padded[6])); + _mm256_mask_storeu_epi32(&out[7 * sizeof(__m256i)], (__mmask8)-1, _mm512_castsi512_si256(padded[7])); + _mm256_mask_storeu_epi32(&out[8 * sizeof(__m256i)], (__mmask8)-1, _mm512_castsi512_si256(padded[8])); + _mm256_mask_storeu_epi32(&out[9 * sizeof(__m256i)], (__mmask8)-1, _mm512_castsi512_si256(padded[9])); + _mm256_mask_storeu_epi32(&out[10 * sizeof(__m256i)], (__mmask8)-1, _mm512_castsi512_si256(padded[10])); + _mm256_mask_storeu_epi32(&out[11 * sizeof(__m256i)], (__mmask8)-1, _mm512_castsi512_si256(padded[11])); + _mm256_mask_storeu_epi32(&out[12 * sizeof(__m256i)], (__mmask8)-1, _mm512_castsi512_si256(padded[12])); + _mm256_mask_storeu_epi32(&out[13 * sizeof(__m256i)], (__mmask8)-1, _mm512_castsi512_si256(padded[13])); + _mm256_mask_storeu_epi32(&out[14 * sizeof(__m256i)], (__mmask8)-1, _mm512_castsi512_si256(padded[14])); + _mm256_mask_storeu_epi32(&out[15 * sizeof(__m256i)], (__mmask8)-1, _mm512_castsi512_si256(padded[15])); +} + +/* + * ---------------------------------------------------------------------------- + * hash_many_avx512 + * ---------------------------------------------------------------------------- + */ + +INLINE void hash_one_avx512(const uint8_t *input, size_t blocks, + const uint32_t key[8], uint64_t counter, + uint8_t flags, uint8_t flags_start, + uint8_t flags_end, uint8_t out[BLAKE3_OUT_LEN]) { + uint32_t cv[8]; + memcpy(cv, key, BLAKE3_KEY_LEN); + uint8_t block_flags = flags | flags_start; + while (blocks > 0) { + if (blocks == 1) { + block_flags |= flags_end; + } + blake3_compress_in_place_avx512(cv, input, BLAKE3_BLOCK_LEN, counter, + block_flags); + input = &input[BLAKE3_BLOCK_LEN]; + blocks -= 1; + block_flags = flags; + } + memcpy(out, cv, BLAKE3_OUT_LEN); +} + +void blake3_hash_many_avx512(const uint8_t *const *inputs, size_t num_inputs, + size_t blocks, const uint32_t key[8], + uint64_t counter, bool increment_counter, + uint8_t flags, uint8_t flags_start, + uint8_t flags_end, uint8_t *out) { + while (num_inputs >= 16) { + blake3_hash16_avx512(inputs, blocks, key, counter, increment_counter, flags, + flags_start, flags_end, out); + if (increment_counter) { + counter += 16; + } + inputs += 16; + num_inputs -= 16; + out = &out[16 * BLAKE3_OUT_LEN]; + } + while (num_inputs >= 8) { + blake3_hash8_avx512(inputs, blocks, key, counter, increment_counter, flags, + flags_start, flags_end, out); + if (increment_counter) { + counter += 8; + } + inputs += 8; + num_inputs -= 8; + out = &out[8 * BLAKE3_OUT_LEN]; + } + while (num_inputs >= 4) { + blake3_hash4_avx512(inputs, blocks, key, counter, increment_counter, flags, + flags_start, flags_end, out); + if (increment_counter) { + counter += 4; + } + inputs += 4; + num_inputs -= 4; + out = &out[4 * BLAKE3_OUT_LEN]; + } + while (num_inputs > 0) { + hash_one_avx512(inputs[0], blocks, key, counter, flags, flags_start, + flags_end, out); + if (increment_counter) { + counter += 1; + } + inputs += 1; + num_inputs -= 1; + out = &out[BLAKE3_OUT_LEN]; + } +} diff --git a/src/third_party/blake3/blake3_avx512_x86-64_unix.S b/src/third_party/blake3/blake3_avx512_x86-64_unix.S new file mode 100644 index 0000000..a06aede --- /dev/null +++ b/src/third_party/blake3/blake3_avx512_x86-64_unix.S @@ -0,0 +1,2585 @@ +#if defined(__ELF__) && defined(__linux__) +.section .note.GNU-stack,"",%progbits +#endif + +#if defined(__ELF__) && defined(__CET__) && defined(__has_include) +#if __has_include() +#include +#endif +#endif + +#if !defined(_CET_ENDBR) +#define _CET_ENDBR +#endif + +.intel_syntax noprefix +.global _blake3_hash_many_avx512 +.global blake3_hash_many_avx512 +.global blake3_compress_in_place_avx512 +.global _blake3_compress_in_place_avx512 +.global blake3_compress_xof_avx512 +.global _blake3_compress_xof_avx512 + +#ifdef __APPLE__ +.text +#else +.section .text +#endif +.p2align 6 +_blake3_hash_many_avx512: +blake3_hash_many_avx512: + _CET_ENDBR + push r15 + push r14 + push r13 + push r12 + push rbx + push rbp + mov rbp, rsp + sub rsp, 144 + and rsp, 0xFFFFFFFFFFFFFFC0 + neg r9 + kmovw k1, r9d + vmovd xmm0, r8d + vpbroadcastd ymm0, xmm0 + shr r8, 32 + vmovd xmm1, r8d + vpbroadcastd ymm1, xmm1 + vmovdqa ymm4, ymm1 + vmovdqa ymm5, ymm1 + vpaddd ymm2, ymm0, ymmword ptr [ADD0+rip] + vpaddd ymm3, ymm0, ymmword ptr [ADD0+32+rip] + vpcmpltud k2, ymm2, ymm0 + vpcmpltud k3, ymm3, ymm0 + vpaddd ymm4 {k2}, ymm4, dword ptr [ADD1+rip] {1to8} + vpaddd ymm5 {k3}, ymm5, dword ptr [ADD1+rip] {1to8} + knotw k2, k1 + vmovdqa32 ymm2 {k2}, ymm0 + vmovdqa32 ymm3 {k2}, ymm0 + vmovdqa32 ymm4 {k2}, ymm1 + vmovdqa32 ymm5 {k2}, ymm1 + vmovdqa ymmword ptr [rsp], ymm2 + vmovdqa ymmword ptr [rsp+0x1*0x20], ymm3 + vmovdqa ymmword ptr [rsp+0x2*0x20], ymm4 + vmovdqa ymmword ptr [rsp+0x3*0x20], ymm5 + shl rdx, 6 + mov qword ptr [rsp+0x80], rdx + cmp rsi, 16 + jc 3f +2: + vpbroadcastd zmm0, dword ptr [rcx] + vpbroadcastd zmm1, dword ptr [rcx+0x1*0x4] + vpbroadcastd zmm2, dword ptr [rcx+0x2*0x4] + vpbroadcastd zmm3, dword ptr [rcx+0x3*0x4] + vpbroadcastd zmm4, dword ptr [rcx+0x4*0x4] + vpbroadcastd zmm5, dword ptr [rcx+0x5*0x4] + vpbroadcastd zmm6, dword ptr [rcx+0x6*0x4] + vpbroadcastd zmm7, dword ptr [rcx+0x7*0x4] + movzx eax, byte ptr [rbp+0x38] + movzx ebx, byte ptr [rbp+0x40] + or eax, ebx + xor edx, edx +.p2align 5 +9: + movzx ebx, byte ptr [rbp+0x48] + or ebx, eax + add rdx, 64 + cmp rdx, qword ptr [rsp+0x80] + cmove eax, ebx + mov dword ptr [rsp+0x88], eax + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + mov r10, qword ptr [rdi+0x10] + mov r11, qword ptr [rdi+0x18] + mov r12, qword ptr [rdi+0x40] + mov r13, qword ptr [rdi+0x48] + mov r14, qword ptr [rdi+0x50] + mov r15, qword ptr [rdi+0x58] + vmovdqu32 ymm16, ymmword ptr [rdx+r8-0x2*0x20] + vinserti64x4 zmm16, zmm16, ymmword ptr [rdx+r12-0x2*0x20], 0x01 + vmovdqu32 ymm17, ymmword ptr [rdx+r9-0x2*0x20] + vinserti64x4 zmm17, zmm17, ymmword ptr [rdx+r13-0x2*0x20], 0x01 + vpunpcklqdq zmm8, zmm16, zmm17 + vpunpckhqdq zmm9, zmm16, zmm17 + vmovdqu32 ymm18, ymmword ptr [rdx+r10-0x2*0x20] + vinserti64x4 zmm18, zmm18, ymmword ptr [rdx+r14-0x2*0x20], 0x01 + vmovdqu32 ymm19, ymmword ptr [rdx+r11-0x2*0x20] + vinserti64x4 zmm19, zmm19, ymmword ptr [rdx+r15-0x2*0x20], 0x01 + vpunpcklqdq zmm10, zmm18, zmm19 + vpunpckhqdq zmm11, zmm18, zmm19 + mov r8, qword ptr [rdi+0x20] + mov r9, qword ptr [rdi+0x28] + mov r10, qword ptr [rdi+0x30] + mov r11, qword ptr [rdi+0x38] + mov r12, qword ptr [rdi+0x60] + mov r13, qword ptr [rdi+0x68] + mov r14, qword ptr [rdi+0x70] + mov r15, qword ptr [rdi+0x78] + vmovdqu32 ymm16, ymmword ptr [rdx+r8-0x2*0x20] + vinserti64x4 zmm16, zmm16, ymmword ptr [rdx+r12-0x2*0x20], 0x01 + vmovdqu32 ymm17, ymmword ptr [rdx+r9-0x2*0x20] + vinserti64x4 zmm17, zmm17, ymmword ptr [rdx+r13-0x2*0x20], 0x01 + vpunpcklqdq zmm12, zmm16, zmm17 + vpunpckhqdq zmm13, zmm16, zmm17 + vmovdqu32 ymm18, ymmword ptr [rdx+r10-0x2*0x20] + vinserti64x4 zmm18, zmm18, ymmword ptr [rdx+r14-0x2*0x20], 0x01 + vmovdqu32 ymm19, ymmword ptr [rdx+r11-0x2*0x20] + vinserti64x4 zmm19, zmm19, ymmword ptr [rdx+r15-0x2*0x20], 0x01 + vpunpcklqdq zmm14, zmm18, zmm19 + vpunpckhqdq zmm15, zmm18, zmm19 + vmovdqa32 zmm27, zmmword ptr [INDEX0+rip] + vmovdqa32 zmm31, zmmword ptr [INDEX1+rip] + vshufps zmm16, zmm8, zmm10, 136 + vshufps zmm17, zmm12, zmm14, 136 + vmovdqa32 zmm20, zmm16 + vpermt2d zmm16, zmm27, zmm17 + vpermt2d zmm20, zmm31, zmm17 + vshufps zmm17, zmm8, zmm10, 221 + vshufps zmm30, zmm12, zmm14, 221 + vmovdqa32 zmm21, zmm17 + vpermt2d zmm17, zmm27, zmm30 + vpermt2d zmm21, zmm31, zmm30 + vshufps zmm18, zmm9, zmm11, 136 + vshufps zmm8, zmm13, zmm15, 136 + vmovdqa32 zmm22, zmm18 + vpermt2d zmm18, zmm27, zmm8 + vpermt2d zmm22, zmm31, zmm8 + vshufps zmm19, zmm9, zmm11, 221 + vshufps zmm8, zmm13, zmm15, 221 + vmovdqa32 zmm23, zmm19 + vpermt2d zmm19, zmm27, zmm8 + vpermt2d zmm23, zmm31, zmm8 + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + mov r10, qword ptr [rdi+0x10] + mov r11, qword ptr [rdi+0x18] + mov r12, qword ptr [rdi+0x40] + mov r13, qword ptr [rdi+0x48] + mov r14, qword ptr [rdi+0x50] + mov r15, qword ptr [rdi+0x58] + vmovdqu32 ymm24, ymmword ptr [r8+rdx-0x1*0x20] + vinserti64x4 zmm24, zmm24, ymmword ptr [r12+rdx-0x1*0x20], 0x01 + vmovdqu32 ymm25, ymmword ptr [r9+rdx-0x1*0x20] + vinserti64x4 zmm25, zmm25, ymmword ptr [r13+rdx-0x1*0x20], 0x01 + vpunpcklqdq zmm8, zmm24, zmm25 + vpunpckhqdq zmm9, zmm24, zmm25 + vmovdqu32 ymm24, ymmword ptr [r10+rdx-0x1*0x20] + vinserti64x4 zmm24, zmm24, ymmword ptr [r14+rdx-0x1*0x20], 0x01 + vmovdqu32 ymm25, ymmword ptr [r11+rdx-0x1*0x20] + vinserti64x4 zmm25, zmm25, ymmword ptr [r15+rdx-0x1*0x20], 0x01 + vpunpcklqdq zmm10, zmm24, zmm25 + vpunpckhqdq zmm11, zmm24, zmm25 + prefetcht0 [r8+rdx+0x80] + prefetcht0 [r12+rdx+0x80] + prefetcht0 [r9+rdx+0x80] + prefetcht0 [r13+rdx+0x80] + prefetcht0 [r10+rdx+0x80] + prefetcht0 [r14+rdx+0x80] + prefetcht0 [r11+rdx+0x80] + prefetcht0 [r15+rdx+0x80] + mov r8, qword ptr [rdi+0x20] + mov r9, qword ptr [rdi+0x28] + mov r10, qword ptr [rdi+0x30] + mov r11, qword ptr [rdi+0x38] + mov r12, qword ptr [rdi+0x60] + mov r13, qword ptr [rdi+0x68] + mov r14, qword ptr [rdi+0x70] + mov r15, qword ptr [rdi+0x78] + vmovdqu32 ymm24, ymmword ptr [r8+rdx-0x1*0x20] + vinserti64x4 zmm24, zmm24, ymmword ptr [r12+rdx-0x1*0x20], 0x01 + vmovdqu32 ymm25, ymmword ptr [r9+rdx-0x1*0x20] + vinserti64x4 zmm25, zmm25, ymmword ptr [r13+rdx-0x1*0x20], 0x01 + vpunpcklqdq zmm12, zmm24, zmm25 + vpunpckhqdq zmm13, zmm24, zmm25 + vmovdqu32 ymm24, ymmword ptr [r10+rdx-0x1*0x20] + vinserti64x4 zmm24, zmm24, ymmword ptr [r14+rdx-0x1*0x20], 0x01 + vmovdqu32 ymm25, ymmword ptr [r11+rdx-0x1*0x20] + vinserti64x4 zmm25, zmm25, ymmword ptr [r15+rdx-0x1*0x20], 0x01 + vpunpcklqdq zmm14, zmm24, zmm25 + vpunpckhqdq zmm15, zmm24, zmm25 + prefetcht0 [r8+rdx+0x80] + prefetcht0 [r12+rdx+0x80] + prefetcht0 [r9+rdx+0x80] + prefetcht0 [r13+rdx+0x80] + prefetcht0 [r10+rdx+0x80] + prefetcht0 [r14+rdx+0x80] + prefetcht0 [r11+rdx+0x80] + prefetcht0 [r15+rdx+0x80] + vshufps zmm24, zmm8, zmm10, 136 + vshufps zmm30, zmm12, zmm14, 136 + vmovdqa32 zmm28, zmm24 + vpermt2d zmm24, zmm27, zmm30 + vpermt2d zmm28, zmm31, zmm30 + vshufps zmm25, zmm8, zmm10, 221 + vshufps zmm30, zmm12, zmm14, 221 + vmovdqa32 zmm29, zmm25 + vpermt2d zmm25, zmm27, zmm30 + vpermt2d zmm29, zmm31, zmm30 + vshufps zmm26, zmm9, zmm11, 136 + vshufps zmm8, zmm13, zmm15, 136 + vmovdqa32 zmm30, zmm26 + vpermt2d zmm26, zmm27, zmm8 + vpermt2d zmm30, zmm31, zmm8 + vshufps zmm8, zmm9, zmm11, 221 + vshufps zmm10, zmm13, zmm15, 221 + vpermi2d zmm27, zmm8, zmm10 + vpermi2d zmm31, zmm8, zmm10 + vpbroadcastd zmm8, dword ptr [BLAKE3_IV_0+rip] + vpbroadcastd zmm9, dword ptr [BLAKE3_IV_1+rip] + vpbroadcastd zmm10, dword ptr [BLAKE3_IV_2+rip] + vpbroadcastd zmm11, dword ptr [BLAKE3_IV_3+rip] + vmovdqa32 zmm12, zmmword ptr [rsp] + vmovdqa32 zmm13, zmmword ptr [rsp+0x1*0x40] + vpbroadcastd zmm14, dword ptr [BLAKE3_BLOCK_LEN+rip] + vpbroadcastd zmm15, dword ptr [rsp+0x22*0x4] + vpaddd zmm0, zmm0, zmm16 + vpaddd zmm1, zmm1, zmm18 + vpaddd zmm2, zmm2, zmm20 + vpaddd zmm3, zmm3, zmm22 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vprord zmm15, zmm15, 16 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 12 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vpaddd zmm0, zmm0, zmm17 + vpaddd zmm1, zmm1, zmm19 + vpaddd zmm2, zmm2, zmm21 + vpaddd zmm3, zmm3, zmm23 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vprord zmm15, zmm15, 8 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 7 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vpaddd zmm0, zmm0, zmm24 + vpaddd zmm1, zmm1, zmm26 + vpaddd zmm2, zmm2, zmm28 + vpaddd zmm3, zmm3, zmm30 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 16 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vprord zmm4, zmm4, 12 + vpaddd zmm0, zmm0, zmm25 + vpaddd zmm1, zmm1, zmm27 + vpaddd zmm2, zmm2, zmm29 + vpaddd zmm3, zmm3, zmm31 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 8 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vprord zmm4, zmm4, 7 + vpaddd zmm0, zmm0, zmm18 + vpaddd zmm1, zmm1, zmm19 + vpaddd zmm2, zmm2, zmm23 + vpaddd zmm3, zmm3, zmm20 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vprord zmm15, zmm15, 16 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 12 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vpaddd zmm0, zmm0, zmm22 + vpaddd zmm1, zmm1, zmm26 + vpaddd zmm2, zmm2, zmm16 + vpaddd zmm3, zmm3, zmm29 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vprord zmm15, zmm15, 8 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 7 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vpaddd zmm0, zmm0, zmm17 + vpaddd zmm1, zmm1, zmm28 + vpaddd zmm2, zmm2, zmm25 + vpaddd zmm3, zmm3, zmm31 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 16 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vprord zmm4, zmm4, 12 + vpaddd zmm0, zmm0, zmm27 + vpaddd zmm1, zmm1, zmm21 + vpaddd zmm2, zmm2, zmm30 + vpaddd zmm3, zmm3, zmm24 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 8 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vprord zmm4, zmm4, 7 + vpaddd zmm0, zmm0, zmm19 + vpaddd zmm1, zmm1, zmm26 + vpaddd zmm2, zmm2, zmm29 + vpaddd zmm3, zmm3, zmm23 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vprord zmm15, zmm15, 16 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 12 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vpaddd zmm0, zmm0, zmm20 + vpaddd zmm1, zmm1, zmm28 + vpaddd zmm2, zmm2, zmm18 + vpaddd zmm3, zmm3, zmm30 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vprord zmm15, zmm15, 8 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 7 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vpaddd zmm0, zmm0, zmm22 + vpaddd zmm1, zmm1, zmm25 + vpaddd zmm2, zmm2, zmm27 + vpaddd zmm3, zmm3, zmm24 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 16 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vprord zmm4, zmm4, 12 + vpaddd zmm0, zmm0, zmm21 + vpaddd zmm1, zmm1, zmm16 + vpaddd zmm2, zmm2, zmm31 + vpaddd zmm3, zmm3, zmm17 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 8 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vprord zmm4, zmm4, 7 + vpaddd zmm0, zmm0, zmm26 + vpaddd zmm1, zmm1, zmm28 + vpaddd zmm2, zmm2, zmm30 + vpaddd zmm3, zmm3, zmm29 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vprord zmm15, zmm15, 16 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 12 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vpaddd zmm0, zmm0, zmm23 + vpaddd zmm1, zmm1, zmm25 + vpaddd zmm2, zmm2, zmm19 + vpaddd zmm3, zmm3, zmm31 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vprord zmm15, zmm15, 8 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 7 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vpaddd zmm0, zmm0, zmm20 + vpaddd zmm1, zmm1, zmm27 + vpaddd zmm2, zmm2, zmm21 + vpaddd zmm3, zmm3, zmm17 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 16 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vprord zmm4, zmm4, 12 + vpaddd zmm0, zmm0, zmm16 + vpaddd zmm1, zmm1, zmm18 + vpaddd zmm2, zmm2, zmm24 + vpaddd zmm3, zmm3, zmm22 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 8 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vprord zmm4, zmm4, 7 + vpaddd zmm0, zmm0, zmm28 + vpaddd zmm1, zmm1, zmm25 + vpaddd zmm2, zmm2, zmm31 + vpaddd zmm3, zmm3, zmm30 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vprord zmm15, zmm15, 16 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 12 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vpaddd zmm0, zmm0, zmm29 + vpaddd zmm1, zmm1, zmm27 + vpaddd zmm2, zmm2, zmm26 + vpaddd zmm3, zmm3, zmm24 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vprord zmm15, zmm15, 8 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 7 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vpaddd zmm0, zmm0, zmm23 + vpaddd zmm1, zmm1, zmm21 + vpaddd zmm2, zmm2, zmm16 + vpaddd zmm3, zmm3, zmm22 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 16 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vprord zmm4, zmm4, 12 + vpaddd zmm0, zmm0, zmm18 + vpaddd zmm1, zmm1, zmm19 + vpaddd zmm2, zmm2, zmm17 + vpaddd zmm3, zmm3, zmm20 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 8 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vprord zmm4, zmm4, 7 + vpaddd zmm0, zmm0, zmm25 + vpaddd zmm1, zmm1, zmm27 + vpaddd zmm2, zmm2, zmm24 + vpaddd zmm3, zmm3, zmm31 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vprord zmm15, zmm15, 16 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 12 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vpaddd zmm0, zmm0, zmm30 + vpaddd zmm1, zmm1, zmm21 + vpaddd zmm2, zmm2, zmm28 + vpaddd zmm3, zmm3, zmm17 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vprord zmm15, zmm15, 8 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 7 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vpaddd zmm0, zmm0, zmm29 + vpaddd zmm1, zmm1, zmm16 + vpaddd zmm2, zmm2, zmm18 + vpaddd zmm3, zmm3, zmm20 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 16 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vprord zmm4, zmm4, 12 + vpaddd zmm0, zmm0, zmm19 + vpaddd zmm1, zmm1, zmm26 + vpaddd zmm2, zmm2, zmm22 + vpaddd zmm3, zmm3, zmm23 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 8 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vprord zmm4, zmm4, 7 + vpaddd zmm0, zmm0, zmm27 + vpaddd zmm1, zmm1, zmm21 + vpaddd zmm2, zmm2, zmm17 + vpaddd zmm3, zmm3, zmm24 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vprord zmm15, zmm15, 16 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 12 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vpaddd zmm0, zmm0, zmm31 + vpaddd zmm1, zmm1, zmm16 + vpaddd zmm2, zmm2, zmm25 + vpaddd zmm3, zmm3, zmm22 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vprord zmm15, zmm15, 8 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 7 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vpaddd zmm0, zmm0, zmm30 + vpaddd zmm1, zmm1, zmm18 + vpaddd zmm2, zmm2, zmm19 + vpaddd zmm3, zmm3, zmm23 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 16 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vprord zmm4, zmm4, 12 + vpaddd zmm0, zmm0, zmm26 + vpaddd zmm1, zmm1, zmm28 + vpaddd zmm2, zmm2, zmm20 + vpaddd zmm3, zmm3, zmm29 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 8 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vprord zmm4, zmm4, 7 + vpxord zmm0, zmm0, zmm8 + vpxord zmm1, zmm1, zmm9 + vpxord zmm2, zmm2, zmm10 + vpxord zmm3, zmm3, zmm11 + vpxord zmm4, zmm4, zmm12 + vpxord zmm5, zmm5, zmm13 + vpxord zmm6, zmm6, zmm14 + vpxord zmm7, zmm7, zmm15 + movzx eax, byte ptr [rbp+0x38] + jne 9b + mov rbx, qword ptr [rbp+0x50] + vpunpckldq zmm16, zmm0, zmm1 + vpunpckhdq zmm17, zmm0, zmm1 + vpunpckldq zmm18, zmm2, zmm3 + vpunpckhdq zmm19, zmm2, zmm3 + vpunpckldq zmm20, zmm4, zmm5 + vpunpckhdq zmm21, zmm4, zmm5 + vpunpckldq zmm22, zmm6, zmm7 + vpunpckhdq zmm23, zmm6, zmm7 + vpunpcklqdq zmm0, zmm16, zmm18 + vpunpckhqdq zmm1, zmm16, zmm18 + vpunpcklqdq zmm2, zmm17, zmm19 + vpunpckhqdq zmm3, zmm17, zmm19 + vpunpcklqdq zmm4, zmm20, zmm22 + vpunpckhqdq zmm5, zmm20, zmm22 + vpunpcklqdq zmm6, zmm21, zmm23 + vpunpckhqdq zmm7, zmm21, zmm23 + vshufi32x4 zmm16, zmm0, zmm4, 0x88 + vshufi32x4 zmm17, zmm1, zmm5, 0x88 + vshufi32x4 zmm18, zmm2, zmm6, 0x88 + vshufi32x4 zmm19, zmm3, zmm7, 0x88 + vshufi32x4 zmm20, zmm0, zmm4, 0xDD + vshufi32x4 zmm21, zmm1, zmm5, 0xDD + vshufi32x4 zmm22, zmm2, zmm6, 0xDD + vshufi32x4 zmm23, zmm3, zmm7, 0xDD + vshufi32x4 zmm0, zmm16, zmm17, 0x88 + vshufi32x4 zmm1, zmm18, zmm19, 0x88 + vshufi32x4 zmm2, zmm20, zmm21, 0x88 + vshufi32x4 zmm3, zmm22, zmm23, 0x88 + vshufi32x4 zmm4, zmm16, zmm17, 0xDD + vshufi32x4 zmm5, zmm18, zmm19, 0xDD + vshufi32x4 zmm6, zmm20, zmm21, 0xDD + vshufi32x4 zmm7, zmm22, zmm23, 0xDD + vmovdqu32 zmmword ptr [rbx], zmm0 + vmovdqu32 zmmword ptr [rbx+0x1*0x40], zmm1 + vmovdqu32 zmmword ptr [rbx+0x2*0x40], zmm2 + vmovdqu32 zmmword ptr [rbx+0x3*0x40], zmm3 + vmovdqu32 zmmword ptr [rbx+0x4*0x40], zmm4 + vmovdqu32 zmmword ptr [rbx+0x5*0x40], zmm5 + vmovdqu32 zmmword ptr [rbx+0x6*0x40], zmm6 + vmovdqu32 zmmword ptr [rbx+0x7*0x40], zmm7 + vmovdqa32 zmm0, zmmword ptr [rsp] + vmovdqa32 zmm1, zmmword ptr [rsp+0x1*0x40] + vmovdqa32 zmm2, zmm0 + vpaddd zmm2{k1}, zmm0, dword ptr [ADD16+rip] {1to16} + vpcmpltud k2, zmm2, zmm0 + vpaddd zmm1 {k2}, zmm1, dword ptr [ADD1+rip] {1to16} + vmovdqa32 zmmword ptr [rsp], zmm2 + vmovdqa32 zmmword ptr [rsp+0x1*0x40], zmm1 + add rdi, 128 + add rbx, 512 + mov qword ptr [rbp+0x50], rbx + sub rsi, 16 + cmp rsi, 16 + jnc 2b + test rsi, rsi + jnz 3f +4: + vzeroupper + mov rsp, rbp + pop rbp + pop rbx + pop r12 + pop r13 + pop r14 + pop r15 + ret +.p2align 6 +3: + test esi, 0x8 + je 3f + vpbroadcastd ymm0, dword ptr [rcx] + vpbroadcastd ymm1, dword ptr [rcx+0x4] + vpbroadcastd ymm2, dword ptr [rcx+0x8] + vpbroadcastd ymm3, dword ptr [rcx+0xC] + vpbroadcastd ymm4, dword ptr [rcx+0x10] + vpbroadcastd ymm5, dword ptr [rcx+0x14] + vpbroadcastd ymm6, dword ptr [rcx+0x18] + vpbroadcastd ymm7, dword ptr [rcx+0x1C] + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + mov r10, qword ptr [rdi+0x10] + mov r11, qword ptr [rdi+0x18] + mov r12, qword ptr [rdi+0x20] + mov r13, qword ptr [rdi+0x28] + mov r14, qword ptr [rdi+0x30] + mov r15, qword ptr [rdi+0x38] + movzx eax, byte ptr [rbp+0x38] + movzx ebx, byte ptr [rbp+0x40] + or eax, ebx + xor edx, edx +2: + movzx ebx, byte ptr [rbp+0x48] + or ebx, eax + add rdx, 64 + cmp rdx, qword ptr [rsp+0x80] + cmove eax, ebx + mov dword ptr [rsp+0x88], eax + vmovups xmm8, xmmword ptr [r8+rdx-0x40] + vinsertf128 ymm8, ymm8, xmmword ptr [r12+rdx-0x40], 0x01 + vmovups xmm9, xmmword ptr [r9+rdx-0x40] + vinsertf128 ymm9, ymm9, xmmword ptr [r13+rdx-0x40], 0x01 + vunpcklpd ymm12, ymm8, ymm9 + vunpckhpd ymm13, ymm8, ymm9 + vmovups xmm10, xmmword ptr [r10+rdx-0x40] + vinsertf128 ymm10, ymm10, xmmword ptr [r14+rdx-0x40], 0x01 + vmovups xmm11, xmmword ptr [r11+rdx-0x40] + vinsertf128 ymm11, ymm11, xmmword ptr [r15+rdx-0x40], 0x01 + vunpcklpd ymm14, ymm10, ymm11 + vunpckhpd ymm15, ymm10, ymm11 + vshufps ymm16, ymm12, ymm14, 136 + vshufps ymm17, ymm12, ymm14, 221 + vshufps ymm18, ymm13, ymm15, 136 + vshufps ymm19, ymm13, ymm15, 221 + vmovups xmm8, xmmword ptr [r8+rdx-0x30] + vinsertf128 ymm8, ymm8, xmmword ptr [r12+rdx-0x30], 0x01 + vmovups xmm9, xmmword ptr [r9+rdx-0x30] + vinsertf128 ymm9, ymm9, xmmword ptr [r13+rdx-0x30], 0x01 + vunpcklpd ymm12, ymm8, ymm9 + vunpckhpd ymm13, ymm8, ymm9 + vmovups xmm10, xmmword ptr [r10+rdx-0x30] + vinsertf128 ymm10, ymm10, xmmword ptr [r14+rdx-0x30], 0x01 + vmovups xmm11, xmmword ptr [r11+rdx-0x30] + vinsertf128 ymm11, ymm11, xmmword ptr [r15+rdx-0x30], 0x01 + vunpcklpd ymm14, ymm10, ymm11 + vunpckhpd ymm15, ymm10, ymm11 + vshufps ymm20, ymm12, ymm14, 136 + vshufps ymm21, ymm12, ymm14, 221 + vshufps ymm22, ymm13, ymm15, 136 + vshufps ymm23, ymm13, ymm15, 221 + vmovups xmm8, xmmword ptr [r8+rdx-0x20] + vinsertf128 ymm8, ymm8, xmmword ptr [r12+rdx-0x20], 0x01 + vmovups xmm9, xmmword ptr [r9+rdx-0x20] + vinsertf128 ymm9, ymm9, xmmword ptr [r13+rdx-0x20], 0x01 + vunpcklpd ymm12, ymm8, ymm9 + vunpckhpd ymm13, ymm8, ymm9 + vmovups xmm10, xmmword ptr [r10+rdx-0x20] + vinsertf128 ymm10, ymm10, xmmword ptr [r14+rdx-0x20], 0x01 + vmovups xmm11, xmmword ptr [r11+rdx-0x20] + vinsertf128 ymm11, ymm11, xmmword ptr [r15+rdx-0x20], 0x01 + vunpcklpd ymm14, ymm10, ymm11 + vunpckhpd ymm15, ymm10, ymm11 + vshufps ymm24, ymm12, ymm14, 136 + vshufps ymm25, ymm12, ymm14, 221 + vshufps ymm26, ymm13, ymm15, 136 + vshufps ymm27, ymm13, ymm15, 221 + vmovups xmm8, xmmword ptr [r8+rdx-0x10] + vinsertf128 ymm8, ymm8, xmmword ptr [r12+rdx-0x10], 0x01 + vmovups xmm9, xmmword ptr [r9+rdx-0x10] + vinsertf128 ymm9, ymm9, xmmword ptr [r13+rdx-0x10], 0x01 + vunpcklpd ymm12, ymm8, ymm9 + vunpckhpd ymm13, ymm8, ymm9 + vmovups xmm10, xmmword ptr [r10+rdx-0x10] + vinsertf128 ymm10, ymm10, xmmword ptr [r14+rdx-0x10], 0x01 + vmovups xmm11, xmmword ptr [r11+rdx-0x10] + vinsertf128 ymm11, ymm11, xmmword ptr [r15+rdx-0x10], 0x01 + vunpcklpd ymm14, ymm10, ymm11 + vunpckhpd ymm15, ymm10, ymm11 + vshufps ymm28, ymm12, ymm14, 136 + vshufps ymm29, ymm12, ymm14, 221 + vshufps ymm30, ymm13, ymm15, 136 + vshufps ymm31, ymm13, ymm15, 221 + vpbroadcastd ymm8, dword ptr [BLAKE3_IV_0+rip] + vpbroadcastd ymm9, dword ptr [BLAKE3_IV_1+rip] + vpbroadcastd ymm10, dword ptr [BLAKE3_IV_2+rip] + vpbroadcastd ymm11, dword ptr [BLAKE3_IV_3+rip] + vmovdqa ymm12, ymmword ptr [rsp] + vmovdqa ymm13, ymmword ptr [rsp+0x40] + vpbroadcastd ymm14, dword ptr [BLAKE3_BLOCK_LEN+rip] + vpbroadcastd ymm15, dword ptr [rsp+0x88] + vpaddd ymm0, ymm0, ymm16 + vpaddd ymm1, ymm1, ymm18 + vpaddd ymm2, ymm2, ymm20 + vpaddd ymm3, ymm3, ymm22 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vprord ymm15, ymm15, 16 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 12 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vpaddd ymm0, ymm0, ymm17 + vpaddd ymm1, ymm1, ymm19 + vpaddd ymm2, ymm2, ymm21 + vpaddd ymm3, ymm3, ymm23 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vprord ymm15, ymm15, 8 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 7 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vpaddd ymm0, ymm0, ymm24 + vpaddd ymm1, ymm1, ymm26 + vpaddd ymm2, ymm2, ymm28 + vpaddd ymm3, ymm3, ymm30 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 16 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vprord ymm4, ymm4, 12 + vpaddd ymm0, ymm0, ymm25 + vpaddd ymm1, ymm1, ymm27 + vpaddd ymm2, ymm2, ymm29 + vpaddd ymm3, ymm3, ymm31 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 8 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vprord ymm4, ymm4, 7 + vpaddd ymm0, ymm0, ymm18 + vpaddd ymm1, ymm1, ymm19 + vpaddd ymm2, ymm2, ymm23 + vpaddd ymm3, ymm3, ymm20 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vprord ymm15, ymm15, 16 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 12 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vpaddd ymm0, ymm0, ymm22 + vpaddd ymm1, ymm1, ymm26 + vpaddd ymm2, ymm2, ymm16 + vpaddd ymm3, ymm3, ymm29 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vprord ymm15, ymm15, 8 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 7 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vpaddd ymm0, ymm0, ymm17 + vpaddd ymm1, ymm1, ymm28 + vpaddd ymm2, ymm2, ymm25 + vpaddd ymm3, ymm3, ymm31 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 16 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vprord ymm4, ymm4, 12 + vpaddd ymm0, ymm0, ymm27 + vpaddd ymm1, ymm1, ymm21 + vpaddd ymm2, ymm2, ymm30 + vpaddd ymm3, ymm3, ymm24 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 8 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vprord ymm4, ymm4, 7 + vpaddd ymm0, ymm0, ymm19 + vpaddd ymm1, ymm1, ymm26 + vpaddd ymm2, ymm2, ymm29 + vpaddd ymm3, ymm3, ymm23 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vprord ymm15, ymm15, 16 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 12 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vpaddd ymm0, ymm0, ymm20 + vpaddd ymm1, ymm1, ymm28 + vpaddd ymm2, ymm2, ymm18 + vpaddd ymm3, ymm3, ymm30 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vprord ymm15, ymm15, 8 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 7 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vpaddd ymm0, ymm0, ymm22 + vpaddd ymm1, ymm1, ymm25 + vpaddd ymm2, ymm2, ymm27 + vpaddd ymm3, ymm3, ymm24 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 16 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vprord ymm4, ymm4, 12 + vpaddd ymm0, ymm0, ymm21 + vpaddd ymm1, ymm1, ymm16 + vpaddd ymm2, ymm2, ymm31 + vpaddd ymm3, ymm3, ymm17 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 8 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vprord ymm4, ymm4, 7 + vpaddd ymm0, ymm0, ymm26 + vpaddd ymm1, ymm1, ymm28 + vpaddd ymm2, ymm2, ymm30 + vpaddd ymm3, ymm3, ymm29 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vprord ymm15, ymm15, 16 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 12 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vpaddd ymm0, ymm0, ymm23 + vpaddd ymm1, ymm1, ymm25 + vpaddd ymm2, ymm2, ymm19 + vpaddd ymm3, ymm3, ymm31 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vprord ymm15, ymm15, 8 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 7 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vpaddd ymm0, ymm0, ymm20 + vpaddd ymm1, ymm1, ymm27 + vpaddd ymm2, ymm2, ymm21 + vpaddd ymm3, ymm3, ymm17 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 16 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vprord ymm4, ymm4, 12 + vpaddd ymm0, ymm0, ymm16 + vpaddd ymm1, ymm1, ymm18 + vpaddd ymm2, ymm2, ymm24 + vpaddd ymm3, ymm3, ymm22 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 8 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vprord ymm4, ymm4, 7 + vpaddd ymm0, ymm0, ymm28 + vpaddd ymm1, ymm1, ymm25 + vpaddd ymm2, ymm2, ymm31 + vpaddd ymm3, ymm3, ymm30 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vprord ymm15, ymm15, 16 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 12 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vpaddd ymm0, ymm0, ymm29 + vpaddd ymm1, ymm1, ymm27 + vpaddd ymm2, ymm2, ymm26 + vpaddd ymm3, ymm3, ymm24 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vprord ymm15, ymm15, 8 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 7 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vpaddd ymm0, ymm0, ymm23 + vpaddd ymm1, ymm1, ymm21 + vpaddd ymm2, ymm2, ymm16 + vpaddd ymm3, ymm3, ymm22 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 16 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vprord ymm4, ymm4, 12 + vpaddd ymm0, ymm0, ymm18 + vpaddd ymm1, ymm1, ymm19 + vpaddd ymm2, ymm2, ymm17 + vpaddd ymm3, ymm3, ymm20 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 8 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vprord ymm4, ymm4, 7 + vpaddd ymm0, ymm0, ymm25 + vpaddd ymm1, ymm1, ymm27 + vpaddd ymm2, ymm2, ymm24 + vpaddd ymm3, ymm3, ymm31 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vprord ymm15, ymm15, 16 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 12 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vpaddd ymm0, ymm0, ymm30 + vpaddd ymm1, ymm1, ymm21 + vpaddd ymm2, ymm2, ymm28 + vpaddd ymm3, ymm3, ymm17 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vprord ymm15, ymm15, 8 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 7 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vpaddd ymm0, ymm0, ymm29 + vpaddd ymm1, ymm1, ymm16 + vpaddd ymm2, ymm2, ymm18 + vpaddd ymm3, ymm3, ymm20 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 16 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vprord ymm4, ymm4, 12 + vpaddd ymm0, ymm0, ymm19 + vpaddd ymm1, ymm1, ymm26 + vpaddd ymm2, ymm2, ymm22 + vpaddd ymm3, ymm3, ymm23 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 8 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vprord ymm4, ymm4, 7 + vpaddd ymm0, ymm0, ymm27 + vpaddd ymm1, ymm1, ymm21 + vpaddd ymm2, ymm2, ymm17 + vpaddd ymm3, ymm3, ymm24 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vprord ymm15, ymm15, 16 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 12 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vpaddd ymm0, ymm0, ymm31 + vpaddd ymm1, ymm1, ymm16 + vpaddd ymm2, ymm2, ymm25 + vpaddd ymm3, ymm3, ymm22 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vprord ymm15, ymm15, 8 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 7 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vpaddd ymm0, ymm0, ymm30 + vpaddd ymm1, ymm1, ymm18 + vpaddd ymm2, ymm2, ymm19 + vpaddd ymm3, ymm3, ymm23 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 16 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vprord ymm4, ymm4, 12 + vpaddd ymm0, ymm0, ymm26 + vpaddd ymm1, ymm1, ymm28 + vpaddd ymm2, ymm2, ymm20 + vpaddd ymm3, ymm3, ymm29 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 8 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vprord ymm4, ymm4, 7 + vpxor ymm0, ymm0, ymm8 + vpxor ymm1, ymm1, ymm9 + vpxor ymm2, ymm2, ymm10 + vpxor ymm3, ymm3, ymm11 + vpxor ymm4, ymm4, ymm12 + vpxor ymm5, ymm5, ymm13 + vpxor ymm6, ymm6, ymm14 + vpxor ymm7, ymm7, ymm15 + movzx eax, byte ptr [rbp+0x38] + jne 2b + mov rbx, qword ptr [rbp+0x50] + vunpcklps ymm8, ymm0, ymm1 + vunpcklps ymm9, ymm2, ymm3 + vunpckhps ymm10, ymm0, ymm1 + vunpcklps ymm11, ymm4, ymm5 + vunpcklps ymm0, ymm6, ymm7 + vshufps ymm12, ymm8, ymm9, 78 + vblendps ymm1, ymm8, ymm12, 0xCC + vshufps ymm8, ymm11, ymm0, 78 + vunpckhps ymm13, ymm2, ymm3 + vblendps ymm2, ymm11, ymm8, 0xCC + vblendps ymm3, ymm12, ymm9, 0xCC + vperm2f128 ymm12, ymm1, ymm2, 0x20 + vmovups ymmword ptr [rbx], ymm12 + vunpckhps ymm14, ymm4, ymm5 + vblendps ymm4, ymm8, ymm0, 0xCC + vunpckhps ymm15, ymm6, ymm7 + vperm2f128 ymm7, ymm3, ymm4, 0x20 + vmovups ymmword ptr [rbx+0x20], ymm7 + vshufps ymm5, ymm10, ymm13, 78 + vblendps ymm6, ymm5, ymm13, 0xCC + vshufps ymm13, ymm14, ymm15, 78 + vblendps ymm10, ymm10, ymm5, 0xCC + vblendps ymm14, ymm14, ymm13, 0xCC + vperm2f128 ymm8, ymm10, ymm14, 0x20 + vmovups ymmword ptr [rbx+0x40], ymm8 + vblendps ymm15, ymm13, ymm15, 0xCC + vperm2f128 ymm13, ymm6, ymm15, 0x20 + vmovups ymmword ptr [rbx+0x60], ymm13 + vperm2f128 ymm9, ymm1, ymm2, 0x31 + vperm2f128 ymm11, ymm3, ymm4, 0x31 + vmovups ymmword ptr [rbx+0x80], ymm9 + vperm2f128 ymm14, ymm10, ymm14, 0x31 + vperm2f128 ymm15, ymm6, ymm15, 0x31 + vmovups ymmword ptr [rbx+0xA0], ymm11 + vmovups ymmword ptr [rbx+0xC0], ymm14 + vmovups ymmword ptr [rbx+0xE0], ymm15 + vmovdqa ymm0, ymmword ptr [rsp] + vmovdqa ymm2, ymmword ptr [rsp+0x2*0x20] + vmovdqa32 ymm0 {k1}, ymmword ptr [rsp+0x1*0x20] + vmovdqa32 ymm2 {k1}, ymmword ptr [rsp+0x3*0x20] + vmovdqa ymmword ptr [rsp], ymm0 + vmovdqa ymmword ptr [rsp+0x2*0x20], ymm2 + add rbx, 256 + mov qword ptr [rbp+0x50], rbx + add rdi, 64 + sub rsi, 8 +3: + mov rbx, qword ptr [rbp+0x50] + mov r15, qword ptr [rsp+0x80] + movzx r13, byte ptr [rbp+0x38] + movzx r12, byte ptr [rbp+0x48] + test esi, 0x4 + je 3f + vbroadcasti32x4 zmm0, xmmword ptr [rcx] + vbroadcasti32x4 zmm1, xmmword ptr [rcx+0x1*0x10] + vmovdqa xmm12, xmmword ptr [rsp] + vmovdqa xmm13, xmmword ptr [rsp+0x4*0x10] + vpunpckldq xmm14, xmm12, xmm13 + vpunpckhdq xmm15, xmm12, xmm13 + vpermq ymm14, ymm14, 0xDC + vpermq ymm15, ymm15, 0xDC + vpbroadcastd zmm12, dword ptr [BLAKE3_BLOCK_LEN+rip] + vinserti64x4 zmm13, zmm14, ymm15, 0x01 + mov eax, 17476 + kmovw k2, eax + vpblendmd zmm13 {k2}, zmm13, zmm12 + vbroadcasti32x4 zmm15, xmmword ptr [BLAKE3_IV+rip] + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + mov r10, qword ptr [rdi+0x10] + mov r11, qword ptr [rdi+0x18] + mov eax, 43690 + kmovw k3, eax + mov eax, 34952 + kmovw k4, eax + movzx eax, byte ptr [rbp+0x40] + or eax, r13d + xor edx, edx +.p2align 5 +2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + mov dword ptr [rsp+0x88], eax + vmovdqa32 zmm2, zmm15 + vpbroadcastd zmm8, dword ptr [rsp+0x22*0x4] + vpblendmd zmm3 {k4}, zmm13, zmm8 + vmovups zmm8, zmmword ptr [r8+rdx-0x1*0x40] + vinserti32x4 zmm8, zmm8, xmmword ptr [r9+rdx-0x4*0x10], 0x01 + vinserti32x4 zmm8, zmm8, xmmword ptr [r10+rdx-0x4*0x10], 0x02 + vinserti32x4 zmm8, zmm8, xmmword ptr [r11+rdx-0x4*0x10], 0x03 + vmovups zmm9, zmmword ptr [r8+rdx-0x30] + vinserti32x4 zmm9, zmm9, xmmword ptr [r9+rdx-0x3*0x10], 0x01 + vinserti32x4 zmm9, zmm9, xmmword ptr [r10+rdx-0x3*0x10], 0x02 + vinserti32x4 zmm9, zmm9, xmmword ptr [r11+rdx-0x3*0x10], 0x03 + vshufps zmm4, zmm8, zmm9, 136 + vshufps zmm5, zmm8, zmm9, 221 + vmovups zmm8, zmmword ptr [r8+rdx-0x20] + vinserti32x4 zmm8, zmm8, xmmword ptr [r9+rdx-0x2*0x10], 0x01 + vinserti32x4 zmm8, zmm8, xmmword ptr [r10+rdx-0x2*0x10], 0x02 + vinserti32x4 zmm8, zmm8, xmmword ptr [r11+rdx-0x2*0x10], 0x03 + vmovups zmm9, zmmword ptr [r8+rdx-0x10] + vinserti32x4 zmm9, zmm9, xmmword ptr [r9+rdx-0x1*0x10], 0x01 + vinserti32x4 zmm9, zmm9, xmmword ptr [r10+rdx-0x1*0x10], 0x02 + vinserti32x4 zmm9, zmm9, xmmword ptr [r11+rdx-0x1*0x10], 0x03 + vshufps zmm6, zmm8, zmm9, 136 + vshufps zmm7, zmm8, zmm9, 221 + vpshufd zmm6, zmm6, 0x93 + vpshufd zmm7, zmm7, 0x93 + mov al, 7 +9: + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm0, zmm0, zmm1 + vpxord zmm3, zmm3, zmm0 + vprord zmm3, zmm3, 16 + vpaddd zmm2, zmm2, zmm3 + vpxord zmm1, zmm1, zmm2 + vprord zmm1, zmm1, 12 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm0, zmm0, zmm1 + vpxord zmm3, zmm3, zmm0 + vprord zmm3, zmm3, 8 + vpaddd zmm2, zmm2, zmm3 + vpxord zmm1, zmm1, zmm2 + vprord zmm1, zmm1, 7 + vpshufd zmm0, zmm0, 0x93 + vpshufd zmm3, zmm3, 0x4E + vpshufd zmm2, zmm2, 0x39 + vpaddd zmm0, zmm0, zmm6 + vpaddd zmm0, zmm0, zmm1 + vpxord zmm3, zmm3, zmm0 + vprord zmm3, zmm3, 16 + vpaddd zmm2, zmm2, zmm3 + vpxord zmm1, zmm1, zmm2 + vprord zmm1, zmm1, 12 + vpaddd zmm0, zmm0, zmm7 + vpaddd zmm0, zmm0, zmm1 + vpxord zmm3, zmm3, zmm0 + vprord zmm3, zmm3, 8 + vpaddd zmm2, zmm2, zmm3 + vpxord zmm1, zmm1, zmm2 + vprord zmm1, zmm1, 7 + vpshufd zmm0, zmm0, 0x39 + vpshufd zmm3, zmm3, 0x4E + vpshufd zmm2, zmm2, 0x93 + dec al + jz 9f + vshufps zmm8, zmm4, zmm5, 214 + vpshufd zmm9, zmm4, 0x0F + vpshufd zmm4, zmm8, 0x39 + vshufps zmm8, zmm6, zmm7, 250 + vpblendmd zmm9 {k3}, zmm9, zmm8 + vpunpcklqdq zmm8, zmm7, zmm5 + vpblendmd zmm8 {k4}, zmm8, zmm6 + vpshufd zmm8, zmm8, 0x78 + vpunpckhdq zmm5, zmm5, zmm7 + vpunpckldq zmm6, zmm6, zmm5 + vpshufd zmm7, zmm6, 0x1E + vmovdqa32 zmm5, zmm9 + vmovdqa32 zmm6, zmm8 + jmp 9b +9: + vpxord zmm0, zmm0, zmm2 + vpxord zmm1, zmm1, zmm3 + mov eax, r13d + cmp rdx, r15 + jne 2b + vmovdqu xmmword ptr [rbx], xmm0 + vmovdqu xmmword ptr [rbx+0x10], xmm1 + vextracti128 xmmword ptr [rbx+0x20], ymm0, 0x01 + vextracti128 xmmword ptr [rbx+0x30], ymm1, 0x01 + vextracti32x4 xmmword ptr [rbx+0x4*0x10], zmm0, 0x02 + vextracti32x4 xmmword ptr [rbx+0x5*0x10], zmm1, 0x02 + vextracti32x4 xmmword ptr [rbx+0x6*0x10], zmm0, 0x03 + vextracti32x4 xmmword ptr [rbx+0x7*0x10], zmm1, 0x03 + vmovdqa xmm0, xmmword ptr [rsp] + vmovdqa xmm2, xmmword ptr [rsp+0x40] + vmovdqa32 xmm0 {k1}, xmmword ptr [rsp+0x1*0x10] + vmovdqa32 xmm2 {k1}, xmmword ptr [rsp+0x5*0x10] + vmovdqa xmmword ptr [rsp], xmm0 + vmovdqa xmmword ptr [rsp+0x40], xmm2 + add rbx, 128 + add rdi, 32 + sub rsi, 4 +3: + test esi, 0x2 + je 3f + vbroadcasti128 ymm0, xmmword ptr [rcx] + vbroadcasti128 ymm1, xmmword ptr [rcx+0x10] + vmovd xmm13, dword ptr [rsp] + vpinsrd xmm13, xmm13, dword ptr [rsp+0x40], 1 + vpinsrd xmm13, xmm13, dword ptr [BLAKE3_BLOCK_LEN+rip], 2 + vmovd xmm14, dword ptr [rsp+0x4] + vpinsrd xmm14, xmm14, dword ptr [rsp+0x44], 1 + vpinsrd xmm14, xmm14, dword ptr [BLAKE3_BLOCK_LEN+rip], 2 + vinserti128 ymm13, ymm13, xmm14, 0x01 + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + movzx eax, byte ptr [rbp+0x40] + or eax, r13d + xor edx, edx +.p2align 5 +2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + mov dword ptr [rsp+0x88], eax + vbroadcasti128 ymm2, xmmword ptr [BLAKE3_IV+rip] + vpbroadcastd ymm8, dword ptr [rsp+0x88] + vpblendd ymm3, ymm13, ymm8, 0x88 + vmovups ymm8, ymmword ptr [r8+rdx-0x40] + vinsertf128 ymm8, ymm8, xmmword ptr [r9+rdx-0x40], 0x01 + vmovups ymm9, ymmword ptr [r8+rdx-0x30] + vinsertf128 ymm9, ymm9, xmmword ptr [r9+rdx-0x30], 0x01 + vshufps ymm4, ymm8, ymm9, 136 + vshufps ymm5, ymm8, ymm9, 221 + vmovups ymm8, ymmword ptr [r8+rdx-0x20] + vinsertf128 ymm8, ymm8, xmmword ptr [r9+rdx-0x20], 0x01 + vmovups ymm9, ymmword ptr [r8+rdx-0x10] + vinsertf128 ymm9, ymm9, xmmword ptr [r9+rdx-0x10], 0x01 + vshufps ymm6, ymm8, ymm9, 136 + vshufps ymm7, ymm8, ymm9, 221 + vpshufd ymm6, ymm6, 0x93 + vpshufd ymm7, ymm7, 0x93 + mov al, 7 +9: + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm0, ymm0, ymm1 + vpxord ymm3, ymm3, ymm0 + vprord ymm3, ymm3, 16 + vpaddd ymm2, ymm2, ymm3 + vpxord ymm1, ymm1, ymm2 + vprord ymm1, ymm1, 12 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm0, ymm0, ymm1 + vpxord ymm3, ymm3, ymm0 + vprord ymm3, ymm3, 8 + vpaddd ymm2, ymm2, ymm3 + vpxord ymm1, ymm1, ymm2 + vprord ymm1, ymm1, 7 + vpshufd ymm0, ymm0, 0x93 + vpshufd ymm3, ymm3, 0x4E + vpshufd ymm2, ymm2, 0x39 + vpaddd ymm0, ymm0, ymm6 + vpaddd ymm0, ymm0, ymm1 + vpxord ymm3, ymm3, ymm0 + vprord ymm3, ymm3, 16 + vpaddd ymm2, ymm2, ymm3 + vpxord ymm1, ymm1, ymm2 + vprord ymm1, ymm1, 12 + vpaddd ymm0, ymm0, ymm7 + vpaddd ymm0, ymm0, ymm1 + vpxord ymm3, ymm3, ymm0 + vprord ymm3, ymm3, 8 + vpaddd ymm2, ymm2, ymm3 + vpxord ymm1, ymm1, ymm2 + vprord ymm1, ymm1, 7 + vpshufd ymm0, ymm0, 0x39 + vpshufd ymm3, ymm3, 0x4E + vpshufd ymm2, ymm2, 0x93 + dec al + jz 9f + vshufps ymm8, ymm4, ymm5, 214 + vpshufd ymm9, ymm4, 0x0F + vpshufd ymm4, ymm8, 0x39 + vshufps ymm8, ymm6, ymm7, 250 + vpblendd ymm9, ymm9, ymm8, 0xAA + vpunpcklqdq ymm8, ymm7, ymm5 + vpblendd ymm8, ymm8, ymm6, 0x88 + vpshufd ymm8, ymm8, 0x78 + vpunpckhdq ymm5, ymm5, ymm7 + vpunpckldq ymm6, ymm6, ymm5 + vpshufd ymm7, ymm6, 0x1E + vmovdqa ymm5, ymm9 + vmovdqa ymm6, ymm8 + jmp 9b +9: + vpxor ymm0, ymm0, ymm2 + vpxor ymm1, ymm1, ymm3 + mov eax, r13d + cmp rdx, r15 + jne 2b + vmovdqu xmmword ptr [rbx], xmm0 + vmovdqu xmmword ptr [rbx+0x10], xmm1 + vextracti128 xmmword ptr [rbx+0x20], ymm0, 0x01 + vextracti128 xmmword ptr [rbx+0x30], ymm1, 0x01 + vmovdqa xmm0, xmmword ptr [rsp] + vmovdqa xmm2, xmmword ptr [rsp+0x4*0x10] + vmovdqu32 xmm0 {k1}, xmmword ptr [rsp+0x8] + vmovdqu32 xmm2 {k1}, xmmword ptr [rsp+0x48] + vmovdqa xmmword ptr [rsp], xmm0 + vmovdqa xmmword ptr [rsp+0x4*0x10], xmm2 + add rbx, 64 + add rdi, 16 + sub rsi, 2 +3: + test esi, 0x1 + je 4b + vmovdqu xmm0, xmmword ptr [rcx] + vmovdqu xmm1, xmmword ptr [rcx+0x10] + vmovd xmm14, dword ptr [rsp] + vpinsrd xmm14, xmm14, dword ptr [rsp+0x40], 1 + vpinsrd xmm14, xmm14, dword ptr [BLAKE3_BLOCK_LEN+rip], 2 + vmovdqa xmm15, xmmword ptr [BLAKE3_IV+rip] + mov r8, qword ptr [rdi] + movzx eax, byte ptr [rbp+0x40] + or eax, r13d + xor edx, edx +.p2align 5 +2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + vpinsrd xmm3, xmm14, eax, 3 + vmovdqa xmm2, xmm15 + vmovups xmm8, xmmword ptr [r8+rdx-0x40] + vmovups xmm9, xmmword ptr [r8+rdx-0x30] + vshufps xmm4, xmm8, xmm9, 136 + vshufps xmm5, xmm8, xmm9, 221 + vmovups xmm8, xmmword ptr [r8+rdx-0x20] + vmovups xmm9, xmmword ptr [r8+rdx-0x10] + vshufps xmm6, xmm8, xmm9, 136 + vshufps xmm7, xmm8, xmm9, 221 + vpshufd xmm6, xmm6, 0x93 + vpshufd xmm7, xmm7, 0x93 + mov al, 7 +9: + vpaddd xmm0, xmm0, xmm4 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 16 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 12 + vpaddd xmm0, xmm0, xmm5 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 8 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 7 + vpshufd xmm0, xmm0, 0x93 + vpshufd xmm3, xmm3, 0x4E + vpshufd xmm2, xmm2, 0x39 + vpaddd xmm0, xmm0, xmm6 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 16 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 12 + vpaddd xmm0, xmm0, xmm7 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 8 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 7 + vpshufd xmm0, xmm0, 0x39 + vpshufd xmm3, xmm3, 0x4E + vpshufd xmm2, xmm2, 0x93 + dec al + jz 9f + vshufps xmm8, xmm4, xmm5, 214 + vpshufd xmm9, xmm4, 0x0F + vpshufd xmm4, xmm8, 0x39 + vshufps xmm8, xmm6, xmm7, 250 + vpblendd xmm9, xmm9, xmm8, 0xAA + vpunpcklqdq xmm8, xmm7, xmm5 + vpblendd xmm8, xmm8, xmm6, 0x88 + vpshufd xmm8, xmm8, 0x78 + vpunpckhdq xmm5, xmm5, xmm7 + vpunpckldq xmm6, xmm6, xmm5 + vpshufd xmm7, xmm6, 0x1E + vmovdqa xmm5, xmm9 + vmovdqa xmm6, xmm8 + jmp 9b +9: + vpxor xmm0, xmm0, xmm2 + vpxor xmm1, xmm1, xmm3 + mov eax, r13d + cmp rdx, r15 + jne 2b + vmovdqu xmmword ptr [rbx], xmm0 + vmovdqu xmmword ptr [rbx+0x10], xmm1 + jmp 4b +.p2align 6 +_blake3_compress_in_place_avx512: +blake3_compress_in_place_avx512: + _CET_ENDBR + vmovdqu xmm0, xmmword ptr [rdi] + vmovdqu xmm1, xmmword ptr [rdi+0x10] + movzx eax, r8b + movzx edx, dl + shl rax, 32 + add rdx, rax + vmovq xmm3, rcx + vmovq xmm4, rdx + vpunpcklqdq xmm3, xmm3, xmm4 + vmovaps xmm2, xmmword ptr [BLAKE3_IV+rip] + vmovups xmm8, xmmword ptr [rsi] + vmovups xmm9, xmmword ptr [rsi+0x10] + vshufps xmm4, xmm8, xmm9, 136 + vshufps xmm5, xmm8, xmm9, 221 + vmovups xmm8, xmmword ptr [rsi+0x20] + vmovups xmm9, xmmword ptr [rsi+0x30] + vshufps xmm6, xmm8, xmm9, 136 + vshufps xmm7, xmm8, xmm9, 221 + vpshufd xmm6, xmm6, 0x93 + vpshufd xmm7, xmm7, 0x93 + mov al, 7 +9: + vpaddd xmm0, xmm0, xmm4 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 16 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 12 + vpaddd xmm0, xmm0, xmm5 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 8 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 7 + vpshufd xmm0, xmm0, 0x93 + vpshufd xmm3, xmm3, 0x4E + vpshufd xmm2, xmm2, 0x39 + vpaddd xmm0, xmm0, xmm6 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 16 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 12 + vpaddd xmm0, xmm0, xmm7 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 8 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 7 + vpshufd xmm0, xmm0, 0x39 + vpshufd xmm3, xmm3, 0x4E + vpshufd xmm2, xmm2, 0x93 + dec al + jz 9f + vshufps xmm8, xmm4, xmm5, 214 + vpshufd xmm9, xmm4, 0x0F + vpshufd xmm4, xmm8, 0x39 + vshufps xmm8, xmm6, xmm7, 250 + vpblendd xmm9, xmm9, xmm8, 0xAA + vpunpcklqdq xmm8, xmm7, xmm5 + vpblendd xmm8, xmm8, xmm6, 0x88 + vpshufd xmm8, xmm8, 0x78 + vpunpckhdq xmm5, xmm5, xmm7 + vpunpckldq xmm6, xmm6, xmm5 + vpshufd xmm7, xmm6, 0x1E + vmovdqa xmm5, xmm9 + vmovdqa xmm6, xmm8 + jmp 9b +9: + vpxor xmm0, xmm0, xmm2 + vpxor xmm1, xmm1, xmm3 + vmovdqu xmmword ptr [rdi], xmm0 + vmovdqu xmmword ptr [rdi+0x10], xmm1 + ret + +.p2align 6 +_blake3_compress_xof_avx512: +blake3_compress_xof_avx512: + _CET_ENDBR + vmovdqu xmm0, xmmword ptr [rdi] + vmovdqu xmm1, xmmword ptr [rdi+0x10] + movzx eax, r8b + movzx edx, dl + shl rax, 32 + add rdx, rax + vmovq xmm3, rcx + vmovq xmm4, rdx + vpunpcklqdq xmm3, xmm3, xmm4 + vmovaps xmm2, xmmword ptr [BLAKE3_IV+rip] + vmovups xmm8, xmmword ptr [rsi] + vmovups xmm9, xmmword ptr [rsi+0x10] + vshufps xmm4, xmm8, xmm9, 136 + vshufps xmm5, xmm8, xmm9, 221 + vmovups xmm8, xmmword ptr [rsi+0x20] + vmovups xmm9, xmmword ptr [rsi+0x30] + vshufps xmm6, xmm8, xmm9, 136 + vshufps xmm7, xmm8, xmm9, 221 + vpshufd xmm6, xmm6, 0x93 + vpshufd xmm7, xmm7, 0x93 + mov al, 7 +9: + vpaddd xmm0, xmm0, xmm4 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 16 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 12 + vpaddd xmm0, xmm0, xmm5 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 8 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 7 + vpshufd xmm0, xmm0, 0x93 + vpshufd xmm3, xmm3, 0x4E + vpshufd xmm2, xmm2, 0x39 + vpaddd xmm0, xmm0, xmm6 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 16 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 12 + vpaddd xmm0, xmm0, xmm7 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 8 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 7 + vpshufd xmm0, xmm0, 0x39 + vpshufd xmm3, xmm3, 0x4E + vpshufd xmm2, xmm2, 0x93 + dec al + jz 9f + vshufps xmm8, xmm4, xmm5, 214 + vpshufd xmm9, xmm4, 0x0F + vpshufd xmm4, xmm8, 0x39 + vshufps xmm8, xmm6, xmm7, 250 + vpblendd xmm9, xmm9, xmm8, 0xAA + vpunpcklqdq xmm8, xmm7, xmm5 + vpblendd xmm8, xmm8, xmm6, 0x88 + vpshufd xmm8, xmm8, 0x78 + vpunpckhdq xmm5, xmm5, xmm7 + vpunpckldq xmm6, xmm6, xmm5 + vpshufd xmm7, xmm6, 0x1E + vmovdqa xmm5, xmm9 + vmovdqa xmm6, xmm8 + jmp 9b +9: + vpxor xmm0, xmm0, xmm2 + vpxor xmm1, xmm1, xmm3 + vpxor xmm2, xmm2, [rdi] + vpxor xmm3, xmm3, [rdi+0x10] + vmovdqu xmmword ptr [r9], xmm0 + vmovdqu xmmword ptr [r9+0x10], xmm1 + vmovdqu xmmword ptr [r9+0x20], xmm2 + vmovdqu xmmword ptr [r9+0x30], xmm3 + ret + +#ifdef __APPLE__ +.static_data +#else +.section .rodata +#endif +.p2align 6 +INDEX0: + .long 0, 1, 2, 3, 16, 17, 18, 19 + .long 8, 9, 10, 11, 24, 25, 26, 27 +INDEX1: + .long 4, 5, 6, 7, 20, 21, 22, 23 + .long 12, 13, 14, 15, 28, 29, 30, 31 +ADD0: + .long 0, 1, 2, 3, 4, 5, 6, 7 + .long 8, 9, 10, 11, 12, 13, 14, 15 +ADD1: .long 1 + +ADD16: .long 16 +BLAKE3_BLOCK_LEN: + .long 64 +.p2align 6 +BLAKE3_IV: +BLAKE3_IV_0: + .long 0x6A09E667 +BLAKE3_IV_1: + .long 0xBB67AE85 +BLAKE3_IV_2: + .long 0x3C6EF372 +BLAKE3_IV_3: + .long 0xA54FF53A diff --git a/src/third_party/blake3/blake3_avx512_x86-64_windows_gnu.S b/src/third_party/blake3/blake3_avx512_x86-64_windows_gnu.S new file mode 100644 index 0000000..e10b9f3 --- /dev/null +++ b/src/third_party/blake3/blake3_avx512_x86-64_windows_gnu.S @@ -0,0 +1,2615 @@ +.intel_syntax noprefix + +.global _blake3_hash_many_avx512 +.global blake3_hash_many_avx512 +.global blake3_compress_in_place_avx512 +.global _blake3_compress_in_place_avx512 +.global blake3_compress_xof_avx512 +.global _blake3_compress_xof_avx512 + +.section .text +.p2align 6 +_blake3_hash_many_avx512: +blake3_hash_many_avx512: + push r15 + push r14 + push r13 + push r12 + push rdi + push rsi + push rbx + push rbp + mov rbp, rsp + sub rsp, 304 + and rsp, 0xFFFFFFFFFFFFFFC0 + vmovdqa xmmword ptr [rsp+0x90], xmm6 + vmovdqa xmmword ptr [rsp+0xA0], xmm7 + vmovdqa xmmword ptr [rsp+0xB0], xmm8 + vmovdqa xmmword ptr [rsp+0xC0], xmm9 + vmovdqa xmmword ptr [rsp+0xD0], xmm10 + vmovdqa xmmword ptr [rsp+0xE0], xmm11 + vmovdqa xmmword ptr [rsp+0xF0], xmm12 + vmovdqa xmmword ptr [rsp+0x100], xmm13 + vmovdqa xmmword ptr [rsp+0x110], xmm14 + vmovdqa xmmword ptr [rsp+0x120], xmm15 + mov rdi, rcx + mov rsi, rdx + mov rdx, r8 + mov rcx, r9 + mov r8, qword ptr [rbp+0x68] + movzx r9, byte ptr [rbp+0x70] + neg r9 + kmovw k1, r9d + vmovd xmm0, r8d + vpbroadcastd ymm0, xmm0 + shr r8, 32 + vmovd xmm1, r8d + vpbroadcastd ymm1, xmm1 + vmovdqa ymm4, ymm1 + vmovdqa ymm5, ymm1 + vpaddd ymm2, ymm0, ymmword ptr [ADD0+rip] + vpaddd ymm3, ymm0, ymmword ptr [ADD0+32+rip] + vpcmpltud k2, ymm2, ymm0 + vpcmpltud k3, ymm3, ymm0 + vpaddd ymm4 {k2}, ymm4, dword ptr [ADD1+rip] {1to8} + vpaddd ymm5 {k3}, ymm5, dword ptr [ADD1+rip] {1to8} + knotw k2, k1 + vmovdqa32 ymm2 {k2}, ymm0 + vmovdqa32 ymm3 {k2}, ymm0 + vmovdqa32 ymm4 {k2}, ymm1 + vmovdqa32 ymm5 {k2}, ymm1 + vmovdqa ymmword ptr [rsp], ymm2 + vmovdqa ymmword ptr [rsp+0x20], ymm3 + vmovdqa ymmword ptr [rsp+0x40], ymm4 + vmovdqa ymmword ptr [rsp+0x60], ymm5 + shl rdx, 6 + mov qword ptr [rsp+0x80], rdx + cmp rsi, 16 + jc 3f +2: + vpbroadcastd zmm0, dword ptr [rcx] + vpbroadcastd zmm1, dword ptr [rcx+0x1*0x4] + vpbroadcastd zmm2, dword ptr [rcx+0x2*0x4] + vpbroadcastd zmm3, dword ptr [rcx+0x3*0x4] + vpbroadcastd zmm4, dword ptr [rcx+0x4*0x4] + vpbroadcastd zmm5, dword ptr [rcx+0x5*0x4] + vpbroadcastd zmm6, dword ptr [rcx+0x6*0x4] + vpbroadcastd zmm7, dword ptr [rcx+0x7*0x4] + movzx eax, byte ptr [rbp+0x78] + movzx ebx, byte ptr [rbp+0x80] + or eax, ebx + xor edx, edx +.p2align 5 +9: + movzx ebx, byte ptr [rbp+0x88] + or ebx, eax + add rdx, 64 + cmp rdx, qword ptr [rsp+0x80] + cmove eax, ebx + mov dword ptr [rsp+0x88], eax + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + mov r10, qword ptr [rdi+0x10] + mov r11, qword ptr [rdi+0x18] + mov r12, qword ptr [rdi+0x40] + mov r13, qword ptr [rdi+0x48] + mov r14, qword ptr [rdi+0x50] + mov r15, qword ptr [rdi+0x58] + vmovdqu32 ymm16, ymmword ptr [rdx+r8-0x2*0x20] + vinserti64x4 zmm16, zmm16, ymmword ptr [rdx+r12-0x2*0x20], 0x01 + vmovdqu32 ymm17, ymmword ptr [rdx+r9-0x2*0x20] + vinserti64x4 zmm17, zmm17, ymmword ptr [rdx+r13-0x2*0x20], 0x01 + vpunpcklqdq zmm8, zmm16, zmm17 + vpunpckhqdq zmm9, zmm16, zmm17 + vmovdqu32 ymm18, ymmword ptr [rdx+r10-0x2*0x20] + vinserti64x4 zmm18, zmm18, ymmword ptr [rdx+r14-0x2*0x20], 0x01 + vmovdqu32 ymm19, ymmword ptr [rdx+r11-0x2*0x20] + vinserti64x4 zmm19, zmm19, ymmword ptr [rdx+r15-0x2*0x20], 0x01 + vpunpcklqdq zmm10, zmm18, zmm19 + vpunpckhqdq zmm11, zmm18, zmm19 + mov r8, qword ptr [rdi+0x20] + mov r9, qword ptr [rdi+0x28] + mov r10, qword ptr [rdi+0x30] + mov r11, qword ptr [rdi+0x38] + mov r12, qword ptr [rdi+0x60] + mov r13, qword ptr [rdi+0x68] + mov r14, qword ptr [rdi+0x70] + mov r15, qword ptr [rdi+0x78] + vmovdqu32 ymm16, ymmword ptr [rdx+r8-0x2*0x20] + vinserti64x4 zmm16, zmm16, ymmword ptr [rdx+r12-0x2*0x20], 0x01 + vmovdqu32 ymm17, ymmword ptr [rdx+r9-0x2*0x20] + vinserti64x4 zmm17, zmm17, ymmword ptr [rdx+r13-0x2*0x20], 0x01 + vpunpcklqdq zmm12, zmm16, zmm17 + vpunpckhqdq zmm13, zmm16, zmm17 + vmovdqu32 ymm18, ymmword ptr [rdx+r10-0x2*0x20] + vinserti64x4 zmm18, zmm18, ymmword ptr [rdx+r14-0x2*0x20], 0x01 + vmovdqu32 ymm19, ymmword ptr [rdx+r11-0x2*0x20] + vinserti64x4 zmm19, zmm19, ymmword ptr [rdx+r15-0x2*0x20], 0x01 + vpunpcklqdq zmm14, zmm18, zmm19 + vpunpckhqdq zmm15, zmm18, zmm19 + vmovdqa32 zmm27, zmmword ptr [INDEX0+rip] + vmovdqa32 zmm31, zmmword ptr [INDEX1+rip] + vshufps zmm16, zmm8, zmm10, 136 + vshufps zmm17, zmm12, zmm14, 136 + vmovdqa32 zmm20, zmm16 + vpermt2d zmm16, zmm27, zmm17 + vpermt2d zmm20, zmm31, zmm17 + vshufps zmm17, zmm8, zmm10, 221 + vshufps zmm30, zmm12, zmm14, 221 + vmovdqa32 zmm21, zmm17 + vpermt2d zmm17, zmm27, zmm30 + vpermt2d zmm21, zmm31, zmm30 + vshufps zmm18, zmm9, zmm11, 136 + vshufps zmm8, zmm13, zmm15, 136 + vmovdqa32 zmm22, zmm18 + vpermt2d zmm18, zmm27, zmm8 + vpermt2d zmm22, zmm31, zmm8 + vshufps zmm19, zmm9, zmm11, 221 + vshufps zmm8, zmm13, zmm15, 221 + vmovdqa32 zmm23, zmm19 + vpermt2d zmm19, zmm27, zmm8 + vpermt2d zmm23, zmm31, zmm8 + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + mov r10, qword ptr [rdi+0x10] + mov r11, qword ptr [rdi+0x18] + mov r12, qword ptr [rdi+0x40] + mov r13, qword ptr [rdi+0x48] + mov r14, qword ptr [rdi+0x50] + mov r15, qword ptr [rdi+0x58] + vmovdqu32 ymm24, ymmword ptr [r8+rdx-0x1*0x20] + vinserti64x4 zmm24, zmm24, ymmword ptr [r12+rdx-0x1*0x20], 0x01 + vmovdqu32 ymm25, ymmword ptr [r9+rdx-0x1*0x20] + vinserti64x4 zmm25, zmm25, ymmword ptr [r13+rdx-0x1*0x20], 0x01 + vpunpcklqdq zmm8, zmm24, zmm25 + vpunpckhqdq zmm9, zmm24, zmm25 + vmovdqu32 ymm24, ymmword ptr [r10+rdx-0x1*0x20] + vinserti64x4 zmm24, zmm24, ymmword ptr [r14+rdx-0x1*0x20], 0x01 + vmovdqu32 ymm25, ymmword ptr [r11+rdx-0x1*0x20] + vinserti64x4 zmm25, zmm25, ymmword ptr [r15+rdx-0x1*0x20], 0x01 + vpunpcklqdq zmm10, zmm24, zmm25 + vpunpckhqdq zmm11, zmm24, zmm25 + prefetcht0 [r8+rdx+0x80] + prefetcht0 [r12+rdx+0x80] + prefetcht0 [r9+rdx+0x80] + prefetcht0 [r13+rdx+0x80] + prefetcht0 [r10+rdx+0x80] + prefetcht0 [r14+rdx+0x80] + prefetcht0 [r11+rdx+0x80] + prefetcht0 [r15+rdx+0x80] + mov r8, qword ptr [rdi+0x20] + mov r9, qword ptr [rdi+0x28] + mov r10, qword ptr [rdi+0x30] + mov r11, qword ptr [rdi+0x38] + mov r12, qword ptr [rdi+0x60] + mov r13, qword ptr [rdi+0x68] + mov r14, qword ptr [rdi+0x70] + mov r15, qword ptr [rdi+0x78] + vmovdqu32 ymm24, ymmword ptr [r8+rdx-0x1*0x20] + vinserti64x4 zmm24, zmm24, ymmword ptr [r12+rdx-0x1*0x20], 0x01 + vmovdqu32 ymm25, ymmword ptr [r9+rdx-0x1*0x20] + vinserti64x4 zmm25, zmm25, ymmword ptr [r13+rdx-0x1*0x20], 0x01 + vpunpcklqdq zmm12, zmm24, zmm25 + vpunpckhqdq zmm13, zmm24, zmm25 + vmovdqu32 ymm24, ymmword ptr [r10+rdx-0x1*0x20] + vinserti64x4 zmm24, zmm24, ymmword ptr [r14+rdx-0x1*0x20], 0x01 + vmovdqu32 ymm25, ymmword ptr [r11+rdx-0x1*0x20] + vinserti64x4 zmm25, zmm25, ymmword ptr [r15+rdx-0x1*0x20], 0x01 + vpunpcklqdq zmm14, zmm24, zmm25 + vpunpckhqdq zmm15, zmm24, zmm25 + prefetcht0 [r8+rdx+0x80] + prefetcht0 [r12+rdx+0x80] + prefetcht0 [r9+rdx+0x80] + prefetcht0 [r13+rdx+0x80] + prefetcht0 [r10+rdx+0x80] + prefetcht0 [r14+rdx+0x80] + prefetcht0 [r11+rdx+0x80] + prefetcht0 [r15+rdx+0x80] + vshufps zmm24, zmm8, zmm10, 136 + vshufps zmm30, zmm12, zmm14, 136 + vmovdqa32 zmm28, zmm24 + vpermt2d zmm24, zmm27, zmm30 + vpermt2d zmm28, zmm31, zmm30 + vshufps zmm25, zmm8, zmm10, 221 + vshufps zmm30, zmm12, zmm14, 221 + vmovdqa32 zmm29, zmm25 + vpermt2d zmm25, zmm27, zmm30 + vpermt2d zmm29, zmm31, zmm30 + vshufps zmm26, zmm9, zmm11, 136 + vshufps zmm8, zmm13, zmm15, 136 + vmovdqa32 zmm30, zmm26 + vpermt2d zmm26, zmm27, zmm8 + vpermt2d zmm30, zmm31, zmm8 + vshufps zmm8, zmm9, zmm11, 221 + vshufps zmm10, zmm13, zmm15, 221 + vpermi2d zmm27, zmm8, zmm10 + vpermi2d zmm31, zmm8, zmm10 + vpbroadcastd zmm8, dword ptr [BLAKE3_IV_0+rip] + vpbroadcastd zmm9, dword ptr [BLAKE3_IV_1+rip] + vpbroadcastd zmm10, dword ptr [BLAKE3_IV_2+rip] + vpbroadcastd zmm11, dword ptr [BLAKE3_IV_3+rip] + vmovdqa32 zmm12, zmmword ptr [rsp] + vmovdqa32 zmm13, zmmword ptr [rsp+0x1*0x40] + vpbroadcastd zmm14, dword ptr [BLAKE3_BLOCK_LEN+rip] + vpbroadcastd zmm15, dword ptr [rsp+0x22*0x4] + vpaddd zmm0, zmm0, zmm16 + vpaddd zmm1, zmm1, zmm18 + vpaddd zmm2, zmm2, zmm20 + vpaddd zmm3, zmm3, zmm22 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vprord zmm15, zmm15, 16 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 12 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vpaddd zmm0, zmm0, zmm17 + vpaddd zmm1, zmm1, zmm19 + vpaddd zmm2, zmm2, zmm21 + vpaddd zmm3, zmm3, zmm23 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vprord zmm15, zmm15, 8 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 7 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vpaddd zmm0, zmm0, zmm24 + vpaddd zmm1, zmm1, zmm26 + vpaddd zmm2, zmm2, zmm28 + vpaddd zmm3, zmm3, zmm30 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 16 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vprord zmm4, zmm4, 12 + vpaddd zmm0, zmm0, zmm25 + vpaddd zmm1, zmm1, zmm27 + vpaddd zmm2, zmm2, zmm29 + vpaddd zmm3, zmm3, zmm31 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 8 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vprord zmm4, zmm4, 7 + vpaddd zmm0, zmm0, zmm18 + vpaddd zmm1, zmm1, zmm19 + vpaddd zmm2, zmm2, zmm23 + vpaddd zmm3, zmm3, zmm20 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vprord zmm15, zmm15, 16 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 12 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vpaddd zmm0, zmm0, zmm22 + vpaddd zmm1, zmm1, zmm26 + vpaddd zmm2, zmm2, zmm16 + vpaddd zmm3, zmm3, zmm29 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vprord zmm15, zmm15, 8 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 7 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vpaddd zmm0, zmm0, zmm17 + vpaddd zmm1, zmm1, zmm28 + vpaddd zmm2, zmm2, zmm25 + vpaddd zmm3, zmm3, zmm31 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 16 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vprord zmm4, zmm4, 12 + vpaddd zmm0, zmm0, zmm27 + vpaddd zmm1, zmm1, zmm21 + vpaddd zmm2, zmm2, zmm30 + vpaddd zmm3, zmm3, zmm24 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 8 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vprord zmm4, zmm4, 7 + vpaddd zmm0, zmm0, zmm19 + vpaddd zmm1, zmm1, zmm26 + vpaddd zmm2, zmm2, zmm29 + vpaddd zmm3, zmm3, zmm23 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vprord zmm15, zmm15, 16 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 12 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vpaddd zmm0, zmm0, zmm20 + vpaddd zmm1, zmm1, zmm28 + vpaddd zmm2, zmm2, zmm18 + vpaddd zmm3, zmm3, zmm30 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vprord zmm15, zmm15, 8 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 7 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vpaddd zmm0, zmm0, zmm22 + vpaddd zmm1, zmm1, zmm25 + vpaddd zmm2, zmm2, zmm27 + vpaddd zmm3, zmm3, zmm24 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 16 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vprord zmm4, zmm4, 12 + vpaddd zmm0, zmm0, zmm21 + vpaddd zmm1, zmm1, zmm16 + vpaddd zmm2, zmm2, zmm31 + vpaddd zmm3, zmm3, zmm17 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 8 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vprord zmm4, zmm4, 7 + vpaddd zmm0, zmm0, zmm26 + vpaddd zmm1, zmm1, zmm28 + vpaddd zmm2, zmm2, zmm30 + vpaddd zmm3, zmm3, zmm29 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vprord zmm15, zmm15, 16 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 12 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vpaddd zmm0, zmm0, zmm23 + vpaddd zmm1, zmm1, zmm25 + vpaddd zmm2, zmm2, zmm19 + vpaddd zmm3, zmm3, zmm31 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vprord zmm15, zmm15, 8 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 7 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vpaddd zmm0, zmm0, zmm20 + vpaddd zmm1, zmm1, zmm27 + vpaddd zmm2, zmm2, zmm21 + vpaddd zmm3, zmm3, zmm17 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 16 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vprord zmm4, zmm4, 12 + vpaddd zmm0, zmm0, zmm16 + vpaddd zmm1, zmm1, zmm18 + vpaddd zmm2, zmm2, zmm24 + vpaddd zmm3, zmm3, zmm22 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 8 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vprord zmm4, zmm4, 7 + vpaddd zmm0, zmm0, zmm28 + vpaddd zmm1, zmm1, zmm25 + vpaddd zmm2, zmm2, zmm31 + vpaddd zmm3, zmm3, zmm30 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vprord zmm15, zmm15, 16 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 12 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vpaddd zmm0, zmm0, zmm29 + vpaddd zmm1, zmm1, zmm27 + vpaddd zmm2, zmm2, zmm26 + vpaddd zmm3, zmm3, zmm24 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vprord zmm15, zmm15, 8 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 7 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vpaddd zmm0, zmm0, zmm23 + vpaddd zmm1, zmm1, zmm21 + vpaddd zmm2, zmm2, zmm16 + vpaddd zmm3, zmm3, zmm22 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 16 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vprord zmm4, zmm4, 12 + vpaddd zmm0, zmm0, zmm18 + vpaddd zmm1, zmm1, zmm19 + vpaddd zmm2, zmm2, zmm17 + vpaddd zmm3, zmm3, zmm20 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 8 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vprord zmm4, zmm4, 7 + vpaddd zmm0, zmm0, zmm25 + vpaddd zmm1, zmm1, zmm27 + vpaddd zmm2, zmm2, zmm24 + vpaddd zmm3, zmm3, zmm31 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vprord zmm15, zmm15, 16 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 12 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vpaddd zmm0, zmm0, zmm30 + vpaddd zmm1, zmm1, zmm21 + vpaddd zmm2, zmm2, zmm28 + vpaddd zmm3, zmm3, zmm17 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vprord zmm15, zmm15, 8 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 7 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vpaddd zmm0, zmm0, zmm29 + vpaddd zmm1, zmm1, zmm16 + vpaddd zmm2, zmm2, zmm18 + vpaddd zmm3, zmm3, zmm20 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 16 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vprord zmm4, zmm4, 12 + vpaddd zmm0, zmm0, zmm19 + vpaddd zmm1, zmm1, zmm26 + vpaddd zmm2, zmm2, zmm22 + vpaddd zmm3, zmm3, zmm23 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 8 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vprord zmm4, zmm4, 7 + vpaddd zmm0, zmm0, zmm27 + vpaddd zmm1, zmm1, zmm21 + vpaddd zmm2, zmm2, zmm17 + vpaddd zmm3, zmm3, zmm24 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vprord zmm15, zmm15, 16 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 12 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vpaddd zmm0, zmm0, zmm31 + vpaddd zmm1, zmm1, zmm16 + vpaddd zmm2, zmm2, zmm25 + vpaddd zmm3, zmm3, zmm22 + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm1, zmm1, zmm5 + vpaddd zmm2, zmm2, zmm6 + vpaddd zmm3, zmm3, zmm7 + vpxord zmm12, zmm12, zmm0 + vpxord zmm13, zmm13, zmm1 + vpxord zmm14, zmm14, zmm2 + vpxord zmm15, zmm15, zmm3 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vprord zmm15, zmm15, 8 + vpaddd zmm8, zmm8, zmm12 + vpaddd zmm9, zmm9, zmm13 + vpaddd zmm10, zmm10, zmm14 + vpaddd zmm11, zmm11, zmm15 + vpxord zmm4, zmm4, zmm8 + vpxord zmm5, zmm5, zmm9 + vpxord zmm6, zmm6, zmm10 + vpxord zmm7, zmm7, zmm11 + vprord zmm4, zmm4, 7 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vpaddd zmm0, zmm0, zmm30 + vpaddd zmm1, zmm1, zmm18 + vpaddd zmm2, zmm2, zmm19 + vpaddd zmm3, zmm3, zmm23 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 16 + vprord zmm12, zmm12, 16 + vprord zmm13, zmm13, 16 + vprord zmm14, zmm14, 16 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 12 + vprord zmm6, zmm6, 12 + vprord zmm7, zmm7, 12 + vprord zmm4, zmm4, 12 + vpaddd zmm0, zmm0, zmm26 + vpaddd zmm1, zmm1, zmm28 + vpaddd zmm2, zmm2, zmm20 + vpaddd zmm3, zmm3, zmm29 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm1, zmm1, zmm6 + vpaddd zmm2, zmm2, zmm7 + vpaddd zmm3, zmm3, zmm4 + vpxord zmm15, zmm15, zmm0 + vpxord zmm12, zmm12, zmm1 + vpxord zmm13, zmm13, zmm2 + vpxord zmm14, zmm14, zmm3 + vprord zmm15, zmm15, 8 + vprord zmm12, zmm12, 8 + vprord zmm13, zmm13, 8 + vprord zmm14, zmm14, 8 + vpaddd zmm10, zmm10, zmm15 + vpaddd zmm11, zmm11, zmm12 + vpaddd zmm8, zmm8, zmm13 + vpaddd zmm9, zmm9, zmm14 + vpxord zmm5, zmm5, zmm10 + vpxord zmm6, zmm6, zmm11 + vpxord zmm7, zmm7, zmm8 + vpxord zmm4, zmm4, zmm9 + vprord zmm5, zmm5, 7 + vprord zmm6, zmm6, 7 + vprord zmm7, zmm7, 7 + vprord zmm4, zmm4, 7 + vpxord zmm0, zmm0, zmm8 + vpxord zmm1, zmm1, zmm9 + vpxord zmm2, zmm2, zmm10 + vpxord zmm3, zmm3, zmm11 + vpxord zmm4, zmm4, zmm12 + vpxord zmm5, zmm5, zmm13 + vpxord zmm6, zmm6, zmm14 + vpxord zmm7, zmm7, zmm15 + movzx eax, byte ptr [rbp+0x78] + jne 9b + mov rbx, qword ptr [rbp+0x90] + vpunpckldq zmm16, zmm0, zmm1 + vpunpckhdq zmm17, zmm0, zmm1 + vpunpckldq zmm18, zmm2, zmm3 + vpunpckhdq zmm19, zmm2, zmm3 + vpunpckldq zmm20, zmm4, zmm5 + vpunpckhdq zmm21, zmm4, zmm5 + vpunpckldq zmm22, zmm6, zmm7 + vpunpckhdq zmm23, zmm6, zmm7 + vpunpcklqdq zmm0, zmm16, zmm18 + vpunpckhqdq zmm1, zmm16, zmm18 + vpunpcklqdq zmm2, zmm17, zmm19 + vpunpckhqdq zmm3, zmm17, zmm19 + vpunpcklqdq zmm4, zmm20, zmm22 + vpunpckhqdq zmm5, zmm20, zmm22 + vpunpcklqdq zmm6, zmm21, zmm23 + vpunpckhqdq zmm7, zmm21, zmm23 + vshufi32x4 zmm16, zmm0, zmm4, 0x88 + vshufi32x4 zmm17, zmm1, zmm5, 0x88 + vshufi32x4 zmm18, zmm2, zmm6, 0x88 + vshufi32x4 zmm19, zmm3, zmm7, 0x88 + vshufi32x4 zmm20, zmm0, zmm4, 0xDD + vshufi32x4 zmm21, zmm1, zmm5, 0xDD + vshufi32x4 zmm22, zmm2, zmm6, 0xDD + vshufi32x4 zmm23, zmm3, zmm7, 0xDD + vshufi32x4 zmm0, zmm16, zmm17, 0x88 + vshufi32x4 zmm1, zmm18, zmm19, 0x88 + vshufi32x4 zmm2, zmm20, zmm21, 0x88 + vshufi32x4 zmm3, zmm22, zmm23, 0x88 + vshufi32x4 zmm4, zmm16, zmm17, 0xDD + vshufi32x4 zmm5, zmm18, zmm19, 0xDD + vshufi32x4 zmm6, zmm20, zmm21, 0xDD + vshufi32x4 zmm7, zmm22, zmm23, 0xDD + vmovdqu32 zmmword ptr [rbx], zmm0 + vmovdqu32 zmmword ptr [rbx+0x1*0x40], zmm1 + vmovdqu32 zmmword ptr [rbx+0x2*0x40], zmm2 + vmovdqu32 zmmword ptr [rbx+0x3*0x40], zmm3 + vmovdqu32 zmmword ptr [rbx+0x4*0x40], zmm4 + vmovdqu32 zmmword ptr [rbx+0x5*0x40], zmm5 + vmovdqu32 zmmword ptr [rbx+0x6*0x40], zmm6 + vmovdqu32 zmmword ptr [rbx+0x7*0x40], zmm7 + vmovdqa32 zmm0, zmmword ptr [rsp] + vmovdqa32 zmm1, zmmword ptr [rsp+0x1*0x40] + vmovdqa32 zmm2, zmm0 + vpaddd zmm2{k1}, zmm0, dword ptr [ADD16+rip] {1to16} + vpcmpltud k2, zmm2, zmm0 + vpaddd zmm1 {k2}, zmm1, dword ptr [ADD1+rip] {1to16} + vmovdqa32 zmmword ptr [rsp], zmm2 + vmovdqa32 zmmword ptr [rsp+0x1*0x40], zmm1 + add rdi, 128 + add rbx, 512 + mov qword ptr [rbp+0x90], rbx + sub rsi, 16 + cmp rsi, 16 + jnc 2b + test rsi, rsi + jne 3f +4: + vzeroupper + vmovdqa xmm6, xmmword ptr [rsp+0x90] + vmovdqa xmm7, xmmword ptr [rsp+0xA0] + vmovdqa xmm8, xmmword ptr [rsp+0xB0] + vmovdqa xmm9, xmmword ptr [rsp+0xC0] + vmovdqa xmm10, xmmword ptr [rsp+0xD0] + vmovdqa xmm11, xmmword ptr [rsp+0xE0] + vmovdqa xmm12, xmmword ptr [rsp+0xF0] + vmovdqa xmm13, xmmword ptr [rsp+0x100] + vmovdqa xmm14, xmmword ptr [rsp+0x110] + vmovdqa xmm15, xmmword ptr [rsp+0x120] + mov rsp, rbp + pop rbp + pop rbx + pop rsi + pop rdi + pop r12 + pop r13 + pop r14 + pop r15 + ret +.p2align 6 +3: + test esi, 0x8 + je 3f + vpbroadcastd ymm0, dword ptr [rcx] + vpbroadcastd ymm1, dword ptr [rcx+0x4] + vpbroadcastd ymm2, dword ptr [rcx+0x8] + vpbroadcastd ymm3, dword ptr [rcx+0xC] + vpbroadcastd ymm4, dword ptr [rcx+0x10] + vpbroadcastd ymm5, dword ptr [rcx+0x14] + vpbroadcastd ymm6, dword ptr [rcx+0x18] + vpbroadcastd ymm7, dword ptr [rcx+0x1C] + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + mov r10, qword ptr [rdi+0x10] + mov r11, qword ptr [rdi+0x18] + mov r12, qword ptr [rdi+0x20] + mov r13, qword ptr [rdi+0x28] + mov r14, qword ptr [rdi+0x30] + mov r15, qword ptr [rdi+0x38] + movzx eax, byte ptr [rbp+0x78] + movzx ebx, byte ptr [rbp+0x80] + or eax, ebx + xor edx, edx +2: + movzx ebx, byte ptr [rbp+0x88] + or ebx, eax + add rdx, 64 + cmp rdx, qword ptr [rsp+0x80] + cmove eax, ebx + mov dword ptr [rsp+0x88], eax + vmovups xmm8, xmmword ptr [r8+rdx-0x40] + vinsertf128 ymm8, ymm8, xmmword ptr [r12+rdx-0x40], 0x01 + vmovups xmm9, xmmword ptr [r9+rdx-0x40] + vinsertf128 ymm9, ymm9, xmmword ptr [r13+rdx-0x40], 0x01 + vunpcklpd ymm12, ymm8, ymm9 + vunpckhpd ymm13, ymm8, ymm9 + vmovups xmm10, xmmword ptr [r10+rdx-0x40] + vinsertf128 ymm10, ymm10, xmmword ptr [r14+rdx-0x40], 0x01 + vmovups xmm11, xmmword ptr [r11+rdx-0x40] + vinsertf128 ymm11, ymm11, xmmword ptr [r15+rdx-0x40], 0x01 + vunpcklpd ymm14, ymm10, ymm11 + vunpckhpd ymm15, ymm10, ymm11 + vshufps ymm16, ymm12, ymm14, 136 + vshufps ymm17, ymm12, ymm14, 221 + vshufps ymm18, ymm13, ymm15, 136 + vshufps ymm19, ymm13, ymm15, 221 + vmovups xmm8, xmmword ptr [r8+rdx-0x30] + vinsertf128 ymm8, ymm8, xmmword ptr [r12+rdx-0x30], 0x01 + vmovups xmm9, xmmword ptr [r9+rdx-0x30] + vinsertf128 ymm9, ymm9, xmmword ptr [r13+rdx-0x30], 0x01 + vunpcklpd ymm12, ymm8, ymm9 + vunpckhpd ymm13, ymm8, ymm9 + vmovups xmm10, xmmword ptr [r10+rdx-0x30] + vinsertf128 ymm10, ymm10, xmmword ptr [r14+rdx-0x30], 0x01 + vmovups xmm11, xmmword ptr [r11+rdx-0x30] + vinsertf128 ymm11, ymm11, xmmword ptr [r15+rdx-0x30], 0x01 + vunpcklpd ymm14, ymm10, ymm11 + vunpckhpd ymm15, ymm10, ymm11 + vshufps ymm20, ymm12, ymm14, 136 + vshufps ymm21, ymm12, ymm14, 221 + vshufps ymm22, ymm13, ymm15, 136 + vshufps ymm23, ymm13, ymm15, 221 + vmovups xmm8, xmmword ptr [r8+rdx-0x20] + vinsertf128 ymm8, ymm8, xmmword ptr [r12+rdx-0x20], 0x01 + vmovups xmm9, xmmword ptr [r9+rdx-0x20] + vinsertf128 ymm9, ymm9, xmmword ptr [r13+rdx-0x20], 0x01 + vunpcklpd ymm12, ymm8, ymm9 + vunpckhpd ymm13, ymm8, ymm9 + vmovups xmm10, xmmword ptr [r10+rdx-0x20] + vinsertf128 ymm10, ymm10, xmmword ptr [r14+rdx-0x20], 0x01 + vmovups xmm11, xmmword ptr [r11+rdx-0x20] + vinsertf128 ymm11, ymm11, xmmword ptr [r15+rdx-0x20], 0x01 + vunpcklpd ymm14, ymm10, ymm11 + vunpckhpd ymm15, ymm10, ymm11 + vshufps ymm24, ymm12, ymm14, 136 + vshufps ymm25, ymm12, ymm14, 221 + vshufps ymm26, ymm13, ymm15, 136 + vshufps ymm27, ymm13, ymm15, 221 + vmovups xmm8, xmmword ptr [r8+rdx-0x10] + vinsertf128 ymm8, ymm8, xmmword ptr [r12+rdx-0x10], 0x01 + vmovups xmm9, xmmword ptr [r9+rdx-0x10] + vinsertf128 ymm9, ymm9, xmmword ptr [r13+rdx-0x10], 0x01 + vunpcklpd ymm12, ymm8, ymm9 + vunpckhpd ymm13, ymm8, ymm9 + vmovups xmm10, xmmword ptr [r10+rdx-0x10] + vinsertf128 ymm10, ymm10, xmmword ptr [r14+rdx-0x10], 0x01 + vmovups xmm11, xmmword ptr [r11+rdx-0x10] + vinsertf128 ymm11, ymm11, xmmword ptr [r15+rdx-0x10], 0x01 + vunpcklpd ymm14, ymm10, ymm11 + vunpckhpd ymm15, ymm10, ymm11 + vshufps ymm28, ymm12, ymm14, 136 + vshufps ymm29, ymm12, ymm14, 221 + vshufps ymm30, ymm13, ymm15, 136 + vshufps ymm31, ymm13, ymm15, 221 + vpbroadcastd ymm8, dword ptr [BLAKE3_IV_0+rip] + vpbroadcastd ymm9, dword ptr [BLAKE3_IV_1+rip] + vpbroadcastd ymm10, dword ptr [BLAKE3_IV_2+rip] + vpbroadcastd ymm11, dword ptr [BLAKE3_IV_3+rip] + vmovdqa ymm12, ymmword ptr [rsp] + vmovdqa ymm13, ymmword ptr [rsp+0x40] + vpbroadcastd ymm14, dword ptr [BLAKE3_BLOCK_LEN+rip] + vpbroadcastd ymm15, dword ptr [rsp+0x88] + vpaddd ymm0, ymm0, ymm16 + vpaddd ymm1, ymm1, ymm18 + vpaddd ymm2, ymm2, ymm20 + vpaddd ymm3, ymm3, ymm22 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vprord ymm15, ymm15, 16 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 12 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vpaddd ymm0, ymm0, ymm17 + vpaddd ymm1, ymm1, ymm19 + vpaddd ymm2, ymm2, ymm21 + vpaddd ymm3, ymm3, ymm23 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vprord ymm15, ymm15, 8 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 7 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vpaddd ymm0, ymm0, ymm24 + vpaddd ymm1, ymm1, ymm26 + vpaddd ymm2, ymm2, ymm28 + vpaddd ymm3, ymm3, ymm30 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 16 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vprord ymm4, ymm4, 12 + vpaddd ymm0, ymm0, ymm25 + vpaddd ymm1, ymm1, ymm27 + vpaddd ymm2, ymm2, ymm29 + vpaddd ymm3, ymm3, ymm31 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 8 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vprord ymm4, ymm4, 7 + vpaddd ymm0, ymm0, ymm18 + vpaddd ymm1, ymm1, ymm19 + vpaddd ymm2, ymm2, ymm23 + vpaddd ymm3, ymm3, ymm20 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vprord ymm15, ymm15, 16 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 12 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vpaddd ymm0, ymm0, ymm22 + vpaddd ymm1, ymm1, ymm26 + vpaddd ymm2, ymm2, ymm16 + vpaddd ymm3, ymm3, ymm29 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vprord ymm15, ymm15, 8 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 7 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vpaddd ymm0, ymm0, ymm17 + vpaddd ymm1, ymm1, ymm28 + vpaddd ymm2, ymm2, ymm25 + vpaddd ymm3, ymm3, ymm31 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 16 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vprord ymm4, ymm4, 12 + vpaddd ymm0, ymm0, ymm27 + vpaddd ymm1, ymm1, ymm21 + vpaddd ymm2, ymm2, ymm30 + vpaddd ymm3, ymm3, ymm24 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 8 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vprord ymm4, ymm4, 7 + vpaddd ymm0, ymm0, ymm19 + vpaddd ymm1, ymm1, ymm26 + vpaddd ymm2, ymm2, ymm29 + vpaddd ymm3, ymm3, ymm23 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vprord ymm15, ymm15, 16 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 12 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vpaddd ymm0, ymm0, ymm20 + vpaddd ymm1, ymm1, ymm28 + vpaddd ymm2, ymm2, ymm18 + vpaddd ymm3, ymm3, ymm30 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vprord ymm15, ymm15, 8 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 7 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vpaddd ymm0, ymm0, ymm22 + vpaddd ymm1, ymm1, ymm25 + vpaddd ymm2, ymm2, ymm27 + vpaddd ymm3, ymm3, ymm24 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 16 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vprord ymm4, ymm4, 12 + vpaddd ymm0, ymm0, ymm21 + vpaddd ymm1, ymm1, ymm16 + vpaddd ymm2, ymm2, ymm31 + vpaddd ymm3, ymm3, ymm17 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 8 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vprord ymm4, ymm4, 7 + vpaddd ymm0, ymm0, ymm26 + vpaddd ymm1, ymm1, ymm28 + vpaddd ymm2, ymm2, ymm30 + vpaddd ymm3, ymm3, ymm29 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vprord ymm15, ymm15, 16 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 12 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vpaddd ymm0, ymm0, ymm23 + vpaddd ymm1, ymm1, ymm25 + vpaddd ymm2, ymm2, ymm19 + vpaddd ymm3, ymm3, ymm31 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vprord ymm15, ymm15, 8 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 7 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vpaddd ymm0, ymm0, ymm20 + vpaddd ymm1, ymm1, ymm27 + vpaddd ymm2, ymm2, ymm21 + vpaddd ymm3, ymm3, ymm17 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 16 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vprord ymm4, ymm4, 12 + vpaddd ymm0, ymm0, ymm16 + vpaddd ymm1, ymm1, ymm18 + vpaddd ymm2, ymm2, ymm24 + vpaddd ymm3, ymm3, ymm22 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 8 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vprord ymm4, ymm4, 7 + vpaddd ymm0, ymm0, ymm28 + vpaddd ymm1, ymm1, ymm25 + vpaddd ymm2, ymm2, ymm31 + vpaddd ymm3, ymm3, ymm30 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vprord ymm15, ymm15, 16 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 12 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vpaddd ymm0, ymm0, ymm29 + vpaddd ymm1, ymm1, ymm27 + vpaddd ymm2, ymm2, ymm26 + vpaddd ymm3, ymm3, ymm24 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vprord ymm15, ymm15, 8 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 7 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vpaddd ymm0, ymm0, ymm23 + vpaddd ymm1, ymm1, ymm21 + vpaddd ymm2, ymm2, ymm16 + vpaddd ymm3, ymm3, ymm22 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 16 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vprord ymm4, ymm4, 12 + vpaddd ymm0, ymm0, ymm18 + vpaddd ymm1, ymm1, ymm19 + vpaddd ymm2, ymm2, ymm17 + vpaddd ymm3, ymm3, ymm20 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 8 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vprord ymm4, ymm4, 7 + vpaddd ymm0, ymm0, ymm25 + vpaddd ymm1, ymm1, ymm27 + vpaddd ymm2, ymm2, ymm24 + vpaddd ymm3, ymm3, ymm31 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vprord ymm15, ymm15, 16 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 12 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vpaddd ymm0, ymm0, ymm30 + vpaddd ymm1, ymm1, ymm21 + vpaddd ymm2, ymm2, ymm28 + vpaddd ymm3, ymm3, ymm17 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vprord ymm15, ymm15, 8 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 7 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vpaddd ymm0, ymm0, ymm29 + vpaddd ymm1, ymm1, ymm16 + vpaddd ymm2, ymm2, ymm18 + vpaddd ymm3, ymm3, ymm20 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 16 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vprord ymm4, ymm4, 12 + vpaddd ymm0, ymm0, ymm19 + vpaddd ymm1, ymm1, ymm26 + vpaddd ymm2, ymm2, ymm22 + vpaddd ymm3, ymm3, ymm23 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 8 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vprord ymm4, ymm4, 7 + vpaddd ymm0, ymm0, ymm27 + vpaddd ymm1, ymm1, ymm21 + vpaddd ymm2, ymm2, ymm17 + vpaddd ymm3, ymm3, ymm24 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vprord ymm15, ymm15, 16 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 12 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vpaddd ymm0, ymm0, ymm31 + vpaddd ymm1, ymm1, ymm16 + vpaddd ymm2, ymm2, ymm25 + vpaddd ymm3, ymm3, ymm22 + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm1, ymm1, ymm5 + vpaddd ymm2, ymm2, ymm6 + vpaddd ymm3, ymm3, ymm7 + vpxord ymm12, ymm12, ymm0 + vpxord ymm13, ymm13, ymm1 + vpxord ymm14, ymm14, ymm2 + vpxord ymm15, ymm15, ymm3 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vprord ymm15, ymm15, 8 + vpaddd ymm8, ymm8, ymm12 + vpaddd ymm9, ymm9, ymm13 + vpaddd ymm10, ymm10, ymm14 + vpaddd ymm11, ymm11, ymm15 + vpxord ymm4, ymm4, ymm8 + vpxord ymm5, ymm5, ymm9 + vpxord ymm6, ymm6, ymm10 + vpxord ymm7, ymm7, ymm11 + vprord ymm4, ymm4, 7 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vpaddd ymm0, ymm0, ymm30 + vpaddd ymm1, ymm1, ymm18 + vpaddd ymm2, ymm2, ymm19 + vpaddd ymm3, ymm3, ymm23 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 16 + vprord ymm12, ymm12, 16 + vprord ymm13, ymm13, 16 + vprord ymm14, ymm14, 16 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 12 + vprord ymm6, ymm6, 12 + vprord ymm7, ymm7, 12 + vprord ymm4, ymm4, 12 + vpaddd ymm0, ymm0, ymm26 + vpaddd ymm1, ymm1, ymm28 + vpaddd ymm2, ymm2, ymm20 + vpaddd ymm3, ymm3, ymm29 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm1, ymm1, ymm6 + vpaddd ymm2, ymm2, ymm7 + vpaddd ymm3, ymm3, ymm4 + vpxord ymm15, ymm15, ymm0 + vpxord ymm12, ymm12, ymm1 + vpxord ymm13, ymm13, ymm2 + vpxord ymm14, ymm14, ymm3 + vprord ymm15, ymm15, 8 + vprord ymm12, ymm12, 8 + vprord ymm13, ymm13, 8 + vprord ymm14, ymm14, 8 + vpaddd ymm10, ymm10, ymm15 + vpaddd ymm11, ymm11, ymm12 + vpaddd ymm8, ymm8, ymm13 + vpaddd ymm9, ymm9, ymm14 + vpxord ymm5, ymm5, ymm10 + vpxord ymm6, ymm6, ymm11 + vpxord ymm7, ymm7, ymm8 + vpxord ymm4, ymm4, ymm9 + vprord ymm5, ymm5, 7 + vprord ymm6, ymm6, 7 + vprord ymm7, ymm7, 7 + vprord ymm4, ymm4, 7 + vpxor ymm0, ymm0, ymm8 + vpxor ymm1, ymm1, ymm9 + vpxor ymm2, ymm2, ymm10 + vpxor ymm3, ymm3, ymm11 + vpxor ymm4, ymm4, ymm12 + vpxor ymm5, ymm5, ymm13 + vpxor ymm6, ymm6, ymm14 + vpxor ymm7, ymm7, ymm15 + movzx eax, byte ptr [rbp+0x78] + jne 2b + mov rbx, qword ptr [rbp+0x90] + vunpcklps ymm8, ymm0, ymm1 + vunpcklps ymm9, ymm2, ymm3 + vunpckhps ymm10, ymm0, ymm1 + vunpcklps ymm11, ymm4, ymm5 + vunpcklps ymm0, ymm6, ymm7 + vshufps ymm12, ymm8, ymm9, 78 + vblendps ymm1, ymm8, ymm12, 0xCC + vshufps ymm8, ymm11, ymm0, 78 + vunpckhps ymm13, ymm2, ymm3 + vblendps ymm2, ymm11, ymm8, 0xCC + vblendps ymm3, ymm12, ymm9, 0xCC + vperm2f128 ymm12, ymm1, ymm2, 0x20 + vmovups ymmword ptr [rbx], ymm12 + vunpckhps ymm14, ymm4, ymm5 + vblendps ymm4, ymm8, ymm0, 0xCC + vunpckhps ymm15, ymm6, ymm7 + vperm2f128 ymm7, ymm3, ymm4, 0x20 + vmovups ymmword ptr [rbx+0x20], ymm7 + vshufps ymm5, ymm10, ymm13, 78 + vblendps ymm6, ymm5, ymm13, 0xCC + vshufps ymm13, ymm14, ymm15, 78 + vblendps ymm10, ymm10, ymm5, 0xCC + vblendps ymm14, ymm14, ymm13, 0xCC + vperm2f128 ymm8, ymm10, ymm14, 0x20 + vmovups ymmword ptr [rbx+0x40], ymm8 + vblendps ymm15, ymm13, ymm15, 0xCC + vperm2f128 ymm13, ymm6, ymm15, 0x20 + vmovups ymmword ptr [rbx+0x60], ymm13 + vperm2f128 ymm9, ymm1, ymm2, 0x31 + vperm2f128 ymm11, ymm3, ymm4, 0x31 + vmovups ymmword ptr [rbx+0x80], ymm9 + vperm2f128 ymm14, ymm10, ymm14, 0x31 + vperm2f128 ymm15, ymm6, ymm15, 0x31 + vmovups ymmword ptr [rbx+0xA0], ymm11 + vmovups ymmword ptr [rbx+0xC0], ymm14 + vmovups ymmword ptr [rbx+0xE0], ymm15 + vmovdqa ymm0, ymmword ptr [rsp] + vmovdqa ymm2, ymmword ptr [rsp+0x40] + vmovdqa32 ymm0 {k1}, ymmword ptr [rsp+0x1*0x20] + vmovdqa32 ymm2 {k1}, ymmword ptr [rsp+0x3*0x20] + vmovdqa ymmword ptr [rsp], ymm0 + vmovdqa ymmword ptr [rsp+0x40], ymm2 + add rbx, 256 + mov qword ptr [rbp+0x90], rbx + add rdi, 64 + sub rsi, 8 +3: + mov rbx, qword ptr [rbp+0x90] + mov r15, qword ptr [rsp+0x80] + movzx r13, byte ptr [rbp+0x78] + movzx r12, byte ptr [rbp+0x88] + test esi, 0x4 + je 3f + vbroadcasti32x4 zmm0, xmmword ptr [rcx] + vbroadcasti32x4 zmm1, xmmword ptr [rcx+0x1*0x10] + vmovdqa xmm12, xmmword ptr [rsp] + vmovdqa xmm13, xmmword ptr [rsp+0x40] + vpunpckldq xmm14, xmm12, xmm13 + vpunpckhdq xmm15, xmm12, xmm13 + vpermq ymm14, ymm14, 0xDC + vpermq ymm15, ymm15, 0xDC + vpbroadcastd zmm12, dword ptr [BLAKE3_BLOCK_LEN+rip] + vinserti64x4 zmm13, zmm14, ymm15, 0x01 + mov eax, 17476 + kmovw k2, eax + vpblendmd zmm13 {k2}, zmm13, zmm12 + vbroadcasti32x4 zmm15, xmmword ptr [BLAKE3_IV+rip] + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + mov r10, qword ptr [rdi+0x10] + mov r11, qword ptr [rdi+0x18] + mov eax, 43690 + kmovw k3, eax + mov eax, 34952 + kmovw k4, eax + movzx eax, byte ptr [rbp+0x80] + or eax, r13d + xor edx, edx +.p2align 5 +2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + mov dword ptr [rsp+0x88], eax + vmovdqa32 zmm2, zmm15 + vpbroadcastd zmm8, dword ptr [rsp+0x22*0x4] + vpblendmd zmm3 {k4}, zmm13, zmm8 + vmovups zmm8, zmmword ptr [r8+rdx-0x1*0x40] + vinserti32x4 zmm8, zmm8, xmmword ptr [r9+rdx-0x4*0x10], 0x01 + vinserti32x4 zmm8, zmm8, xmmword ptr [r10+rdx-0x4*0x10], 0x02 + vinserti32x4 zmm8, zmm8, xmmword ptr [r11+rdx-0x4*0x10], 0x03 + vmovups zmm9, zmmword ptr [r8+rdx-0x30] + vinserti32x4 zmm9, zmm9, xmmword ptr [r9+rdx-0x3*0x10], 0x01 + vinserti32x4 zmm9, zmm9, xmmword ptr [r10+rdx-0x3*0x10], 0x02 + vinserti32x4 zmm9, zmm9, xmmword ptr [r11+rdx-0x3*0x10], 0x03 + vshufps zmm4, zmm8, zmm9, 136 + vshufps zmm5, zmm8, zmm9, 221 + vmovups zmm8, zmmword ptr [r8+rdx-0x20] + vinserti32x4 zmm8, zmm8, xmmword ptr [r9+rdx-0x2*0x10], 0x01 + vinserti32x4 zmm8, zmm8, xmmword ptr [r10+rdx-0x2*0x10], 0x02 + vinserti32x4 zmm8, zmm8, xmmword ptr [r11+rdx-0x2*0x10], 0x03 + vmovups zmm9, zmmword ptr [r8+rdx-0x10] + vinserti32x4 zmm9, zmm9, xmmword ptr [r9+rdx-0x1*0x10], 0x01 + vinserti32x4 zmm9, zmm9, xmmword ptr [r10+rdx-0x1*0x10], 0x02 + vinserti32x4 zmm9, zmm9, xmmword ptr [r11+rdx-0x1*0x10], 0x03 + vshufps zmm6, zmm8, zmm9, 136 + vshufps zmm7, zmm8, zmm9, 221 + vpshufd zmm6, zmm6, 0x93 + vpshufd zmm7, zmm7, 0x93 + mov al, 7 +9: + vpaddd zmm0, zmm0, zmm4 + vpaddd zmm0, zmm0, zmm1 + vpxord zmm3, zmm3, zmm0 + vprord zmm3, zmm3, 16 + vpaddd zmm2, zmm2, zmm3 + vpxord zmm1, zmm1, zmm2 + vprord zmm1, zmm1, 12 + vpaddd zmm0, zmm0, zmm5 + vpaddd zmm0, zmm0, zmm1 + vpxord zmm3, zmm3, zmm0 + vprord zmm3, zmm3, 8 + vpaddd zmm2, zmm2, zmm3 + vpxord zmm1, zmm1, zmm2 + vprord zmm1, zmm1, 7 + vpshufd zmm0, zmm0, 0x93 + vpshufd zmm3, zmm3, 0x4E + vpshufd zmm2, zmm2, 0x39 + vpaddd zmm0, zmm0, zmm6 + vpaddd zmm0, zmm0, zmm1 + vpxord zmm3, zmm3, zmm0 + vprord zmm3, zmm3, 16 + vpaddd zmm2, zmm2, zmm3 + vpxord zmm1, zmm1, zmm2 + vprord zmm1, zmm1, 12 + vpaddd zmm0, zmm0, zmm7 + vpaddd zmm0, zmm0, zmm1 + vpxord zmm3, zmm3, zmm0 + vprord zmm3, zmm3, 8 + vpaddd zmm2, zmm2, zmm3 + vpxord zmm1, zmm1, zmm2 + vprord zmm1, zmm1, 7 + vpshufd zmm0, zmm0, 0x39 + vpshufd zmm3, zmm3, 0x4E + vpshufd zmm2, zmm2, 0x93 + dec al + jz 9f + vshufps zmm8, zmm4, zmm5, 214 + vpshufd zmm9, zmm4, 0x0F + vpshufd zmm4, zmm8, 0x39 + vshufps zmm8, zmm6, zmm7, 250 + vpblendmd zmm9 {k3}, zmm9, zmm8 + vpunpcklqdq zmm8, zmm7, zmm5 + vpblendmd zmm8 {k4}, zmm8, zmm6 + vpshufd zmm8, zmm8, 0x78 + vpunpckhdq zmm5, zmm5, zmm7 + vpunpckldq zmm6, zmm6, zmm5 + vpshufd zmm7, zmm6, 0x1E + vmovdqa32 zmm5, zmm9 + vmovdqa32 zmm6, zmm8 + jmp 9b +9: + vpxord zmm0, zmm0, zmm2 + vpxord zmm1, zmm1, zmm3 + mov eax, r13d + cmp rdx, r15 + jne 2b + vmovdqu xmmword ptr [rbx], xmm0 + vmovdqu xmmword ptr [rbx+0x10], xmm1 + vextracti128 xmmword ptr [rbx+0x20], ymm0, 0x01 + vextracti128 xmmword ptr [rbx+0x30], ymm1, 0x01 + vextracti32x4 xmmword ptr [rbx+0x4*0x10], zmm0, 0x02 + vextracti32x4 xmmword ptr [rbx+0x5*0x10], zmm1, 0x02 + vextracti32x4 xmmword ptr [rbx+0x6*0x10], zmm0, 0x03 + vextracti32x4 xmmword ptr [rbx+0x7*0x10], zmm1, 0x03 + vmovdqa xmm0, xmmword ptr [rsp] + vmovdqa xmm2, xmmword ptr [rsp+0x40] + vmovdqa32 xmm0 {k1}, xmmword ptr [rsp+0x1*0x10] + vmovdqa32 xmm2 {k1}, xmmword ptr [rsp+0x5*0x10] + vmovdqa xmmword ptr [rsp], xmm0 + vmovdqa xmmword ptr [rsp+0x40], xmm2 + add rbx, 128 + add rdi, 32 + sub rsi, 4 +3: + test esi, 0x2 + je 3f + vbroadcasti128 ymm0, xmmword ptr [rcx] + vbroadcasti128 ymm1, xmmword ptr [rcx+0x10] + vmovd xmm13, dword ptr [rsp] + vpinsrd xmm13, xmm13, dword ptr [rsp+0x40], 1 + vpinsrd xmm13, xmm13, dword ptr [BLAKE3_BLOCK_LEN+rip], 2 + vmovd xmm14, dword ptr [rsp+0x4] + vpinsrd xmm14, xmm14, dword ptr [rsp+0x44], 1 + vpinsrd xmm14, xmm14, dword ptr [BLAKE3_BLOCK_LEN+rip], 2 + vinserti128 ymm13, ymm13, xmm14, 0x01 + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + movzx eax, byte ptr [rbp+0x80] + or eax, r13d + xor edx, edx +.p2align 5 +2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + mov dword ptr [rsp+0x88], eax + vbroadcasti128 ymm2, xmmword ptr [BLAKE3_IV+rip] + vpbroadcastd ymm8, dword ptr [rsp+0x88] + vpblendd ymm3, ymm13, ymm8, 0x88 + vmovups ymm8, ymmword ptr [r8+rdx-0x40] + vinsertf128 ymm8, ymm8, xmmword ptr [r9+rdx-0x40], 0x01 + vmovups ymm9, ymmword ptr [r8+rdx-0x30] + vinsertf128 ymm9, ymm9, xmmword ptr [r9+rdx-0x30], 0x01 + vshufps ymm4, ymm8, ymm9, 136 + vshufps ymm5, ymm8, ymm9, 221 + vmovups ymm8, ymmword ptr [r8+rdx-0x20] + vinsertf128 ymm8, ymm8, xmmword ptr [r9+rdx-0x20], 0x01 + vmovups ymm9, ymmword ptr [r8+rdx-0x10] + vinsertf128 ymm9, ymm9, xmmword ptr [r9+rdx-0x10], 0x01 + vshufps ymm6, ymm8, ymm9, 136 + vshufps ymm7, ymm8, ymm9, 221 + vpshufd ymm6, ymm6, 0x93 + vpshufd ymm7, ymm7, 0x93 + mov al, 7 +9: + vpaddd ymm0, ymm0, ymm4 + vpaddd ymm0, ymm0, ymm1 + vpxord ymm3, ymm3, ymm0 + vprord ymm3, ymm3, 16 + vpaddd ymm2, ymm2, ymm3 + vpxord ymm1, ymm1, ymm2 + vprord ymm1, ymm1, 12 + vpaddd ymm0, ymm0, ymm5 + vpaddd ymm0, ymm0, ymm1 + vpxord ymm3, ymm3, ymm0 + vprord ymm3, ymm3, 8 + vpaddd ymm2, ymm2, ymm3 + vpxord ymm1, ymm1, ymm2 + vprord ymm1, ymm1, 7 + vpshufd ymm0, ymm0, 0x93 + vpshufd ymm3, ymm3, 0x4E + vpshufd ymm2, ymm2, 0x39 + vpaddd ymm0, ymm0, ymm6 + vpaddd ymm0, ymm0, ymm1 + vpxord ymm3, ymm3, ymm0 + vprord ymm3, ymm3, 16 + vpaddd ymm2, ymm2, ymm3 + vpxord ymm1, ymm1, ymm2 + vprord ymm1, ymm1, 12 + vpaddd ymm0, ymm0, ymm7 + vpaddd ymm0, ymm0, ymm1 + vpxord ymm3, ymm3, ymm0 + vprord ymm3, ymm3, 8 + vpaddd ymm2, ymm2, ymm3 + vpxord ymm1, ymm1, ymm2 + vprord ymm1, ymm1, 7 + vpshufd ymm0, ymm0, 0x39 + vpshufd ymm3, ymm3, 0x4E + vpshufd ymm2, ymm2, 0x93 + dec al + jz 9f + vshufps ymm8, ymm4, ymm5, 214 + vpshufd ymm9, ymm4, 0x0F + vpshufd ymm4, ymm8, 0x39 + vshufps ymm8, ymm6, ymm7, 250 + vpblendd ymm9, ymm9, ymm8, 0xAA + vpunpcklqdq ymm8, ymm7, ymm5 + vpblendd ymm8, ymm8, ymm6, 0x88 + vpshufd ymm8, ymm8, 0x78 + vpunpckhdq ymm5, ymm5, ymm7 + vpunpckldq ymm6, ymm6, ymm5 + vpshufd ymm7, ymm6, 0x1E + vmovdqa ymm5, ymm9 + vmovdqa ymm6, ymm8 + jmp 9b +9: + vpxor ymm0, ymm0, ymm2 + vpxor ymm1, ymm1, ymm3 + mov eax, r13d + cmp rdx, r15 + jne 2b + vmovdqu xmmword ptr [rbx], xmm0 + vmovdqu xmmword ptr [rbx+0x10], xmm1 + vextracti128 xmmword ptr [rbx+0x20], ymm0, 0x01 + vextracti128 xmmword ptr [rbx+0x30], ymm1, 0x01 + vmovdqa xmm0, xmmword ptr [rsp] + vmovdqa xmm2, xmmword ptr [rsp+0x40] + vmovdqu32 xmm0 {k1}, xmmword ptr [rsp+0x8] + vmovdqu32 xmm2 {k1}, xmmword ptr [rsp+0x48] + vmovdqa xmmword ptr [rsp], xmm0 + vmovdqa xmmword ptr [rsp+0x40], xmm2 + add rbx, 64 + add rdi, 16 + sub rsi, 2 +3: + test esi, 0x1 + je 4b + vmovdqu xmm0, xmmword ptr [rcx] + vmovdqu xmm1, xmmword ptr [rcx+0x10] + vmovd xmm14, dword ptr [rsp] + vpinsrd xmm14, xmm14, dword ptr [rsp+0x40], 1 + vpinsrd xmm14, xmm14, dword ptr [BLAKE3_BLOCK_LEN+rip], 2 + vmovdqa xmm15, xmmword ptr [BLAKE3_IV+rip] + mov r8, qword ptr [rdi] + movzx eax, byte ptr [rbp+0x80] + or eax, r13d + xor edx, edx +.p2align 5 +2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + vpinsrd xmm3, xmm14, eax, 3 + vmovdqa xmm2, xmm15 + vmovups xmm8, xmmword ptr [r8+rdx-0x40] + vmovups xmm9, xmmword ptr [r8+rdx-0x30] + vshufps xmm4, xmm8, xmm9, 136 + vshufps xmm5, xmm8, xmm9, 221 + vmovups xmm8, xmmword ptr [r8+rdx-0x20] + vmovups xmm9, xmmword ptr [r8+rdx-0x10] + vshufps xmm6, xmm8, xmm9, 136 + vshufps xmm7, xmm8, xmm9, 221 + vpshufd xmm6, xmm6, 0x93 + vpshufd xmm7, xmm7, 0x93 + mov al, 7 +9: + vpaddd xmm0, xmm0, xmm4 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 16 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 12 + vpaddd xmm0, xmm0, xmm5 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 8 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 7 + vpshufd xmm0, xmm0, 0x93 + vpshufd xmm3, xmm3, 0x4E + vpshufd xmm2, xmm2, 0x39 + vpaddd xmm0, xmm0, xmm6 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 16 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 12 + vpaddd xmm0, xmm0, xmm7 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 8 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 7 + vpshufd xmm0, xmm0, 0x39 + vpshufd xmm3, xmm3, 0x4E + vpshufd xmm2, xmm2, 0x93 + dec al + jz 9f + vshufps xmm8, xmm4, xmm5, 214 + vpshufd xmm9, xmm4, 0x0F + vpshufd xmm4, xmm8, 0x39 + vshufps xmm8, xmm6, xmm7, 250 + vpblendd xmm9, xmm9, xmm8, 0xAA + vpunpcklqdq xmm8, xmm7, xmm5 + vpblendd xmm8, xmm8, xmm6, 0x88 + vpshufd xmm8, xmm8, 0x78 + vpunpckhdq xmm5, xmm5, xmm7 + vpunpckldq xmm6, xmm6, xmm5 + vpshufd xmm7, xmm6, 0x1E + vmovdqa xmm5, xmm9 + vmovdqa xmm6, xmm8 + jmp 9b +9: + vpxor xmm0, xmm0, xmm2 + vpxor xmm1, xmm1, xmm3 + mov eax, r13d + cmp rdx, r15 + jne 2b + vmovdqu xmmword ptr [rbx], xmm0 + vmovdqu xmmword ptr [rbx+0x10], xmm1 + jmp 4b + + +.p2align 6 +_blake3_compress_in_place_avx512: +blake3_compress_in_place_avx512: + sub rsp, 72 + vmovdqa xmmword ptr [rsp], xmm6 + vmovdqa xmmword ptr [rsp+0x10], xmm7 + vmovdqa xmmword ptr [rsp+0x20], xmm8 + vmovdqa xmmword ptr [rsp+0x30], xmm9 + vmovdqu xmm0, xmmword ptr [rcx] + vmovdqu xmm1, xmmword ptr [rcx+0x10] + movzx eax, byte ptr [rsp+0x70] + movzx r8d, r8b + shl rax, 32 + add r8, rax + vmovq xmm3, r9 + vmovq xmm4, r8 + vpunpcklqdq xmm3, xmm3, xmm4 + vmovaps xmm2, xmmword ptr [BLAKE3_IV+rip] + vmovups xmm8, xmmword ptr [rdx] + vmovups xmm9, xmmword ptr [rdx+0x10] + vshufps xmm4, xmm8, xmm9, 136 + vshufps xmm5, xmm8, xmm9, 221 + vmovups xmm8, xmmword ptr [rdx+0x20] + vmovups xmm9, xmmword ptr [rdx+0x30] + vshufps xmm6, xmm8, xmm9, 136 + vshufps xmm7, xmm8, xmm9, 221 + vpshufd xmm6, xmm6, 0x93 + vpshufd xmm7, xmm7, 0x93 + mov al, 7 +9: + vpaddd xmm0, xmm0, xmm4 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 16 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 12 + vpaddd xmm0, xmm0, xmm5 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 8 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 7 + vpshufd xmm0, xmm0, 0x93 + vpshufd xmm3, xmm3, 0x4E + vpshufd xmm2, xmm2, 0x39 + vpaddd xmm0, xmm0, xmm6 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 16 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 12 + vpaddd xmm0, xmm0, xmm7 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 8 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 7 + vpshufd xmm0, xmm0, 0x39 + vpshufd xmm3, xmm3, 0x4E + vpshufd xmm2, xmm2, 0x93 + dec al + jz 9f + vshufps xmm8, xmm4, xmm5, 214 + vpshufd xmm9, xmm4, 0x0F + vpshufd xmm4, xmm8, 0x39 + vshufps xmm8, xmm6, xmm7, 250 + vpblendd xmm9, xmm9, xmm8, 0xAA + vpunpcklqdq xmm8, xmm7, xmm5 + vpblendd xmm8, xmm8, xmm6, 0x88 + vpshufd xmm8, xmm8, 0x78 + vpunpckhdq xmm5, xmm5, xmm7 + vpunpckldq xmm6, xmm6, xmm5 + vpshufd xmm7, xmm6, 0x1E + vmovdqa xmm5, xmm9 + vmovdqa xmm6, xmm8 + jmp 9b +9: + vpxor xmm0, xmm0, xmm2 + vpxor xmm1, xmm1, xmm3 + vmovdqu xmmword ptr [rcx], xmm0 + vmovdqu xmmword ptr [rcx+0x10], xmm1 + vmovdqa xmm6, xmmword ptr [rsp] + vmovdqa xmm7, xmmword ptr [rsp+0x10] + vmovdqa xmm8, xmmword ptr [rsp+0x20] + vmovdqa xmm9, xmmword ptr [rsp+0x30] + add rsp, 72 + ret + + +.p2align 6 +_blake3_compress_xof_avx512: +blake3_compress_xof_avx512: + sub rsp, 72 + vmovdqa xmmword ptr [rsp], xmm6 + vmovdqa xmmword ptr [rsp+0x10], xmm7 + vmovdqa xmmword ptr [rsp+0x20], xmm8 + vmovdqa xmmword ptr [rsp+0x30], xmm9 + vmovdqu xmm0, xmmword ptr [rcx] + vmovdqu xmm1, xmmword ptr [rcx+0x10] + movzx eax, byte ptr [rsp+0x70] + movzx r8d, r8b + mov r10, qword ptr [rsp+0x78] + shl rax, 32 + add r8, rax + vmovq xmm3, r9 + vmovq xmm4, r8 + vpunpcklqdq xmm3, xmm3, xmm4 + vmovaps xmm2, xmmword ptr [BLAKE3_IV+rip] + vmovups xmm8, xmmword ptr [rdx] + vmovups xmm9, xmmword ptr [rdx+0x10] + vshufps xmm4, xmm8, xmm9, 136 + vshufps xmm5, xmm8, xmm9, 221 + vmovups xmm8, xmmword ptr [rdx+0x20] + vmovups xmm9, xmmword ptr [rdx+0x30] + vshufps xmm6, xmm8, xmm9, 136 + vshufps xmm7, xmm8, xmm9, 221 + vpshufd xmm6, xmm6, 0x93 + vpshufd xmm7, xmm7, 0x93 + mov al, 7 +9: + vpaddd xmm0, xmm0, xmm4 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 16 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 12 + vpaddd xmm0, xmm0, xmm5 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 8 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 7 + vpshufd xmm0, xmm0, 0x93 + vpshufd xmm3, xmm3, 0x4E + vpshufd xmm2, xmm2, 0x39 + vpaddd xmm0, xmm0, xmm6 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 16 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 12 + vpaddd xmm0, xmm0, xmm7 + vpaddd xmm0, xmm0, xmm1 + vpxord xmm3, xmm3, xmm0 + vprord xmm3, xmm3, 8 + vpaddd xmm2, xmm2, xmm3 + vpxord xmm1, xmm1, xmm2 + vprord xmm1, xmm1, 7 + vpshufd xmm0, xmm0, 0x39 + vpshufd xmm3, xmm3, 0x4E + vpshufd xmm2, xmm2, 0x93 + dec al + jz 9f + vshufps xmm8, xmm4, xmm5, 214 + vpshufd xmm9, xmm4, 0x0F + vpshufd xmm4, xmm8, 0x39 + vshufps xmm8, xmm6, xmm7, 250 + vpblendd xmm9, xmm9, xmm8, 0xAA + vpunpcklqdq xmm8, xmm7, xmm5 + vpblendd xmm8, xmm8, xmm6, 0x88 + vpshufd xmm8, xmm8, 0x78 + vpunpckhdq xmm5, xmm5, xmm7 + vpunpckldq xmm6, xmm6, xmm5 + vpshufd xmm7, xmm6, 0x1E + vmovdqa xmm5, xmm9 + vmovdqa xmm6, xmm8 + jmp 9b +9: + vpxor xmm0, xmm0, xmm2 + vpxor xmm1, xmm1, xmm3 + vpxor xmm2, xmm2, xmmword ptr [rcx] + vpxor xmm3, xmm3, xmmword ptr [rcx+0x10] + vmovdqu xmmword ptr [r10], xmm0 + vmovdqu xmmword ptr [r10+0x10], xmm1 + vmovdqu xmmword ptr [r10+0x20], xmm2 + vmovdqu xmmword ptr [r10+0x30], xmm3 + vmovdqa xmm6, xmmword ptr [rsp] + vmovdqa xmm7, xmmword ptr [rsp+0x10] + vmovdqa xmm8, xmmword ptr [rsp+0x20] + vmovdqa xmm9, xmmword ptr [rsp+0x30] + add rsp, 72 + ret + +.section .rodata +.p2align 6 +INDEX0: + .long 0, 1, 2, 3, 16, 17, 18, 19 + .long 8, 9, 10, 11, 24, 25, 26, 27 +INDEX1: + .long 4, 5, 6, 7, 20, 21, 22, 23 + .long 12, 13, 14, 15, 28, 29, 30, 31 +ADD0: + .long 0, 1, 2, 3, 4, 5, 6, 7 + .long 8, 9, 10, 11, 12, 13, 14, 15 +ADD1: .long 1 + +ADD16: .long 16 +BLAKE3_BLOCK_LEN: + .long 64 +.p2align 6 +BLAKE3_IV: +BLAKE3_IV_0: + .long 0x6A09E667 +BLAKE3_IV_1: + .long 0xBB67AE85 +BLAKE3_IV_2: + .long 0x3C6EF372 +BLAKE3_IV_3: + .long 0xA54FF53A diff --git a/src/third_party/blake3/blake3_dispatch.c b/src/third_party/blake3/blake3_dispatch.c new file mode 100644 index 0000000..a4c0fa9 --- /dev/null +++ b/src/third_party/blake3/blake3_dispatch.c @@ -0,0 +1,270 @@ +#include +#include +#include + +#include "blake3_impl.h" + +#if defined(IS_X86) +#if defined(_MSC_VER) +#include +#elif defined(__GNUC__) +#include +#else +#error "Unimplemented!" +#endif +#endif + +#if defined(IS_X86) +static uint64_t xgetbv() { +#if defined(_MSC_VER) + return _xgetbv(0); +#else + uint32_t eax = 0, edx = 0; + __asm__ __volatile__("xgetbv\n" : "=a"(eax), "=d"(edx) : "c"(0)); + return ((uint64_t)edx << 32) | eax; +#endif +} + +static void cpuid(uint32_t out[4], uint32_t id) { +#if defined(_MSC_VER) + __cpuid((int *)out, id); +#elif defined(__i386__) || defined(_M_IX86) + __asm__ __volatile__("movl %%ebx, %1\n" + "cpuid\n" + "xchgl %1, %%ebx\n" + : "=a"(out[0]), "=r"(out[1]), "=c"(out[2]), "=d"(out[3]) + : "a"(id)); +#else + __asm__ __volatile__("cpuid\n" + : "=a"(out[0]), "=b"(out[1]), "=c"(out[2]), "=d"(out[3]) + : "a"(id)); +#endif +} + +static void cpuidex(uint32_t out[4], uint32_t id, uint32_t sid) { +#if defined(_MSC_VER) + __cpuidex((int *)out, id, sid); +#elif defined(__i386__) || defined(_M_IX86) + __asm__ __volatile__("movl %%ebx, %1\n" + "cpuid\n" + "xchgl %1, %%ebx\n" + : "=a"(out[0]), "=r"(out[1]), "=c"(out[2]), "=d"(out[3]) + : "a"(id), "c"(sid)); +#else + __asm__ __volatile__("cpuid\n" + : "=a"(out[0]), "=b"(out[1]), "=c"(out[2]), "=d"(out[3]) + : "a"(id), "c"(sid)); +#endif +} + +#endif + +enum cpu_feature { + SSE2 = 1 << 0, + SSSE3 = 1 << 1, + SSE41 = 1 << 2, + AVX = 1 << 3, + AVX2 = 1 << 4, + AVX512F = 1 << 5, + AVX512VL = 1 << 6, + /* ... */ + UNDEFINED = 1 << 30 +}; + +#if !defined(BLAKE3_TESTING) +static /* Allow the variable to be controlled manually for testing */ +#endif + enum cpu_feature g_cpu_features = UNDEFINED; + +#if !defined(BLAKE3_TESTING) +static +#endif + enum cpu_feature + get_cpu_features() { + + if (g_cpu_features != UNDEFINED) { + return g_cpu_features; + } else { +#if defined(IS_X86) + uint32_t regs[4] = {0}; + uint32_t *eax = ®s[0], *ebx = ®s[1], *ecx = ®s[2], *edx = ®s[3]; + (void)edx; + enum cpu_feature features = 0; + cpuid(regs, 0); + const int max_id = *eax; + cpuid(regs, 1); +#if defined(__amd64__) || defined(_M_X64) + features |= SSE2; +#else + if (*edx & (1UL << 26)) + features |= SSE2; +#endif + if (*ecx & (1UL << 0)) + features |= SSSE3; + if (*ecx & (1UL << 19)) + features |= SSE41; + + if (*ecx & (1UL << 27)) { // OSXSAVE + const uint64_t mask = xgetbv(); + if ((mask & 6) == 6) { // SSE and AVX states + if (*ecx & (1UL << 28)) + features |= AVX; + if (max_id >= 7) { + cpuidex(regs, 7, 0); + if (*ebx & (1UL << 5)) + features |= AVX2; + if ((mask & 224) == 224) { // Opmask, ZMM_Hi256, Hi16_Zmm + if (*ebx & (1UL << 31)) + features |= AVX512VL; + if (*ebx & (1UL << 16)) + features |= AVX512F; + } + } + } + } + g_cpu_features = features; + return features; +#else + /* How to detect NEON? */ + return 0; +#endif + } +} + +void blake3_compress_in_place(uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, + uint8_t flags) { +#if defined(IS_X86) + const enum cpu_feature features = get_cpu_features(); +#if !defined(BLAKE3_NO_AVX512) + if (features & AVX512VL) { + blake3_compress_in_place_avx512(cv, block, block_len, counter, flags); + return; + } +#endif +#if !defined(BLAKE3_NO_SSE41) + if (features & SSE41) { + blake3_compress_in_place_sse41(cv, block, block_len, counter, flags); + return; + } +#endif +#if !defined(BLAKE3_NO_SSE2) + if (features & SSE2) { + blake3_compress_in_place_sse2(cv, block, block_len, counter, flags); + return; + } +#endif +#endif + blake3_compress_in_place_portable(cv, block, block_len, counter, flags); +} + +void blake3_compress_xof(const uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, uint8_t flags, + uint8_t out[64]) { +#if defined(IS_X86) + const enum cpu_feature features = get_cpu_features(); +#if !defined(BLAKE3_NO_AVX512) + if (features & AVX512VL) { + blake3_compress_xof_avx512(cv, block, block_len, counter, flags, out); + return; + } +#endif +#if !defined(BLAKE3_NO_SSE41) + if (features & SSE41) { + blake3_compress_xof_sse41(cv, block, block_len, counter, flags, out); + return; + } +#endif +#if !defined(BLAKE3_NO_SSE2) + if (features & SSE2) { + blake3_compress_xof_sse2(cv, block, block_len, counter, flags, out); + return; + } +#endif +#endif + blake3_compress_xof_portable(cv, block, block_len, counter, flags, out); +} + +void blake3_hash_many(const uint8_t *const *inputs, size_t num_inputs, + size_t blocks, const uint32_t key[8], uint64_t counter, + bool increment_counter, uint8_t flags, + uint8_t flags_start, uint8_t flags_end, uint8_t *out) { +#if defined(IS_X86) + const enum cpu_feature features = get_cpu_features(); +#if !defined(BLAKE3_NO_AVX512) + if ((features & (AVX512F|AVX512VL)) == (AVX512F|AVX512VL)) { + blake3_hash_many_avx512(inputs, num_inputs, blocks, key, counter, + increment_counter, flags, flags_start, flags_end, + out); + return; + } +#endif +#if !defined(BLAKE3_NO_AVX2) + if (features & AVX2) { + blake3_hash_many_avx2(inputs, num_inputs, blocks, key, counter, + increment_counter, flags, flags_start, flags_end, + out); + return; + } +#endif +#if !defined(BLAKE3_NO_SSE41) + if (features & SSE41) { + blake3_hash_many_sse41(inputs, num_inputs, blocks, key, counter, + increment_counter, flags, flags_start, flags_end, + out); + return; + } +#endif +#if !defined(BLAKE3_NO_SSE2) + if (features & SSE2) { + blake3_hash_many_sse2(inputs, num_inputs, blocks, key, counter, + increment_counter, flags, flags_start, flags_end, + out); + return; + } +#endif +#endif + +#if defined(BLAKE3_USE_NEON) + blake3_hash_many_neon(inputs, num_inputs, blocks, key, counter, + increment_counter, flags, flags_start, flags_end, out); + return; +#endif + + blake3_hash_many_portable(inputs, num_inputs, blocks, key, counter, + increment_counter, flags, flags_start, flags_end, + out); +} + +// The dynamically detected SIMD degree of the current platform. +size_t blake3_simd_degree(void) { +#if defined(IS_X86) + const enum cpu_feature features = get_cpu_features(); +#if !defined(BLAKE3_NO_AVX512) + if ((features & (AVX512F|AVX512VL)) == (AVX512F|AVX512VL)) { + return 16; + } +#endif +#if !defined(BLAKE3_NO_AVX2) + if (features & AVX2) { + return 8; + } +#endif +#if !defined(BLAKE3_NO_SSE41) + if (features & SSE41) { + return 4; + } +#endif +#if !defined(BLAKE3_NO_SSE2) + if (features & SSE2) { + return 4; + } +#endif +#endif +#if defined(BLAKE3_USE_NEON) + return 4; +#endif + return 1; +} diff --git a/src/third_party/blake3/blake3_impl.h b/src/third_party/blake3/blake3_impl.h new file mode 100644 index 0000000..86ab6aa --- /dev/null +++ b/src/third_party/blake3/blake3_impl.h @@ -0,0 +1,269 @@ +#ifndef BLAKE3_IMPL_H +#define BLAKE3_IMPL_H + +#include +#include +#include +#include +#include + +#include "blake3.h" + +// internal flags +enum blake3_flags { + CHUNK_START = 1 << 0, + CHUNK_END = 1 << 1, + PARENT = 1 << 2, + ROOT = 1 << 3, + KEYED_HASH = 1 << 4, + DERIVE_KEY_CONTEXT = 1 << 5, + DERIVE_KEY_MATERIAL = 1 << 6, +}; + +// This C implementation tries to support recent versions of GCC, Clang, and +// MSVC. +#if defined(_MSC_VER) +#define INLINE static __forceinline +#else +#define INLINE static inline __attribute__((always_inline)) +#endif + +#if defined(__x86_64__) || defined(_M_X64) +#define IS_X86 +#define IS_X86_64 +#endif + +#if defined(__i386__) || defined(_M_IX86) +#define IS_X86 +#define IS_X86_32 +#endif + +#if defined(IS_X86) +#if defined(_MSC_VER) +#include +#endif +#include +#endif + +#if defined(IS_X86) +#define MAX_SIMD_DEGREE 16 +#elif defined(BLAKE3_USE_NEON) +#define MAX_SIMD_DEGREE 4 +#else +#define MAX_SIMD_DEGREE 1 +#endif + +// There are some places where we want a static size that's equal to the +// MAX_SIMD_DEGREE, but also at least 2. +#define MAX_SIMD_DEGREE_OR_2 (MAX_SIMD_DEGREE > 2 ? MAX_SIMD_DEGREE : 2) + +static const uint32_t IV[8] = {0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, + 0xA54FF53AUL, 0x510E527FUL, 0x9B05688CUL, + 0x1F83D9ABUL, 0x5BE0CD19UL}; + +static const uint8_t MSG_SCHEDULE[7][16] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8}, + {3, 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1}, + {10, 7, 12, 9, 14, 3, 13, 15, 4, 0, 11, 2, 5, 8, 1, 6}, + {12, 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4}, + {9, 14, 11, 5, 8, 12, 15, 1, 13, 3, 0, 10, 2, 6, 4, 7}, + {11, 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13}, +}; + +/* Find index of the highest set bit */ +/* x is assumed to be nonzero. */ +static unsigned int highest_one(uint64_t x) { +#if defined(__GNUC__) || defined(__clang__) + return 63 ^ __builtin_clzll(x); +#elif defined(_MSC_VER) && defined(IS_X86_64) + unsigned long index; + _BitScanReverse64(&index, x); + return index; +#elif defined(_MSC_VER) && defined(IS_X86_32) + if(x >> 32) { + unsigned long index; + _BitScanReverse(&index, x >> 32); + return 32 + index; + } else { + unsigned long index; + _BitScanReverse(&index, x); + return index; + } +#else + unsigned int c = 0; + if(x & 0xffffffff00000000ULL) { x >>= 32; c += 32; } + if(x & 0x00000000ffff0000ULL) { x >>= 16; c += 16; } + if(x & 0x000000000000ff00ULL) { x >>= 8; c += 8; } + if(x & 0x00000000000000f0ULL) { x >>= 4; c += 4; } + if(x & 0x000000000000000cULL) { x >>= 2; c += 2; } + if(x & 0x0000000000000002ULL) { c += 1; } + return c; +#endif +} + +// Count the number of 1 bits. +INLINE unsigned int popcnt(uint64_t x) { +#if defined(__GNUC__) || defined(__clang__) + return __builtin_popcountll(x); +#else + unsigned int count = 0; + while (x != 0) { + count += 1; + x &= x - 1; + } + return count; +#endif +} + +// Largest power of two less than or equal to x. As a special case, returns 1 +// when x is 0. +INLINE uint64_t round_down_to_power_of_2(uint64_t x) { + return 1ULL << highest_one(x | 1); +} + +INLINE uint32_t counter_low(uint64_t counter) { return (uint32_t)counter; } + +INLINE uint32_t counter_high(uint64_t counter) { + return (uint32_t)(counter >> 32); +} + +INLINE uint32_t load32(const void *src) { + const uint8_t *p = (const uint8_t *)src; + return ((uint32_t)(p[0]) << 0) | ((uint32_t)(p[1]) << 8) | + ((uint32_t)(p[2]) << 16) | ((uint32_t)(p[3]) << 24); +} + +INLINE void load_key_words(const uint8_t key[BLAKE3_KEY_LEN], + uint32_t key_words[8]) { + key_words[0] = load32(&key[0 * 4]); + key_words[1] = load32(&key[1 * 4]); + key_words[2] = load32(&key[2 * 4]); + key_words[3] = load32(&key[3 * 4]); + key_words[4] = load32(&key[4 * 4]); + key_words[5] = load32(&key[5 * 4]); + key_words[6] = load32(&key[6 * 4]); + key_words[7] = load32(&key[7 * 4]); +} + +INLINE void store32(void *dst, uint32_t w) { + uint8_t *p = (uint8_t *)dst; + p[0] = (uint8_t)(w >> 0); + p[1] = (uint8_t)(w >> 8); + p[2] = (uint8_t)(w >> 16); + p[3] = (uint8_t)(w >> 24); +} + +INLINE void store_cv_words(uint8_t bytes_out[32], uint32_t cv_words[8]) { + store32(&bytes_out[0 * 4], cv_words[0]); + store32(&bytes_out[1 * 4], cv_words[1]); + store32(&bytes_out[2 * 4], cv_words[2]); + store32(&bytes_out[3 * 4], cv_words[3]); + store32(&bytes_out[4 * 4], cv_words[4]); + store32(&bytes_out[5 * 4], cv_words[5]); + store32(&bytes_out[6 * 4], cv_words[6]); + store32(&bytes_out[7 * 4], cv_words[7]); +} + +void blake3_compress_in_place(uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, + uint8_t flags); + +void blake3_compress_xof(const uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, uint8_t flags, + uint8_t out[64]); + +void blake3_hash_many(const uint8_t *const *inputs, size_t num_inputs, + size_t blocks, const uint32_t key[8], uint64_t counter, + bool increment_counter, uint8_t flags, + uint8_t flags_start, uint8_t flags_end, uint8_t *out); + +size_t blake3_simd_degree(void); + + +// Declarations for implementation-specific functions. +void blake3_compress_in_place_portable(uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, + uint8_t flags); + +void blake3_compress_xof_portable(const uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, + uint8_t flags, uint8_t out[64]); + +void blake3_hash_many_portable(const uint8_t *const *inputs, size_t num_inputs, + size_t blocks, const uint32_t key[8], + uint64_t counter, bool increment_counter, + uint8_t flags, uint8_t flags_start, + uint8_t flags_end, uint8_t *out); + +#if defined(IS_X86) +#if !defined(BLAKE3_NO_SSE2) +void blake3_compress_in_place_sse2(uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, + uint8_t flags); +void blake3_compress_xof_sse2(const uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, + uint8_t flags, uint8_t out[64]); +void blake3_hash_many_sse2(const uint8_t *const *inputs, size_t num_inputs, + size_t blocks, const uint32_t key[8], + uint64_t counter, bool increment_counter, + uint8_t flags, uint8_t flags_start, + uint8_t flags_end, uint8_t *out); +#endif +#if !defined(BLAKE3_NO_SSE41) +void blake3_compress_in_place_sse41(uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, + uint8_t flags); +void blake3_compress_xof_sse41(const uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, + uint8_t flags, uint8_t out[64]); +void blake3_hash_many_sse41(const uint8_t *const *inputs, size_t num_inputs, + size_t blocks, const uint32_t key[8], + uint64_t counter, bool increment_counter, + uint8_t flags, uint8_t flags_start, + uint8_t flags_end, uint8_t *out); +#endif +#if !defined(BLAKE3_NO_AVX2) +void blake3_hash_many_avx2(const uint8_t *const *inputs, size_t num_inputs, + size_t blocks, const uint32_t key[8], + uint64_t counter, bool increment_counter, + uint8_t flags, uint8_t flags_start, + uint8_t flags_end, uint8_t *out); +#endif +#if !defined(BLAKE3_NO_AVX512) +void blake3_compress_in_place_avx512(uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, + uint8_t flags); + +void blake3_compress_xof_avx512(const uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, + uint8_t flags, uint8_t out[64]); + +void blake3_hash_many_avx512(const uint8_t *const *inputs, size_t num_inputs, + size_t blocks, const uint32_t key[8], + uint64_t counter, bool increment_counter, + uint8_t flags, uint8_t flags_start, + uint8_t flags_end, uint8_t *out); +#endif +#endif + +#if defined(BLAKE3_USE_NEON) +void blake3_hash_many_neon(const uint8_t *const *inputs, size_t num_inputs, + size_t blocks, const uint32_t key[8], + uint64_t counter, bool increment_counter, + uint8_t flags, uint8_t flags_start, + uint8_t flags_end, uint8_t *out); +#endif + + +#endif /* BLAKE3_IMPL_H */ diff --git a/src/third_party/blake3/blake3_neon.c b/src/third_party/blake3/blake3_neon.c new file mode 100644 index 0000000..46691f5 --- /dev/null +++ b/src/third_party/blake3/blake3_neon.c @@ -0,0 +1,346 @@ +#include "blake3_impl.h" + +#include + +// TODO: This is probably incorrect for big-endian ARM. How should that work? +INLINE uint32x4_t loadu_128(const uint8_t src[16]) { + // vld1q_u32 has alignment requirements. Don't use it. + uint32x4_t x; + memcpy(&x, src, 16); + return x; +} + +INLINE void storeu_128(uint32x4_t src, uint8_t dest[16]) { + // vst1q_u32 has alignment requirements. Don't use it. + memcpy(dest, &src, 16); +} + +INLINE uint32x4_t add_128(uint32x4_t a, uint32x4_t b) { + return vaddq_u32(a, b); +} + +INLINE uint32x4_t xor_128(uint32x4_t a, uint32x4_t b) { + return veorq_u32(a, b); +} + +INLINE uint32x4_t set1_128(uint32_t x) { return vld1q_dup_u32(&x); } + +INLINE uint32x4_t set4(uint32_t a, uint32_t b, uint32_t c, uint32_t d) { + uint32_t array[4] = {a, b, c, d}; + return vld1q_u32(array); +} + +INLINE uint32x4_t rot16_128(uint32x4_t x) { + return vorrq_u32(vshrq_n_u32(x, 16), vshlq_n_u32(x, 32 - 16)); +} + +INLINE uint32x4_t rot12_128(uint32x4_t x) { + return vorrq_u32(vshrq_n_u32(x, 12), vshlq_n_u32(x, 32 - 12)); +} + +INLINE uint32x4_t rot8_128(uint32x4_t x) { + return vorrq_u32(vshrq_n_u32(x, 8), vshlq_n_u32(x, 32 - 8)); +} + +INLINE uint32x4_t rot7_128(uint32x4_t x) { + return vorrq_u32(vshrq_n_u32(x, 7), vshlq_n_u32(x, 32 - 7)); +} + +// TODO: compress_neon + +// TODO: hash2_neon + +/* + * ---------------------------------------------------------------------------- + * hash4_neon + * ---------------------------------------------------------------------------- + */ + +INLINE void round_fn4(uint32x4_t v[16], uint32x4_t m[16], size_t r) { + v[0] = add_128(v[0], m[(size_t)MSG_SCHEDULE[r][0]]); + v[1] = add_128(v[1], m[(size_t)MSG_SCHEDULE[r][2]]); + v[2] = add_128(v[2], m[(size_t)MSG_SCHEDULE[r][4]]); + v[3] = add_128(v[3], m[(size_t)MSG_SCHEDULE[r][6]]); + v[0] = add_128(v[0], v[4]); + v[1] = add_128(v[1], v[5]); + v[2] = add_128(v[2], v[6]); + v[3] = add_128(v[3], v[7]); + v[12] = xor_128(v[12], v[0]); + v[13] = xor_128(v[13], v[1]); + v[14] = xor_128(v[14], v[2]); + v[15] = xor_128(v[15], v[3]); + v[12] = rot16_128(v[12]); + v[13] = rot16_128(v[13]); + v[14] = rot16_128(v[14]); + v[15] = rot16_128(v[15]); + v[8] = add_128(v[8], v[12]); + v[9] = add_128(v[9], v[13]); + v[10] = add_128(v[10], v[14]); + v[11] = add_128(v[11], v[15]); + v[4] = xor_128(v[4], v[8]); + v[5] = xor_128(v[5], v[9]); + v[6] = xor_128(v[6], v[10]); + v[7] = xor_128(v[7], v[11]); + v[4] = rot12_128(v[4]); + v[5] = rot12_128(v[5]); + v[6] = rot12_128(v[6]); + v[7] = rot12_128(v[7]); + v[0] = add_128(v[0], m[(size_t)MSG_SCHEDULE[r][1]]); + v[1] = add_128(v[1], m[(size_t)MSG_SCHEDULE[r][3]]); + v[2] = add_128(v[2], m[(size_t)MSG_SCHEDULE[r][5]]); + v[3] = add_128(v[3], m[(size_t)MSG_SCHEDULE[r][7]]); + v[0] = add_128(v[0], v[4]); + v[1] = add_128(v[1], v[5]); + v[2] = add_128(v[2], v[6]); + v[3] = add_128(v[3], v[7]); + v[12] = xor_128(v[12], v[0]); + v[13] = xor_128(v[13], v[1]); + v[14] = xor_128(v[14], v[2]); + v[15] = xor_128(v[15], v[3]); + v[12] = rot8_128(v[12]); + v[13] = rot8_128(v[13]); + v[14] = rot8_128(v[14]); + v[15] = rot8_128(v[15]); + v[8] = add_128(v[8], v[12]); + v[9] = add_128(v[9], v[13]); + v[10] = add_128(v[10], v[14]); + v[11] = add_128(v[11], v[15]); + v[4] = xor_128(v[4], v[8]); + v[5] = xor_128(v[5], v[9]); + v[6] = xor_128(v[6], v[10]); + v[7] = xor_128(v[7], v[11]); + v[4] = rot7_128(v[4]); + v[5] = rot7_128(v[5]); + v[6] = rot7_128(v[6]); + v[7] = rot7_128(v[7]); + + v[0] = add_128(v[0], m[(size_t)MSG_SCHEDULE[r][8]]); + v[1] = add_128(v[1], m[(size_t)MSG_SCHEDULE[r][10]]); + v[2] = add_128(v[2], m[(size_t)MSG_SCHEDULE[r][12]]); + v[3] = add_128(v[3], m[(size_t)MSG_SCHEDULE[r][14]]); + v[0] = add_128(v[0], v[5]); + v[1] = add_128(v[1], v[6]); + v[2] = add_128(v[2], v[7]); + v[3] = add_128(v[3], v[4]); + v[15] = xor_128(v[15], v[0]); + v[12] = xor_128(v[12], v[1]); + v[13] = xor_128(v[13], v[2]); + v[14] = xor_128(v[14], v[3]); + v[15] = rot16_128(v[15]); + v[12] = rot16_128(v[12]); + v[13] = rot16_128(v[13]); + v[14] = rot16_128(v[14]); + v[10] = add_128(v[10], v[15]); + v[11] = add_128(v[11], v[12]); + v[8] = add_128(v[8], v[13]); + v[9] = add_128(v[9], v[14]); + v[5] = xor_128(v[5], v[10]); + v[6] = xor_128(v[6], v[11]); + v[7] = xor_128(v[7], v[8]); + v[4] = xor_128(v[4], v[9]); + v[5] = rot12_128(v[5]); + v[6] = rot12_128(v[6]); + v[7] = rot12_128(v[7]); + v[4] = rot12_128(v[4]); + v[0] = add_128(v[0], m[(size_t)MSG_SCHEDULE[r][9]]); + v[1] = add_128(v[1], m[(size_t)MSG_SCHEDULE[r][11]]); + v[2] = add_128(v[2], m[(size_t)MSG_SCHEDULE[r][13]]); + v[3] = add_128(v[3], m[(size_t)MSG_SCHEDULE[r][15]]); + v[0] = add_128(v[0], v[5]); + v[1] = add_128(v[1], v[6]); + v[2] = add_128(v[2], v[7]); + v[3] = add_128(v[3], v[4]); + v[15] = xor_128(v[15], v[0]); + v[12] = xor_128(v[12], v[1]); + v[13] = xor_128(v[13], v[2]); + v[14] = xor_128(v[14], v[3]); + v[15] = rot8_128(v[15]); + v[12] = rot8_128(v[12]); + v[13] = rot8_128(v[13]); + v[14] = rot8_128(v[14]); + v[10] = add_128(v[10], v[15]); + v[11] = add_128(v[11], v[12]); + v[8] = add_128(v[8], v[13]); + v[9] = add_128(v[9], v[14]); + v[5] = xor_128(v[5], v[10]); + v[6] = xor_128(v[6], v[11]); + v[7] = xor_128(v[7], v[8]); + v[4] = xor_128(v[4], v[9]); + v[5] = rot7_128(v[5]); + v[6] = rot7_128(v[6]); + v[7] = rot7_128(v[7]); + v[4] = rot7_128(v[4]); +} + +INLINE void transpose_vecs_128(uint32x4_t vecs[4]) { + // Individually transpose the four 2x2 sub-matrices in each corner. + uint32x4x2_t rows01 = vtrnq_u32(vecs[0], vecs[1]); + uint32x4x2_t rows23 = vtrnq_u32(vecs[2], vecs[3]); + + // Swap the top-right and bottom-left 2x2s (which just got transposed). + vecs[0] = + vcombine_u32(vget_low_u32(rows01.val[0]), vget_low_u32(rows23.val[0])); + vecs[1] = + vcombine_u32(vget_low_u32(rows01.val[1]), vget_low_u32(rows23.val[1])); + vecs[2] = + vcombine_u32(vget_high_u32(rows01.val[0]), vget_high_u32(rows23.val[0])); + vecs[3] = + vcombine_u32(vget_high_u32(rows01.val[1]), vget_high_u32(rows23.val[1])); +} + +INLINE void transpose_msg_vecs4(const uint8_t *const *inputs, + size_t block_offset, uint32x4_t out[16]) { + out[0] = loadu_128(&inputs[0][block_offset + 0 * sizeof(uint32x4_t)]); + out[1] = loadu_128(&inputs[1][block_offset + 0 * sizeof(uint32x4_t)]); + out[2] = loadu_128(&inputs[2][block_offset + 0 * sizeof(uint32x4_t)]); + out[3] = loadu_128(&inputs[3][block_offset + 0 * sizeof(uint32x4_t)]); + out[4] = loadu_128(&inputs[0][block_offset + 1 * sizeof(uint32x4_t)]); + out[5] = loadu_128(&inputs[1][block_offset + 1 * sizeof(uint32x4_t)]); + out[6] = loadu_128(&inputs[2][block_offset + 1 * sizeof(uint32x4_t)]); + out[7] = loadu_128(&inputs[3][block_offset + 1 * sizeof(uint32x4_t)]); + out[8] = loadu_128(&inputs[0][block_offset + 2 * sizeof(uint32x4_t)]); + out[9] = loadu_128(&inputs[1][block_offset + 2 * sizeof(uint32x4_t)]); + out[10] = loadu_128(&inputs[2][block_offset + 2 * sizeof(uint32x4_t)]); + out[11] = loadu_128(&inputs[3][block_offset + 2 * sizeof(uint32x4_t)]); + out[12] = loadu_128(&inputs[0][block_offset + 3 * sizeof(uint32x4_t)]); + out[13] = loadu_128(&inputs[1][block_offset + 3 * sizeof(uint32x4_t)]); + out[14] = loadu_128(&inputs[2][block_offset + 3 * sizeof(uint32x4_t)]); + out[15] = loadu_128(&inputs[3][block_offset + 3 * sizeof(uint32x4_t)]); + transpose_vecs_128(&out[0]); + transpose_vecs_128(&out[4]); + transpose_vecs_128(&out[8]); + transpose_vecs_128(&out[12]); +} + +INLINE void load_counters4(uint64_t counter, bool increment_counter, + uint32x4_t *out_low, uint32x4_t *out_high) { + uint64_t mask = (increment_counter ? ~0 : 0); + *out_low = set4( + counter_low(counter + (mask & 0)), counter_low(counter + (mask & 1)), + counter_low(counter + (mask & 2)), counter_low(counter + (mask & 3))); + *out_high = set4( + counter_high(counter + (mask & 0)), counter_high(counter + (mask & 1)), + counter_high(counter + (mask & 2)), counter_high(counter + (mask & 3))); +} + +void blake3_hash4_neon(const uint8_t *const *inputs, size_t blocks, + const uint32_t key[8], uint64_t counter, + bool increment_counter, uint8_t flags, + uint8_t flags_start, uint8_t flags_end, uint8_t *out) { + uint32x4_t h_vecs[8] = { + set1_128(key[0]), set1_128(key[1]), set1_128(key[2]), set1_128(key[3]), + set1_128(key[4]), set1_128(key[5]), set1_128(key[6]), set1_128(key[7]), + }; + uint32x4_t counter_low_vec, counter_high_vec; + load_counters4(counter, increment_counter, &counter_low_vec, + &counter_high_vec); + uint8_t block_flags = flags | flags_start; + + for (size_t block = 0; block < blocks; block++) { + if (block + 1 == blocks) { + block_flags |= flags_end; + } + uint32x4_t block_len_vec = set1_128(BLAKE3_BLOCK_LEN); + uint32x4_t block_flags_vec = set1_128(block_flags); + uint32x4_t msg_vecs[16]; + transpose_msg_vecs4(inputs, block * BLAKE3_BLOCK_LEN, msg_vecs); + + uint32x4_t v[16] = { + h_vecs[0], h_vecs[1], h_vecs[2], h_vecs[3], + h_vecs[4], h_vecs[5], h_vecs[6], h_vecs[7], + set1_128(IV[0]), set1_128(IV[1]), set1_128(IV[2]), set1_128(IV[3]), + counter_low_vec, counter_high_vec, block_len_vec, block_flags_vec, + }; + round_fn4(v, msg_vecs, 0); + round_fn4(v, msg_vecs, 1); + round_fn4(v, msg_vecs, 2); + round_fn4(v, msg_vecs, 3); + round_fn4(v, msg_vecs, 4); + round_fn4(v, msg_vecs, 5); + round_fn4(v, msg_vecs, 6); + h_vecs[0] = xor_128(v[0], v[8]); + h_vecs[1] = xor_128(v[1], v[9]); + h_vecs[2] = xor_128(v[2], v[10]); + h_vecs[3] = xor_128(v[3], v[11]); + h_vecs[4] = xor_128(v[4], v[12]); + h_vecs[5] = xor_128(v[5], v[13]); + h_vecs[6] = xor_128(v[6], v[14]); + h_vecs[7] = xor_128(v[7], v[15]); + + block_flags = flags; + } + + transpose_vecs_128(&h_vecs[0]); + transpose_vecs_128(&h_vecs[4]); + // The first four vecs now contain the first half of each output, and the + // second four vecs contain the second half of each output. + storeu_128(h_vecs[0], &out[0 * sizeof(uint32x4_t)]); + storeu_128(h_vecs[4], &out[1 * sizeof(uint32x4_t)]); + storeu_128(h_vecs[1], &out[2 * sizeof(uint32x4_t)]); + storeu_128(h_vecs[5], &out[3 * sizeof(uint32x4_t)]); + storeu_128(h_vecs[2], &out[4 * sizeof(uint32x4_t)]); + storeu_128(h_vecs[6], &out[5 * sizeof(uint32x4_t)]); + storeu_128(h_vecs[3], &out[6 * sizeof(uint32x4_t)]); + storeu_128(h_vecs[7], &out[7 * sizeof(uint32x4_t)]); +} + +/* + * ---------------------------------------------------------------------------- + * hash_many_neon + * ---------------------------------------------------------------------------- + */ + +void blake3_compress_in_place_portable(uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, + uint8_t flags); + +INLINE void hash_one_neon(const uint8_t *input, size_t blocks, + const uint32_t key[8], uint64_t counter, + uint8_t flags, uint8_t flags_start, uint8_t flags_end, + uint8_t out[BLAKE3_OUT_LEN]) { + uint32_t cv[8]; + memcpy(cv, key, BLAKE3_KEY_LEN); + uint8_t block_flags = flags | flags_start; + while (blocks > 0) { + if (blocks == 1) { + block_flags |= flags_end; + } + // TODO: Implement compress_neon. However note that according to + // https://github.com/BLAKE2/BLAKE2/commit/7965d3e6e1b4193438b8d3a656787587d2579227, + // compress_neon might not be any faster than compress_portable. + blake3_compress_in_place_portable(cv, input, BLAKE3_BLOCK_LEN, counter, + block_flags); + input = &input[BLAKE3_BLOCK_LEN]; + blocks -= 1; + block_flags = flags; + } + memcpy(out, cv, BLAKE3_OUT_LEN); +} + +void blake3_hash_many_neon(const uint8_t *const *inputs, size_t num_inputs, + size_t blocks, const uint32_t key[8], + uint64_t counter, bool increment_counter, + uint8_t flags, uint8_t flags_start, + uint8_t flags_end, uint8_t *out) { + while (num_inputs >= 4) { + blake3_hash4_neon(inputs, blocks, key, counter, increment_counter, flags, + flags_start, flags_end, out); + if (increment_counter) { + counter += 4; + } + inputs += 4; + num_inputs -= 4; + out = &out[4 * BLAKE3_OUT_LEN]; + } + while (num_inputs > 0) { + hash_one_neon(inputs[0], blocks, key, counter, flags, flags_start, + flags_end, out); + if (increment_counter) { + counter += 1; + } + inputs += 1; + num_inputs -= 1; + out = &out[BLAKE3_OUT_LEN]; + } +} diff --git a/src/third_party/blake3/blake3_portable.c b/src/third_party/blake3/blake3_portable.c new file mode 100644 index 0000000..062dd1b --- /dev/null +++ b/src/third_party/blake3/blake3_portable.c @@ -0,0 +1,160 @@ +#include "blake3_impl.h" +#include + +INLINE uint32_t rotr32(uint32_t w, uint32_t c) { + return (w >> c) | (w << (32 - c)); +} + +INLINE void g(uint32_t *state, size_t a, size_t b, size_t c, size_t d, + uint32_t x, uint32_t y) { + state[a] = state[a] + state[b] + x; + state[d] = rotr32(state[d] ^ state[a], 16); + state[c] = state[c] + state[d]; + state[b] = rotr32(state[b] ^ state[c], 12); + state[a] = state[a] + state[b] + y; + state[d] = rotr32(state[d] ^ state[a], 8); + state[c] = state[c] + state[d]; + state[b] = rotr32(state[b] ^ state[c], 7); +} + +INLINE void round_fn(uint32_t state[16], const uint32_t *msg, size_t round) { + // Select the message schedule based on the round. + const uint8_t *schedule = MSG_SCHEDULE[round]; + + // Mix the columns. + g(state, 0, 4, 8, 12, msg[schedule[0]], msg[schedule[1]]); + g(state, 1, 5, 9, 13, msg[schedule[2]], msg[schedule[3]]); + g(state, 2, 6, 10, 14, msg[schedule[4]], msg[schedule[5]]); + g(state, 3, 7, 11, 15, msg[schedule[6]], msg[schedule[7]]); + + // Mix the rows. + g(state, 0, 5, 10, 15, msg[schedule[8]], msg[schedule[9]]); + g(state, 1, 6, 11, 12, msg[schedule[10]], msg[schedule[11]]); + g(state, 2, 7, 8, 13, msg[schedule[12]], msg[schedule[13]]); + g(state, 3, 4, 9, 14, msg[schedule[14]], msg[schedule[15]]); +} + +INLINE void compress_pre(uint32_t state[16], const uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, uint8_t flags) { + uint32_t block_words[16]; + block_words[0] = load32(block + 4 * 0); + block_words[1] = load32(block + 4 * 1); + block_words[2] = load32(block + 4 * 2); + block_words[3] = load32(block + 4 * 3); + block_words[4] = load32(block + 4 * 4); + block_words[5] = load32(block + 4 * 5); + block_words[6] = load32(block + 4 * 6); + block_words[7] = load32(block + 4 * 7); + block_words[8] = load32(block + 4 * 8); + block_words[9] = load32(block + 4 * 9); + block_words[10] = load32(block + 4 * 10); + block_words[11] = load32(block + 4 * 11); + block_words[12] = load32(block + 4 * 12); + block_words[13] = load32(block + 4 * 13); + block_words[14] = load32(block + 4 * 14); + block_words[15] = load32(block + 4 * 15); + + state[0] = cv[0]; + state[1] = cv[1]; + state[2] = cv[2]; + state[3] = cv[3]; + state[4] = cv[4]; + state[5] = cv[5]; + state[6] = cv[6]; + state[7] = cv[7]; + state[8] = IV[0]; + state[9] = IV[1]; + state[10] = IV[2]; + state[11] = IV[3]; + state[12] = counter_low(counter); + state[13] = counter_high(counter); + state[14] = (uint32_t)block_len; + state[15] = (uint32_t)flags; + + round_fn(state, &block_words[0], 0); + round_fn(state, &block_words[0], 1); + round_fn(state, &block_words[0], 2); + round_fn(state, &block_words[0], 3); + round_fn(state, &block_words[0], 4); + round_fn(state, &block_words[0], 5); + round_fn(state, &block_words[0], 6); +} + +void blake3_compress_in_place_portable(uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, + uint8_t flags) { + uint32_t state[16]; + compress_pre(state, cv, block, block_len, counter, flags); + cv[0] = state[0] ^ state[8]; + cv[1] = state[1] ^ state[9]; + cv[2] = state[2] ^ state[10]; + cv[3] = state[3] ^ state[11]; + cv[4] = state[4] ^ state[12]; + cv[5] = state[5] ^ state[13]; + cv[6] = state[6] ^ state[14]; + cv[7] = state[7] ^ state[15]; +} + +void blake3_compress_xof_portable(const uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, + uint8_t flags, uint8_t out[64]) { + uint32_t state[16]; + compress_pre(state, cv, block, block_len, counter, flags); + + store32(&out[0 * 4], state[0] ^ state[8]); + store32(&out[1 * 4], state[1] ^ state[9]); + store32(&out[2 * 4], state[2] ^ state[10]); + store32(&out[3 * 4], state[3] ^ state[11]); + store32(&out[4 * 4], state[4] ^ state[12]); + store32(&out[5 * 4], state[5] ^ state[13]); + store32(&out[6 * 4], state[6] ^ state[14]); + store32(&out[7 * 4], state[7] ^ state[15]); + store32(&out[8 * 4], state[8] ^ cv[0]); + store32(&out[9 * 4], state[9] ^ cv[1]); + store32(&out[10 * 4], state[10] ^ cv[2]); + store32(&out[11 * 4], state[11] ^ cv[3]); + store32(&out[12 * 4], state[12] ^ cv[4]); + store32(&out[13 * 4], state[13] ^ cv[5]); + store32(&out[14 * 4], state[14] ^ cv[6]); + store32(&out[15 * 4], state[15] ^ cv[7]); +} + +INLINE void hash_one_portable(const uint8_t *input, size_t blocks, + const uint32_t key[8], uint64_t counter, + uint8_t flags, uint8_t flags_start, + uint8_t flags_end, uint8_t out[BLAKE3_OUT_LEN]) { + uint32_t cv[8]; + memcpy(cv, key, BLAKE3_KEY_LEN); + uint8_t block_flags = flags | flags_start; + while (blocks > 0) { + if (blocks == 1) { + block_flags |= flags_end; + } + blake3_compress_in_place_portable(cv, input, BLAKE3_BLOCK_LEN, counter, + block_flags); + input = &input[BLAKE3_BLOCK_LEN]; + blocks -= 1; + block_flags = flags; + } + store_cv_words(out, cv); +} + +void blake3_hash_many_portable(const uint8_t *const *inputs, size_t num_inputs, + size_t blocks, const uint32_t key[8], + uint64_t counter, bool increment_counter, + uint8_t flags, uint8_t flags_start, + uint8_t flags_end, uint8_t *out) { + while (num_inputs > 0) { + hash_one_portable(inputs[0], blocks, key, counter, flags, flags_start, + flags_end, out); + if (increment_counter) { + counter += 1; + } + inputs += 1; + num_inputs -= 1; + out = &out[BLAKE3_OUT_LEN]; + } +} diff --git a/src/third_party/blake3/blake3_sse2.c b/src/third_party/blake3/blake3_sse2.c new file mode 100644 index 0000000..1592966 --- /dev/null +++ b/src/third_party/blake3/blake3_sse2.c @@ -0,0 +1,565 @@ +#include "blake3_impl.h" + +#include + +#define DEGREE 4 + +#define _mm_shuffle_ps2(a, b, c) \ + (_mm_castps_si128( \ + _mm_shuffle_ps(_mm_castsi128_ps(a), _mm_castsi128_ps(b), (c)))) + +INLINE __m128i loadu(const uint8_t src[16]) { + return _mm_loadu_si128((const __m128i *)src); +} + +INLINE void storeu(__m128i src, uint8_t dest[16]) { + _mm_storeu_si128((__m128i *)dest, src); +} + +INLINE __m128i addv(__m128i a, __m128i b) { return _mm_add_epi32(a, b); } + +// Note that clang-format doesn't like the name "xor" for some reason. +INLINE __m128i xorv(__m128i a, __m128i b) { return _mm_xor_si128(a, b); } + +INLINE __m128i set1(uint32_t x) { return _mm_set1_epi32((int32_t)x); } + +INLINE __m128i set4(uint32_t a, uint32_t b, uint32_t c, uint32_t d) { + return _mm_setr_epi32((int32_t)a, (int32_t)b, (int32_t)c, (int32_t)d); +} + +INLINE __m128i rot16(__m128i x) { + return _mm_shufflehi_epi16(_mm_shufflelo_epi16(x, 0xB1), 0xB1); +} + +INLINE __m128i rot12(__m128i x) { + return xorv(_mm_srli_epi32(x, 12), _mm_slli_epi32(x, 32 - 12)); +} + +INLINE __m128i rot8(__m128i x) { + return xorv(_mm_srli_epi32(x, 8), _mm_slli_epi32(x, 32 - 8)); +} + +INLINE __m128i rot7(__m128i x) { + return xorv(_mm_srli_epi32(x, 7), _mm_slli_epi32(x, 32 - 7)); +} + +INLINE void g1(__m128i *row0, __m128i *row1, __m128i *row2, __m128i *row3, + __m128i m) { + *row0 = addv(addv(*row0, m), *row1); + *row3 = xorv(*row3, *row0); + *row3 = rot16(*row3); + *row2 = addv(*row2, *row3); + *row1 = xorv(*row1, *row2); + *row1 = rot12(*row1); +} + +INLINE void g2(__m128i *row0, __m128i *row1, __m128i *row2, __m128i *row3, + __m128i m) { + *row0 = addv(addv(*row0, m), *row1); + *row3 = xorv(*row3, *row0); + *row3 = rot8(*row3); + *row2 = addv(*row2, *row3); + *row1 = xorv(*row1, *row2); + *row1 = rot7(*row1); +} + +// Note the optimization here of leaving row1 as the unrotated row, rather than +// row0. All the message loads below are adjusted to compensate for this. See +// discussion at https://github.com/sneves/blake2-avx2/pull/4 +INLINE void diagonalize(__m128i *row0, __m128i *row2, __m128i *row3) { + *row0 = _mm_shuffle_epi32(*row0, _MM_SHUFFLE(2, 1, 0, 3)); + *row3 = _mm_shuffle_epi32(*row3, _MM_SHUFFLE(1, 0, 3, 2)); + *row2 = _mm_shuffle_epi32(*row2, _MM_SHUFFLE(0, 3, 2, 1)); +} + +INLINE void undiagonalize(__m128i *row0, __m128i *row2, __m128i *row3) { + *row0 = _mm_shuffle_epi32(*row0, _MM_SHUFFLE(0, 3, 2, 1)); + *row3 = _mm_shuffle_epi32(*row3, _MM_SHUFFLE(1, 0, 3, 2)); + *row2 = _mm_shuffle_epi32(*row2, _MM_SHUFFLE(2, 1, 0, 3)); +} + +INLINE __m128i blend_epi16(__m128i a, __m128i b, const int imm8) { + const __m128i bits = _mm_set_epi16(0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01); + __m128i mask = _mm_set1_epi16(imm8); + mask = _mm_and_si128(mask, bits); + mask = _mm_cmpeq_epi16(mask, bits); + return _mm_or_si128(_mm_and_si128(mask, b), _mm_andnot_si128(mask, a)); +} + +INLINE void compress_pre(__m128i rows[4], const uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, uint8_t flags) { + rows[0] = loadu((uint8_t *)&cv[0]); + rows[1] = loadu((uint8_t *)&cv[4]); + rows[2] = set4(IV[0], IV[1], IV[2], IV[3]); + rows[3] = set4(counter_low(counter), counter_high(counter), + (uint32_t)block_len, (uint32_t)flags); + + __m128i m0 = loadu(&block[sizeof(__m128i) * 0]); + __m128i m1 = loadu(&block[sizeof(__m128i) * 1]); + __m128i m2 = loadu(&block[sizeof(__m128i) * 2]); + __m128i m3 = loadu(&block[sizeof(__m128i) * 3]); + + __m128i t0, t1, t2, t3, tt; + + // Round 1. The first round permutes the message words from the original + // input order, into the groups that get mixed in parallel. + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(2, 0, 2, 0)); // 6 4 2 0 + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 3, 1)); // 7 5 3 1 + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(2, 0, 2, 0)); // 14 12 10 8 + t2 = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2, 1, 0, 3)); // 12 10 8 14 + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 1, 3, 1)); // 15 13 11 9 + t3 = _mm_shuffle_epi32(t3, _MM_SHUFFLE(2, 1, 0, 3)); // 13 11 9 15 + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); + m0 = t0; + m1 = t1; + m2 = t2; + m3 = t3; + + // Round 2. This round and all following rounds apply a fixed permutation + // to the message words from the round before. + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 1, 2)); + t0 = _mm_shuffle_epi32(t0, _MM_SHUFFLE(0, 3, 2, 1)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 3, 2, 2)); + tt = _mm_shuffle_epi32(m0, _MM_SHUFFLE(0, 0, 3, 3)); + t1 = blend_epi16(tt, t1, 0xCC); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_unpacklo_epi64(m3, m1); + tt = blend_epi16(t2, m2, 0xC0); + t2 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(1, 3, 2, 0)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_unpackhi_epi32(m1, m3); + tt = _mm_unpacklo_epi32(m2, t3); + t3 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(0, 1, 3, 2)); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); + m0 = t0; + m1 = t1; + m2 = t2; + m3 = t3; + + // Round 3 + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 1, 2)); + t0 = _mm_shuffle_epi32(t0, _MM_SHUFFLE(0, 3, 2, 1)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 3, 2, 2)); + tt = _mm_shuffle_epi32(m0, _MM_SHUFFLE(0, 0, 3, 3)); + t1 = blend_epi16(tt, t1, 0xCC); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_unpacklo_epi64(m3, m1); + tt = blend_epi16(t2, m2, 0xC0); + t2 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(1, 3, 2, 0)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_unpackhi_epi32(m1, m3); + tt = _mm_unpacklo_epi32(m2, t3); + t3 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(0, 1, 3, 2)); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); + m0 = t0; + m1 = t1; + m2 = t2; + m3 = t3; + + // Round 4 + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 1, 2)); + t0 = _mm_shuffle_epi32(t0, _MM_SHUFFLE(0, 3, 2, 1)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 3, 2, 2)); + tt = _mm_shuffle_epi32(m0, _MM_SHUFFLE(0, 0, 3, 3)); + t1 = blend_epi16(tt, t1, 0xCC); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_unpacklo_epi64(m3, m1); + tt = blend_epi16(t2, m2, 0xC0); + t2 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(1, 3, 2, 0)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_unpackhi_epi32(m1, m3); + tt = _mm_unpacklo_epi32(m2, t3); + t3 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(0, 1, 3, 2)); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); + m0 = t0; + m1 = t1; + m2 = t2; + m3 = t3; + + // Round 5 + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 1, 2)); + t0 = _mm_shuffle_epi32(t0, _MM_SHUFFLE(0, 3, 2, 1)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 3, 2, 2)); + tt = _mm_shuffle_epi32(m0, _MM_SHUFFLE(0, 0, 3, 3)); + t1 = blend_epi16(tt, t1, 0xCC); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_unpacklo_epi64(m3, m1); + tt = blend_epi16(t2, m2, 0xC0); + t2 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(1, 3, 2, 0)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_unpackhi_epi32(m1, m3); + tt = _mm_unpacklo_epi32(m2, t3); + t3 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(0, 1, 3, 2)); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); + m0 = t0; + m1 = t1; + m2 = t2; + m3 = t3; + + // Round 6 + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 1, 2)); + t0 = _mm_shuffle_epi32(t0, _MM_SHUFFLE(0, 3, 2, 1)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 3, 2, 2)); + tt = _mm_shuffle_epi32(m0, _MM_SHUFFLE(0, 0, 3, 3)); + t1 = blend_epi16(tt, t1, 0xCC); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_unpacklo_epi64(m3, m1); + tt = blend_epi16(t2, m2, 0xC0); + t2 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(1, 3, 2, 0)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_unpackhi_epi32(m1, m3); + tt = _mm_unpacklo_epi32(m2, t3); + t3 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(0, 1, 3, 2)); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); + m0 = t0; + m1 = t1; + m2 = t2; + m3 = t3; + + // Round 7 + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 1, 2)); + t0 = _mm_shuffle_epi32(t0, _MM_SHUFFLE(0, 3, 2, 1)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 3, 2, 2)); + tt = _mm_shuffle_epi32(m0, _MM_SHUFFLE(0, 0, 3, 3)); + t1 = blend_epi16(tt, t1, 0xCC); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_unpacklo_epi64(m3, m1); + tt = blend_epi16(t2, m2, 0xC0); + t2 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(1, 3, 2, 0)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_unpackhi_epi32(m1, m3); + tt = _mm_unpacklo_epi32(m2, t3); + t3 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(0, 1, 3, 2)); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); +} + +void blake3_compress_in_place_sse2(uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, + uint8_t flags) { + __m128i rows[4]; + compress_pre(rows, cv, block, block_len, counter, flags); + storeu(xorv(rows[0], rows[2]), (uint8_t *)&cv[0]); + storeu(xorv(rows[1], rows[3]), (uint8_t *)&cv[4]); +} + +void blake3_compress_xof_sse2(const uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, + uint8_t flags, uint8_t out[64]) { + __m128i rows[4]; + compress_pre(rows, cv, block, block_len, counter, flags); + storeu(xorv(rows[0], rows[2]), &out[0]); + storeu(xorv(rows[1], rows[3]), &out[16]); + storeu(xorv(rows[2], loadu((uint8_t *)&cv[0])), &out[32]); + storeu(xorv(rows[3], loadu((uint8_t *)&cv[4])), &out[48]); +} + +INLINE void round_fn(__m128i v[16], __m128i m[16], size_t r) { + v[0] = addv(v[0], m[(size_t)MSG_SCHEDULE[r][0]]); + v[1] = addv(v[1], m[(size_t)MSG_SCHEDULE[r][2]]); + v[2] = addv(v[2], m[(size_t)MSG_SCHEDULE[r][4]]); + v[3] = addv(v[3], m[(size_t)MSG_SCHEDULE[r][6]]); + v[0] = addv(v[0], v[4]); + v[1] = addv(v[1], v[5]); + v[2] = addv(v[2], v[6]); + v[3] = addv(v[3], v[7]); + v[12] = xorv(v[12], v[0]); + v[13] = xorv(v[13], v[1]); + v[14] = xorv(v[14], v[2]); + v[15] = xorv(v[15], v[3]); + v[12] = rot16(v[12]); + v[13] = rot16(v[13]); + v[14] = rot16(v[14]); + v[15] = rot16(v[15]); + v[8] = addv(v[8], v[12]); + v[9] = addv(v[9], v[13]); + v[10] = addv(v[10], v[14]); + v[11] = addv(v[11], v[15]); + v[4] = xorv(v[4], v[8]); + v[5] = xorv(v[5], v[9]); + v[6] = xorv(v[6], v[10]); + v[7] = xorv(v[7], v[11]); + v[4] = rot12(v[4]); + v[5] = rot12(v[5]); + v[6] = rot12(v[6]); + v[7] = rot12(v[7]); + v[0] = addv(v[0], m[(size_t)MSG_SCHEDULE[r][1]]); + v[1] = addv(v[1], m[(size_t)MSG_SCHEDULE[r][3]]); + v[2] = addv(v[2], m[(size_t)MSG_SCHEDULE[r][5]]); + v[3] = addv(v[3], m[(size_t)MSG_SCHEDULE[r][7]]); + v[0] = addv(v[0], v[4]); + v[1] = addv(v[1], v[5]); + v[2] = addv(v[2], v[6]); + v[3] = addv(v[3], v[7]); + v[12] = xorv(v[12], v[0]); + v[13] = xorv(v[13], v[1]); + v[14] = xorv(v[14], v[2]); + v[15] = xorv(v[15], v[3]); + v[12] = rot8(v[12]); + v[13] = rot8(v[13]); + v[14] = rot8(v[14]); + v[15] = rot8(v[15]); + v[8] = addv(v[8], v[12]); + v[9] = addv(v[9], v[13]); + v[10] = addv(v[10], v[14]); + v[11] = addv(v[11], v[15]); + v[4] = xorv(v[4], v[8]); + v[5] = xorv(v[5], v[9]); + v[6] = xorv(v[6], v[10]); + v[7] = xorv(v[7], v[11]); + v[4] = rot7(v[4]); + v[5] = rot7(v[5]); + v[6] = rot7(v[6]); + v[7] = rot7(v[7]); + + v[0] = addv(v[0], m[(size_t)MSG_SCHEDULE[r][8]]); + v[1] = addv(v[1], m[(size_t)MSG_SCHEDULE[r][10]]); + v[2] = addv(v[2], m[(size_t)MSG_SCHEDULE[r][12]]); + v[3] = addv(v[3], m[(size_t)MSG_SCHEDULE[r][14]]); + v[0] = addv(v[0], v[5]); + v[1] = addv(v[1], v[6]); + v[2] = addv(v[2], v[7]); + v[3] = addv(v[3], v[4]); + v[15] = xorv(v[15], v[0]); + v[12] = xorv(v[12], v[1]); + v[13] = xorv(v[13], v[2]); + v[14] = xorv(v[14], v[3]); + v[15] = rot16(v[15]); + v[12] = rot16(v[12]); + v[13] = rot16(v[13]); + v[14] = rot16(v[14]); + v[10] = addv(v[10], v[15]); + v[11] = addv(v[11], v[12]); + v[8] = addv(v[8], v[13]); + v[9] = addv(v[9], v[14]); + v[5] = xorv(v[5], v[10]); + v[6] = xorv(v[6], v[11]); + v[7] = xorv(v[7], v[8]); + v[4] = xorv(v[4], v[9]); + v[5] = rot12(v[5]); + v[6] = rot12(v[6]); + v[7] = rot12(v[7]); + v[4] = rot12(v[4]); + v[0] = addv(v[0], m[(size_t)MSG_SCHEDULE[r][9]]); + v[1] = addv(v[1], m[(size_t)MSG_SCHEDULE[r][11]]); + v[2] = addv(v[2], m[(size_t)MSG_SCHEDULE[r][13]]); + v[3] = addv(v[3], m[(size_t)MSG_SCHEDULE[r][15]]); + v[0] = addv(v[0], v[5]); + v[1] = addv(v[1], v[6]); + v[2] = addv(v[2], v[7]); + v[3] = addv(v[3], v[4]); + v[15] = xorv(v[15], v[0]); + v[12] = xorv(v[12], v[1]); + v[13] = xorv(v[13], v[2]); + v[14] = xorv(v[14], v[3]); + v[15] = rot8(v[15]); + v[12] = rot8(v[12]); + v[13] = rot8(v[13]); + v[14] = rot8(v[14]); + v[10] = addv(v[10], v[15]); + v[11] = addv(v[11], v[12]); + v[8] = addv(v[8], v[13]); + v[9] = addv(v[9], v[14]); + v[5] = xorv(v[5], v[10]); + v[6] = xorv(v[6], v[11]); + v[7] = xorv(v[7], v[8]); + v[4] = xorv(v[4], v[9]); + v[5] = rot7(v[5]); + v[6] = rot7(v[6]); + v[7] = rot7(v[7]); + v[4] = rot7(v[4]); +} + +INLINE void transpose_vecs(__m128i vecs[DEGREE]) { + // Interleave 32-bit lates. The low unpack is lanes 00/11 and the high is + // 22/33. Note that this doesn't split the vector into two lanes, as the + // AVX2 counterparts do. + __m128i ab_01 = _mm_unpacklo_epi32(vecs[0], vecs[1]); + __m128i ab_23 = _mm_unpackhi_epi32(vecs[0], vecs[1]); + __m128i cd_01 = _mm_unpacklo_epi32(vecs[2], vecs[3]); + __m128i cd_23 = _mm_unpackhi_epi32(vecs[2], vecs[3]); + + // Interleave 64-bit lanes. + __m128i abcd_0 = _mm_unpacklo_epi64(ab_01, cd_01); + __m128i abcd_1 = _mm_unpackhi_epi64(ab_01, cd_01); + __m128i abcd_2 = _mm_unpacklo_epi64(ab_23, cd_23); + __m128i abcd_3 = _mm_unpackhi_epi64(ab_23, cd_23); + + vecs[0] = abcd_0; + vecs[1] = abcd_1; + vecs[2] = abcd_2; + vecs[3] = abcd_3; +} + +INLINE void transpose_msg_vecs(const uint8_t *const *inputs, + size_t block_offset, __m128i out[16]) { + out[0] = loadu(&inputs[0][block_offset + 0 * sizeof(__m128i)]); + out[1] = loadu(&inputs[1][block_offset + 0 * sizeof(__m128i)]); + out[2] = loadu(&inputs[2][block_offset + 0 * sizeof(__m128i)]); + out[3] = loadu(&inputs[3][block_offset + 0 * sizeof(__m128i)]); + out[4] = loadu(&inputs[0][block_offset + 1 * sizeof(__m128i)]); + out[5] = loadu(&inputs[1][block_offset + 1 * sizeof(__m128i)]); + out[6] = loadu(&inputs[2][block_offset + 1 * sizeof(__m128i)]); + out[7] = loadu(&inputs[3][block_offset + 1 * sizeof(__m128i)]); + out[8] = loadu(&inputs[0][block_offset + 2 * sizeof(__m128i)]); + out[9] = loadu(&inputs[1][block_offset + 2 * sizeof(__m128i)]); + out[10] = loadu(&inputs[2][block_offset + 2 * sizeof(__m128i)]); + out[11] = loadu(&inputs[3][block_offset + 2 * sizeof(__m128i)]); + out[12] = loadu(&inputs[0][block_offset + 3 * sizeof(__m128i)]); + out[13] = loadu(&inputs[1][block_offset + 3 * sizeof(__m128i)]); + out[14] = loadu(&inputs[2][block_offset + 3 * sizeof(__m128i)]); + out[15] = loadu(&inputs[3][block_offset + 3 * sizeof(__m128i)]); + for (size_t i = 0; i < 4; ++i) { + _mm_prefetch(&inputs[i][block_offset + 256], _MM_HINT_T0); + } + transpose_vecs(&out[0]); + transpose_vecs(&out[4]); + transpose_vecs(&out[8]); + transpose_vecs(&out[12]); +} + +INLINE void load_counters(uint64_t counter, bool increment_counter, + __m128i *out_lo, __m128i *out_hi) { + const __m128i mask = _mm_set1_epi32(-(int32_t)increment_counter); + const __m128i add0 = _mm_set_epi32(3, 2, 1, 0); + const __m128i add1 = _mm_and_si128(mask, add0); + __m128i l = _mm_add_epi32(_mm_set1_epi32(counter), add1); + __m128i carry = _mm_cmpgt_epi32(_mm_xor_si128(add1, _mm_set1_epi32(0x80000000)), + _mm_xor_si128( l, _mm_set1_epi32(0x80000000))); + __m128i h = _mm_sub_epi32(_mm_set1_epi32(counter >> 32), carry); + *out_lo = l; + *out_hi = h; +} + +void blake3_hash4_sse2(const uint8_t *const *inputs, size_t blocks, + const uint32_t key[8], uint64_t counter, + bool increment_counter, uint8_t flags, + uint8_t flags_start, uint8_t flags_end, uint8_t *out) { + __m128i h_vecs[8] = { + set1(key[0]), set1(key[1]), set1(key[2]), set1(key[3]), + set1(key[4]), set1(key[5]), set1(key[6]), set1(key[7]), + }; + __m128i counter_low_vec, counter_high_vec; + load_counters(counter, increment_counter, &counter_low_vec, + &counter_high_vec); + uint8_t block_flags = flags | flags_start; + + for (size_t block = 0; block < blocks; block++) { + if (block + 1 == blocks) { + block_flags |= flags_end; + } + __m128i block_len_vec = set1(BLAKE3_BLOCK_LEN); + __m128i block_flags_vec = set1(block_flags); + __m128i msg_vecs[16]; + transpose_msg_vecs(inputs, block * BLAKE3_BLOCK_LEN, msg_vecs); + + __m128i v[16] = { + h_vecs[0], h_vecs[1], h_vecs[2], h_vecs[3], + h_vecs[4], h_vecs[5], h_vecs[6], h_vecs[7], + set1(IV[0]), set1(IV[1]), set1(IV[2]), set1(IV[3]), + counter_low_vec, counter_high_vec, block_len_vec, block_flags_vec, + }; + round_fn(v, msg_vecs, 0); + round_fn(v, msg_vecs, 1); + round_fn(v, msg_vecs, 2); + round_fn(v, msg_vecs, 3); + round_fn(v, msg_vecs, 4); + round_fn(v, msg_vecs, 5); + round_fn(v, msg_vecs, 6); + h_vecs[0] = xorv(v[0], v[8]); + h_vecs[1] = xorv(v[1], v[9]); + h_vecs[2] = xorv(v[2], v[10]); + h_vecs[3] = xorv(v[3], v[11]); + h_vecs[4] = xorv(v[4], v[12]); + h_vecs[5] = xorv(v[5], v[13]); + h_vecs[6] = xorv(v[6], v[14]); + h_vecs[7] = xorv(v[7], v[15]); + + block_flags = flags; + } + + transpose_vecs(&h_vecs[0]); + transpose_vecs(&h_vecs[4]); + // The first four vecs now contain the first half of each output, and the + // second four vecs contain the second half of each output. + storeu(h_vecs[0], &out[0 * sizeof(__m128i)]); + storeu(h_vecs[4], &out[1 * sizeof(__m128i)]); + storeu(h_vecs[1], &out[2 * sizeof(__m128i)]); + storeu(h_vecs[5], &out[3 * sizeof(__m128i)]); + storeu(h_vecs[2], &out[4 * sizeof(__m128i)]); + storeu(h_vecs[6], &out[5 * sizeof(__m128i)]); + storeu(h_vecs[3], &out[6 * sizeof(__m128i)]); + storeu(h_vecs[7], &out[7 * sizeof(__m128i)]); +} + +INLINE void hash_one_sse2(const uint8_t *input, size_t blocks, + const uint32_t key[8], uint64_t counter, + uint8_t flags, uint8_t flags_start, + uint8_t flags_end, uint8_t out[BLAKE3_OUT_LEN]) { + uint32_t cv[8]; + memcpy(cv, key, BLAKE3_KEY_LEN); + uint8_t block_flags = flags | flags_start; + while (blocks > 0) { + if (blocks == 1) { + block_flags |= flags_end; + } + blake3_compress_in_place_sse2(cv, input, BLAKE3_BLOCK_LEN, counter, + block_flags); + input = &input[BLAKE3_BLOCK_LEN]; + blocks -= 1; + block_flags = flags; + } + memcpy(out, cv, BLAKE3_OUT_LEN); +} + +void blake3_hash_many_sse2(const uint8_t *const *inputs, size_t num_inputs, + size_t blocks, const uint32_t key[8], + uint64_t counter, bool increment_counter, + uint8_t flags, uint8_t flags_start, + uint8_t flags_end, uint8_t *out) { + while (num_inputs >= DEGREE) { + blake3_hash4_sse2(inputs, blocks, key, counter, increment_counter, flags, + flags_start, flags_end, out); + if (increment_counter) { + counter += DEGREE; + } + inputs += DEGREE; + num_inputs -= DEGREE; + out = &out[DEGREE * BLAKE3_OUT_LEN]; + } + while (num_inputs > 0) { + hash_one_sse2(inputs[0], blocks, key, counter, flags, flags_start, + flags_end, out); + if (increment_counter) { + counter += 1; + } + inputs += 1; + num_inputs -= 1; + out = &out[BLAKE3_OUT_LEN]; + } +} diff --git a/src/third_party/blake3/blake3_sse2_x86-64_unix.S b/src/third_party/blake3/blake3_sse2_x86-64_unix.S new file mode 100644 index 0000000..d144046 --- /dev/null +++ b/src/third_party/blake3/blake3_sse2_x86-64_unix.S @@ -0,0 +1,2291 @@ +#if defined(__ELF__) && defined(__linux__) +.section .note.GNU-stack,"",%progbits +#endif + +#if defined(__ELF__) && defined(__CET__) && defined(__has_include) +#if __has_include() +#include +#endif +#endif + +#if !defined(_CET_ENDBR) +#define _CET_ENDBR +#endif + +.intel_syntax noprefix +.global blake3_hash_many_sse2 +.global _blake3_hash_many_sse2 +.global blake3_compress_in_place_sse2 +.global _blake3_compress_in_place_sse2 +.global blake3_compress_xof_sse2 +.global _blake3_compress_xof_sse2 +#ifdef __APPLE__ +.text +#else +.section .text +#endif + .p2align 6 +_blake3_hash_many_sse2: +blake3_hash_many_sse2: + _CET_ENDBR + push r15 + push r14 + push r13 + push r12 + push rbx + push rbp + mov rbp, rsp + sub rsp, 360 + and rsp, 0xFFFFFFFFFFFFFFC0 + neg r9d + movd xmm0, r9d + pshufd xmm0, xmm0, 0x00 + movdqa xmmword ptr [rsp+0x130], xmm0 + movdqa xmm1, xmm0 + pand xmm1, xmmword ptr [ADD0+rip] + pand xmm0, xmmword ptr [ADD1+rip] + movdqa xmmword ptr [rsp+0x150], xmm0 + movd xmm0, r8d + pshufd xmm0, xmm0, 0x00 + paddd xmm0, xmm1 + movdqa xmmword ptr [rsp+0x110], xmm0 + pxor xmm0, xmmword ptr [CMP_MSB_MASK+rip] + pxor xmm1, xmmword ptr [CMP_MSB_MASK+rip] + pcmpgtd xmm1, xmm0 + shr r8, 32 + movd xmm2, r8d + pshufd xmm2, xmm2, 0x00 + psubd xmm2, xmm1 + movdqa xmmword ptr [rsp+0x120], xmm2 + mov rbx, qword ptr [rbp+0x50] + mov r15, rdx + shl r15, 6 + movzx r13d, byte ptr [rbp+0x38] + movzx r12d, byte ptr [rbp+0x48] + cmp rsi, 4 + jc 3f +2: + movdqu xmm3, xmmword ptr [rcx] + pshufd xmm0, xmm3, 0x00 + pshufd xmm1, xmm3, 0x55 + pshufd xmm2, xmm3, 0xAA + pshufd xmm3, xmm3, 0xFF + movdqu xmm7, xmmword ptr [rcx+0x10] + pshufd xmm4, xmm7, 0x00 + pshufd xmm5, xmm7, 0x55 + pshufd xmm6, xmm7, 0xAA + pshufd xmm7, xmm7, 0xFF + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + mov r10, qword ptr [rdi+0x10] + mov r11, qword ptr [rdi+0x18] + movzx eax, byte ptr [rbp+0x40] + or eax, r13d + xor edx, edx +9: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + movdqu xmm8, xmmword ptr [r8+rdx-0x40] + movdqu xmm9, xmmword ptr [r9+rdx-0x40] + movdqu xmm10, xmmword ptr [r10+rdx-0x40] + movdqu xmm11, xmmword ptr [r11+rdx-0x40] + movdqa xmm12, xmm8 + punpckldq xmm8, xmm9 + punpckhdq xmm12, xmm9 + movdqa xmm14, xmm10 + punpckldq xmm10, xmm11 + punpckhdq xmm14, xmm11 + movdqa xmm9, xmm8 + punpcklqdq xmm8, xmm10 + punpckhqdq xmm9, xmm10 + movdqa xmm13, xmm12 + punpcklqdq xmm12, xmm14 + punpckhqdq xmm13, xmm14 + movdqa xmmword ptr [rsp], xmm8 + movdqa xmmword ptr [rsp+0x10], xmm9 + movdqa xmmword ptr [rsp+0x20], xmm12 + movdqa xmmword ptr [rsp+0x30], xmm13 + movdqu xmm8, xmmword ptr [r8+rdx-0x30] + movdqu xmm9, xmmword ptr [r9+rdx-0x30] + movdqu xmm10, xmmword ptr [r10+rdx-0x30] + movdqu xmm11, xmmword ptr [r11+rdx-0x30] + movdqa xmm12, xmm8 + punpckldq xmm8, xmm9 + punpckhdq xmm12, xmm9 + movdqa xmm14, xmm10 + punpckldq xmm10, xmm11 + punpckhdq xmm14, xmm11 + movdqa xmm9, xmm8 + punpcklqdq xmm8, xmm10 + punpckhqdq xmm9, xmm10 + movdqa xmm13, xmm12 + punpcklqdq xmm12, xmm14 + punpckhqdq xmm13, xmm14 + movdqa xmmword ptr [rsp+0x40], xmm8 + movdqa xmmword ptr [rsp+0x50], xmm9 + movdqa xmmword ptr [rsp+0x60], xmm12 + movdqa xmmword ptr [rsp+0x70], xmm13 + movdqu xmm8, xmmword ptr [r8+rdx-0x20] + movdqu xmm9, xmmword ptr [r9+rdx-0x20] + movdqu xmm10, xmmword ptr [r10+rdx-0x20] + movdqu xmm11, xmmword ptr [r11+rdx-0x20] + movdqa xmm12, xmm8 + punpckldq xmm8, xmm9 + punpckhdq xmm12, xmm9 + movdqa xmm14, xmm10 + punpckldq xmm10, xmm11 + punpckhdq xmm14, xmm11 + movdqa xmm9, xmm8 + punpcklqdq xmm8, xmm10 + punpckhqdq xmm9, xmm10 + movdqa xmm13, xmm12 + punpcklqdq xmm12, xmm14 + punpckhqdq xmm13, xmm14 + movdqa xmmword ptr [rsp+0x80], xmm8 + movdqa xmmword ptr [rsp+0x90], xmm9 + movdqa xmmword ptr [rsp+0xA0], xmm12 + movdqa xmmword ptr [rsp+0xB0], xmm13 + movdqu xmm8, xmmword ptr [r8+rdx-0x10] + movdqu xmm9, xmmword ptr [r9+rdx-0x10] + movdqu xmm10, xmmword ptr [r10+rdx-0x10] + movdqu xmm11, xmmword ptr [r11+rdx-0x10] + movdqa xmm12, xmm8 + punpckldq xmm8, xmm9 + punpckhdq xmm12, xmm9 + movdqa xmm14, xmm10 + punpckldq xmm10, xmm11 + punpckhdq xmm14, xmm11 + movdqa xmm9, xmm8 + punpcklqdq xmm8, xmm10 + punpckhqdq xmm9, xmm10 + movdqa xmm13, xmm12 + punpcklqdq xmm12, xmm14 + punpckhqdq xmm13, xmm14 + movdqa xmmword ptr [rsp+0xC0], xmm8 + movdqa xmmword ptr [rsp+0xD0], xmm9 + movdqa xmmword ptr [rsp+0xE0], xmm12 + movdqa xmmword ptr [rsp+0xF0], xmm13 + movdqa xmm9, xmmword ptr [BLAKE3_IV_1+rip] + movdqa xmm10, xmmword ptr [BLAKE3_IV_2+rip] + movdqa xmm11, xmmword ptr [BLAKE3_IV_3+rip] + movdqa xmm12, xmmword ptr [rsp+0x110] + movdqa xmm13, xmmword ptr [rsp+0x120] + movdqa xmm14, xmmword ptr [BLAKE3_BLOCK_LEN+rip] + movd xmm15, eax + pshufd xmm15, xmm15, 0x00 + prefetcht0 [r8+rdx+0x80] + prefetcht0 [r9+rdx+0x80] + prefetcht0 [r10+rdx+0x80] + prefetcht0 [r11+rdx+0x80] + paddd xmm0, xmmword ptr [rsp] + paddd xmm1, xmmword ptr [rsp+0x20] + paddd xmm2, xmmword ptr [rsp+0x40] + paddd xmm3, xmmword ptr [rsp+0x60] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + movdqa xmm8, xmmword ptr [BLAKE3_IV_0+rip] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x10] + paddd xmm1, xmmword ptr [rsp+0x30] + paddd xmm2, xmmword ptr [rsp+0x50] + paddd xmm3, xmmword ptr [rsp+0x70] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x80] + paddd xmm1, xmmword ptr [rsp+0xA0] + paddd xmm2, xmmword ptr [rsp+0xC0] + paddd xmm3, xmmword ptr [rsp+0xE0] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x90] + paddd xmm1, xmmword ptr [rsp+0xB0] + paddd xmm2, xmmword ptr [rsp+0xD0] + paddd xmm3, xmmword ptr [rsp+0xF0] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x20] + paddd xmm1, xmmword ptr [rsp+0x30] + paddd xmm2, xmmword ptr [rsp+0x70] + paddd xmm3, xmmword ptr [rsp+0x40] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x60] + paddd xmm1, xmmword ptr [rsp+0xA0] + paddd xmm2, xmmword ptr [rsp] + paddd xmm3, xmmword ptr [rsp+0xD0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x10] + paddd xmm1, xmmword ptr [rsp+0xC0] + paddd xmm2, xmmword ptr [rsp+0x90] + paddd xmm3, xmmword ptr [rsp+0xF0] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0xB0] + paddd xmm1, xmmword ptr [rsp+0x50] + paddd xmm2, xmmword ptr [rsp+0xE0] + paddd xmm3, xmmword ptr [rsp+0x80] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x30] + paddd xmm1, xmmword ptr [rsp+0xA0] + paddd xmm2, xmmword ptr [rsp+0xD0] + paddd xmm3, xmmword ptr [rsp+0x70] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x40] + paddd xmm1, xmmword ptr [rsp+0xC0] + paddd xmm2, xmmword ptr [rsp+0x20] + paddd xmm3, xmmword ptr [rsp+0xE0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x60] + paddd xmm1, xmmword ptr [rsp+0x90] + paddd xmm2, xmmword ptr [rsp+0xB0] + paddd xmm3, xmmword ptr [rsp+0x80] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x50] + paddd xmm1, xmmword ptr [rsp] + paddd xmm2, xmmword ptr [rsp+0xF0] + paddd xmm3, xmmword ptr [rsp+0x10] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0xA0] + paddd xmm1, xmmword ptr [rsp+0xC0] + paddd xmm2, xmmword ptr [rsp+0xE0] + paddd xmm3, xmmword ptr [rsp+0xD0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x70] + paddd xmm1, xmmword ptr [rsp+0x90] + paddd xmm2, xmmword ptr [rsp+0x30] + paddd xmm3, xmmword ptr [rsp+0xF0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x40] + paddd xmm1, xmmword ptr [rsp+0xB0] + paddd xmm2, xmmword ptr [rsp+0x50] + paddd xmm3, xmmword ptr [rsp+0x10] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp] + paddd xmm1, xmmword ptr [rsp+0x20] + paddd xmm2, xmmword ptr [rsp+0x80] + paddd xmm3, xmmword ptr [rsp+0x60] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0xC0] + paddd xmm1, xmmword ptr [rsp+0x90] + paddd xmm2, xmmword ptr [rsp+0xF0] + paddd xmm3, xmmword ptr [rsp+0xE0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0xD0] + paddd xmm1, xmmword ptr [rsp+0xB0] + paddd xmm2, xmmword ptr [rsp+0xA0] + paddd xmm3, xmmword ptr [rsp+0x80] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x70] + paddd xmm1, xmmword ptr [rsp+0x50] + paddd xmm2, xmmword ptr [rsp] + paddd xmm3, xmmword ptr [rsp+0x60] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x20] + paddd xmm1, xmmword ptr [rsp+0x30] + paddd xmm2, xmmword ptr [rsp+0x10] + paddd xmm3, xmmword ptr [rsp+0x40] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x90] + paddd xmm1, xmmword ptr [rsp+0xB0] + paddd xmm2, xmmword ptr [rsp+0x80] + paddd xmm3, xmmword ptr [rsp+0xF0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0xE0] + paddd xmm1, xmmword ptr [rsp+0x50] + paddd xmm2, xmmword ptr [rsp+0xC0] + paddd xmm3, xmmword ptr [rsp+0x10] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0xD0] + paddd xmm1, xmmword ptr [rsp] + paddd xmm2, xmmword ptr [rsp+0x20] + paddd xmm3, xmmword ptr [rsp+0x40] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x30] + paddd xmm1, xmmword ptr [rsp+0xA0] + paddd xmm2, xmmword ptr [rsp+0x60] + paddd xmm3, xmmword ptr [rsp+0x70] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0xB0] + paddd xmm1, xmmword ptr [rsp+0x50] + paddd xmm2, xmmword ptr [rsp+0x10] + paddd xmm3, xmmword ptr [rsp+0x80] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0xF0] + paddd xmm1, xmmword ptr [rsp] + paddd xmm2, xmmword ptr [rsp+0x90] + paddd xmm3, xmmword ptr [rsp+0x60] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0xE0] + paddd xmm1, xmmword ptr [rsp+0x20] + paddd xmm2, xmmword ptr [rsp+0x30] + paddd xmm3, xmmword ptr [rsp+0x70] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0xA0] + paddd xmm1, xmmword ptr [rsp+0xC0] + paddd xmm2, xmmword ptr [rsp+0x40] + paddd xmm3, xmmword ptr [rsp+0xD0] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + pxor xmm0, xmm8 + pxor xmm1, xmm9 + pxor xmm2, xmm10 + pxor xmm3, xmm11 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + pxor xmm4, xmm12 + pxor xmm5, xmm13 + pxor xmm6, xmm14 + pxor xmm7, xmm15 + mov eax, r13d + jne 9b + movdqa xmm9, xmm0 + punpckldq xmm0, xmm1 + punpckhdq xmm9, xmm1 + movdqa xmm11, xmm2 + punpckldq xmm2, xmm3 + punpckhdq xmm11, xmm3 + movdqa xmm1, xmm0 + punpcklqdq xmm0, xmm2 + punpckhqdq xmm1, xmm2 + movdqa xmm3, xmm9 + punpcklqdq xmm9, xmm11 + punpckhqdq xmm3, xmm11 + movdqu xmmword ptr [rbx], xmm0 + movdqu xmmword ptr [rbx+0x20], xmm1 + movdqu xmmword ptr [rbx+0x40], xmm9 + movdqu xmmword ptr [rbx+0x60], xmm3 + movdqa xmm9, xmm4 + punpckldq xmm4, xmm5 + punpckhdq xmm9, xmm5 + movdqa xmm11, xmm6 + punpckldq xmm6, xmm7 + punpckhdq xmm11, xmm7 + movdqa xmm5, xmm4 + punpcklqdq xmm4, xmm6 + punpckhqdq xmm5, xmm6 + movdqa xmm7, xmm9 + punpcklqdq xmm9, xmm11 + punpckhqdq xmm7, xmm11 + movdqu xmmword ptr [rbx+0x10], xmm4 + movdqu xmmword ptr [rbx+0x30], xmm5 + movdqu xmmword ptr [rbx+0x50], xmm9 + movdqu xmmword ptr [rbx+0x70], xmm7 + movdqa xmm1, xmmword ptr [rsp+0x110] + movdqa xmm0, xmm1 + paddd xmm1, xmmword ptr [rsp+0x150] + movdqa xmmword ptr [rsp+0x110], xmm1 + pxor xmm0, xmmword ptr [CMP_MSB_MASK+rip] + pxor xmm1, xmmword ptr [CMP_MSB_MASK+rip] + pcmpgtd xmm0, xmm1 + movdqa xmm1, xmmword ptr [rsp+0x120] + psubd xmm1, xmm0 + movdqa xmmword ptr [rsp+0x120], xmm1 + add rbx, 128 + add rdi, 32 + sub rsi, 4 + cmp rsi, 4 + jnc 2b + test rsi, rsi + jnz 3f +4: + mov rsp, rbp + pop rbp + pop rbx + pop r12 + pop r13 + pop r14 + pop r15 + ret +.p2align 5 +3: + test esi, 0x2 + je 3f + movups xmm0, xmmword ptr [rcx] + movups xmm1, xmmword ptr [rcx+0x10] + movaps xmm8, xmm0 + movaps xmm9, xmm1 + movd xmm13, dword ptr [rsp+0x110] + movd xmm14, dword ptr [rsp+0x120] + punpckldq xmm13, xmm14 + movaps xmmword ptr [rsp], xmm13 + movd xmm14, dword ptr [rsp+0x114] + movd xmm13, dword ptr [rsp+0x124] + punpckldq xmm14, xmm13 + movaps xmmword ptr [rsp+0x10], xmm14 + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + movzx eax, byte ptr [rbp+0x40] + or eax, r13d + xor edx, edx +2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + movaps xmm2, xmmword ptr [BLAKE3_IV+rip] + movaps xmm10, xmm2 + movups xmm4, xmmword ptr [r8+rdx-0x40] + movups xmm5, xmmword ptr [r8+rdx-0x30] + movaps xmm3, xmm4 + shufps xmm4, xmm5, 136 + shufps xmm3, xmm5, 221 + movaps xmm5, xmm3 + movups xmm6, xmmword ptr [r8+rdx-0x20] + movups xmm7, xmmword ptr [r8+rdx-0x10] + movaps xmm3, xmm6 + shufps xmm6, xmm7, 136 + pshufd xmm6, xmm6, 0x93 + shufps xmm3, xmm7, 221 + pshufd xmm7, xmm3, 0x93 + movups xmm12, xmmword ptr [r9+rdx-0x40] + movups xmm13, xmmword ptr [r9+rdx-0x30] + movaps xmm11, xmm12 + shufps xmm12, xmm13, 136 + shufps xmm11, xmm13, 221 + movaps xmm13, xmm11 + movups xmm14, xmmword ptr [r9+rdx-0x20] + movups xmm15, xmmword ptr [r9+rdx-0x10] + movaps xmm11, xmm14 + shufps xmm14, xmm15, 136 + pshufd xmm14, xmm14, 0x93 + shufps xmm11, xmm15, 221 + pshufd xmm15, xmm11, 0x93 + shl rax, 0x20 + or rax, 0x40 + movd xmm3, rax + movdqa xmmword ptr [rsp+0x20], xmm3 + movaps xmm3, xmmword ptr [rsp] + movaps xmm11, xmmword ptr [rsp+0x10] + punpcklqdq xmm3, xmmword ptr [rsp+0x20] + punpcklqdq xmm11, xmmword ptr [rsp+0x20] + mov al, 7 +9: + paddd xmm0, xmm4 + paddd xmm8, xmm12 + movaps xmmword ptr [rsp+0x20], xmm4 + movaps xmmword ptr [rsp+0x30], xmm12 + paddd xmm0, xmm1 + paddd xmm8, xmm9 + pxor xmm3, xmm0 + pxor xmm11, xmm8 + pshuflw xmm3, xmm3, 0xB1 + pshufhw xmm3, xmm3, 0xB1 + pshuflw xmm11, xmm11, 0xB1 + pshufhw xmm11, xmm11, 0xB1 + paddd xmm2, xmm3 + paddd xmm10, xmm11 + pxor xmm1, xmm2 + pxor xmm9, xmm10 + movdqa xmm4, xmm1 + pslld xmm1, 20 + psrld xmm4, 12 + por xmm1, xmm4 + movdqa xmm4, xmm9 + pslld xmm9, 20 + psrld xmm4, 12 + por xmm9, xmm4 + paddd xmm0, xmm5 + paddd xmm8, xmm13 + movaps xmmword ptr [rsp+0x40], xmm5 + movaps xmmword ptr [rsp+0x50], xmm13 + paddd xmm0, xmm1 + paddd xmm8, xmm9 + pxor xmm3, xmm0 + pxor xmm11, xmm8 + movdqa xmm13, xmm3 + psrld xmm3, 8 + pslld xmm13, 24 + pxor xmm3, xmm13 + movdqa xmm13, xmm11 + psrld xmm11, 8 + pslld xmm13, 24 + pxor xmm11, xmm13 + paddd xmm2, xmm3 + paddd xmm10, xmm11 + pxor xmm1, xmm2 + pxor xmm9, xmm10 + movdqa xmm4, xmm1 + pslld xmm1, 25 + psrld xmm4, 7 + por xmm1, xmm4 + movdqa xmm4, xmm9 + pslld xmm9, 25 + psrld xmm4, 7 + por xmm9, xmm4 + pshufd xmm0, xmm0, 0x93 + pshufd xmm8, xmm8, 0x93 + pshufd xmm3, xmm3, 0x4E + pshufd xmm11, xmm11, 0x4E + pshufd xmm2, xmm2, 0x39 + pshufd xmm10, xmm10, 0x39 + paddd xmm0, xmm6 + paddd xmm8, xmm14 + paddd xmm0, xmm1 + paddd xmm8, xmm9 + pxor xmm3, xmm0 + pxor xmm11, xmm8 + pshuflw xmm3, xmm3, 0xB1 + pshufhw xmm3, xmm3, 0xB1 + pshuflw xmm11, xmm11, 0xB1 + pshufhw xmm11, xmm11, 0xB1 + paddd xmm2, xmm3 + paddd xmm10, xmm11 + pxor xmm1, xmm2 + pxor xmm9, xmm10 + movdqa xmm4, xmm1 + pslld xmm1, 20 + psrld xmm4, 12 + por xmm1, xmm4 + movdqa xmm4, xmm9 + pslld xmm9, 20 + psrld xmm4, 12 + por xmm9, xmm4 + paddd xmm0, xmm7 + paddd xmm8, xmm15 + paddd xmm0, xmm1 + paddd xmm8, xmm9 + pxor xmm3, xmm0 + pxor xmm11, xmm8 + movdqa xmm13, xmm3 + psrld xmm3, 8 + pslld xmm13, 24 + pxor xmm3, xmm13 + movdqa xmm13, xmm11 + psrld xmm11, 8 + pslld xmm13, 24 + pxor xmm11, xmm13 + paddd xmm2, xmm3 + paddd xmm10, xmm11 + pxor xmm1, xmm2 + pxor xmm9, xmm10 + movdqa xmm4, xmm1 + pslld xmm1, 25 + psrld xmm4, 7 + por xmm1, xmm4 + movdqa xmm4, xmm9 + pslld xmm9, 25 + psrld xmm4, 7 + por xmm9, xmm4 + pshufd xmm0, xmm0, 0x39 + pshufd xmm8, xmm8, 0x39 + pshufd xmm3, xmm3, 0x4E + pshufd xmm11, xmm11, 0x4E + pshufd xmm2, xmm2, 0x93 + pshufd xmm10, xmm10, 0x93 + dec al + je 9f + movdqa xmm12, xmmword ptr [rsp+0x20] + movdqa xmm5, xmmword ptr [rsp+0x40] + pshufd xmm13, xmm12, 0x0F + shufps xmm12, xmm5, 214 + pshufd xmm4, xmm12, 0x39 + movdqa xmm12, xmm6 + shufps xmm12, xmm7, 250 + pand xmm13, xmmword ptr [PBLENDW_0x33_MASK+rip] + pand xmm12, xmmword ptr [PBLENDW_0xCC_MASK+rip] + por xmm13, xmm12 + movdqa xmmword ptr [rsp+0x20], xmm13 + movdqa xmm12, xmm7 + punpcklqdq xmm12, xmm5 + movdqa xmm13, xmm6 + pand xmm12, xmmword ptr [PBLENDW_0x3F_MASK+rip] + pand xmm13, xmmword ptr [PBLENDW_0xC0_MASK+rip] + por xmm12, xmm13 + pshufd xmm12, xmm12, 0x78 + punpckhdq xmm5, xmm7 + punpckldq xmm6, xmm5 + pshufd xmm7, xmm6, 0x1E + movdqa xmmword ptr [rsp+0x40], xmm12 + movdqa xmm5, xmmword ptr [rsp+0x30] + movdqa xmm13, xmmword ptr [rsp+0x50] + pshufd xmm6, xmm5, 0x0F + shufps xmm5, xmm13, 214 + pshufd xmm12, xmm5, 0x39 + movdqa xmm5, xmm14 + shufps xmm5, xmm15, 250 + pand xmm6, xmmword ptr [PBLENDW_0x33_MASK+rip] + pand xmm5, xmmword ptr [PBLENDW_0xCC_MASK+rip] + por xmm6, xmm5 + movdqa xmm5, xmm15 + punpcklqdq xmm5, xmm13 + movdqa xmmword ptr [rsp+0x30], xmm2 + movdqa xmm2, xmm14 + pand xmm5, xmmword ptr [PBLENDW_0x3F_MASK+rip] + pand xmm2, xmmword ptr [PBLENDW_0xC0_MASK+rip] + por xmm5, xmm2 + movdqa xmm2, xmmword ptr [rsp+0x30] + pshufd xmm5, xmm5, 0x78 + punpckhdq xmm13, xmm15 + punpckldq xmm14, xmm13 + pshufd xmm15, xmm14, 0x1E + movdqa xmm13, xmm6 + movdqa xmm14, xmm5 + movdqa xmm5, xmmword ptr [rsp+0x20] + movdqa xmm6, xmmword ptr [rsp+0x40] + jmp 9b +9: + pxor xmm0, xmm2 + pxor xmm1, xmm3 + pxor xmm8, xmm10 + pxor xmm9, xmm11 + mov eax, r13d + cmp rdx, r15 + jne 2b + movups xmmword ptr [rbx], xmm0 + movups xmmword ptr [rbx+0x10], xmm1 + movups xmmword ptr [rbx+0x20], xmm8 + movups xmmword ptr [rbx+0x30], xmm9 + mov eax, dword ptr [rsp+0x130] + neg eax + mov r10d, dword ptr [rsp+0x110+8*rax] + mov r11d, dword ptr [rsp+0x120+8*rax] + mov dword ptr [rsp+0x110], r10d + mov dword ptr [rsp+0x120], r11d + add rdi, 16 + add rbx, 64 + sub rsi, 2 +3: + test esi, 0x1 + je 4b + movups xmm0, xmmword ptr [rcx] + movups xmm1, xmmword ptr [rcx+0x10] + movd xmm13, dword ptr [rsp+0x110] + movd xmm14, dword ptr [rsp+0x120] + punpckldq xmm13, xmm14 + mov r8, qword ptr [rdi] + movzx eax, byte ptr [rbp+0x40] + or eax, r13d + xor edx, edx +2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + movaps xmm2, xmmword ptr [BLAKE3_IV+rip] + shl rax, 32 + or rax, 64 + movd xmm12, rax + movdqa xmm3, xmm13 + punpcklqdq xmm3, xmm12 + movups xmm4, xmmword ptr [r8+rdx-0x40] + movups xmm5, xmmword ptr [r8+rdx-0x30] + movaps xmm8, xmm4 + shufps xmm4, xmm5, 136 + shufps xmm8, xmm5, 221 + movaps xmm5, xmm8 + movups xmm6, xmmword ptr [r8+rdx-0x20] + movups xmm7, xmmword ptr [r8+rdx-0x10] + movaps xmm8, xmm6 + shufps xmm6, xmm7, 136 + pshufd xmm6, xmm6, 0x93 + shufps xmm8, xmm7, 221 + pshufd xmm7, xmm8, 0x93 + mov al, 7 +9: + paddd xmm0, xmm4 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshuflw xmm3, xmm3, 0xB1 + pshufhw xmm3, xmm3, 0xB1 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm5 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + movdqa xmm14, xmm3 + psrld xmm3, 8 + pslld xmm14, 24 + pxor xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x93 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x39 + paddd xmm0, xmm6 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshuflw xmm3, xmm3, 0xB1 + pshufhw xmm3, xmm3, 0xB1 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm7 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + movdqa xmm14, xmm3 + psrld xmm3, 8 + pslld xmm14, 24 + pxor xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x39 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x93 + dec al + jz 9f + movdqa xmm8, xmm4 + shufps xmm8, xmm5, 214 + pshufd xmm9, xmm4, 0x0F + pshufd xmm4, xmm8, 0x39 + movdqa xmm8, xmm6 + shufps xmm8, xmm7, 250 + pand xmm9, xmmword ptr [PBLENDW_0x33_MASK+rip] + pand xmm8, xmmword ptr [PBLENDW_0xCC_MASK+rip] + por xmm9, xmm8 + movdqa xmm8, xmm7 + punpcklqdq xmm8, xmm5 + movdqa xmm10, xmm6 + pand xmm8, xmmword ptr [PBLENDW_0x3F_MASK+rip] + pand xmm10, xmmword ptr [PBLENDW_0xC0_MASK+rip] + por xmm8, xmm10 + pshufd xmm8, xmm8, 0x78 + punpckhdq xmm5, xmm7 + punpckldq xmm6, xmm5 + pshufd xmm7, xmm6, 0x1E + movdqa xmm5, xmm9 + movdqa xmm6, xmm8 + jmp 9b +9: + pxor xmm0, xmm2 + pxor xmm1, xmm3 + mov eax, r13d + cmp rdx, r15 + jne 2b + movups xmmword ptr [rbx], xmm0 + movups xmmword ptr [rbx+0x10], xmm1 + jmp 4b + +.p2align 6 +blake3_compress_in_place_sse2: +_blake3_compress_in_place_sse2: + _CET_ENDBR + movups xmm0, xmmword ptr [rdi] + movups xmm1, xmmword ptr [rdi+0x10] + movaps xmm2, xmmword ptr [BLAKE3_IV+rip] + shl r8, 32 + add rdx, r8 + movq xmm3, rcx + movq xmm4, rdx + punpcklqdq xmm3, xmm4 + movups xmm4, xmmword ptr [rsi] + movups xmm5, xmmword ptr [rsi+0x10] + movaps xmm8, xmm4 + shufps xmm4, xmm5, 136 + shufps xmm8, xmm5, 221 + movaps xmm5, xmm8 + movups xmm6, xmmword ptr [rsi+0x20] + movups xmm7, xmmword ptr [rsi+0x30] + movaps xmm8, xmm6 + shufps xmm6, xmm7, 136 + pshufd xmm6, xmm6, 0x93 + shufps xmm8, xmm7, 221 + pshufd xmm7, xmm8, 0x93 + mov al, 7 +9: + paddd xmm0, xmm4 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshuflw xmm3, xmm3, 0xB1 + pshufhw xmm3, xmm3, 0xB1 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm5 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + movdqa xmm14, xmm3 + psrld xmm3, 8 + pslld xmm14, 24 + pxor xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x93 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x39 + paddd xmm0, xmm6 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshuflw xmm3, xmm3, 0xB1 + pshufhw xmm3, xmm3, 0xB1 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm7 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + movdqa xmm14, xmm3 + psrld xmm3, 8 + pslld xmm14, 24 + pxor xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x39 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x93 + dec al + jz 9f + movdqa xmm8, xmm4 + shufps xmm8, xmm5, 214 + pshufd xmm9, xmm4, 0x0F + pshufd xmm4, xmm8, 0x39 + movdqa xmm8, xmm6 + shufps xmm8, xmm7, 250 + pand xmm9, xmmword ptr [PBLENDW_0x33_MASK+rip] + pand xmm8, xmmword ptr [PBLENDW_0xCC_MASK+rip] + por xmm9, xmm8 + movdqa xmm8, xmm7 + punpcklqdq xmm8, xmm5 + movdqa xmm10, xmm6 + pand xmm8, xmmword ptr [PBLENDW_0x3F_MASK+rip] + pand xmm10, xmmword ptr [PBLENDW_0xC0_MASK+rip] + por xmm8, xmm10 + pshufd xmm8, xmm8, 0x78 + punpckhdq xmm5, xmm7 + punpckldq xmm6, xmm5 + pshufd xmm7, xmm6, 0x1E + movdqa xmm5, xmm9 + movdqa xmm6, xmm8 + jmp 9b +9: + pxor xmm0, xmm2 + pxor xmm1, xmm3 + movups xmmword ptr [rdi], xmm0 + movups xmmword ptr [rdi+0x10], xmm1 + ret + +.p2align 6 +blake3_compress_xof_sse2: +_blake3_compress_xof_sse2: + _CET_ENDBR + movups xmm0, xmmword ptr [rdi] + movups xmm1, xmmword ptr [rdi+0x10] + movaps xmm2, xmmword ptr [BLAKE3_IV+rip] + movzx eax, r8b + movzx edx, dl + shl rax, 32 + add rdx, rax + movq xmm3, rcx + movq xmm4, rdx + punpcklqdq xmm3, xmm4 + movups xmm4, xmmword ptr [rsi] + movups xmm5, xmmword ptr [rsi+0x10] + movaps xmm8, xmm4 + shufps xmm4, xmm5, 136 + shufps xmm8, xmm5, 221 + movaps xmm5, xmm8 + movups xmm6, xmmword ptr [rsi+0x20] + movups xmm7, xmmword ptr [rsi+0x30] + movaps xmm8, xmm6 + shufps xmm6, xmm7, 136 + pshufd xmm6, xmm6, 0x93 + shufps xmm8, xmm7, 221 + pshufd xmm7, xmm8, 0x93 + mov al, 7 +9: + paddd xmm0, xmm4 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshuflw xmm3, xmm3, 0xB1 + pshufhw xmm3, xmm3, 0xB1 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm5 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + movdqa xmm14, xmm3 + psrld xmm3, 8 + pslld xmm14, 24 + pxor xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x93 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x39 + paddd xmm0, xmm6 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshuflw xmm3, xmm3, 0xB1 + pshufhw xmm3, xmm3, 0xB1 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm7 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + movdqa xmm14, xmm3 + psrld xmm3, 8 + pslld xmm14, 24 + pxor xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x39 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x93 + dec al + jz 9f + movdqa xmm8, xmm4 + shufps xmm8, xmm5, 214 + pshufd xmm9, xmm4, 0x0F + pshufd xmm4, xmm8, 0x39 + movdqa xmm8, xmm6 + shufps xmm8, xmm7, 250 + pand xmm9, xmmword ptr [PBLENDW_0x33_MASK+rip] + pand xmm8, xmmword ptr [PBLENDW_0xCC_MASK+rip] + por xmm9, xmm8 + movdqa xmm8, xmm7 + punpcklqdq xmm8, xmm5 + movdqa xmm10, xmm6 + pand xmm8, xmmword ptr [PBLENDW_0x3F_MASK+rip] + pand xmm10, xmmword ptr [PBLENDW_0xC0_MASK+rip] + por xmm8, xmm10 + pshufd xmm8, xmm8, 0x78 + punpckhdq xmm5, xmm7 + punpckldq xmm6, xmm5 + pshufd xmm7, xmm6, 0x1E + movdqa xmm5, xmm9 + movdqa xmm6, xmm8 + jmp 9b +9: + movdqu xmm4, xmmword ptr [rdi] + movdqu xmm5, xmmword ptr [rdi+0x10] + pxor xmm0, xmm2 + pxor xmm1, xmm3 + pxor xmm2, xmm4 + pxor xmm3, xmm5 + movups xmmword ptr [r9], xmm0 + movups xmmword ptr [r9+0x10], xmm1 + movups xmmword ptr [r9+0x20], xmm2 + movups xmmword ptr [r9+0x30], xmm3 + ret + + +#ifdef __APPLE__ +.static_data +#else +.section .rodata +#endif +.p2align 6 +BLAKE3_IV: + .long 0x6A09E667, 0xBB67AE85 + .long 0x3C6EF372, 0xA54FF53A +ADD0: + .long 0, 1, 2, 3 +ADD1: + .long 4, 4, 4, 4 +BLAKE3_IV_0: + .long 0x6A09E667, 0x6A09E667, 0x6A09E667, 0x6A09E667 +BLAKE3_IV_1: + .long 0xBB67AE85, 0xBB67AE85, 0xBB67AE85, 0xBB67AE85 +BLAKE3_IV_2: + .long 0x3C6EF372, 0x3C6EF372, 0x3C6EF372, 0x3C6EF372 +BLAKE3_IV_3: + .long 0xA54FF53A, 0xA54FF53A, 0xA54FF53A, 0xA54FF53A +BLAKE3_BLOCK_LEN: + .long 64, 64, 64, 64 +CMP_MSB_MASK: + .long 0x80000000, 0x80000000, 0x80000000, 0x80000000 +PBLENDW_0x33_MASK: + .long 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000 +PBLENDW_0xCC_MASK: + .long 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF +PBLENDW_0x3F_MASK: + .long 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000 +PBLENDW_0xC0_MASK: + .long 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF diff --git a/src/third_party/blake3/blake3_sse2_x86-64_windows_gnu.S b/src/third_party/blake3/blake3_sse2_x86-64_windows_gnu.S new file mode 100644 index 0000000..494c0c6 --- /dev/null +++ b/src/third_party/blake3/blake3_sse2_x86-64_windows_gnu.S @@ -0,0 +1,2332 @@ +.intel_syntax noprefix +.global blake3_hash_many_sse2 +.global _blake3_hash_many_sse2 +.global blake3_compress_in_place_sse2 +.global _blake3_compress_in_place_sse2 +.global blake3_compress_xof_sse2 +.global _blake3_compress_xof_sse2 +.section .text + .p2align 6 +_blake3_hash_many_sse2: +blake3_hash_many_sse2: + push r15 + push r14 + push r13 + push r12 + push rsi + push rdi + push rbx + push rbp + mov rbp, rsp + sub rsp, 528 + and rsp, 0xFFFFFFFFFFFFFFC0 + movdqa xmmword ptr [rsp+0x170], xmm6 + movdqa xmmword ptr [rsp+0x180], xmm7 + movdqa xmmword ptr [rsp+0x190], xmm8 + movdqa xmmword ptr [rsp+0x1A0], xmm9 + movdqa xmmword ptr [rsp+0x1B0], xmm10 + movdqa xmmword ptr [rsp+0x1C0], xmm11 + movdqa xmmword ptr [rsp+0x1D0], xmm12 + movdqa xmmword ptr [rsp+0x1E0], xmm13 + movdqa xmmword ptr [rsp+0x1F0], xmm14 + movdqa xmmword ptr [rsp+0x200], xmm15 + mov rdi, rcx + mov rsi, rdx + mov rdx, r8 + mov rcx, r9 + mov r8, qword ptr [rbp+0x68] + movzx r9, byte ptr [rbp+0x70] + neg r9d + movd xmm0, r9d + pshufd xmm0, xmm0, 0x00 + movdqa xmmword ptr [rsp+0x130], xmm0 + movdqa xmm1, xmm0 + pand xmm1, xmmword ptr [ADD0+rip] + pand xmm0, xmmword ptr [ADD1+rip] + movdqa xmmword ptr [rsp+0x150], xmm0 + movd xmm0, r8d + pshufd xmm0, xmm0, 0x00 + paddd xmm0, xmm1 + movdqa xmmword ptr [rsp+0x110], xmm0 + pxor xmm0, xmmword ptr [CMP_MSB_MASK+rip] + pxor xmm1, xmmword ptr [CMP_MSB_MASK+rip] + pcmpgtd xmm1, xmm0 + shr r8, 32 + movd xmm2, r8d + pshufd xmm2, xmm2, 0x00 + psubd xmm2, xmm1 + movdqa xmmword ptr [rsp+0x120], xmm2 + mov rbx, qword ptr [rbp+0x90] + mov r15, rdx + shl r15, 6 + movzx r13d, byte ptr [rbp+0x78] + movzx r12d, byte ptr [rbp+0x88] + cmp rsi, 4 + jc 3f +2: + movdqu xmm3, xmmword ptr [rcx] + pshufd xmm0, xmm3, 0x00 + pshufd xmm1, xmm3, 0x55 + pshufd xmm2, xmm3, 0xAA + pshufd xmm3, xmm3, 0xFF + movdqu xmm7, xmmword ptr [rcx+0x10] + pshufd xmm4, xmm7, 0x00 + pshufd xmm5, xmm7, 0x55 + pshufd xmm6, xmm7, 0xAA + pshufd xmm7, xmm7, 0xFF + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + mov r10, qword ptr [rdi+0x10] + mov r11, qword ptr [rdi+0x18] + movzx eax, byte ptr [rbp+0x80] + or eax, r13d + xor edx, edx +9: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + movdqu xmm8, xmmword ptr [r8+rdx-0x40] + movdqu xmm9, xmmword ptr [r9+rdx-0x40] + movdqu xmm10, xmmword ptr [r10+rdx-0x40] + movdqu xmm11, xmmword ptr [r11+rdx-0x40] + movdqa xmm12, xmm8 + punpckldq xmm8, xmm9 + punpckhdq xmm12, xmm9 + movdqa xmm14, xmm10 + punpckldq xmm10, xmm11 + punpckhdq xmm14, xmm11 + movdqa xmm9, xmm8 + punpcklqdq xmm8, xmm10 + punpckhqdq xmm9, xmm10 + movdqa xmm13, xmm12 + punpcklqdq xmm12, xmm14 + punpckhqdq xmm13, xmm14 + movdqa xmmword ptr [rsp], xmm8 + movdqa xmmword ptr [rsp+0x10], xmm9 + movdqa xmmword ptr [rsp+0x20], xmm12 + movdqa xmmword ptr [rsp+0x30], xmm13 + movdqu xmm8, xmmword ptr [r8+rdx-0x30] + movdqu xmm9, xmmword ptr [r9+rdx-0x30] + movdqu xmm10, xmmword ptr [r10+rdx-0x30] + movdqu xmm11, xmmword ptr [r11+rdx-0x30] + movdqa xmm12, xmm8 + punpckldq xmm8, xmm9 + punpckhdq xmm12, xmm9 + movdqa xmm14, xmm10 + punpckldq xmm10, xmm11 + punpckhdq xmm14, xmm11 + movdqa xmm9, xmm8 + punpcklqdq xmm8, xmm10 + punpckhqdq xmm9, xmm10 + movdqa xmm13, xmm12 + punpcklqdq xmm12, xmm14 + punpckhqdq xmm13, xmm14 + movdqa xmmword ptr [rsp+0x40], xmm8 + movdqa xmmword ptr [rsp+0x50], xmm9 + movdqa xmmword ptr [rsp+0x60], xmm12 + movdqa xmmword ptr [rsp+0x70], xmm13 + movdqu xmm8, xmmword ptr [r8+rdx-0x20] + movdqu xmm9, xmmword ptr [r9+rdx-0x20] + movdqu xmm10, xmmword ptr [r10+rdx-0x20] + movdqu xmm11, xmmword ptr [r11+rdx-0x20] + movdqa xmm12, xmm8 + punpckldq xmm8, xmm9 + punpckhdq xmm12, xmm9 + movdqa xmm14, xmm10 + punpckldq xmm10, xmm11 + punpckhdq xmm14, xmm11 + movdqa xmm9, xmm8 + punpcklqdq xmm8, xmm10 + punpckhqdq xmm9, xmm10 + movdqa xmm13, xmm12 + punpcklqdq xmm12, xmm14 + punpckhqdq xmm13, xmm14 + movdqa xmmword ptr [rsp+0x80], xmm8 + movdqa xmmword ptr [rsp+0x90], xmm9 + movdqa xmmword ptr [rsp+0xA0], xmm12 + movdqa xmmword ptr [rsp+0xB0], xmm13 + movdqu xmm8, xmmword ptr [r8+rdx-0x10] + movdqu xmm9, xmmword ptr [r9+rdx-0x10] + movdqu xmm10, xmmword ptr [r10+rdx-0x10] + movdqu xmm11, xmmword ptr [r11+rdx-0x10] + movdqa xmm12, xmm8 + punpckldq xmm8, xmm9 + punpckhdq xmm12, xmm9 + movdqa xmm14, xmm10 + punpckldq xmm10, xmm11 + punpckhdq xmm14, xmm11 + movdqa xmm9, xmm8 + punpcklqdq xmm8, xmm10 + punpckhqdq xmm9, xmm10 + movdqa xmm13, xmm12 + punpcklqdq xmm12, xmm14 + punpckhqdq xmm13, xmm14 + movdqa xmmword ptr [rsp+0xC0], xmm8 + movdqa xmmword ptr [rsp+0xD0], xmm9 + movdqa xmmword ptr [rsp+0xE0], xmm12 + movdqa xmmword ptr [rsp+0xF0], xmm13 + movdqa xmm9, xmmword ptr [BLAKE3_IV_1+rip] + movdqa xmm10, xmmword ptr [BLAKE3_IV_2+rip] + movdqa xmm11, xmmword ptr [BLAKE3_IV_3+rip] + movdqa xmm12, xmmword ptr [rsp+0x110] + movdqa xmm13, xmmword ptr [rsp+0x120] + movdqa xmm14, xmmword ptr [BLAKE3_BLOCK_LEN+rip] + movd xmm15, eax + pshufd xmm15, xmm15, 0x00 + prefetcht0 [r8+rdx+0x80] + prefetcht0 [r9+rdx+0x80] + prefetcht0 [r10+rdx+0x80] + prefetcht0 [r11+rdx+0x80] + paddd xmm0, xmmword ptr [rsp] + paddd xmm1, xmmword ptr [rsp+0x20] + paddd xmm2, xmmword ptr [rsp+0x40] + paddd xmm3, xmmword ptr [rsp+0x60] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + movdqa xmm8, xmmword ptr [BLAKE3_IV_0+rip] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x10] + paddd xmm1, xmmword ptr [rsp+0x30] + paddd xmm2, xmmword ptr [rsp+0x50] + paddd xmm3, xmmword ptr [rsp+0x70] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x80] + paddd xmm1, xmmword ptr [rsp+0xA0] + paddd xmm2, xmmword ptr [rsp+0xC0] + paddd xmm3, xmmword ptr [rsp+0xE0] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x90] + paddd xmm1, xmmword ptr [rsp+0xB0] + paddd xmm2, xmmword ptr [rsp+0xD0] + paddd xmm3, xmmword ptr [rsp+0xF0] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x20] + paddd xmm1, xmmword ptr [rsp+0x30] + paddd xmm2, xmmword ptr [rsp+0x70] + paddd xmm3, xmmword ptr [rsp+0x40] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x60] + paddd xmm1, xmmword ptr [rsp+0xA0] + paddd xmm2, xmmword ptr [rsp] + paddd xmm3, xmmword ptr [rsp+0xD0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x10] + paddd xmm1, xmmword ptr [rsp+0xC0] + paddd xmm2, xmmword ptr [rsp+0x90] + paddd xmm3, xmmword ptr [rsp+0xF0] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0xB0] + paddd xmm1, xmmword ptr [rsp+0x50] + paddd xmm2, xmmword ptr [rsp+0xE0] + paddd xmm3, xmmword ptr [rsp+0x80] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x30] + paddd xmm1, xmmword ptr [rsp+0xA0] + paddd xmm2, xmmword ptr [rsp+0xD0] + paddd xmm3, xmmword ptr [rsp+0x70] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x40] + paddd xmm1, xmmword ptr [rsp+0xC0] + paddd xmm2, xmmword ptr [rsp+0x20] + paddd xmm3, xmmword ptr [rsp+0xE0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x60] + paddd xmm1, xmmword ptr [rsp+0x90] + paddd xmm2, xmmword ptr [rsp+0xB0] + paddd xmm3, xmmword ptr [rsp+0x80] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x50] + paddd xmm1, xmmword ptr [rsp] + paddd xmm2, xmmword ptr [rsp+0xF0] + paddd xmm3, xmmword ptr [rsp+0x10] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0xA0] + paddd xmm1, xmmword ptr [rsp+0xC0] + paddd xmm2, xmmword ptr [rsp+0xE0] + paddd xmm3, xmmword ptr [rsp+0xD0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x70] + paddd xmm1, xmmword ptr [rsp+0x90] + paddd xmm2, xmmword ptr [rsp+0x30] + paddd xmm3, xmmword ptr [rsp+0xF0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x40] + paddd xmm1, xmmword ptr [rsp+0xB0] + paddd xmm2, xmmword ptr [rsp+0x50] + paddd xmm3, xmmword ptr [rsp+0x10] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp] + paddd xmm1, xmmword ptr [rsp+0x20] + paddd xmm2, xmmword ptr [rsp+0x80] + paddd xmm3, xmmword ptr [rsp+0x60] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0xC0] + paddd xmm1, xmmword ptr [rsp+0x90] + paddd xmm2, xmmword ptr [rsp+0xF0] + paddd xmm3, xmmword ptr [rsp+0xE0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0xD0] + paddd xmm1, xmmword ptr [rsp+0xB0] + paddd xmm2, xmmword ptr [rsp+0xA0] + paddd xmm3, xmmword ptr [rsp+0x80] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x70] + paddd xmm1, xmmword ptr [rsp+0x50] + paddd xmm2, xmmword ptr [rsp] + paddd xmm3, xmmword ptr [rsp+0x60] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x20] + paddd xmm1, xmmword ptr [rsp+0x30] + paddd xmm2, xmmword ptr [rsp+0x10] + paddd xmm3, xmmword ptr [rsp+0x40] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x90] + paddd xmm1, xmmword ptr [rsp+0xB0] + paddd xmm2, xmmword ptr [rsp+0x80] + paddd xmm3, xmmword ptr [rsp+0xF0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0xE0] + paddd xmm1, xmmword ptr [rsp+0x50] + paddd xmm2, xmmword ptr [rsp+0xC0] + paddd xmm3, xmmword ptr [rsp+0x10] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0xD0] + paddd xmm1, xmmword ptr [rsp] + paddd xmm2, xmmword ptr [rsp+0x20] + paddd xmm3, xmmword ptr [rsp+0x40] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x30] + paddd xmm1, xmmword ptr [rsp+0xA0] + paddd xmm2, xmmword ptr [rsp+0x60] + paddd xmm3, xmmword ptr [rsp+0x70] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0xB0] + paddd xmm1, xmmword ptr [rsp+0x50] + paddd xmm2, xmmword ptr [rsp+0x10] + paddd xmm3, xmmword ptr [rsp+0x80] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0xF0] + paddd xmm1, xmmword ptr [rsp] + paddd xmm2, xmmword ptr [rsp+0x90] + paddd xmm3, xmmword ptr [rsp+0x60] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0xE0] + paddd xmm1, xmmword ptr [rsp+0x20] + paddd xmm2, xmmword ptr [rsp+0x30] + paddd xmm3, xmmword ptr [rsp+0x70] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0xB1 + pshufhw xmm15, xmm15, 0xB1 + pshuflw xmm12, xmm12, 0xB1 + pshufhw xmm12, xmm12, 0xB1 + pshuflw xmm13, xmm13, 0xB1 + pshufhw xmm13, xmm13, 0xB1 + pshuflw xmm14, xmm14, 0xB1 + pshufhw xmm14, xmm14, 0xB1 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0xA0] + paddd xmm1, xmmword ptr [rsp+0xC0] + paddd xmm2, xmmword ptr [rsp+0x40] + paddd xmm3, xmmword ptr [rsp+0xD0] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + pxor xmm0, xmm8 + pxor xmm1, xmm9 + pxor xmm2, xmm10 + pxor xmm3, xmm11 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + pxor xmm4, xmm12 + pxor xmm5, xmm13 + pxor xmm6, xmm14 + pxor xmm7, xmm15 + mov eax, r13d + jne 9b + movdqa xmm9, xmm0 + punpckldq xmm0, xmm1 + punpckhdq xmm9, xmm1 + movdqa xmm11, xmm2 + punpckldq xmm2, xmm3 + punpckhdq xmm11, xmm3 + movdqa xmm1, xmm0 + punpcklqdq xmm0, xmm2 + punpckhqdq xmm1, xmm2 + movdqa xmm3, xmm9 + punpcklqdq xmm9, xmm11 + punpckhqdq xmm3, xmm11 + movdqu xmmword ptr [rbx], xmm0 + movdqu xmmword ptr [rbx+0x20], xmm1 + movdqu xmmword ptr [rbx+0x40], xmm9 + movdqu xmmword ptr [rbx+0x60], xmm3 + movdqa xmm9, xmm4 + punpckldq xmm4, xmm5 + punpckhdq xmm9, xmm5 + movdqa xmm11, xmm6 + punpckldq xmm6, xmm7 + punpckhdq xmm11, xmm7 + movdqa xmm5, xmm4 + punpcklqdq xmm4, xmm6 + punpckhqdq xmm5, xmm6 + movdqa xmm7, xmm9 + punpcklqdq xmm9, xmm11 + punpckhqdq xmm7, xmm11 + movdqu xmmword ptr [rbx+0x10], xmm4 + movdqu xmmword ptr [rbx+0x30], xmm5 + movdqu xmmword ptr [rbx+0x50], xmm9 + movdqu xmmword ptr [rbx+0x70], xmm7 + movdqa xmm1, xmmword ptr [rsp+0x110] + movdqa xmm0, xmm1 + paddd xmm1, xmmword ptr [rsp+0x150] + movdqa xmmword ptr [rsp+0x110], xmm1 + pxor xmm0, xmmword ptr [CMP_MSB_MASK+rip] + pxor xmm1, xmmword ptr [CMP_MSB_MASK+rip] + pcmpgtd xmm0, xmm1 + movdqa xmm1, xmmword ptr [rsp+0x120] + psubd xmm1, xmm0 + movdqa xmmword ptr [rsp+0x120], xmm1 + add rbx, 128 + add rdi, 32 + sub rsi, 4 + cmp rsi, 4 + jnc 2b + test rsi, rsi + jne 3f +4: + movdqa xmm6, xmmword ptr [rsp+0x170] + movdqa xmm7, xmmword ptr [rsp+0x180] + movdqa xmm8, xmmword ptr [rsp+0x190] + movdqa xmm9, xmmword ptr [rsp+0x1A0] + movdqa xmm10, xmmword ptr [rsp+0x1B0] + movdqa xmm11, xmmword ptr [rsp+0x1C0] + movdqa xmm12, xmmword ptr [rsp+0x1D0] + movdqa xmm13, xmmword ptr [rsp+0x1E0] + movdqa xmm14, xmmword ptr [rsp+0x1F0] + movdqa xmm15, xmmword ptr [rsp+0x200] + mov rsp, rbp + pop rbp + pop rbx + pop rdi + pop rsi + pop r12 + pop r13 + pop r14 + pop r15 + ret +.p2align 5 +3: + test esi, 0x2 + je 3f + movups xmm0, xmmword ptr [rcx] + movups xmm1, xmmword ptr [rcx+0x10] + movaps xmm8, xmm0 + movaps xmm9, xmm1 + movd xmm13, dword ptr [rsp+0x110] + movd xmm14, dword ptr [rsp+0x120] + punpckldq xmm13, xmm14 + movaps xmmword ptr [rsp], xmm13 + movd xmm14, dword ptr [rsp+0x114] + movd xmm13, dword ptr [rsp+0x124] + punpckldq xmm14, xmm13 + movaps xmmword ptr [rsp+0x10], xmm14 + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + movzx eax, byte ptr [rbp+0x80] + or eax, r13d + xor edx, edx +2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + movaps xmm2, xmmword ptr [BLAKE3_IV+rip] + movaps xmm10, xmm2 + movups xmm4, xmmword ptr [r8+rdx-0x40] + movups xmm5, xmmword ptr [r8+rdx-0x30] + movaps xmm3, xmm4 + shufps xmm4, xmm5, 136 + shufps xmm3, xmm5, 221 + movaps xmm5, xmm3 + movups xmm6, xmmword ptr [r8+rdx-0x20] + movups xmm7, xmmword ptr [r8+rdx-0x10] + movaps xmm3, xmm6 + shufps xmm6, xmm7, 136 + pshufd xmm6, xmm6, 0x93 + shufps xmm3, xmm7, 221 + pshufd xmm7, xmm3, 0x93 + movups xmm12, xmmword ptr [r9+rdx-0x40] + movups xmm13, xmmword ptr [r9+rdx-0x30] + movaps xmm11, xmm12 + shufps xmm12, xmm13, 136 + shufps xmm11, xmm13, 221 + movaps xmm13, xmm11 + movups xmm14, xmmword ptr [r9+rdx-0x20] + movups xmm15, xmmword ptr [r9+rdx-0x10] + movaps xmm11, xmm14 + shufps xmm14, xmm15, 136 + pshufd xmm14, xmm14, 0x93 + shufps xmm11, xmm15, 221 + pshufd xmm15, xmm11, 0x93 + shl rax, 0x20 + or rax, 0x40 + movd xmm3, rax + movdqa xmmword ptr [rsp+0x20], xmm3 + movaps xmm3, xmmword ptr [rsp] + movaps xmm11, xmmword ptr [rsp+0x10] + punpcklqdq xmm3, xmmword ptr [rsp+0x20] + punpcklqdq xmm11, xmmword ptr [rsp+0x20] + mov al, 7 +9: + paddd xmm0, xmm4 + paddd xmm8, xmm12 + movaps xmmword ptr [rsp+0x20], xmm4 + movaps xmmword ptr [rsp+0x30], xmm12 + paddd xmm0, xmm1 + paddd xmm8, xmm9 + pxor xmm3, xmm0 + pxor xmm11, xmm8 + pshuflw xmm3, xmm3, 0xB1 + pshufhw xmm3, xmm3, 0xB1 + pshuflw xmm11, xmm11, 0xB1 + pshufhw xmm11, xmm11, 0xB1 + paddd xmm2, xmm3 + paddd xmm10, xmm11 + pxor xmm1, xmm2 + pxor xmm9, xmm10 + movdqa xmm4, xmm1 + pslld xmm1, 20 + psrld xmm4, 12 + por xmm1, xmm4 + movdqa xmm4, xmm9 + pslld xmm9, 20 + psrld xmm4, 12 + por xmm9, xmm4 + paddd xmm0, xmm5 + paddd xmm8, xmm13 + movaps xmmword ptr [rsp+0x40], xmm5 + movaps xmmword ptr [rsp+0x50], xmm13 + paddd xmm0, xmm1 + paddd xmm8, xmm9 + pxor xmm3, xmm0 + pxor xmm11, xmm8 + movdqa xmm13, xmm3 + psrld xmm3, 8 + pslld xmm13, 24 + pxor xmm3, xmm13 + movdqa xmm13, xmm11 + psrld xmm11, 8 + pslld xmm13, 24 + pxor xmm11, xmm13 + paddd xmm2, xmm3 + paddd xmm10, xmm11 + pxor xmm1, xmm2 + pxor xmm9, xmm10 + movdqa xmm4, xmm1 + pslld xmm1, 25 + psrld xmm4, 7 + por xmm1, xmm4 + movdqa xmm4, xmm9 + pslld xmm9, 25 + psrld xmm4, 7 + por xmm9, xmm4 + pshufd xmm0, xmm0, 0x93 + pshufd xmm8, xmm8, 0x93 + pshufd xmm3, xmm3, 0x4E + pshufd xmm11, xmm11, 0x4E + pshufd xmm2, xmm2, 0x39 + pshufd xmm10, xmm10, 0x39 + paddd xmm0, xmm6 + paddd xmm8, xmm14 + paddd xmm0, xmm1 + paddd xmm8, xmm9 + pxor xmm3, xmm0 + pxor xmm11, xmm8 + pshuflw xmm3, xmm3, 0xB1 + pshufhw xmm3, xmm3, 0xB1 + pshuflw xmm11, xmm11, 0xB1 + pshufhw xmm11, xmm11, 0xB1 + paddd xmm2, xmm3 + paddd xmm10, xmm11 + pxor xmm1, xmm2 + pxor xmm9, xmm10 + movdqa xmm4, xmm1 + pslld xmm1, 20 + psrld xmm4, 12 + por xmm1, xmm4 + movdqa xmm4, xmm9 + pslld xmm9, 20 + psrld xmm4, 12 + por xmm9, xmm4 + paddd xmm0, xmm7 + paddd xmm8, xmm15 + paddd xmm0, xmm1 + paddd xmm8, xmm9 + pxor xmm3, xmm0 + pxor xmm11, xmm8 + movdqa xmm13, xmm3 + psrld xmm3, 8 + pslld xmm13, 24 + pxor xmm3, xmm13 + movdqa xmm13, xmm11 + psrld xmm11, 8 + pslld xmm13, 24 + pxor xmm11, xmm13 + paddd xmm2, xmm3 + paddd xmm10, xmm11 + pxor xmm1, xmm2 + pxor xmm9, xmm10 + movdqa xmm4, xmm1 + pslld xmm1, 25 + psrld xmm4, 7 + por xmm1, xmm4 + movdqa xmm4, xmm9 + pslld xmm9, 25 + psrld xmm4, 7 + por xmm9, xmm4 + pshufd xmm0, xmm0, 0x39 + pshufd xmm8, xmm8, 0x39 + pshufd xmm3, xmm3, 0x4E + pshufd xmm11, xmm11, 0x4E + pshufd xmm2, xmm2, 0x93 + pshufd xmm10, xmm10, 0x93 + dec al + je 9f + movdqa xmm12, xmmword ptr [rsp+0x20] + movdqa xmm5, xmmword ptr [rsp+0x40] + pshufd xmm13, xmm12, 0x0F + shufps xmm12, xmm5, 214 + pshufd xmm4, xmm12, 0x39 + movdqa xmm12, xmm6 + shufps xmm12, xmm7, 250 + pand xmm13, xmmword ptr [PBLENDW_0x33_MASK+rip] + pand xmm12, xmmword ptr [PBLENDW_0xCC_MASK+rip] + por xmm13, xmm12 + movdqa xmmword ptr [rsp+0x20], xmm13 + movdqa xmm12, xmm7 + punpcklqdq xmm12, xmm5 + movdqa xmm13, xmm6 + pand xmm12, xmmword ptr [PBLENDW_0x3F_MASK+rip] + pand xmm13, xmmword ptr [PBLENDW_0xC0_MASK+rip] + por xmm12, xmm13 + pshufd xmm12, xmm12, 0x78 + punpckhdq xmm5, xmm7 + punpckldq xmm6, xmm5 + pshufd xmm7, xmm6, 0x1E + movdqa xmmword ptr [rsp+0x40], xmm12 + movdqa xmm5, xmmword ptr [rsp+0x30] + movdqa xmm13, xmmword ptr [rsp+0x50] + pshufd xmm6, xmm5, 0x0F + shufps xmm5, xmm13, 214 + pshufd xmm12, xmm5, 0x39 + movdqa xmm5, xmm14 + shufps xmm5, xmm15, 250 + pand xmm6, xmmword ptr [PBLENDW_0x33_MASK+rip] + pand xmm5, xmmword ptr [PBLENDW_0xCC_MASK+rip] + por xmm6, xmm5 + movdqa xmm5, xmm15 + punpcklqdq xmm5, xmm13 + movdqa xmmword ptr [rsp+0x30], xmm2 + movdqa xmm2, xmm14 + pand xmm5, xmmword ptr [PBLENDW_0x3F_MASK+rip] + pand xmm2, xmmword ptr [PBLENDW_0xC0_MASK+rip] + por xmm5, xmm2 + movdqa xmm2, xmmword ptr [rsp+0x30] + pshufd xmm5, xmm5, 0x78 + punpckhdq xmm13, xmm15 + punpckldq xmm14, xmm13 + pshufd xmm15, xmm14, 0x1E + movdqa xmm13, xmm6 + movdqa xmm14, xmm5 + movdqa xmm5, xmmword ptr [rsp+0x20] + movdqa xmm6, xmmword ptr [rsp+0x40] + jmp 9b +9: + pxor xmm0, xmm2 + pxor xmm1, xmm3 + pxor xmm8, xmm10 + pxor xmm9, xmm11 + mov eax, r13d + cmp rdx, r15 + jne 2b + movups xmmword ptr [rbx], xmm0 + movups xmmword ptr [rbx+0x10], xmm1 + movups xmmword ptr [rbx+0x20], xmm8 + movups xmmword ptr [rbx+0x30], xmm9 + mov eax, dword ptr [rsp+0x130] + neg eax + mov r10d, dword ptr [rsp+0x110+8*rax] + mov r11d, dword ptr [rsp+0x120+8*rax] + mov dword ptr [rsp+0x110], r10d + mov dword ptr [rsp+0x120], r11d + add rdi, 16 + add rbx, 64 + sub rsi, 2 +3: + test esi, 0x1 + je 4b + movups xmm0, xmmword ptr [rcx] + movups xmm1, xmmword ptr [rcx+0x10] + movd xmm13, dword ptr [rsp+0x110] + movd xmm14, dword ptr [rsp+0x120] + punpckldq xmm13, xmm14 + mov r8, qword ptr [rdi] + movzx eax, byte ptr [rbp+0x80] + or eax, r13d + xor edx, edx +2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + movaps xmm2, xmmword ptr [BLAKE3_IV+rip] + shl rax, 32 + or rax, 64 + movd xmm12, rax + movdqa xmm3, xmm13 + punpcklqdq xmm3, xmm12 + movups xmm4, xmmword ptr [r8+rdx-0x40] + movups xmm5, xmmword ptr [r8+rdx-0x30] + movaps xmm8, xmm4 + shufps xmm4, xmm5, 136 + shufps xmm8, xmm5, 221 + movaps xmm5, xmm8 + movups xmm6, xmmword ptr [r8+rdx-0x20] + movups xmm7, xmmword ptr [r8+rdx-0x10] + movaps xmm8, xmm6 + shufps xmm6, xmm7, 136 + pshufd xmm6, xmm6, 0x93 + shufps xmm8, xmm7, 221 + pshufd xmm7, xmm8, 0x93 + mov al, 7 +9: + paddd xmm0, xmm4 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshuflw xmm3, xmm3, 0xB1 + pshufhw xmm3, xmm3, 0xB1 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm5 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + movdqa xmm14, xmm3 + psrld xmm3, 8 + pslld xmm14, 24 + pxor xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x93 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x39 + paddd xmm0, xmm6 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshuflw xmm3, xmm3, 0xB1 + pshufhw xmm3, xmm3, 0xB1 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm7 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + movdqa xmm14, xmm3 + psrld xmm3, 8 + pslld xmm14, 24 + pxor xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x39 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x93 + dec al + jz 9f + movdqa xmm8, xmm4 + shufps xmm8, xmm5, 214 + pshufd xmm9, xmm4, 0x0F + pshufd xmm4, xmm8, 0x39 + movdqa xmm8, xmm6 + shufps xmm8, xmm7, 250 + pand xmm9, xmmword ptr [PBLENDW_0x33_MASK+rip] + pand xmm8, xmmword ptr [PBLENDW_0xCC_MASK+rip] + por xmm9, xmm8 + movdqa xmm8, xmm7 + punpcklqdq xmm8, xmm5 + movdqa xmm10, xmm6 + pand xmm8, xmmword ptr [PBLENDW_0x3F_MASK+rip] + pand xmm10, xmmword ptr [PBLENDW_0xC0_MASK+rip] + por xmm8, xmm10 + pshufd xmm8, xmm8, 0x78 + punpckhdq xmm5, xmm7 + punpckldq xmm6, xmm5 + pshufd xmm7, xmm6, 0x1E + movdqa xmm5, xmm9 + movdqa xmm6, xmm8 + jmp 9b +9: + pxor xmm0, xmm2 + pxor xmm1, xmm3 + mov eax, r13d + cmp rdx, r15 + jne 2b + movups xmmword ptr [rbx], xmm0 + movups xmmword ptr [rbx+0x10], xmm1 + jmp 4b + +.p2align 6 +blake3_compress_in_place_sse2: +_blake3_compress_in_place_sse2: + sub rsp, 120 + movdqa xmmword ptr [rsp], xmm6 + movdqa xmmword ptr [rsp+0x10], xmm7 + movdqa xmmword ptr [rsp+0x20], xmm8 + movdqa xmmword ptr [rsp+0x30], xmm9 + movdqa xmmword ptr [rsp+0x40], xmm11 + movdqa xmmword ptr [rsp+0x50], xmm14 + movdqa xmmword ptr [rsp+0x60], xmm15 + movups xmm0, xmmword ptr [rcx] + movups xmm1, xmmword ptr [rcx+0x10] + movaps xmm2, xmmword ptr [BLAKE3_IV+rip] + movzx eax, byte ptr [rsp+0xA0] + movzx r8d, r8b + shl rax, 32 + add r8, rax + movq xmm3, r9 + movq xmm4, r8 + punpcklqdq xmm3, xmm4 + movups xmm4, xmmword ptr [rdx] + movups xmm5, xmmword ptr [rdx+0x10] + movaps xmm8, xmm4 + shufps xmm4, xmm5, 136 + shufps xmm8, xmm5, 221 + movaps xmm5, xmm8 + movups xmm6, xmmword ptr [rdx+0x20] + movups xmm7, xmmword ptr [rdx+0x30] + movaps xmm8, xmm6 + shufps xmm6, xmm7, 136 + pshufd xmm6, xmm6, 0x93 + shufps xmm8, xmm7, 221 + pshufd xmm7, xmm8, 0x93 + mov al, 7 +9: + paddd xmm0, xmm4 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshuflw xmm3, xmm3, 0xB1 + pshufhw xmm3, xmm3, 0xB1 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm5 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + movdqa xmm14, xmm3 + psrld xmm3, 8 + pslld xmm14, 24 + pxor xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x93 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x39 + paddd xmm0, xmm6 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshuflw xmm3, xmm3, 0xB1 + pshufhw xmm3, xmm3, 0xB1 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm7 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + movdqa xmm14, xmm3 + psrld xmm3, 8 + pslld xmm14, 24 + pxor xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x39 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x93 + dec al + jz 9f + movdqa xmm8, xmm4 + shufps xmm8, xmm5, 214 + pshufd xmm9, xmm4, 0x0F + pshufd xmm4, xmm8, 0x39 + movdqa xmm8, xmm6 + shufps xmm8, xmm7, 250 + pand xmm9, xmmword ptr [PBLENDW_0x33_MASK+rip] + pand xmm8, xmmword ptr [PBLENDW_0xCC_MASK+rip] + por xmm9, xmm8 + movdqa xmm8, xmm7 + punpcklqdq xmm8, xmm5 + movdqa xmm10, xmm6 + pand xmm8, xmmword ptr [PBLENDW_0x3F_MASK+rip] + pand xmm10, xmmword ptr [PBLENDW_0xC0_MASK+rip] + por xmm8, xmm10 + pshufd xmm8, xmm8, 0x78 + punpckhdq xmm5, xmm7 + punpckldq xmm6, xmm5 + pshufd xmm7, xmm6, 0x1E + movdqa xmm5, xmm9 + movdqa xmm6, xmm8 + jmp 9b +9: + pxor xmm0, xmm2 + pxor xmm1, xmm3 + movups xmmword ptr [rcx], xmm0 + movups xmmword ptr [rcx+0x10], xmm1 + movdqa xmm6, xmmword ptr [rsp] + movdqa xmm7, xmmword ptr [rsp+0x10] + movdqa xmm8, xmmword ptr [rsp+0x20] + movdqa xmm9, xmmword ptr [rsp+0x30] + movdqa xmm11, xmmword ptr [rsp+0x40] + movdqa xmm14, xmmword ptr [rsp+0x50] + movdqa xmm15, xmmword ptr [rsp+0x60] + add rsp, 120 + ret + + +.p2align 6 +_blake3_compress_xof_sse2: +blake3_compress_xof_sse2: + sub rsp, 120 + movdqa xmmword ptr [rsp], xmm6 + movdqa xmmword ptr [rsp+0x10], xmm7 + movdqa xmmword ptr [rsp+0x20], xmm8 + movdqa xmmword ptr [rsp+0x30], xmm9 + movdqa xmmword ptr [rsp+0x40], xmm11 + movdqa xmmword ptr [rsp+0x50], xmm14 + movdqa xmmword ptr [rsp+0x60], xmm15 + movups xmm0, xmmword ptr [rcx] + movups xmm1, xmmword ptr [rcx+0x10] + movaps xmm2, xmmword ptr [BLAKE3_IV+rip] + movzx eax, byte ptr [rsp+0xA0] + movzx r8d, r8b + mov r10, qword ptr [rsp+0xA8] + shl rax, 32 + add r8, rax + movq xmm3, r9 + movq xmm4, r8 + punpcklqdq xmm3, xmm4 + movups xmm4, xmmword ptr [rdx] + movups xmm5, xmmword ptr [rdx+0x10] + movaps xmm8, xmm4 + shufps xmm4, xmm5, 136 + shufps xmm8, xmm5, 221 + movaps xmm5, xmm8 + movups xmm6, xmmword ptr [rdx+0x20] + movups xmm7, xmmword ptr [rdx+0x30] + movaps xmm8, xmm6 + shufps xmm6, xmm7, 136 + pshufd xmm6, xmm6, 0x93 + shufps xmm8, xmm7, 221 + pshufd xmm7, xmm8, 0x93 + mov al, 7 +9: + paddd xmm0, xmm4 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshuflw xmm3, xmm3, 0xB1 + pshufhw xmm3, xmm3, 0xB1 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm5 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + movdqa xmm14, xmm3 + psrld xmm3, 8 + pslld xmm14, 24 + pxor xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x93 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x39 + paddd xmm0, xmm6 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshuflw xmm3, xmm3, 0xB1 + pshufhw xmm3, xmm3, 0xB1 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm7 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + movdqa xmm14, xmm3 + psrld xmm3, 8 + pslld xmm14, 24 + pxor xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x39 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x93 + dec al + jz 9f + movdqa xmm8, xmm4 + shufps xmm8, xmm5, 214 + pshufd xmm9, xmm4, 0x0F + pshufd xmm4, xmm8, 0x39 + movdqa xmm8, xmm6 + shufps xmm8, xmm7, 250 + pand xmm9, xmmword ptr [PBLENDW_0x33_MASK+rip] + pand xmm8, xmmword ptr [PBLENDW_0xCC_MASK+rip] + por xmm9, xmm8 + movdqa xmm8, xmm7 + punpcklqdq xmm8, xmm5 + movdqa xmm10, xmm6 + pand xmm8, xmmword ptr [PBLENDW_0x3F_MASK+rip] + pand xmm10, xmmword ptr [PBLENDW_0xC0_MASK+rip] + por xmm8, xmm10 + pshufd xmm8, xmm8, 0x78 + punpckhdq xmm5, xmm7 + punpckldq xmm6, xmm5 + pshufd xmm7, xmm6, 0x1E + movdqa xmm5, xmm9 + movdqa xmm6, xmm8 + jmp 9b +9: + movdqu xmm4, xmmword ptr [rcx] + movdqu xmm5, xmmword ptr [rcx+0x10] + pxor xmm0, xmm2 + pxor xmm1, xmm3 + pxor xmm2, xmm4 + pxor xmm3, xmm5 + movups xmmword ptr [r10], xmm0 + movups xmmword ptr [r10+0x10], xmm1 + movups xmmword ptr [r10+0x20], xmm2 + movups xmmword ptr [r10+0x30], xmm3 + movdqa xmm6, xmmword ptr [rsp] + movdqa xmm7, xmmword ptr [rsp+0x10] + movdqa xmm8, xmmword ptr [rsp+0x20] + movdqa xmm9, xmmword ptr [rsp+0x30] + movdqa xmm11, xmmword ptr [rsp+0x40] + movdqa xmm14, xmmword ptr [rsp+0x50] + movdqa xmm15, xmmword ptr [rsp+0x60] + add rsp, 120 + ret + + +.section .rodata +.p2align 6 +BLAKE3_IV: + .long 0x6A09E667, 0xBB67AE85 + .long 0x3C6EF372, 0xA54FF53A +ADD0: + .long 0, 1, 2, 3 +ADD1: + .long 4, 4, 4, 4 +BLAKE3_IV_0: + .long 0x6A09E667, 0x6A09E667, 0x6A09E667, 0x6A09E667 +BLAKE3_IV_1: + .long 0xBB67AE85, 0xBB67AE85, 0xBB67AE85, 0xBB67AE85 +BLAKE3_IV_2: + .long 0x3C6EF372, 0x3C6EF372, 0x3C6EF372, 0x3C6EF372 +BLAKE3_IV_3: + .long 0xA54FF53A, 0xA54FF53A, 0xA54FF53A, 0xA54FF53A +BLAKE3_BLOCK_LEN: + .long 64, 64, 64, 64 +CMP_MSB_MASK: + .long 0x80000000, 0x80000000, 0x80000000, 0x80000000 +PBLENDW_0x33_MASK: + .long 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000 +PBLENDW_0xCC_MASK: + .long 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF +PBLENDW_0x3F_MASK: + .long 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000 +PBLENDW_0xC0_MASK: + .long 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF diff --git a/src/third_party/blake3/blake3_sse2_x86-64_windows_msvc.asm b/src/third_party/blake3/blake3_sse2_x86-64_windows_msvc.asm new file mode 100644 index 0000000..72deb7b --- /dev/null +++ b/src/third_party/blake3/blake3_sse2_x86-64_windows_msvc.asm @@ -0,0 +1,2350 @@ +public _blake3_hash_many_sse2 +public blake3_hash_many_sse2 +public blake3_compress_in_place_sse2 +public _blake3_compress_in_place_sse2 +public blake3_compress_xof_sse2 +public _blake3_compress_xof_sse2 + +_TEXT SEGMENT ALIGN(16) 'CODE' + +ALIGN 16 +blake3_hash_many_sse2 PROC +_blake3_hash_many_sse2 PROC + push r15 + push r14 + push r13 + push r12 + push rsi + push rdi + push rbx + push rbp + mov rbp, rsp + sub rsp, 528 + and rsp, 0FFFFFFFFFFFFFFC0H + movdqa xmmword ptr [rsp+170H], xmm6 + movdqa xmmword ptr [rsp+180H], xmm7 + movdqa xmmword ptr [rsp+190H], xmm8 + movdqa xmmword ptr [rsp+1A0H], xmm9 + movdqa xmmword ptr [rsp+1B0H], xmm10 + movdqa xmmword ptr [rsp+1C0H], xmm11 + movdqa xmmword ptr [rsp+1D0H], xmm12 + movdqa xmmword ptr [rsp+1E0H], xmm13 + movdqa xmmword ptr [rsp+1F0H], xmm14 + movdqa xmmword ptr [rsp+200H], xmm15 + mov rdi, rcx + mov rsi, rdx + mov rdx, r8 + mov rcx, r9 + mov r8, qword ptr [rbp+68H] + movzx r9, byte ptr [rbp+70H] + neg r9d + movd xmm0, r9d + pshufd xmm0, xmm0, 00H + movdqa xmmword ptr [rsp+130H], xmm0 + movdqa xmm1, xmm0 + pand xmm1, xmmword ptr [ADD0] + pand xmm0, xmmword ptr [ADD1] + movdqa xmmword ptr [rsp+150H], xmm0 + movd xmm0, r8d + pshufd xmm0, xmm0, 00H + paddd xmm0, xmm1 + movdqa xmmword ptr [rsp+110H], xmm0 + pxor xmm0, xmmword ptr [CMP_MSB_MASK] + pxor xmm1, xmmword ptr [CMP_MSB_MASK] + pcmpgtd xmm1, xmm0 + shr r8, 32 + movd xmm2, r8d + pshufd xmm2, xmm2, 00H + psubd xmm2, xmm1 + movdqa xmmword ptr [rsp+120H], xmm2 + mov rbx, qword ptr [rbp+90H] + mov r15, rdx + shl r15, 6 + movzx r13d, byte ptr [rbp+78H] + movzx r12d, byte ptr [rbp+88H] + cmp rsi, 4 + jc final3blocks +outerloop4: + movdqu xmm3, xmmword ptr [rcx] + pshufd xmm0, xmm3, 00H + pshufd xmm1, xmm3, 55H + pshufd xmm2, xmm3, 0AAH + pshufd xmm3, xmm3, 0FFH + movdqu xmm7, xmmword ptr [rcx+10H] + pshufd xmm4, xmm7, 00H + pshufd xmm5, xmm7, 55H + pshufd xmm6, xmm7, 0AAH + pshufd xmm7, xmm7, 0FFH + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+8H] + mov r10, qword ptr [rdi+10H] + mov r11, qword ptr [rdi+18H] + movzx eax, byte ptr [rbp+80H] + or eax, r13d + xor edx, edx +innerloop4: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + movdqu xmm8, xmmword ptr [r8+rdx-40H] + movdqu xmm9, xmmword ptr [r9+rdx-40H] + movdqu xmm10, xmmword ptr [r10+rdx-40H] + movdqu xmm11, xmmword ptr [r11+rdx-40H] + movdqa xmm12, xmm8 + punpckldq xmm8, xmm9 + punpckhdq xmm12, xmm9 + movdqa xmm14, xmm10 + punpckldq xmm10, xmm11 + punpckhdq xmm14, xmm11 + movdqa xmm9, xmm8 + punpcklqdq xmm8, xmm10 + punpckhqdq xmm9, xmm10 + movdqa xmm13, xmm12 + punpcklqdq xmm12, xmm14 + punpckhqdq xmm13, xmm14 + movdqa xmmword ptr [rsp], xmm8 + movdqa xmmword ptr [rsp+10H], xmm9 + movdqa xmmword ptr [rsp+20H], xmm12 + movdqa xmmword ptr [rsp+30H], xmm13 + movdqu xmm8, xmmword ptr [r8+rdx-30H] + movdqu xmm9, xmmword ptr [r9+rdx-30H] + movdqu xmm10, xmmword ptr [r10+rdx-30H] + movdqu xmm11, xmmword ptr [r11+rdx-30H] + movdqa xmm12, xmm8 + punpckldq xmm8, xmm9 + punpckhdq xmm12, xmm9 + movdqa xmm14, xmm10 + punpckldq xmm10, xmm11 + punpckhdq xmm14, xmm11 + movdqa xmm9, xmm8 + punpcklqdq xmm8, xmm10 + punpckhqdq xmm9, xmm10 + movdqa xmm13, xmm12 + punpcklqdq xmm12, xmm14 + punpckhqdq xmm13, xmm14 + movdqa xmmword ptr [rsp+40H], xmm8 + movdqa xmmword ptr [rsp+50H], xmm9 + movdqa xmmword ptr [rsp+60H], xmm12 + movdqa xmmword ptr [rsp+70H], xmm13 + movdqu xmm8, xmmword ptr [r8+rdx-20H] + movdqu xmm9, xmmword ptr [r9+rdx-20H] + movdqu xmm10, xmmword ptr [r10+rdx-20H] + movdqu xmm11, xmmword ptr [r11+rdx-20H] + movdqa xmm12, xmm8 + punpckldq xmm8, xmm9 + punpckhdq xmm12, xmm9 + movdqa xmm14, xmm10 + punpckldq xmm10, xmm11 + punpckhdq xmm14, xmm11 + movdqa xmm9, xmm8 + punpcklqdq xmm8, xmm10 + punpckhqdq xmm9, xmm10 + movdqa xmm13, xmm12 + punpcklqdq xmm12, xmm14 + punpckhqdq xmm13, xmm14 + movdqa xmmword ptr [rsp+80H], xmm8 + movdqa xmmword ptr [rsp+90H], xmm9 + movdqa xmmword ptr [rsp+0A0H], xmm12 + movdqa xmmword ptr [rsp+0B0H], xmm13 + movdqu xmm8, xmmword ptr [r8+rdx-10H] + movdqu xmm9, xmmword ptr [r9+rdx-10H] + movdqu xmm10, xmmword ptr [r10+rdx-10H] + movdqu xmm11, xmmword ptr [r11+rdx-10H] + movdqa xmm12, xmm8 + punpckldq xmm8, xmm9 + punpckhdq xmm12, xmm9 + movdqa xmm14, xmm10 + punpckldq xmm10, xmm11 + punpckhdq xmm14, xmm11 + movdqa xmm9, xmm8 + punpcklqdq xmm8, xmm10 + punpckhqdq xmm9, xmm10 + movdqa xmm13, xmm12 + punpcklqdq xmm12, xmm14 + punpckhqdq xmm13, xmm14 + movdqa xmmword ptr [rsp+0C0H], xmm8 + movdqa xmmword ptr [rsp+0D0H], xmm9 + movdqa xmmword ptr [rsp+0E0H], xmm12 + movdqa xmmword ptr [rsp+0F0H], xmm13 + movdqa xmm9, xmmword ptr [BLAKE3_IV_1] + movdqa xmm10, xmmword ptr [BLAKE3_IV_2] + movdqa xmm11, xmmword ptr [BLAKE3_IV_3] + movdqa xmm12, xmmword ptr [rsp+110H] + movdqa xmm13, xmmword ptr [rsp+120H] + movdqa xmm14, xmmword ptr [BLAKE3_BLOCK_LEN] + movd xmm15, eax + pshufd xmm15, xmm15, 00H + prefetcht0 byte ptr [r8+rdx+80H] + prefetcht0 byte ptr [r9+rdx+80H] + prefetcht0 byte ptr [r10+rdx+80H] + prefetcht0 byte ptr [r11+rdx+80H] + paddd xmm0, xmmword ptr [rsp] + paddd xmm1, xmmword ptr [rsp+20H] + paddd xmm2, xmmword ptr [rsp+40H] + paddd xmm3, xmmword ptr [rsp+60H] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0B1H + pshufhw xmm12, xmm12, 0B1H + pshuflw xmm13, xmm13, 0B1H + pshufhw xmm13, xmm13, 0B1H + pshuflw xmm14, xmm14, 0B1H + pshufhw xmm14, xmm14, 0B1H + pshuflw xmm15, xmm15, 0B1H + pshufhw xmm15, xmm15, 0B1H + movdqa xmm8, xmmword ptr [BLAKE3_IV_0] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+10H] + paddd xmm1, xmmword ptr [rsp+30H] + paddd xmm2, xmmword ptr [rsp+50H] + paddd xmm3, xmmword ptr [rsp+70H] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+80H] + paddd xmm1, xmmword ptr [rsp+0A0H] + paddd xmm2, xmmword ptr [rsp+0C0H] + paddd xmm3, xmmword ptr [rsp+0E0H] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0B1H + pshufhw xmm15, xmm15, 0B1H + pshuflw xmm12, xmm12, 0B1H + pshufhw xmm12, xmm12, 0B1H + pshuflw xmm13, xmm13, 0B1H + pshufhw xmm13, xmm13, 0B1H + pshuflw xmm14, xmm14, 0B1H + pshufhw xmm14, xmm14, 0B1H + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+90H] + paddd xmm1, xmmword ptr [rsp+0B0H] + paddd xmm2, xmmword ptr [rsp+0D0H] + paddd xmm3, xmmword ptr [rsp+0F0H] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+20H] + paddd xmm1, xmmword ptr [rsp+30H] + paddd xmm2, xmmword ptr [rsp+70H] + paddd xmm3, xmmword ptr [rsp+40H] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0B1H + pshufhw xmm12, xmm12, 0B1H + pshuflw xmm13, xmm13, 0B1H + pshufhw xmm13, xmm13, 0B1H + pshuflw xmm14, xmm14, 0B1H + pshufhw xmm14, xmm14, 0B1H + pshuflw xmm15, xmm15, 0B1H + pshufhw xmm15, xmm15, 0B1H + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+60H] + paddd xmm1, xmmword ptr [rsp+0A0H] + paddd xmm2, xmmword ptr [rsp] + paddd xmm3, xmmword ptr [rsp+0D0H] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+10H] + paddd xmm1, xmmword ptr [rsp+0C0H] + paddd xmm2, xmmword ptr [rsp+90H] + paddd xmm3, xmmword ptr [rsp+0F0H] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0B1H + pshufhw xmm15, xmm15, 0B1H + pshuflw xmm12, xmm12, 0B1H + pshufhw xmm12, xmm12, 0B1H + pshuflw xmm13, xmm13, 0B1H + pshufhw xmm13, xmm13, 0B1H + pshuflw xmm14, xmm14, 0B1H + pshufhw xmm14, xmm14, 0B1H + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0B0H] + paddd xmm1, xmmword ptr [rsp+50H] + paddd xmm2, xmmword ptr [rsp+0E0H] + paddd xmm3, xmmword ptr [rsp+80H] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+30H] + paddd xmm1, xmmword ptr [rsp+0A0H] + paddd xmm2, xmmword ptr [rsp+0D0H] + paddd xmm3, xmmword ptr [rsp+70H] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0B1H + pshufhw xmm12, xmm12, 0B1H + pshuflw xmm13, xmm13, 0B1H + pshufhw xmm13, xmm13, 0B1H + pshuflw xmm14, xmm14, 0B1H + pshufhw xmm14, xmm14, 0B1H + pshuflw xmm15, xmm15, 0B1H + pshufhw xmm15, xmm15, 0B1H + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+40H] + paddd xmm1, xmmword ptr [rsp+0C0H] + paddd xmm2, xmmword ptr [rsp+20H] + paddd xmm3, xmmword ptr [rsp+0E0H] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+60H] + paddd xmm1, xmmword ptr [rsp+90H] + paddd xmm2, xmmword ptr [rsp+0B0H] + paddd xmm3, xmmword ptr [rsp+80H] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0B1H + pshufhw xmm15, xmm15, 0B1H + pshuflw xmm12, xmm12, 0B1H + pshufhw xmm12, xmm12, 0B1H + pshuflw xmm13, xmm13, 0B1H + pshufhw xmm13, xmm13, 0B1H + pshuflw xmm14, xmm14, 0B1H + pshufhw xmm14, xmm14, 0B1H + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+50H] + paddd xmm1, xmmword ptr [rsp] + paddd xmm2, xmmword ptr [rsp+0F0H] + paddd xmm3, xmmword ptr [rsp+10H] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0A0H] + paddd xmm1, xmmword ptr [rsp+0C0H] + paddd xmm2, xmmword ptr [rsp+0E0H] + paddd xmm3, xmmword ptr [rsp+0D0H] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0B1H + pshufhw xmm12, xmm12, 0B1H + pshuflw xmm13, xmm13, 0B1H + pshufhw xmm13, xmm13, 0B1H + pshuflw xmm14, xmm14, 0B1H + pshufhw xmm14, xmm14, 0B1H + pshuflw xmm15, xmm15, 0B1H + pshufhw xmm15, xmm15, 0B1H + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+70H] + paddd xmm1, xmmword ptr [rsp+90H] + paddd xmm2, xmmword ptr [rsp+30H] + paddd xmm3, xmmword ptr [rsp+0F0H] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+40H] + paddd xmm1, xmmword ptr [rsp+0B0H] + paddd xmm2, xmmword ptr [rsp+50H] + paddd xmm3, xmmword ptr [rsp+10H] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0B1H + pshufhw xmm15, xmm15, 0B1H + pshuflw xmm12, xmm12, 0B1H + pshufhw xmm12, xmm12, 0B1H + pshuflw xmm13, xmm13, 0B1H + pshufhw xmm13, xmm13, 0B1H + pshuflw xmm14, xmm14, 0B1H + pshufhw xmm14, xmm14, 0B1H + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp] + paddd xmm1, xmmword ptr [rsp+20H] + paddd xmm2, xmmword ptr [rsp+80H] + paddd xmm3, xmmword ptr [rsp+60H] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0C0H] + paddd xmm1, xmmword ptr [rsp+90H] + paddd xmm2, xmmword ptr [rsp+0F0H] + paddd xmm3, xmmword ptr [rsp+0E0H] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0B1H + pshufhw xmm12, xmm12, 0B1H + pshuflw xmm13, xmm13, 0B1H + pshufhw xmm13, xmm13, 0B1H + pshuflw xmm14, xmm14, 0B1H + pshufhw xmm14, xmm14, 0B1H + pshuflw xmm15, xmm15, 0B1H + pshufhw xmm15, xmm15, 0B1H + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0D0H] + paddd xmm1, xmmword ptr [rsp+0B0H] + paddd xmm2, xmmword ptr [rsp+0A0H] + paddd xmm3, xmmword ptr [rsp+80H] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+70H] + paddd xmm1, xmmword ptr [rsp+50H] + paddd xmm2, xmmword ptr [rsp] + paddd xmm3, xmmword ptr [rsp+60H] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0B1H + pshufhw xmm15, xmm15, 0B1H + pshuflw xmm12, xmm12, 0B1H + pshufhw xmm12, xmm12, 0B1H + pshuflw xmm13, xmm13, 0B1H + pshufhw xmm13, xmm13, 0B1H + pshuflw xmm14, xmm14, 0B1H + pshufhw xmm14, xmm14, 0B1H + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+20H] + paddd xmm1, xmmword ptr [rsp+30H] + paddd xmm2, xmmword ptr [rsp+10H] + paddd xmm3, xmmword ptr [rsp+40H] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+90H] + paddd xmm1, xmmword ptr [rsp+0B0H] + paddd xmm2, xmmword ptr [rsp+80H] + paddd xmm3, xmmword ptr [rsp+0F0H] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0B1H + pshufhw xmm12, xmm12, 0B1H + pshuflw xmm13, xmm13, 0B1H + pshufhw xmm13, xmm13, 0B1H + pshuflw xmm14, xmm14, 0B1H + pshufhw xmm14, xmm14, 0B1H + pshuflw xmm15, xmm15, 0B1H + pshufhw xmm15, xmm15, 0B1H + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0E0H] + paddd xmm1, xmmword ptr [rsp+50H] + paddd xmm2, xmmword ptr [rsp+0C0H] + paddd xmm3, xmmword ptr [rsp+10H] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0D0H] + paddd xmm1, xmmword ptr [rsp] + paddd xmm2, xmmword ptr [rsp+20H] + paddd xmm3, xmmword ptr [rsp+40H] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0B1H + pshufhw xmm15, xmm15, 0B1H + pshuflw xmm12, xmm12, 0B1H + pshufhw xmm12, xmm12, 0B1H + pshuflw xmm13, xmm13, 0B1H + pshufhw xmm13, xmm13, 0B1H + pshuflw xmm14, xmm14, 0B1H + pshufhw xmm14, xmm14, 0B1H + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+30H] + paddd xmm1, xmmword ptr [rsp+0A0H] + paddd xmm2, xmmword ptr [rsp+60H] + paddd xmm3, xmmword ptr [rsp+70H] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0B0H] + paddd xmm1, xmmword ptr [rsp+50H] + paddd xmm2, xmmword ptr [rsp+10H] + paddd xmm3, xmmword ptr [rsp+80H] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + pshuflw xmm12, xmm12, 0B1H + pshufhw xmm12, xmm12, 0B1H + pshuflw xmm13, xmm13, 0B1H + pshufhw xmm13, xmm13, 0B1H + pshuflw xmm14, xmm14, 0B1H + pshufhw xmm14, xmm14, 0B1H + pshuflw xmm15, xmm15, 0B1H + pshufhw xmm15, xmm15, 0B1H + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0F0H] + paddd xmm1, xmmword ptr [rsp] + paddd xmm2, xmmword ptr [rsp+90H] + paddd xmm3, xmmword ptr [rsp+60H] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0E0H] + paddd xmm1, xmmword ptr [rsp+20H] + paddd xmm2, xmmword ptr [rsp+30H] + paddd xmm3, xmmword ptr [rsp+70H] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + pshuflw xmm15, xmm15, 0B1H + pshufhw xmm15, xmm15, 0B1H + pshuflw xmm12, xmm12, 0B1H + pshufhw xmm12, xmm12, 0B1H + pshuflw xmm13, xmm13, 0B1H + pshufhw xmm13, xmm13, 0B1H + pshuflw xmm14, xmm14, 0B1H + pshufhw xmm14, xmm14, 0B1H + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+100H], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0A0H] + paddd xmm1, xmmword ptr [rsp+0C0H] + paddd xmm2, xmmword ptr [rsp+40H] + paddd xmm3, xmmword ptr [rsp+0D0H] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmm15 + psrld xmm15, 8 + pslld xmm8, 24 + pxor xmm15, xmm8 + movdqa xmm8, xmm12 + psrld xmm12, 8 + pslld xmm8, 24 + pxor xmm12, xmm8 + movdqa xmm8, xmm13 + psrld xmm13, 8 + pslld xmm8, 24 + pxor xmm13, xmm8 + movdqa xmm8, xmm14 + psrld xmm14, 8 + pslld xmm8, 24 + pxor xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+100H] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + pxor xmm0, xmm8 + pxor xmm1, xmm9 + pxor xmm2, xmm10 + pxor xmm3, xmm11 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + pxor xmm4, xmm12 + pxor xmm5, xmm13 + pxor xmm6, xmm14 + pxor xmm7, xmm15 + mov eax, r13d + jne innerloop4 + movdqa xmm9, xmm0 + punpckldq xmm0, xmm1 + punpckhdq xmm9, xmm1 + movdqa xmm11, xmm2 + punpckldq xmm2, xmm3 + punpckhdq xmm11, xmm3 + movdqa xmm1, xmm0 + punpcklqdq xmm0, xmm2 + punpckhqdq xmm1, xmm2 + movdqa xmm3, xmm9 + punpcklqdq xmm9, xmm11 + punpckhqdq xmm3, xmm11 + movdqu xmmword ptr [rbx], xmm0 + movdqu xmmword ptr [rbx+20H], xmm1 + movdqu xmmword ptr [rbx+40H], xmm9 + movdqu xmmword ptr [rbx+60H], xmm3 + movdqa xmm9, xmm4 + punpckldq xmm4, xmm5 + punpckhdq xmm9, xmm5 + movdqa xmm11, xmm6 + punpckldq xmm6, xmm7 + punpckhdq xmm11, xmm7 + movdqa xmm5, xmm4 + punpcklqdq xmm4, xmm6 + punpckhqdq xmm5, xmm6 + movdqa xmm7, xmm9 + punpcklqdq xmm9, xmm11 + punpckhqdq xmm7, xmm11 + movdqu xmmword ptr [rbx+10H], xmm4 + movdqu xmmword ptr [rbx+30H], xmm5 + movdqu xmmword ptr [rbx+50H], xmm9 + movdqu xmmword ptr [rbx+70H], xmm7 + movdqa xmm1, xmmword ptr [rsp+110H] + movdqa xmm0, xmm1 + paddd xmm1, xmmword ptr [rsp+150H] + movdqa xmmword ptr [rsp+110H], xmm1 + pxor xmm0, xmmword ptr [CMP_MSB_MASK] + pxor xmm1, xmmword ptr [CMP_MSB_MASK] + pcmpgtd xmm0, xmm1 + movdqa xmm1, xmmword ptr [rsp+120H] + psubd xmm1, xmm0 + movdqa xmmword ptr [rsp+120H], xmm1 + add rbx, 128 + add rdi, 32 + sub rsi, 4 + cmp rsi, 4 + jnc outerloop4 + test rsi, rsi + jne final3blocks +unwind: + movdqa xmm6, xmmword ptr [rsp+170H] + movdqa xmm7, xmmword ptr [rsp+180H] + movdqa xmm8, xmmword ptr [rsp+190H] + movdqa xmm9, xmmword ptr [rsp+1A0H] + movdqa xmm10, xmmword ptr [rsp+1B0H] + movdqa xmm11, xmmword ptr [rsp+1C0H] + movdqa xmm12, xmmword ptr [rsp+1D0H] + movdqa xmm13, xmmword ptr [rsp+1E0H] + movdqa xmm14, xmmword ptr [rsp+1F0H] + movdqa xmm15, xmmword ptr [rsp+200H] + mov rsp, rbp + pop rbp + pop rbx + pop rdi + pop rsi + pop r12 + pop r13 + pop r14 + pop r15 + ret +ALIGN 16 +final3blocks: + test esi, 2H + je final1block + movups xmm0, xmmword ptr [rcx] + movups xmm1, xmmword ptr [rcx+10H] + movaps xmm8, xmm0 + movaps xmm9, xmm1 + movd xmm13, dword ptr [rsp+110H] + movd xmm14, dword ptr [rsp+120H] + punpckldq xmm13, xmm14 + movaps xmmword ptr [rsp], xmm13 + movd xmm14, dword ptr [rsp+114H] + movd xmm13, dword ptr [rsp+124H] + punpckldq xmm14, xmm13 + movaps xmmword ptr [rsp+10H], xmm14 + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+8H] + movzx eax, byte ptr [rbp+80H] + or eax, r13d + xor edx, edx +innerloop2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + movaps xmm2, xmmword ptr [BLAKE3_IV] + movaps xmm10, xmm2 + movups xmm4, xmmword ptr [r8+rdx-40H] + movups xmm5, xmmword ptr [r8+rdx-30H] + movaps xmm3, xmm4 + shufps xmm4, xmm5, 136 + shufps xmm3, xmm5, 221 + movaps xmm5, xmm3 + movups xmm6, xmmword ptr [r8+rdx-20H] + movups xmm7, xmmword ptr [r8+rdx-10H] + movaps xmm3, xmm6 + shufps xmm6, xmm7, 136 + pshufd xmm6, xmm6, 93H + shufps xmm3, xmm7, 221 + pshufd xmm7, xmm3, 93H + movups xmm12, xmmword ptr [r9+rdx-40H] + movups xmm13, xmmword ptr [r9+rdx-30H] + movaps xmm11, xmm12 + shufps xmm12, xmm13, 136 + shufps xmm11, xmm13, 221 + movaps xmm13, xmm11 + movups xmm14, xmmword ptr [r9+rdx-20H] + movups xmm15, xmmword ptr [r9+rdx-10H] + movaps xmm11, xmm14 + shufps xmm14, xmm15, 136 + pshufd xmm14, xmm14, 93H + shufps xmm11, xmm15, 221 + pshufd xmm15, xmm11, 93H + shl rax, 20H + or rax, 40H + movd xmm3, rax + movdqa xmmword ptr [rsp+20H], xmm3 + movaps xmm3, xmmword ptr [rsp] + movaps xmm11, xmmword ptr [rsp+10H] + punpcklqdq xmm3, xmmword ptr [rsp+20H] + punpcklqdq xmm11, xmmword ptr [rsp+20H] + mov al, 7 +roundloop2: + paddd xmm0, xmm4 + paddd xmm8, xmm12 + movaps xmmword ptr [rsp+20H], xmm4 + movaps xmmword ptr [rsp+30H], xmm12 + paddd xmm0, xmm1 + paddd xmm8, xmm9 + pxor xmm3, xmm0 + pxor xmm11, xmm8 + pshuflw xmm3, xmm3, 0B1H + pshufhw xmm3, xmm3, 0B1H + pshuflw xmm11, xmm11, 0B1H + pshufhw xmm11, xmm11, 0B1H + paddd xmm2, xmm3 + paddd xmm10, xmm11 + pxor xmm1, xmm2 + pxor xmm9, xmm10 + movdqa xmm4, xmm1 + pslld xmm1, 20 + psrld xmm4, 12 + por xmm1, xmm4 + movdqa xmm4, xmm9 + pslld xmm9, 20 + psrld xmm4, 12 + por xmm9, xmm4 + paddd xmm0, xmm5 + paddd xmm8, xmm13 + movaps xmmword ptr [rsp+40H], xmm5 + movaps xmmword ptr [rsp+50H], xmm13 + paddd xmm0, xmm1 + paddd xmm8, xmm9 + pxor xmm3, xmm0 + pxor xmm11, xmm8 + movdqa xmm13, xmm3 + psrld xmm3, 8 + pslld xmm13, 24 + pxor xmm3, xmm13 + movdqa xmm13, xmm11 + psrld xmm11, 8 + pslld xmm13, 24 + pxor xmm11, xmm13 + paddd xmm2, xmm3 + paddd xmm10, xmm11 + pxor xmm1, xmm2 + pxor xmm9, xmm10 + movdqa xmm4, xmm1 + pslld xmm1, 25 + psrld xmm4, 7 + por xmm1, xmm4 + movdqa xmm4, xmm9 + pslld xmm9, 25 + psrld xmm4, 7 + por xmm9, xmm4 + pshufd xmm0, xmm0, 93H + pshufd xmm8, xmm8, 93H + pshufd xmm3, xmm3, 4EH + pshufd xmm11, xmm11, 4EH + pshufd xmm2, xmm2, 39H + pshufd xmm10, xmm10, 39H + paddd xmm0, xmm6 + paddd xmm8, xmm14 + paddd xmm0, xmm1 + paddd xmm8, xmm9 + pxor xmm3, xmm0 + pxor xmm11, xmm8 + pshuflw xmm3, xmm3, 0B1H + pshufhw xmm3, xmm3, 0B1H + pshuflw xmm11, xmm11, 0B1H + pshufhw xmm11, xmm11, 0B1H + paddd xmm2, xmm3 + paddd xmm10, xmm11 + pxor xmm1, xmm2 + pxor xmm9, xmm10 + movdqa xmm4, xmm1 + pslld xmm1, 20 + psrld xmm4, 12 + por xmm1, xmm4 + movdqa xmm4, xmm9 + pslld xmm9, 20 + psrld xmm4, 12 + por xmm9, xmm4 + paddd xmm0, xmm7 + paddd xmm8, xmm15 + paddd xmm0, xmm1 + paddd xmm8, xmm9 + pxor xmm3, xmm0 + pxor xmm11, xmm8 + movdqa xmm13, xmm3 + psrld xmm3, 8 + pslld xmm13, 24 + pxor xmm3, xmm13 + movdqa xmm13, xmm11 + psrld xmm11, 8 + pslld xmm13, 24 + pxor xmm11, xmm13 + paddd xmm2, xmm3 + paddd xmm10, xmm11 + pxor xmm1, xmm2 + pxor xmm9, xmm10 + movdqa xmm4, xmm1 + pslld xmm1, 25 + psrld xmm4, 7 + por xmm1, xmm4 + movdqa xmm4, xmm9 + pslld xmm9, 25 + psrld xmm4, 7 + por xmm9, xmm4 + pshufd xmm0, xmm0, 39H + pshufd xmm8, xmm8, 39H + pshufd xmm3, xmm3, 4EH + pshufd xmm11, xmm11, 4EH + pshufd xmm2, xmm2, 93H + pshufd xmm10, xmm10, 93H + dec al + je endroundloop2 + movdqa xmm12, xmmword ptr [rsp+20H] + movdqa xmm5, xmmword ptr [rsp+40H] + pshufd xmm13, xmm12, 0FH + shufps xmm12, xmm5, 214 + pshufd xmm4, xmm12, 39H + movdqa xmm12, xmm6 + shufps xmm12, xmm7, 250 + pand xmm13, xmmword ptr [PBLENDW_0x33_MASK] + pand xmm12, xmmword ptr [PBLENDW_0xCC_MASK] + por xmm13, xmm12 + movdqa xmmword ptr [rsp+20H], xmm13 + movdqa xmm12, xmm7 + punpcklqdq xmm12, xmm5 + movdqa xmm13, xmm6 + pand xmm12, xmmword ptr [PBLENDW_0x3F_MASK] + pand xmm13, xmmword ptr [PBLENDW_0xC0_MASK] + por xmm12, xmm13 + pshufd xmm12, xmm12, 78H + punpckhdq xmm5, xmm7 + punpckldq xmm6, xmm5 + pshufd xmm7, xmm6, 1EH + movdqa xmmword ptr [rsp+40H], xmm12 + movdqa xmm5, xmmword ptr [rsp+30H] + movdqa xmm13, xmmword ptr [rsp+50H] + pshufd xmm6, xmm5, 0FH + shufps xmm5, xmm13, 214 + pshufd xmm12, xmm5, 39H + movdqa xmm5, xmm14 + shufps xmm5, xmm15, 250 + pand xmm6, xmmword ptr [PBLENDW_0x33_MASK] + pand xmm5, xmmword ptr [PBLENDW_0xCC_MASK] + por xmm6, xmm5 + movdqa xmm5, xmm15 + punpcklqdq xmm5, xmm13 + movdqa xmmword ptr [rsp+30H], xmm2 + movdqa xmm2, xmm14 + pand xmm5, xmmword ptr [PBLENDW_0x3F_MASK] + pand xmm2, xmmword ptr [PBLENDW_0xC0_MASK] + por xmm5, xmm2 + movdqa xmm2, xmmword ptr [rsp+30H] + pshufd xmm5, xmm5, 78H + punpckhdq xmm13, xmm15 + punpckldq xmm14, xmm13 + pshufd xmm15, xmm14, 1EH + movdqa xmm13, xmm6 + movdqa xmm14, xmm5 + movdqa xmm5, xmmword ptr [rsp+20H] + movdqa xmm6, xmmword ptr [rsp+40H] + jmp roundloop2 +endroundloop2: + pxor xmm0, xmm2 + pxor xmm1, xmm3 + pxor xmm8, xmm10 + pxor xmm9, xmm11 + mov eax, r13d + cmp rdx, r15 + jne innerloop2 + movups xmmword ptr [rbx], xmm0 + movups xmmword ptr [rbx+10H], xmm1 + movups xmmword ptr [rbx+20H], xmm8 + movups xmmword ptr [rbx+30H], xmm9 + mov eax, dword ptr [rsp+130H] + neg eax + mov r10d, dword ptr [rsp+110H+8*rax] + mov r11d, dword ptr [rsp+120H+8*rax] + mov dword ptr [rsp+110H], r10d + mov dword ptr [rsp+120H], r11d + add rdi, 16 + add rbx, 64 + sub rsi, 2 +final1block: + test esi, 1H + je unwind + movups xmm0, xmmword ptr [rcx] + movups xmm1, xmmword ptr [rcx+10H] + movd xmm13, dword ptr [rsp+110H] + movd xmm14, dword ptr [rsp+120H] + punpckldq xmm13, xmm14 + mov r8, qword ptr [rdi] + movzx eax, byte ptr [rbp+80H] + or eax, r13d + xor edx, edx +innerloop1: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + movaps xmm2, xmmword ptr [BLAKE3_IV] + shl rax, 32 + or rax, 64 + movd xmm12, rax + movdqa xmm3, xmm13 + punpcklqdq xmm3, xmm12 + movups xmm4, xmmword ptr [r8+rdx-40H] + movups xmm5, xmmword ptr [r8+rdx-30H] + movaps xmm8, xmm4 + shufps xmm4, xmm5, 136 + shufps xmm8, xmm5, 221 + movaps xmm5, xmm8 + movups xmm6, xmmword ptr [r8+rdx-20H] + movups xmm7, xmmword ptr [r8+rdx-10H] + movaps xmm8, xmm6 + shufps xmm6, xmm7, 136 + pshufd xmm6, xmm6, 93H + shufps xmm8, xmm7, 221 + pshufd xmm7, xmm8, 93H + mov al, 7 +roundloop1: + paddd xmm0, xmm4 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshuflw xmm3, xmm3, 0B1H + pshufhw xmm3, xmm3, 0B1H + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm5 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + movdqa xmm14, xmm3 + psrld xmm3, 8 + pslld xmm14, 24 + pxor xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 93H + pshufd xmm3, xmm3, 4EH + pshufd xmm2, xmm2, 39H + paddd xmm0, xmm6 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshuflw xmm3, xmm3, 0B1H + pshufhw xmm3, xmm3, 0B1H + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm7 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + movdqa xmm14, xmm3 + psrld xmm3, 8 + pslld xmm14, 24 + pxor xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 39H + pshufd xmm3, xmm3, 4EH + pshufd xmm2, xmm2, 93H + dec al + jz endroundloop1 + movdqa xmm8, xmm4 + shufps xmm8, xmm5, 214 + pshufd xmm9, xmm4, 0FH + pshufd xmm4, xmm8, 39H + movdqa xmm8, xmm6 + shufps xmm8, xmm7, 250 + pand xmm9, xmmword ptr [PBLENDW_0x33_MASK] + pand xmm8, xmmword ptr [PBLENDW_0xCC_MASK] + por xmm9, xmm8 + movdqa xmm8, xmm7 + punpcklqdq xmm8, xmm5 + movdqa xmm10, xmm6 + pand xmm8, xmmword ptr [PBLENDW_0x3F_MASK] + pand xmm10, xmmword ptr [PBLENDW_0xC0_MASK] + por xmm8, xmm10 + pshufd xmm8, xmm8, 78H + punpckhdq xmm5, xmm7 + punpckldq xmm6, xmm5 + pshufd xmm7, xmm6, 1EH + movdqa xmm5, xmm9 + movdqa xmm6, xmm8 + jmp roundloop1 +endroundloop1: + pxor xmm0, xmm2 + pxor xmm1, xmm3 + mov eax, r13d + cmp rdx, r15 + jne innerloop1 + movups xmmword ptr [rbx], xmm0 + movups xmmword ptr [rbx+10H], xmm1 + jmp unwind +_blake3_hash_many_sse2 ENDP +blake3_hash_many_sse2 ENDP + +blake3_compress_in_place_sse2 PROC +_blake3_compress_in_place_sse2 PROC + sub rsp, 120 + movdqa xmmword ptr [rsp], xmm6 + movdqa xmmword ptr [rsp+10H], xmm7 + movdqa xmmword ptr [rsp+20H], xmm8 + movdqa xmmword ptr [rsp+30H], xmm9 + movdqa xmmword ptr [rsp+40H], xmm11 + movdqa xmmword ptr [rsp+50H], xmm14 + movdqa xmmword ptr [rsp+60H], xmm15 + movups xmm0, xmmword ptr [rcx] + movups xmm1, xmmword ptr [rcx+10H] + movaps xmm2, xmmword ptr [BLAKE3_IV] + movzx eax, byte ptr [rsp+0A0H] + movzx r8d, r8b + shl rax, 32 + add r8, rax + movq xmm3, r9 + movq xmm4, r8 + punpcklqdq xmm3, xmm4 + movups xmm4, xmmword ptr [rdx] + movups xmm5, xmmword ptr [rdx+10H] + movaps xmm8, xmm4 + shufps xmm4, xmm5, 136 + shufps xmm8, xmm5, 221 + movaps xmm5, xmm8 + movups xmm6, xmmword ptr [rdx+20H] + movups xmm7, xmmword ptr [rdx+30H] + movaps xmm8, xmm6 + shufps xmm6, xmm7, 136 + pshufd xmm6, xmm6, 93H + shufps xmm8, xmm7, 221 + pshufd xmm7, xmm8, 93H + mov al, 7 +@@: + paddd xmm0, xmm4 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshuflw xmm3, xmm3, 0B1H + pshufhw xmm3, xmm3, 0B1H + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm5 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + movdqa xmm14, xmm3 + psrld xmm3, 8 + pslld xmm14, 24 + pxor xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 93H + pshufd xmm3, xmm3, 4EH + pshufd xmm2, xmm2, 39H + paddd xmm0, xmm6 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshuflw xmm3, xmm3, 0B1H + pshufhw xmm3, xmm3, 0B1H + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm7 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + movdqa xmm14, xmm3 + psrld xmm3, 8 + pslld xmm14, 24 + pxor xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 39H + pshufd xmm3, xmm3, 4EH + pshufd xmm2, xmm2, 93H + dec al + jz @F + movdqa xmm8, xmm4 + shufps xmm8, xmm5, 214 + pshufd xmm9, xmm4, 0FH + pshufd xmm4, xmm8, 39H + movdqa xmm8, xmm6 + shufps xmm8, xmm7, 250 + pand xmm9, xmmword ptr [PBLENDW_0x33_MASK] + pand xmm8, xmmword ptr [PBLENDW_0xCC_MASK] + por xmm9, xmm8 + movdqa xmm8, xmm7 + punpcklqdq xmm8, xmm5 + movdqa xmm10, xmm6 + pand xmm8, xmmword ptr [PBLENDW_0x3F_MASK] + pand xmm10, xmmword ptr [PBLENDW_0xC0_MASK] + por xmm8, xmm10 + pshufd xmm8, xmm8, 78H + punpckhdq xmm5, xmm7 + punpckldq xmm6, xmm5 + pshufd xmm7, xmm6, 1EH + movdqa xmm5, xmm9 + movdqa xmm6, xmm8 + jmp @B +@@: + pxor xmm0, xmm2 + pxor xmm1, xmm3 + movups xmmword ptr [rcx], xmm0 + movups xmmword ptr [rcx+10H], xmm1 + movdqa xmm6, xmmword ptr [rsp] + movdqa xmm7, xmmword ptr [rsp+10H] + movdqa xmm8, xmmword ptr [rsp+20H] + movdqa xmm9, xmmword ptr [rsp+30H] + movdqa xmm11, xmmword ptr [rsp+40H] + movdqa xmm14, xmmword ptr [rsp+50H] + movdqa xmm15, xmmword ptr [rsp+60H] + add rsp, 120 + ret +_blake3_compress_in_place_sse2 ENDP +blake3_compress_in_place_sse2 ENDP + +ALIGN 16 +blake3_compress_xof_sse2 PROC +_blake3_compress_xof_sse2 PROC + sub rsp, 120 + movdqa xmmword ptr [rsp], xmm6 + movdqa xmmword ptr [rsp+10H], xmm7 + movdqa xmmword ptr [rsp+20H], xmm8 + movdqa xmmword ptr [rsp+30H], xmm9 + movdqa xmmword ptr [rsp+40H], xmm11 + movdqa xmmword ptr [rsp+50H], xmm14 + movdqa xmmword ptr [rsp+60H], xmm15 + movups xmm0, xmmword ptr [rcx] + movups xmm1, xmmword ptr [rcx+10H] + movaps xmm2, xmmword ptr [BLAKE3_IV] + movzx eax, byte ptr [rsp+0A0H] + movzx r8d, r8b + mov r10, qword ptr [rsp+0A8H] + shl rax, 32 + add r8, rax + movq xmm3, r9 + movq xmm4, r8 + punpcklqdq xmm3, xmm4 + movups xmm4, xmmword ptr [rdx] + movups xmm5, xmmword ptr [rdx+10H] + movaps xmm8, xmm4 + shufps xmm4, xmm5, 136 + shufps xmm8, xmm5, 221 + movaps xmm5, xmm8 + movups xmm6, xmmword ptr [rdx+20H] + movups xmm7, xmmword ptr [rdx+30H] + movaps xmm8, xmm6 + shufps xmm6, xmm7, 136 + pshufd xmm6, xmm6, 93H + shufps xmm8, xmm7, 221 + pshufd xmm7, xmm8, 93H + mov al, 7 +@@: + paddd xmm0, xmm4 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshuflw xmm3, xmm3, 0B1H + pshufhw xmm3, xmm3, 0B1H + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm5 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + movdqa xmm14, xmm3 + psrld xmm3, 8 + pslld xmm14, 24 + pxor xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 93H + pshufd xmm3, xmm3, 4EH + pshufd xmm2, xmm2, 39H + paddd xmm0, xmm6 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshuflw xmm3, xmm3, 0B1H + pshufhw xmm3, xmm3, 0B1H + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm7 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + movdqa xmm14, xmm3 + psrld xmm3, 8 + pslld xmm14, 24 + pxor xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 39H + pshufd xmm3, xmm3, 4EH + pshufd xmm2, xmm2, 93H + dec al + jz @F + movdqa xmm8, xmm4 + shufps xmm8, xmm5, 214 + pshufd xmm9, xmm4, 0FH + pshufd xmm4, xmm8, 39H + movdqa xmm8, xmm6 + shufps xmm8, xmm7, 250 + pand xmm9, xmmword ptr [PBLENDW_0x33_MASK] + pand xmm8, xmmword ptr [PBLENDW_0xCC_MASK] + por xmm9, xmm8 + movdqa xmm8, xmm7 + punpcklqdq xmm8, xmm5 + movdqa xmm10, xmm6 + pand xmm8, xmmword ptr [PBLENDW_0x3F_MASK] + pand xmm10, xmmword ptr [PBLENDW_0xC0_MASK] + por xmm8, xmm10 + pshufd xmm8, xmm8, 78H + punpckhdq xmm5, xmm7 + punpckldq xmm6, xmm5 + pshufd xmm7, xmm6, 1EH + movdqa xmm5, xmm9 + movdqa xmm6, xmm8 + jmp @B +@@: + movdqu xmm4, xmmword ptr [rcx] + movdqu xmm5, xmmword ptr [rcx+10H] + pxor xmm0, xmm2 + pxor xmm1, xmm3 + pxor xmm2, xmm4 + pxor xmm3, xmm5 + movups xmmword ptr [r10], xmm0 + movups xmmword ptr [r10+10H], xmm1 + movups xmmword ptr [r10+20H], xmm2 + movups xmmword ptr [r10+30H], xmm3 + movdqa xmm6, xmmword ptr [rsp] + movdqa xmm7, xmmword ptr [rsp+10H] + movdqa xmm8, xmmword ptr [rsp+20H] + movdqa xmm9, xmmword ptr [rsp+30H] + movdqa xmm11, xmmword ptr [rsp+40H] + movdqa xmm14, xmmword ptr [rsp+50H] + movdqa xmm15, xmmword ptr [rsp+60H] + add rsp, 120 + ret +_blake3_compress_xof_sse2 ENDP +blake3_compress_xof_sse2 ENDP + +_TEXT ENDS + + +_RDATA SEGMENT READONLY PAGE ALIAS(".rdata") 'CONST' +ALIGN 64 +BLAKE3_IV: + dd 6A09E667H, 0BB67AE85H, 3C6EF372H, 0A54FF53AH + +ADD0: + dd 0, 1, 2, 3 + +ADD1: + dd 4 dup (4) + +BLAKE3_IV_0: + dd 4 dup (6A09E667H) + +BLAKE3_IV_1: + dd 4 dup (0BB67AE85H) + +BLAKE3_IV_2: + dd 4 dup (3C6EF372H) + +BLAKE3_IV_3: + dd 4 dup (0A54FF53AH) + +BLAKE3_BLOCK_LEN: + dd 4 dup (64) + +CMP_MSB_MASK: + dd 8 dup(80000000H) + +PBLENDW_0x33_MASK: + dd 0FFFFFFFFH, 000000000H, 0FFFFFFFFH, 000000000H +PBLENDW_0xCC_MASK: + dd 000000000H, 0FFFFFFFFH, 000000000H, 0FFFFFFFFH +PBLENDW_0x3F_MASK: + dd 0FFFFFFFFH, 0FFFFFFFFH, 0FFFFFFFFH, 000000000H +PBLENDW_0xC0_MASK: + dd 000000000H, 000000000H, 000000000H, 0FFFFFFFFH + +_RDATA ENDS +END diff --git a/src/third_party/blake3/blake3_sse41.c b/src/third_party/blake3/blake3_sse41.c new file mode 100644 index 0000000..b311225 --- /dev/null +++ b/src/third_party/blake3/blake3_sse41.c @@ -0,0 +1,559 @@ +#include "blake3_impl.h" + +#include + +#define DEGREE 4 + +#define _mm_shuffle_ps2(a, b, c) \ + (_mm_castps_si128( \ + _mm_shuffle_ps(_mm_castsi128_ps(a), _mm_castsi128_ps(b), (c)))) + +INLINE __m128i loadu(const uint8_t src[16]) { + return _mm_loadu_si128((const __m128i *)src); +} + +INLINE void storeu(__m128i src, uint8_t dest[16]) { + _mm_storeu_si128((__m128i *)dest, src); +} + +INLINE __m128i addv(__m128i a, __m128i b) { return _mm_add_epi32(a, b); } + +// Note that clang-format doesn't like the name "xor" for some reason. +INLINE __m128i xorv(__m128i a, __m128i b) { return _mm_xor_si128(a, b); } + +INLINE __m128i set1(uint32_t x) { return _mm_set1_epi32((int32_t)x); } + +INLINE __m128i set4(uint32_t a, uint32_t b, uint32_t c, uint32_t d) { + return _mm_setr_epi32((int32_t)a, (int32_t)b, (int32_t)c, (int32_t)d); +} + +INLINE __m128i rot16(__m128i x) { + return _mm_shuffle_epi8( + x, _mm_set_epi8(13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2)); +} + +INLINE __m128i rot12(__m128i x) { + return xorv(_mm_srli_epi32(x, 12), _mm_slli_epi32(x, 32 - 12)); +} + +INLINE __m128i rot8(__m128i x) { + return _mm_shuffle_epi8( + x, _mm_set_epi8(12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1)); +} + +INLINE __m128i rot7(__m128i x) { + return xorv(_mm_srli_epi32(x, 7), _mm_slli_epi32(x, 32 - 7)); +} + +INLINE void g1(__m128i *row0, __m128i *row1, __m128i *row2, __m128i *row3, + __m128i m) { + *row0 = addv(addv(*row0, m), *row1); + *row3 = xorv(*row3, *row0); + *row3 = rot16(*row3); + *row2 = addv(*row2, *row3); + *row1 = xorv(*row1, *row2); + *row1 = rot12(*row1); +} + +INLINE void g2(__m128i *row0, __m128i *row1, __m128i *row2, __m128i *row3, + __m128i m) { + *row0 = addv(addv(*row0, m), *row1); + *row3 = xorv(*row3, *row0); + *row3 = rot8(*row3); + *row2 = addv(*row2, *row3); + *row1 = xorv(*row1, *row2); + *row1 = rot7(*row1); +} + +// Note the optimization here of leaving row1 as the unrotated row, rather than +// row0. All the message loads below are adjusted to compensate for this. See +// discussion at https://github.com/sneves/blake2-avx2/pull/4 +INLINE void diagonalize(__m128i *row0, __m128i *row2, __m128i *row3) { + *row0 = _mm_shuffle_epi32(*row0, _MM_SHUFFLE(2, 1, 0, 3)); + *row3 = _mm_shuffle_epi32(*row3, _MM_SHUFFLE(1, 0, 3, 2)); + *row2 = _mm_shuffle_epi32(*row2, _MM_SHUFFLE(0, 3, 2, 1)); +} + +INLINE void undiagonalize(__m128i *row0, __m128i *row2, __m128i *row3) { + *row0 = _mm_shuffle_epi32(*row0, _MM_SHUFFLE(0, 3, 2, 1)); + *row3 = _mm_shuffle_epi32(*row3, _MM_SHUFFLE(1, 0, 3, 2)); + *row2 = _mm_shuffle_epi32(*row2, _MM_SHUFFLE(2, 1, 0, 3)); +} + +INLINE void compress_pre(__m128i rows[4], const uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, uint8_t flags) { + rows[0] = loadu((uint8_t *)&cv[0]); + rows[1] = loadu((uint8_t *)&cv[4]); + rows[2] = set4(IV[0], IV[1], IV[2], IV[3]); + rows[3] = set4(counter_low(counter), counter_high(counter), + (uint32_t)block_len, (uint32_t)flags); + + __m128i m0 = loadu(&block[sizeof(__m128i) * 0]); + __m128i m1 = loadu(&block[sizeof(__m128i) * 1]); + __m128i m2 = loadu(&block[sizeof(__m128i) * 2]); + __m128i m3 = loadu(&block[sizeof(__m128i) * 3]); + + __m128i t0, t1, t2, t3, tt; + + // Round 1. The first round permutes the message words from the original + // input order, into the groups that get mixed in parallel. + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(2, 0, 2, 0)); // 6 4 2 0 + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 3, 1)); // 7 5 3 1 + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(2, 0, 2, 0)); // 14 12 10 8 + t2 = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2, 1, 0, 3)); // 12 10 8 14 + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 1, 3, 1)); // 15 13 11 9 + t3 = _mm_shuffle_epi32(t3, _MM_SHUFFLE(2, 1, 0, 3)); // 13 11 9 15 + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); + m0 = t0; + m1 = t1; + m2 = t2; + m3 = t3; + + // Round 2. This round and all following rounds apply a fixed permutation + // to the message words from the round before. + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 1, 2)); + t0 = _mm_shuffle_epi32(t0, _MM_SHUFFLE(0, 3, 2, 1)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 3, 2, 2)); + tt = _mm_shuffle_epi32(m0, _MM_SHUFFLE(0, 0, 3, 3)); + t1 = _mm_blend_epi16(tt, t1, 0xCC); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_unpacklo_epi64(m3, m1); + tt = _mm_blend_epi16(t2, m2, 0xC0); + t2 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(1, 3, 2, 0)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_unpackhi_epi32(m1, m3); + tt = _mm_unpacklo_epi32(m2, t3); + t3 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(0, 1, 3, 2)); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); + m0 = t0; + m1 = t1; + m2 = t2; + m3 = t3; + + // Round 3 + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 1, 2)); + t0 = _mm_shuffle_epi32(t0, _MM_SHUFFLE(0, 3, 2, 1)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 3, 2, 2)); + tt = _mm_shuffle_epi32(m0, _MM_SHUFFLE(0, 0, 3, 3)); + t1 = _mm_blend_epi16(tt, t1, 0xCC); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_unpacklo_epi64(m3, m1); + tt = _mm_blend_epi16(t2, m2, 0xC0); + t2 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(1, 3, 2, 0)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_unpackhi_epi32(m1, m3); + tt = _mm_unpacklo_epi32(m2, t3); + t3 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(0, 1, 3, 2)); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); + m0 = t0; + m1 = t1; + m2 = t2; + m3 = t3; + + // Round 4 + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 1, 2)); + t0 = _mm_shuffle_epi32(t0, _MM_SHUFFLE(0, 3, 2, 1)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 3, 2, 2)); + tt = _mm_shuffle_epi32(m0, _MM_SHUFFLE(0, 0, 3, 3)); + t1 = _mm_blend_epi16(tt, t1, 0xCC); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_unpacklo_epi64(m3, m1); + tt = _mm_blend_epi16(t2, m2, 0xC0); + t2 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(1, 3, 2, 0)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_unpackhi_epi32(m1, m3); + tt = _mm_unpacklo_epi32(m2, t3); + t3 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(0, 1, 3, 2)); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); + m0 = t0; + m1 = t1; + m2 = t2; + m3 = t3; + + // Round 5 + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 1, 2)); + t0 = _mm_shuffle_epi32(t0, _MM_SHUFFLE(0, 3, 2, 1)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 3, 2, 2)); + tt = _mm_shuffle_epi32(m0, _MM_SHUFFLE(0, 0, 3, 3)); + t1 = _mm_blend_epi16(tt, t1, 0xCC); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_unpacklo_epi64(m3, m1); + tt = _mm_blend_epi16(t2, m2, 0xC0); + t2 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(1, 3, 2, 0)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_unpackhi_epi32(m1, m3); + tt = _mm_unpacklo_epi32(m2, t3); + t3 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(0, 1, 3, 2)); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); + m0 = t0; + m1 = t1; + m2 = t2; + m3 = t3; + + // Round 6 + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 1, 2)); + t0 = _mm_shuffle_epi32(t0, _MM_SHUFFLE(0, 3, 2, 1)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 3, 2, 2)); + tt = _mm_shuffle_epi32(m0, _MM_SHUFFLE(0, 0, 3, 3)); + t1 = _mm_blend_epi16(tt, t1, 0xCC); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_unpacklo_epi64(m3, m1); + tt = _mm_blend_epi16(t2, m2, 0xC0); + t2 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(1, 3, 2, 0)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_unpackhi_epi32(m1, m3); + tt = _mm_unpacklo_epi32(m2, t3); + t3 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(0, 1, 3, 2)); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); + m0 = t0; + m1 = t1; + m2 = t2; + m3 = t3; + + // Round 7 + t0 = _mm_shuffle_ps2(m0, m1, _MM_SHUFFLE(3, 1, 1, 2)); + t0 = _mm_shuffle_epi32(t0, _MM_SHUFFLE(0, 3, 2, 1)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t0); + t1 = _mm_shuffle_ps2(m2, m3, _MM_SHUFFLE(3, 3, 2, 2)); + tt = _mm_shuffle_epi32(m0, _MM_SHUFFLE(0, 0, 3, 3)); + t1 = _mm_blend_epi16(tt, t1, 0xCC); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t1); + diagonalize(&rows[0], &rows[2], &rows[3]); + t2 = _mm_unpacklo_epi64(m3, m1); + tt = _mm_blend_epi16(t2, m2, 0xC0); + t2 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(1, 3, 2, 0)); + g1(&rows[0], &rows[1], &rows[2], &rows[3], t2); + t3 = _mm_unpackhi_epi32(m1, m3); + tt = _mm_unpacklo_epi32(m2, t3); + t3 = _mm_shuffle_epi32(tt, _MM_SHUFFLE(0, 1, 3, 2)); + g2(&rows[0], &rows[1], &rows[2], &rows[3], t3); + undiagonalize(&rows[0], &rows[2], &rows[3]); +} + +void blake3_compress_in_place_sse41(uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, + uint8_t flags) { + __m128i rows[4]; + compress_pre(rows, cv, block, block_len, counter, flags); + storeu(xorv(rows[0], rows[2]), (uint8_t *)&cv[0]); + storeu(xorv(rows[1], rows[3]), (uint8_t *)&cv[4]); +} + +void blake3_compress_xof_sse41(const uint32_t cv[8], + const uint8_t block[BLAKE3_BLOCK_LEN], + uint8_t block_len, uint64_t counter, + uint8_t flags, uint8_t out[64]) { + __m128i rows[4]; + compress_pre(rows, cv, block, block_len, counter, flags); + storeu(xorv(rows[0], rows[2]), &out[0]); + storeu(xorv(rows[1], rows[3]), &out[16]); + storeu(xorv(rows[2], loadu((uint8_t *)&cv[0])), &out[32]); + storeu(xorv(rows[3], loadu((uint8_t *)&cv[4])), &out[48]); +} + +INLINE void round_fn(__m128i v[16], __m128i m[16], size_t r) { + v[0] = addv(v[0], m[(size_t)MSG_SCHEDULE[r][0]]); + v[1] = addv(v[1], m[(size_t)MSG_SCHEDULE[r][2]]); + v[2] = addv(v[2], m[(size_t)MSG_SCHEDULE[r][4]]); + v[3] = addv(v[3], m[(size_t)MSG_SCHEDULE[r][6]]); + v[0] = addv(v[0], v[4]); + v[1] = addv(v[1], v[5]); + v[2] = addv(v[2], v[6]); + v[3] = addv(v[3], v[7]); + v[12] = xorv(v[12], v[0]); + v[13] = xorv(v[13], v[1]); + v[14] = xorv(v[14], v[2]); + v[15] = xorv(v[15], v[3]); + v[12] = rot16(v[12]); + v[13] = rot16(v[13]); + v[14] = rot16(v[14]); + v[15] = rot16(v[15]); + v[8] = addv(v[8], v[12]); + v[9] = addv(v[9], v[13]); + v[10] = addv(v[10], v[14]); + v[11] = addv(v[11], v[15]); + v[4] = xorv(v[4], v[8]); + v[5] = xorv(v[5], v[9]); + v[6] = xorv(v[6], v[10]); + v[7] = xorv(v[7], v[11]); + v[4] = rot12(v[4]); + v[5] = rot12(v[5]); + v[6] = rot12(v[6]); + v[7] = rot12(v[7]); + v[0] = addv(v[0], m[(size_t)MSG_SCHEDULE[r][1]]); + v[1] = addv(v[1], m[(size_t)MSG_SCHEDULE[r][3]]); + v[2] = addv(v[2], m[(size_t)MSG_SCHEDULE[r][5]]); + v[3] = addv(v[3], m[(size_t)MSG_SCHEDULE[r][7]]); + v[0] = addv(v[0], v[4]); + v[1] = addv(v[1], v[5]); + v[2] = addv(v[2], v[6]); + v[3] = addv(v[3], v[7]); + v[12] = xorv(v[12], v[0]); + v[13] = xorv(v[13], v[1]); + v[14] = xorv(v[14], v[2]); + v[15] = xorv(v[15], v[3]); + v[12] = rot8(v[12]); + v[13] = rot8(v[13]); + v[14] = rot8(v[14]); + v[15] = rot8(v[15]); + v[8] = addv(v[8], v[12]); + v[9] = addv(v[9], v[13]); + v[10] = addv(v[10], v[14]); + v[11] = addv(v[11], v[15]); + v[4] = xorv(v[4], v[8]); + v[5] = xorv(v[5], v[9]); + v[6] = xorv(v[6], v[10]); + v[7] = xorv(v[7], v[11]); + v[4] = rot7(v[4]); + v[5] = rot7(v[5]); + v[6] = rot7(v[6]); + v[7] = rot7(v[7]); + + v[0] = addv(v[0], m[(size_t)MSG_SCHEDULE[r][8]]); + v[1] = addv(v[1], m[(size_t)MSG_SCHEDULE[r][10]]); + v[2] = addv(v[2], m[(size_t)MSG_SCHEDULE[r][12]]); + v[3] = addv(v[3], m[(size_t)MSG_SCHEDULE[r][14]]); + v[0] = addv(v[0], v[5]); + v[1] = addv(v[1], v[6]); + v[2] = addv(v[2], v[7]); + v[3] = addv(v[3], v[4]); + v[15] = xorv(v[15], v[0]); + v[12] = xorv(v[12], v[1]); + v[13] = xorv(v[13], v[2]); + v[14] = xorv(v[14], v[3]); + v[15] = rot16(v[15]); + v[12] = rot16(v[12]); + v[13] = rot16(v[13]); + v[14] = rot16(v[14]); + v[10] = addv(v[10], v[15]); + v[11] = addv(v[11], v[12]); + v[8] = addv(v[8], v[13]); + v[9] = addv(v[9], v[14]); + v[5] = xorv(v[5], v[10]); + v[6] = xorv(v[6], v[11]); + v[7] = xorv(v[7], v[8]); + v[4] = xorv(v[4], v[9]); + v[5] = rot12(v[5]); + v[6] = rot12(v[6]); + v[7] = rot12(v[7]); + v[4] = rot12(v[4]); + v[0] = addv(v[0], m[(size_t)MSG_SCHEDULE[r][9]]); + v[1] = addv(v[1], m[(size_t)MSG_SCHEDULE[r][11]]); + v[2] = addv(v[2], m[(size_t)MSG_SCHEDULE[r][13]]); + v[3] = addv(v[3], m[(size_t)MSG_SCHEDULE[r][15]]); + v[0] = addv(v[0], v[5]); + v[1] = addv(v[1], v[6]); + v[2] = addv(v[2], v[7]); + v[3] = addv(v[3], v[4]); + v[15] = xorv(v[15], v[0]); + v[12] = xorv(v[12], v[1]); + v[13] = xorv(v[13], v[2]); + v[14] = xorv(v[14], v[3]); + v[15] = rot8(v[15]); + v[12] = rot8(v[12]); + v[13] = rot8(v[13]); + v[14] = rot8(v[14]); + v[10] = addv(v[10], v[15]); + v[11] = addv(v[11], v[12]); + v[8] = addv(v[8], v[13]); + v[9] = addv(v[9], v[14]); + v[5] = xorv(v[5], v[10]); + v[6] = xorv(v[6], v[11]); + v[7] = xorv(v[7], v[8]); + v[4] = xorv(v[4], v[9]); + v[5] = rot7(v[5]); + v[6] = rot7(v[6]); + v[7] = rot7(v[7]); + v[4] = rot7(v[4]); +} + +INLINE void transpose_vecs(__m128i vecs[DEGREE]) { + // Interleave 32-bit lates. The low unpack is lanes 00/11 and the high is + // 22/33. Note that this doesn't split the vector into two lanes, as the + // AVX2 counterparts do. + __m128i ab_01 = _mm_unpacklo_epi32(vecs[0], vecs[1]); + __m128i ab_23 = _mm_unpackhi_epi32(vecs[0], vecs[1]); + __m128i cd_01 = _mm_unpacklo_epi32(vecs[2], vecs[3]); + __m128i cd_23 = _mm_unpackhi_epi32(vecs[2], vecs[3]); + + // Interleave 64-bit lanes. + __m128i abcd_0 = _mm_unpacklo_epi64(ab_01, cd_01); + __m128i abcd_1 = _mm_unpackhi_epi64(ab_01, cd_01); + __m128i abcd_2 = _mm_unpacklo_epi64(ab_23, cd_23); + __m128i abcd_3 = _mm_unpackhi_epi64(ab_23, cd_23); + + vecs[0] = abcd_0; + vecs[1] = abcd_1; + vecs[2] = abcd_2; + vecs[3] = abcd_3; +} + +INLINE void transpose_msg_vecs(const uint8_t *const *inputs, + size_t block_offset, __m128i out[16]) { + out[0] = loadu(&inputs[0][block_offset + 0 * sizeof(__m128i)]); + out[1] = loadu(&inputs[1][block_offset + 0 * sizeof(__m128i)]); + out[2] = loadu(&inputs[2][block_offset + 0 * sizeof(__m128i)]); + out[3] = loadu(&inputs[3][block_offset + 0 * sizeof(__m128i)]); + out[4] = loadu(&inputs[0][block_offset + 1 * sizeof(__m128i)]); + out[5] = loadu(&inputs[1][block_offset + 1 * sizeof(__m128i)]); + out[6] = loadu(&inputs[2][block_offset + 1 * sizeof(__m128i)]); + out[7] = loadu(&inputs[3][block_offset + 1 * sizeof(__m128i)]); + out[8] = loadu(&inputs[0][block_offset + 2 * sizeof(__m128i)]); + out[9] = loadu(&inputs[1][block_offset + 2 * sizeof(__m128i)]); + out[10] = loadu(&inputs[2][block_offset + 2 * sizeof(__m128i)]); + out[11] = loadu(&inputs[3][block_offset + 2 * sizeof(__m128i)]); + out[12] = loadu(&inputs[0][block_offset + 3 * sizeof(__m128i)]); + out[13] = loadu(&inputs[1][block_offset + 3 * sizeof(__m128i)]); + out[14] = loadu(&inputs[2][block_offset + 3 * sizeof(__m128i)]); + out[15] = loadu(&inputs[3][block_offset + 3 * sizeof(__m128i)]); + for (size_t i = 0; i < 4; ++i) { + _mm_prefetch(&inputs[i][block_offset + 256], _MM_HINT_T0); + } + transpose_vecs(&out[0]); + transpose_vecs(&out[4]); + transpose_vecs(&out[8]); + transpose_vecs(&out[12]); +} + +INLINE void load_counters(uint64_t counter, bool increment_counter, + __m128i *out_lo, __m128i *out_hi) { + const __m128i mask = _mm_set1_epi32(-(int32_t)increment_counter); + const __m128i add0 = _mm_set_epi32(3, 2, 1, 0); + const __m128i add1 = _mm_and_si128(mask, add0); + __m128i l = _mm_add_epi32(_mm_set1_epi32(counter), add1); + __m128i carry = _mm_cmpgt_epi32(_mm_xor_si128(add1, _mm_set1_epi32(0x80000000)), + _mm_xor_si128( l, _mm_set1_epi32(0x80000000))); + __m128i h = _mm_sub_epi32(_mm_set1_epi32(counter >> 32), carry); + *out_lo = l; + *out_hi = h; +} + +void blake3_hash4_sse41(const uint8_t *const *inputs, size_t blocks, + const uint32_t key[8], uint64_t counter, + bool increment_counter, uint8_t flags, + uint8_t flags_start, uint8_t flags_end, uint8_t *out) { + __m128i h_vecs[8] = { + set1(key[0]), set1(key[1]), set1(key[2]), set1(key[3]), + set1(key[4]), set1(key[5]), set1(key[6]), set1(key[7]), + }; + __m128i counter_low_vec, counter_high_vec; + load_counters(counter, increment_counter, &counter_low_vec, + &counter_high_vec); + uint8_t block_flags = flags | flags_start; + + for (size_t block = 0; block < blocks; block++) { + if (block + 1 == blocks) { + block_flags |= flags_end; + } + __m128i block_len_vec = set1(BLAKE3_BLOCK_LEN); + __m128i block_flags_vec = set1(block_flags); + __m128i msg_vecs[16]; + transpose_msg_vecs(inputs, block * BLAKE3_BLOCK_LEN, msg_vecs); + + __m128i v[16] = { + h_vecs[0], h_vecs[1], h_vecs[2], h_vecs[3], + h_vecs[4], h_vecs[5], h_vecs[6], h_vecs[7], + set1(IV[0]), set1(IV[1]), set1(IV[2]), set1(IV[3]), + counter_low_vec, counter_high_vec, block_len_vec, block_flags_vec, + }; + round_fn(v, msg_vecs, 0); + round_fn(v, msg_vecs, 1); + round_fn(v, msg_vecs, 2); + round_fn(v, msg_vecs, 3); + round_fn(v, msg_vecs, 4); + round_fn(v, msg_vecs, 5); + round_fn(v, msg_vecs, 6); + h_vecs[0] = xorv(v[0], v[8]); + h_vecs[1] = xorv(v[1], v[9]); + h_vecs[2] = xorv(v[2], v[10]); + h_vecs[3] = xorv(v[3], v[11]); + h_vecs[4] = xorv(v[4], v[12]); + h_vecs[5] = xorv(v[5], v[13]); + h_vecs[6] = xorv(v[6], v[14]); + h_vecs[7] = xorv(v[7], v[15]); + + block_flags = flags; + } + + transpose_vecs(&h_vecs[0]); + transpose_vecs(&h_vecs[4]); + // The first four vecs now contain the first half of each output, and the + // second four vecs contain the second half of each output. + storeu(h_vecs[0], &out[0 * sizeof(__m128i)]); + storeu(h_vecs[4], &out[1 * sizeof(__m128i)]); + storeu(h_vecs[1], &out[2 * sizeof(__m128i)]); + storeu(h_vecs[5], &out[3 * sizeof(__m128i)]); + storeu(h_vecs[2], &out[4 * sizeof(__m128i)]); + storeu(h_vecs[6], &out[5 * sizeof(__m128i)]); + storeu(h_vecs[3], &out[6 * sizeof(__m128i)]); + storeu(h_vecs[7], &out[7 * sizeof(__m128i)]); +} + +INLINE void hash_one_sse41(const uint8_t *input, size_t blocks, + const uint32_t key[8], uint64_t counter, + uint8_t flags, uint8_t flags_start, + uint8_t flags_end, uint8_t out[BLAKE3_OUT_LEN]) { + uint32_t cv[8]; + memcpy(cv, key, BLAKE3_KEY_LEN); + uint8_t block_flags = flags | flags_start; + while (blocks > 0) { + if (blocks == 1) { + block_flags |= flags_end; + } + blake3_compress_in_place_sse41(cv, input, BLAKE3_BLOCK_LEN, counter, + block_flags); + input = &input[BLAKE3_BLOCK_LEN]; + blocks -= 1; + block_flags = flags; + } + memcpy(out, cv, BLAKE3_OUT_LEN); +} + +void blake3_hash_many_sse41(const uint8_t *const *inputs, size_t num_inputs, + size_t blocks, const uint32_t key[8], + uint64_t counter, bool increment_counter, + uint8_t flags, uint8_t flags_start, + uint8_t flags_end, uint8_t *out) { + while (num_inputs >= DEGREE) { + blake3_hash4_sse41(inputs, blocks, key, counter, increment_counter, flags, + flags_start, flags_end, out); + if (increment_counter) { + counter += DEGREE; + } + inputs += DEGREE; + num_inputs -= DEGREE; + out = &out[DEGREE * BLAKE3_OUT_LEN]; + } + while (num_inputs > 0) { + hash_one_sse41(inputs[0], blocks, key, counter, flags, flags_start, + flags_end, out); + if (increment_counter) { + counter += 1; + } + inputs += 1; + num_inputs -= 1; + out = &out[BLAKE3_OUT_LEN]; + } +} diff --git a/src/third_party/blake3/blake3_sse41_x86-64_unix.S b/src/third_party/blake3/blake3_sse41_x86-64_unix.S new file mode 100644 index 0000000..a3ff642 --- /dev/null +++ b/src/third_party/blake3/blake3_sse41_x86-64_unix.S @@ -0,0 +1,2028 @@ +#if defined(__ELF__) && defined(__linux__) +.section .note.GNU-stack,"",%progbits +#endif + +#if defined(__ELF__) && defined(__CET__) && defined(__has_include) +#if __has_include() +#include +#endif +#endif + +#if !defined(_CET_ENDBR) +#define _CET_ENDBR +#endif + +.intel_syntax noprefix +.global blake3_hash_many_sse41 +.global _blake3_hash_many_sse41 +.global blake3_compress_in_place_sse41 +.global _blake3_compress_in_place_sse41 +.global blake3_compress_xof_sse41 +.global _blake3_compress_xof_sse41 +#ifdef __APPLE__ +.text +#else +.section .text +#endif + .p2align 6 +_blake3_hash_many_sse41: +blake3_hash_many_sse41: + _CET_ENDBR + push r15 + push r14 + push r13 + push r12 + push rbx + push rbp + mov rbp, rsp + sub rsp, 360 + and rsp, 0xFFFFFFFFFFFFFFC0 + neg r9d + movd xmm0, r9d + pshufd xmm0, xmm0, 0x00 + movdqa xmmword ptr [rsp+0x130], xmm0 + movdqa xmm1, xmm0 + pand xmm1, xmmword ptr [ADD0+rip] + pand xmm0, xmmword ptr [ADD1+rip] + movdqa xmmword ptr [rsp+0x150], xmm0 + movd xmm0, r8d + pshufd xmm0, xmm0, 0x00 + paddd xmm0, xmm1 + movdqa xmmword ptr [rsp+0x110], xmm0 + pxor xmm0, xmmword ptr [CMP_MSB_MASK+rip] + pxor xmm1, xmmword ptr [CMP_MSB_MASK+rip] + pcmpgtd xmm1, xmm0 + shr r8, 32 + movd xmm2, r8d + pshufd xmm2, xmm2, 0x00 + psubd xmm2, xmm1 + movdqa xmmword ptr [rsp+0x120], xmm2 + mov rbx, qword ptr [rbp+0x50] + mov r15, rdx + shl r15, 6 + movzx r13d, byte ptr [rbp+0x38] + movzx r12d, byte ptr [rbp+0x48] + cmp rsi, 4 + jc 3f +2: + movdqu xmm3, xmmword ptr [rcx] + pshufd xmm0, xmm3, 0x00 + pshufd xmm1, xmm3, 0x55 + pshufd xmm2, xmm3, 0xAA + pshufd xmm3, xmm3, 0xFF + movdqu xmm7, xmmword ptr [rcx+0x10] + pshufd xmm4, xmm7, 0x00 + pshufd xmm5, xmm7, 0x55 + pshufd xmm6, xmm7, 0xAA + pshufd xmm7, xmm7, 0xFF + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + mov r10, qword ptr [rdi+0x10] + mov r11, qword ptr [rdi+0x18] + movzx eax, byte ptr [rbp+0x40] + or eax, r13d + xor edx, edx +9: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + movdqu xmm8, xmmword ptr [r8+rdx-0x40] + movdqu xmm9, xmmword ptr [r9+rdx-0x40] + movdqu xmm10, xmmword ptr [r10+rdx-0x40] + movdqu xmm11, xmmword ptr [r11+rdx-0x40] + movdqa xmm12, xmm8 + punpckldq xmm8, xmm9 + punpckhdq xmm12, xmm9 + movdqa xmm14, xmm10 + punpckldq xmm10, xmm11 + punpckhdq xmm14, xmm11 + movdqa xmm9, xmm8 + punpcklqdq xmm8, xmm10 + punpckhqdq xmm9, xmm10 + movdqa xmm13, xmm12 + punpcklqdq xmm12, xmm14 + punpckhqdq xmm13, xmm14 + movdqa xmmword ptr [rsp], xmm8 + movdqa xmmword ptr [rsp+0x10], xmm9 + movdqa xmmword ptr [rsp+0x20], xmm12 + movdqa xmmword ptr [rsp+0x30], xmm13 + movdqu xmm8, xmmword ptr [r8+rdx-0x30] + movdqu xmm9, xmmword ptr [r9+rdx-0x30] + movdqu xmm10, xmmword ptr [r10+rdx-0x30] + movdqu xmm11, xmmword ptr [r11+rdx-0x30] + movdqa xmm12, xmm8 + punpckldq xmm8, xmm9 + punpckhdq xmm12, xmm9 + movdqa xmm14, xmm10 + punpckldq xmm10, xmm11 + punpckhdq xmm14, xmm11 + movdqa xmm9, xmm8 + punpcklqdq xmm8, xmm10 + punpckhqdq xmm9, xmm10 + movdqa xmm13, xmm12 + punpcklqdq xmm12, xmm14 + punpckhqdq xmm13, xmm14 + movdqa xmmword ptr [rsp+0x40], xmm8 + movdqa xmmword ptr [rsp+0x50], xmm9 + movdqa xmmword ptr [rsp+0x60], xmm12 + movdqa xmmword ptr [rsp+0x70], xmm13 + movdqu xmm8, xmmword ptr [r8+rdx-0x20] + movdqu xmm9, xmmword ptr [r9+rdx-0x20] + movdqu xmm10, xmmword ptr [r10+rdx-0x20] + movdqu xmm11, xmmword ptr [r11+rdx-0x20] + movdqa xmm12, xmm8 + punpckldq xmm8, xmm9 + punpckhdq xmm12, xmm9 + movdqa xmm14, xmm10 + punpckldq xmm10, xmm11 + punpckhdq xmm14, xmm11 + movdqa xmm9, xmm8 + punpcklqdq xmm8, xmm10 + punpckhqdq xmm9, xmm10 + movdqa xmm13, xmm12 + punpcklqdq xmm12, xmm14 + punpckhqdq xmm13, xmm14 + movdqa xmmword ptr [rsp+0x80], xmm8 + movdqa xmmword ptr [rsp+0x90], xmm9 + movdqa xmmword ptr [rsp+0xA0], xmm12 + movdqa xmmword ptr [rsp+0xB0], xmm13 + movdqu xmm8, xmmword ptr [r8+rdx-0x10] + movdqu xmm9, xmmword ptr [r9+rdx-0x10] + movdqu xmm10, xmmword ptr [r10+rdx-0x10] + movdqu xmm11, xmmword ptr [r11+rdx-0x10] + movdqa xmm12, xmm8 + punpckldq xmm8, xmm9 + punpckhdq xmm12, xmm9 + movdqa xmm14, xmm10 + punpckldq xmm10, xmm11 + punpckhdq xmm14, xmm11 + movdqa xmm9, xmm8 + punpcklqdq xmm8, xmm10 + punpckhqdq xmm9, xmm10 + movdqa xmm13, xmm12 + punpcklqdq xmm12, xmm14 + punpckhqdq xmm13, xmm14 + movdqa xmmword ptr [rsp+0xC0], xmm8 + movdqa xmmword ptr [rsp+0xD0], xmm9 + movdqa xmmword ptr [rsp+0xE0], xmm12 + movdqa xmmword ptr [rsp+0xF0], xmm13 + movdqa xmm9, xmmword ptr [BLAKE3_IV_1+rip] + movdqa xmm10, xmmword ptr [BLAKE3_IV_2+rip] + movdqa xmm11, xmmword ptr [BLAKE3_IV_3+rip] + movdqa xmm12, xmmword ptr [rsp+0x110] + movdqa xmm13, xmmword ptr [rsp+0x120] + movdqa xmm14, xmmword ptr [BLAKE3_BLOCK_LEN+rip] + movd xmm15, eax + pshufd xmm15, xmm15, 0x00 + prefetcht0 [r8+rdx+0x80] + prefetcht0 [r9+rdx+0x80] + prefetcht0 [r10+rdx+0x80] + prefetcht0 [r11+rdx+0x80] + paddd xmm0, xmmword ptr [rsp] + paddd xmm1, xmmword ptr [rsp+0x20] + paddd xmm2, xmmword ptr [rsp+0x40] + paddd xmm3, xmmword ptr [rsp+0x60] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [BLAKE3_IV_0+rip] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x10] + paddd xmm1, xmmword ptr [rsp+0x30] + paddd xmm2, xmmword ptr [rsp+0x50] + paddd xmm3, xmmword ptr [rsp+0x70] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x80] + paddd xmm1, xmmword ptr [rsp+0xA0] + paddd xmm2, xmmword ptr [rsp+0xC0] + paddd xmm3, xmmword ptr [rsp+0xE0] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x90] + paddd xmm1, xmmword ptr [rsp+0xB0] + paddd xmm2, xmmword ptr [rsp+0xD0] + paddd xmm3, xmmword ptr [rsp+0xF0] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x20] + paddd xmm1, xmmword ptr [rsp+0x30] + paddd xmm2, xmmword ptr [rsp+0x70] + paddd xmm3, xmmword ptr [rsp+0x40] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x60] + paddd xmm1, xmmword ptr [rsp+0xA0] + paddd xmm2, xmmword ptr [rsp] + paddd xmm3, xmmword ptr [rsp+0xD0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x10] + paddd xmm1, xmmword ptr [rsp+0xC0] + paddd xmm2, xmmword ptr [rsp+0x90] + paddd xmm3, xmmword ptr [rsp+0xF0] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0xB0] + paddd xmm1, xmmword ptr [rsp+0x50] + paddd xmm2, xmmword ptr [rsp+0xE0] + paddd xmm3, xmmword ptr [rsp+0x80] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x30] + paddd xmm1, xmmword ptr [rsp+0xA0] + paddd xmm2, xmmword ptr [rsp+0xD0] + paddd xmm3, xmmword ptr [rsp+0x70] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x40] + paddd xmm1, xmmword ptr [rsp+0xC0] + paddd xmm2, xmmword ptr [rsp+0x20] + paddd xmm3, xmmword ptr [rsp+0xE0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x60] + paddd xmm1, xmmword ptr [rsp+0x90] + paddd xmm2, xmmword ptr [rsp+0xB0] + paddd xmm3, xmmword ptr [rsp+0x80] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x50] + paddd xmm1, xmmword ptr [rsp] + paddd xmm2, xmmword ptr [rsp+0xF0] + paddd xmm3, xmmword ptr [rsp+0x10] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0xA0] + paddd xmm1, xmmword ptr [rsp+0xC0] + paddd xmm2, xmmword ptr [rsp+0xE0] + paddd xmm3, xmmword ptr [rsp+0xD0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x70] + paddd xmm1, xmmword ptr [rsp+0x90] + paddd xmm2, xmmword ptr [rsp+0x30] + paddd xmm3, xmmword ptr [rsp+0xF0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x40] + paddd xmm1, xmmword ptr [rsp+0xB0] + paddd xmm2, xmmword ptr [rsp+0x50] + paddd xmm3, xmmword ptr [rsp+0x10] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp] + paddd xmm1, xmmword ptr [rsp+0x20] + paddd xmm2, xmmword ptr [rsp+0x80] + paddd xmm3, xmmword ptr [rsp+0x60] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0xC0] + paddd xmm1, xmmword ptr [rsp+0x90] + paddd xmm2, xmmword ptr [rsp+0xF0] + paddd xmm3, xmmword ptr [rsp+0xE0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0xD0] + paddd xmm1, xmmword ptr [rsp+0xB0] + paddd xmm2, xmmword ptr [rsp+0xA0] + paddd xmm3, xmmword ptr [rsp+0x80] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x70] + paddd xmm1, xmmword ptr [rsp+0x50] + paddd xmm2, xmmword ptr [rsp] + paddd xmm3, xmmword ptr [rsp+0x60] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x20] + paddd xmm1, xmmword ptr [rsp+0x30] + paddd xmm2, xmmword ptr [rsp+0x10] + paddd xmm3, xmmword ptr [rsp+0x40] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x90] + paddd xmm1, xmmword ptr [rsp+0xB0] + paddd xmm2, xmmword ptr [rsp+0x80] + paddd xmm3, xmmword ptr [rsp+0xF0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0xE0] + paddd xmm1, xmmword ptr [rsp+0x50] + paddd xmm2, xmmword ptr [rsp+0xC0] + paddd xmm3, xmmword ptr [rsp+0x10] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0xD0] + paddd xmm1, xmmword ptr [rsp] + paddd xmm2, xmmword ptr [rsp+0x20] + paddd xmm3, xmmword ptr [rsp+0x40] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x30] + paddd xmm1, xmmword ptr [rsp+0xA0] + paddd xmm2, xmmword ptr [rsp+0x60] + paddd xmm3, xmmword ptr [rsp+0x70] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0xB0] + paddd xmm1, xmmword ptr [rsp+0x50] + paddd xmm2, xmmword ptr [rsp+0x10] + paddd xmm3, xmmword ptr [rsp+0x80] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0xF0] + paddd xmm1, xmmword ptr [rsp] + paddd xmm2, xmmword ptr [rsp+0x90] + paddd xmm3, xmmword ptr [rsp+0x60] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0xE0] + paddd xmm1, xmmword ptr [rsp+0x20] + paddd xmm2, xmmword ptr [rsp+0x30] + paddd xmm3, xmmword ptr [rsp+0x70] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0xA0] + paddd xmm1, xmmword ptr [rsp+0xC0] + paddd xmm2, xmmword ptr [rsp+0x40] + paddd xmm3, xmmword ptr [rsp+0xD0] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + pxor xmm0, xmm8 + pxor xmm1, xmm9 + pxor xmm2, xmm10 + pxor xmm3, xmm11 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + pxor xmm4, xmm12 + pxor xmm5, xmm13 + pxor xmm6, xmm14 + pxor xmm7, xmm15 + mov eax, r13d + jne 9b + movdqa xmm9, xmm0 + punpckldq xmm0, xmm1 + punpckhdq xmm9, xmm1 + movdqa xmm11, xmm2 + punpckldq xmm2, xmm3 + punpckhdq xmm11, xmm3 + movdqa xmm1, xmm0 + punpcklqdq xmm0, xmm2 + punpckhqdq xmm1, xmm2 + movdqa xmm3, xmm9 + punpcklqdq xmm9, xmm11 + punpckhqdq xmm3, xmm11 + movdqu xmmword ptr [rbx], xmm0 + movdqu xmmword ptr [rbx+0x20], xmm1 + movdqu xmmword ptr [rbx+0x40], xmm9 + movdqu xmmword ptr [rbx+0x60], xmm3 + movdqa xmm9, xmm4 + punpckldq xmm4, xmm5 + punpckhdq xmm9, xmm5 + movdqa xmm11, xmm6 + punpckldq xmm6, xmm7 + punpckhdq xmm11, xmm7 + movdqa xmm5, xmm4 + punpcklqdq xmm4, xmm6 + punpckhqdq xmm5, xmm6 + movdqa xmm7, xmm9 + punpcklqdq xmm9, xmm11 + punpckhqdq xmm7, xmm11 + movdqu xmmword ptr [rbx+0x10], xmm4 + movdqu xmmword ptr [rbx+0x30], xmm5 + movdqu xmmword ptr [rbx+0x50], xmm9 + movdqu xmmword ptr [rbx+0x70], xmm7 + movdqa xmm1, xmmword ptr [rsp+0x110] + movdqa xmm0, xmm1 + paddd xmm1, xmmword ptr [rsp+0x150] + movdqa xmmword ptr [rsp+0x110], xmm1 + pxor xmm0, xmmword ptr [CMP_MSB_MASK+rip] + pxor xmm1, xmmword ptr [CMP_MSB_MASK+rip] + pcmpgtd xmm0, xmm1 + movdqa xmm1, xmmword ptr [rsp+0x120] + psubd xmm1, xmm0 + movdqa xmmword ptr [rsp+0x120], xmm1 + add rbx, 128 + add rdi, 32 + sub rsi, 4 + cmp rsi, 4 + jnc 2b + test rsi, rsi + jnz 3f +4: + mov rsp, rbp + pop rbp + pop rbx + pop r12 + pop r13 + pop r14 + pop r15 + ret +.p2align 5 +3: + test esi, 0x2 + je 3f + movups xmm0, xmmword ptr [rcx] + movups xmm1, xmmword ptr [rcx+0x10] + movaps xmm8, xmm0 + movaps xmm9, xmm1 + movd xmm13, dword ptr [rsp+0x110] + pinsrd xmm13, dword ptr [rsp+0x120], 1 + pinsrd xmm13, dword ptr [BLAKE3_BLOCK_LEN+rip], 2 + movaps xmmword ptr [rsp], xmm13 + movd xmm14, dword ptr [rsp+0x114] + pinsrd xmm14, dword ptr [rsp+0x124], 1 + pinsrd xmm14, dword ptr [BLAKE3_BLOCK_LEN+rip], 2 + movaps xmmword ptr [rsp+0x10], xmm14 + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + movzx eax, byte ptr [rbp+0x40] + or eax, r13d + xor edx, edx +2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + movaps xmm2, xmmword ptr [BLAKE3_IV+rip] + movaps xmm10, xmm2 + movups xmm4, xmmword ptr [r8+rdx-0x40] + movups xmm5, xmmword ptr [r8+rdx-0x30] + movaps xmm3, xmm4 + shufps xmm4, xmm5, 136 + shufps xmm3, xmm5, 221 + movaps xmm5, xmm3 + movups xmm6, xmmword ptr [r8+rdx-0x20] + movups xmm7, xmmword ptr [r8+rdx-0x10] + movaps xmm3, xmm6 + shufps xmm6, xmm7, 136 + pshufd xmm6, xmm6, 0x93 + shufps xmm3, xmm7, 221 + pshufd xmm7, xmm3, 0x93 + movups xmm12, xmmword ptr [r9+rdx-0x40] + movups xmm13, xmmword ptr [r9+rdx-0x30] + movaps xmm11, xmm12 + shufps xmm12, xmm13, 136 + shufps xmm11, xmm13, 221 + movaps xmm13, xmm11 + movups xmm14, xmmword ptr [r9+rdx-0x20] + movups xmm15, xmmword ptr [r9+rdx-0x10] + movaps xmm11, xmm14 + shufps xmm14, xmm15, 136 + pshufd xmm14, xmm14, 0x93 + shufps xmm11, xmm15, 221 + pshufd xmm15, xmm11, 0x93 + movaps xmm3, xmmword ptr [rsp] + movaps xmm11, xmmword ptr [rsp+0x10] + pinsrd xmm3, eax, 3 + pinsrd xmm11, eax, 3 + mov al, 7 +9: + paddd xmm0, xmm4 + paddd xmm8, xmm12 + movaps xmmword ptr [rsp+0x20], xmm4 + movaps xmmword ptr [rsp+0x30], xmm12 + paddd xmm0, xmm1 + paddd xmm8, xmm9 + pxor xmm3, xmm0 + pxor xmm11, xmm8 + movaps xmm12, xmmword ptr [ROT16+rip] + pshufb xmm3, xmm12 + pshufb xmm11, xmm12 + paddd xmm2, xmm3 + paddd xmm10, xmm11 + pxor xmm1, xmm2 + pxor xmm9, xmm10 + movdqa xmm4, xmm1 + pslld xmm1, 20 + psrld xmm4, 12 + por xmm1, xmm4 + movdqa xmm4, xmm9 + pslld xmm9, 20 + psrld xmm4, 12 + por xmm9, xmm4 + paddd xmm0, xmm5 + paddd xmm8, xmm13 + movaps xmmword ptr [rsp+0x40], xmm5 + movaps xmmword ptr [rsp+0x50], xmm13 + paddd xmm0, xmm1 + paddd xmm8, xmm9 + pxor xmm3, xmm0 + pxor xmm11, xmm8 + movaps xmm13, xmmword ptr [ROT8+rip] + pshufb xmm3, xmm13 + pshufb xmm11, xmm13 + paddd xmm2, xmm3 + paddd xmm10, xmm11 + pxor xmm1, xmm2 + pxor xmm9, xmm10 + movdqa xmm4, xmm1 + pslld xmm1, 25 + psrld xmm4, 7 + por xmm1, xmm4 + movdqa xmm4, xmm9 + pslld xmm9, 25 + psrld xmm4, 7 + por xmm9, xmm4 + pshufd xmm0, xmm0, 0x93 + pshufd xmm8, xmm8, 0x93 + pshufd xmm3, xmm3, 0x4E + pshufd xmm11, xmm11, 0x4E + pshufd xmm2, xmm2, 0x39 + pshufd xmm10, xmm10, 0x39 + paddd xmm0, xmm6 + paddd xmm8, xmm14 + paddd xmm0, xmm1 + paddd xmm8, xmm9 + pxor xmm3, xmm0 + pxor xmm11, xmm8 + pshufb xmm3, xmm12 + pshufb xmm11, xmm12 + paddd xmm2, xmm3 + paddd xmm10, xmm11 + pxor xmm1, xmm2 + pxor xmm9, xmm10 + movdqa xmm4, xmm1 + pslld xmm1, 20 + psrld xmm4, 12 + por xmm1, xmm4 + movdqa xmm4, xmm9 + pslld xmm9, 20 + psrld xmm4, 12 + por xmm9, xmm4 + paddd xmm0, xmm7 + paddd xmm8, xmm15 + paddd xmm0, xmm1 + paddd xmm8, xmm9 + pxor xmm3, xmm0 + pxor xmm11, xmm8 + pshufb xmm3, xmm13 + pshufb xmm11, xmm13 + paddd xmm2, xmm3 + paddd xmm10, xmm11 + pxor xmm1, xmm2 + pxor xmm9, xmm10 + movdqa xmm4, xmm1 + pslld xmm1, 25 + psrld xmm4, 7 + por xmm1, xmm4 + movdqa xmm4, xmm9 + pslld xmm9, 25 + psrld xmm4, 7 + por xmm9, xmm4 + pshufd xmm0, xmm0, 0x39 + pshufd xmm8, xmm8, 0x39 + pshufd xmm3, xmm3, 0x4E + pshufd xmm11, xmm11, 0x4E + pshufd xmm2, xmm2, 0x93 + pshufd xmm10, xmm10, 0x93 + dec al + je 9f + movdqa xmm12, xmmword ptr [rsp+0x20] + movdqa xmm5, xmmword ptr [rsp+0x40] + pshufd xmm13, xmm12, 0x0F + shufps xmm12, xmm5, 214 + pshufd xmm4, xmm12, 0x39 + movdqa xmm12, xmm6 + shufps xmm12, xmm7, 250 + pblendw xmm13, xmm12, 0xCC + movdqa xmm12, xmm7 + punpcklqdq xmm12, xmm5 + pblendw xmm12, xmm6, 0xC0 + pshufd xmm12, xmm12, 0x78 + punpckhdq xmm5, xmm7 + punpckldq xmm6, xmm5 + pshufd xmm7, xmm6, 0x1E + movdqa xmmword ptr [rsp+0x20], xmm13 + movdqa xmmword ptr [rsp+0x40], xmm12 + movdqa xmm5, xmmword ptr [rsp+0x30] + movdqa xmm13, xmmword ptr [rsp+0x50] + pshufd xmm6, xmm5, 0x0F + shufps xmm5, xmm13, 214 + pshufd xmm12, xmm5, 0x39 + movdqa xmm5, xmm14 + shufps xmm5, xmm15, 250 + pblendw xmm6, xmm5, 0xCC + movdqa xmm5, xmm15 + punpcklqdq xmm5, xmm13 + pblendw xmm5, xmm14, 0xC0 + pshufd xmm5, xmm5, 0x78 + punpckhdq xmm13, xmm15 + punpckldq xmm14, xmm13 + pshufd xmm15, xmm14, 0x1E + movdqa xmm13, xmm6 + movdqa xmm14, xmm5 + movdqa xmm5, xmmword ptr [rsp+0x20] + movdqa xmm6, xmmword ptr [rsp+0x40] + jmp 9b +9: + pxor xmm0, xmm2 + pxor xmm1, xmm3 + pxor xmm8, xmm10 + pxor xmm9, xmm11 + mov eax, r13d + cmp rdx, r15 + jne 2b + movups xmmword ptr [rbx], xmm0 + movups xmmword ptr [rbx+0x10], xmm1 + movups xmmword ptr [rbx+0x20], xmm8 + movups xmmword ptr [rbx+0x30], xmm9 + movdqa xmm0, xmmword ptr [rsp+0x130] + movdqa xmm1, xmmword ptr [rsp+0x110] + movdqa xmm2, xmmword ptr [rsp+0x120] + movdqu xmm3, xmmword ptr [rsp+0x118] + movdqu xmm4, xmmword ptr [rsp+0x128] + blendvps xmm1, xmm3, xmm0 + blendvps xmm2, xmm4, xmm0 + movdqa xmmword ptr [rsp+0x110], xmm1 + movdqa xmmword ptr [rsp+0x120], xmm2 + add rdi, 16 + add rbx, 64 + sub rsi, 2 +3: + test esi, 0x1 + je 4b + movups xmm0, xmmword ptr [rcx] + movups xmm1, xmmword ptr [rcx+0x10] + movd xmm13, dword ptr [rsp+0x110] + pinsrd xmm13, dword ptr [rsp+0x120], 1 + pinsrd xmm13, dword ptr [BLAKE3_BLOCK_LEN+rip], 2 + movaps xmm14, xmmword ptr [ROT8+rip] + movaps xmm15, xmmword ptr [ROT16+rip] + mov r8, qword ptr [rdi] + movzx eax, byte ptr [rbp+0x40] + or eax, r13d + xor edx, edx +2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + movaps xmm2, xmmword ptr [BLAKE3_IV+rip] + movaps xmm3, xmm13 + pinsrd xmm3, eax, 3 + movups xmm4, xmmword ptr [r8+rdx-0x40] + movups xmm5, xmmword ptr [r8+rdx-0x30] + movaps xmm8, xmm4 + shufps xmm4, xmm5, 136 + shufps xmm8, xmm5, 221 + movaps xmm5, xmm8 + movups xmm6, xmmword ptr [r8+rdx-0x20] + movups xmm7, xmmword ptr [r8+rdx-0x10] + movaps xmm8, xmm6 + shufps xmm6, xmm7, 136 + pshufd xmm6, xmm6, 0x93 + shufps xmm8, xmm7, 221 + pshufd xmm7, xmm8, 0x93 + mov al, 7 +9: + paddd xmm0, xmm4 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm15 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm5 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x93 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x39 + paddd xmm0, xmm6 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm15 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm7 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x39 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x93 + dec al + jz 9f + movdqa xmm8, xmm4 + shufps xmm8, xmm5, 214 + pshufd xmm9, xmm4, 0x0F + pshufd xmm4, xmm8, 0x39 + movdqa xmm8, xmm6 + shufps xmm8, xmm7, 250 + pblendw xmm9, xmm8, 0xCC + movdqa xmm8, xmm7 + punpcklqdq xmm8, xmm5 + pblendw xmm8, xmm6, 0xC0 + pshufd xmm8, xmm8, 0x78 + punpckhdq xmm5, xmm7 + punpckldq xmm6, xmm5 + pshufd xmm7, xmm6, 0x1E + movdqa xmm5, xmm9 + movdqa xmm6, xmm8 + jmp 9b +9: + pxor xmm0, xmm2 + pxor xmm1, xmm3 + mov eax, r13d + cmp rdx, r15 + jne 2b + movups xmmword ptr [rbx], xmm0 + movups xmmword ptr [rbx+0x10], xmm1 + jmp 4b + +.p2align 6 +blake3_compress_in_place_sse41: +_blake3_compress_in_place_sse41: + _CET_ENDBR + movups xmm0, xmmword ptr [rdi] + movups xmm1, xmmword ptr [rdi+0x10] + movaps xmm2, xmmword ptr [BLAKE3_IV+rip] + shl r8, 32 + add rdx, r8 + movq xmm3, rcx + movq xmm4, rdx + punpcklqdq xmm3, xmm4 + movups xmm4, xmmword ptr [rsi] + movups xmm5, xmmword ptr [rsi+0x10] + movaps xmm8, xmm4 + shufps xmm4, xmm5, 136 + shufps xmm8, xmm5, 221 + movaps xmm5, xmm8 + movups xmm6, xmmword ptr [rsi+0x20] + movups xmm7, xmmword ptr [rsi+0x30] + movaps xmm8, xmm6 + shufps xmm6, xmm7, 136 + pshufd xmm6, xmm6, 0x93 + shufps xmm8, xmm7, 221 + pshufd xmm7, xmm8, 0x93 + movaps xmm14, xmmword ptr [ROT8+rip] + movaps xmm15, xmmword ptr [ROT16+rip] + mov al, 7 +9: + paddd xmm0, xmm4 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm15 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm5 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x93 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x39 + paddd xmm0, xmm6 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm15 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm7 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x39 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x93 + dec al + jz 9f + movdqa xmm8, xmm4 + shufps xmm8, xmm5, 214 + pshufd xmm9, xmm4, 0x0F + pshufd xmm4, xmm8, 0x39 + movdqa xmm8, xmm6 + shufps xmm8, xmm7, 250 + pblendw xmm9, xmm8, 0xCC + movdqa xmm8, xmm7 + punpcklqdq xmm8, xmm5 + pblendw xmm8, xmm6, 0xC0 + pshufd xmm8, xmm8, 0x78 + punpckhdq xmm5, xmm7 + punpckldq xmm6, xmm5 + pshufd xmm7, xmm6, 0x1E + movdqa xmm5, xmm9 + movdqa xmm6, xmm8 + jmp 9b +9: + pxor xmm0, xmm2 + pxor xmm1, xmm3 + movups xmmword ptr [rdi], xmm0 + movups xmmword ptr [rdi+0x10], xmm1 + ret + +.p2align 6 +blake3_compress_xof_sse41: +_blake3_compress_xof_sse41: + _CET_ENDBR + movups xmm0, xmmword ptr [rdi] + movups xmm1, xmmword ptr [rdi+0x10] + movaps xmm2, xmmword ptr [BLAKE3_IV+rip] + movzx eax, r8b + movzx edx, dl + shl rax, 32 + add rdx, rax + movq xmm3, rcx + movq xmm4, rdx + punpcklqdq xmm3, xmm4 + movups xmm4, xmmword ptr [rsi] + movups xmm5, xmmword ptr [rsi+0x10] + movaps xmm8, xmm4 + shufps xmm4, xmm5, 136 + shufps xmm8, xmm5, 221 + movaps xmm5, xmm8 + movups xmm6, xmmword ptr [rsi+0x20] + movups xmm7, xmmword ptr [rsi+0x30] + movaps xmm8, xmm6 + shufps xmm6, xmm7, 136 + pshufd xmm6, xmm6, 0x93 + shufps xmm8, xmm7, 221 + pshufd xmm7, xmm8, 0x93 + movaps xmm14, xmmword ptr [ROT8+rip] + movaps xmm15, xmmword ptr [ROT16+rip] + mov al, 7 +9: + paddd xmm0, xmm4 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm15 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm5 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x93 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x39 + paddd xmm0, xmm6 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm15 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm7 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x39 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x93 + dec al + jz 9f + movdqa xmm8, xmm4 + shufps xmm8, xmm5, 214 + pshufd xmm9, xmm4, 0x0F + pshufd xmm4, xmm8, 0x39 + movdqa xmm8, xmm6 + shufps xmm8, xmm7, 250 + pblendw xmm9, xmm8, 0xCC + movdqa xmm8, xmm7 + punpcklqdq xmm8, xmm5 + pblendw xmm8, xmm6, 0xC0 + pshufd xmm8, xmm8, 0x78 + punpckhdq xmm5, xmm7 + punpckldq xmm6, xmm5 + pshufd xmm7, xmm6, 0x1E + movdqa xmm5, xmm9 + movdqa xmm6, xmm8 + jmp 9b +9: + movdqu xmm4, xmmword ptr [rdi] + movdqu xmm5, xmmword ptr [rdi+0x10] + pxor xmm0, xmm2 + pxor xmm1, xmm3 + pxor xmm2, xmm4 + pxor xmm3, xmm5 + movups xmmword ptr [r9], xmm0 + movups xmmword ptr [r9+0x10], xmm1 + movups xmmword ptr [r9+0x20], xmm2 + movups xmmword ptr [r9+0x30], xmm3 + ret + + +#ifdef __APPLE__ +.static_data +#else +.section .rodata +#endif +.p2align 6 +BLAKE3_IV: + .long 0x6A09E667, 0xBB67AE85 + .long 0x3C6EF372, 0xA54FF53A +ROT16: + .byte 2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13 +ROT8: + .byte 1, 2, 3, 0, 5, 6, 7, 4, 9, 10, 11, 8, 13, 14, 15, 12 +ADD0: + .long 0, 1, 2, 3 +ADD1: + .long 4, 4, 4, 4 +BLAKE3_IV_0: + .long 0x6A09E667, 0x6A09E667, 0x6A09E667, 0x6A09E667 +BLAKE3_IV_1: + .long 0xBB67AE85, 0xBB67AE85, 0xBB67AE85, 0xBB67AE85 +BLAKE3_IV_2: + .long 0x3C6EF372, 0x3C6EF372, 0x3C6EF372, 0x3C6EF372 +BLAKE3_IV_3: + .long 0xA54FF53A, 0xA54FF53A, 0xA54FF53A, 0xA54FF53A +BLAKE3_BLOCK_LEN: + .long 64, 64, 64, 64 +CMP_MSB_MASK: + .long 0x80000000, 0x80000000, 0x80000000, 0x80000000 diff --git a/src/third_party/blake3/blake3_sse41_x86-64_windows_gnu.S b/src/third_party/blake3/blake3_sse41_x86-64_windows_gnu.S new file mode 100644 index 0000000..60d0a40 --- /dev/null +++ b/src/third_party/blake3/blake3_sse41_x86-64_windows_gnu.S @@ -0,0 +1,2069 @@ +.intel_syntax noprefix +.global blake3_hash_many_sse41 +.global _blake3_hash_many_sse41 +.global blake3_compress_in_place_sse41 +.global _blake3_compress_in_place_sse41 +.global blake3_compress_xof_sse41 +.global _blake3_compress_xof_sse41 +.section .text + .p2align 6 +_blake3_hash_many_sse41: +blake3_hash_many_sse41: + push r15 + push r14 + push r13 + push r12 + push rsi + push rdi + push rbx + push rbp + mov rbp, rsp + sub rsp, 528 + and rsp, 0xFFFFFFFFFFFFFFC0 + movdqa xmmword ptr [rsp+0x170], xmm6 + movdqa xmmword ptr [rsp+0x180], xmm7 + movdqa xmmword ptr [rsp+0x190], xmm8 + movdqa xmmword ptr [rsp+0x1A0], xmm9 + movdqa xmmword ptr [rsp+0x1B0], xmm10 + movdqa xmmword ptr [rsp+0x1C0], xmm11 + movdqa xmmword ptr [rsp+0x1D0], xmm12 + movdqa xmmword ptr [rsp+0x1E0], xmm13 + movdqa xmmword ptr [rsp+0x1F0], xmm14 + movdqa xmmword ptr [rsp+0x200], xmm15 + mov rdi, rcx + mov rsi, rdx + mov rdx, r8 + mov rcx, r9 + mov r8, qword ptr [rbp+0x68] + movzx r9, byte ptr [rbp+0x70] + neg r9d + movd xmm0, r9d + pshufd xmm0, xmm0, 0x00 + movdqa xmmword ptr [rsp+0x130], xmm0 + movdqa xmm1, xmm0 + pand xmm1, xmmword ptr [ADD0+rip] + pand xmm0, xmmword ptr [ADD1+rip] + movdqa xmmword ptr [rsp+0x150], xmm0 + movd xmm0, r8d + pshufd xmm0, xmm0, 0x00 + paddd xmm0, xmm1 + movdqa xmmword ptr [rsp+0x110], xmm0 + pxor xmm0, xmmword ptr [CMP_MSB_MASK+rip] + pxor xmm1, xmmword ptr [CMP_MSB_MASK+rip] + pcmpgtd xmm1, xmm0 + shr r8, 32 + movd xmm2, r8d + pshufd xmm2, xmm2, 0x00 + psubd xmm2, xmm1 + movdqa xmmword ptr [rsp+0x120], xmm2 + mov rbx, qword ptr [rbp+0x90] + mov r15, rdx + shl r15, 6 + movzx r13d, byte ptr [rbp+0x78] + movzx r12d, byte ptr [rbp+0x88] + cmp rsi, 4 + jc 3f +2: + movdqu xmm3, xmmword ptr [rcx] + pshufd xmm0, xmm3, 0x00 + pshufd xmm1, xmm3, 0x55 + pshufd xmm2, xmm3, 0xAA + pshufd xmm3, xmm3, 0xFF + movdqu xmm7, xmmword ptr [rcx+0x10] + pshufd xmm4, xmm7, 0x00 + pshufd xmm5, xmm7, 0x55 + pshufd xmm6, xmm7, 0xAA + pshufd xmm7, xmm7, 0xFF + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + mov r10, qword ptr [rdi+0x10] + mov r11, qword ptr [rdi+0x18] + movzx eax, byte ptr [rbp+0x80] + or eax, r13d + xor edx, edx +9: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + movdqu xmm8, xmmword ptr [r8+rdx-0x40] + movdqu xmm9, xmmword ptr [r9+rdx-0x40] + movdqu xmm10, xmmword ptr [r10+rdx-0x40] + movdqu xmm11, xmmword ptr [r11+rdx-0x40] + movdqa xmm12, xmm8 + punpckldq xmm8, xmm9 + punpckhdq xmm12, xmm9 + movdqa xmm14, xmm10 + punpckldq xmm10, xmm11 + punpckhdq xmm14, xmm11 + movdqa xmm9, xmm8 + punpcklqdq xmm8, xmm10 + punpckhqdq xmm9, xmm10 + movdqa xmm13, xmm12 + punpcklqdq xmm12, xmm14 + punpckhqdq xmm13, xmm14 + movdqa xmmword ptr [rsp], xmm8 + movdqa xmmword ptr [rsp+0x10], xmm9 + movdqa xmmword ptr [rsp+0x20], xmm12 + movdqa xmmword ptr [rsp+0x30], xmm13 + movdqu xmm8, xmmword ptr [r8+rdx-0x30] + movdqu xmm9, xmmword ptr [r9+rdx-0x30] + movdqu xmm10, xmmword ptr [r10+rdx-0x30] + movdqu xmm11, xmmword ptr [r11+rdx-0x30] + movdqa xmm12, xmm8 + punpckldq xmm8, xmm9 + punpckhdq xmm12, xmm9 + movdqa xmm14, xmm10 + punpckldq xmm10, xmm11 + punpckhdq xmm14, xmm11 + movdqa xmm9, xmm8 + punpcklqdq xmm8, xmm10 + punpckhqdq xmm9, xmm10 + movdqa xmm13, xmm12 + punpcklqdq xmm12, xmm14 + punpckhqdq xmm13, xmm14 + movdqa xmmword ptr [rsp+0x40], xmm8 + movdqa xmmword ptr [rsp+0x50], xmm9 + movdqa xmmword ptr [rsp+0x60], xmm12 + movdqa xmmword ptr [rsp+0x70], xmm13 + movdqu xmm8, xmmword ptr [r8+rdx-0x20] + movdqu xmm9, xmmword ptr [r9+rdx-0x20] + movdqu xmm10, xmmword ptr [r10+rdx-0x20] + movdqu xmm11, xmmword ptr [r11+rdx-0x20] + movdqa xmm12, xmm8 + punpckldq xmm8, xmm9 + punpckhdq xmm12, xmm9 + movdqa xmm14, xmm10 + punpckldq xmm10, xmm11 + punpckhdq xmm14, xmm11 + movdqa xmm9, xmm8 + punpcklqdq xmm8, xmm10 + punpckhqdq xmm9, xmm10 + movdqa xmm13, xmm12 + punpcklqdq xmm12, xmm14 + punpckhqdq xmm13, xmm14 + movdqa xmmword ptr [rsp+0x80], xmm8 + movdqa xmmword ptr [rsp+0x90], xmm9 + movdqa xmmword ptr [rsp+0xA0], xmm12 + movdqa xmmword ptr [rsp+0xB0], xmm13 + movdqu xmm8, xmmword ptr [r8+rdx-0x10] + movdqu xmm9, xmmword ptr [r9+rdx-0x10] + movdqu xmm10, xmmword ptr [r10+rdx-0x10] + movdqu xmm11, xmmword ptr [r11+rdx-0x10] + movdqa xmm12, xmm8 + punpckldq xmm8, xmm9 + punpckhdq xmm12, xmm9 + movdqa xmm14, xmm10 + punpckldq xmm10, xmm11 + punpckhdq xmm14, xmm11 + movdqa xmm9, xmm8 + punpcklqdq xmm8, xmm10 + punpckhqdq xmm9, xmm10 + movdqa xmm13, xmm12 + punpcklqdq xmm12, xmm14 + punpckhqdq xmm13, xmm14 + movdqa xmmword ptr [rsp+0xC0], xmm8 + movdqa xmmword ptr [rsp+0xD0], xmm9 + movdqa xmmword ptr [rsp+0xE0], xmm12 + movdqa xmmword ptr [rsp+0xF0], xmm13 + movdqa xmm9, xmmword ptr [BLAKE3_IV_1+rip] + movdqa xmm10, xmmword ptr [BLAKE3_IV_2+rip] + movdqa xmm11, xmmword ptr [BLAKE3_IV_3+rip] + movdqa xmm12, xmmword ptr [rsp+0x110] + movdqa xmm13, xmmword ptr [rsp+0x120] + movdqa xmm14, xmmword ptr [BLAKE3_BLOCK_LEN+rip] + movd xmm15, eax + pshufd xmm15, xmm15, 0x00 + prefetcht0 [r8+rdx+0x80] + prefetcht0 [r9+rdx+0x80] + prefetcht0 [r10+rdx+0x80] + prefetcht0 [r11+rdx+0x80] + paddd xmm0, xmmword ptr [rsp] + paddd xmm1, xmmword ptr [rsp+0x20] + paddd xmm2, xmmword ptr [rsp+0x40] + paddd xmm3, xmmword ptr [rsp+0x60] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [BLAKE3_IV_0+rip] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x10] + paddd xmm1, xmmword ptr [rsp+0x30] + paddd xmm2, xmmword ptr [rsp+0x50] + paddd xmm3, xmmword ptr [rsp+0x70] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x80] + paddd xmm1, xmmword ptr [rsp+0xA0] + paddd xmm2, xmmword ptr [rsp+0xC0] + paddd xmm3, xmmword ptr [rsp+0xE0] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x90] + paddd xmm1, xmmword ptr [rsp+0xB0] + paddd xmm2, xmmword ptr [rsp+0xD0] + paddd xmm3, xmmword ptr [rsp+0xF0] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x20] + paddd xmm1, xmmword ptr [rsp+0x30] + paddd xmm2, xmmword ptr [rsp+0x70] + paddd xmm3, xmmword ptr [rsp+0x40] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x60] + paddd xmm1, xmmword ptr [rsp+0xA0] + paddd xmm2, xmmword ptr [rsp] + paddd xmm3, xmmword ptr [rsp+0xD0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x10] + paddd xmm1, xmmword ptr [rsp+0xC0] + paddd xmm2, xmmword ptr [rsp+0x90] + paddd xmm3, xmmword ptr [rsp+0xF0] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0xB0] + paddd xmm1, xmmword ptr [rsp+0x50] + paddd xmm2, xmmword ptr [rsp+0xE0] + paddd xmm3, xmmword ptr [rsp+0x80] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x30] + paddd xmm1, xmmword ptr [rsp+0xA0] + paddd xmm2, xmmword ptr [rsp+0xD0] + paddd xmm3, xmmword ptr [rsp+0x70] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x40] + paddd xmm1, xmmword ptr [rsp+0xC0] + paddd xmm2, xmmword ptr [rsp+0x20] + paddd xmm3, xmmword ptr [rsp+0xE0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x60] + paddd xmm1, xmmword ptr [rsp+0x90] + paddd xmm2, xmmword ptr [rsp+0xB0] + paddd xmm3, xmmword ptr [rsp+0x80] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x50] + paddd xmm1, xmmword ptr [rsp] + paddd xmm2, xmmword ptr [rsp+0xF0] + paddd xmm3, xmmword ptr [rsp+0x10] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0xA0] + paddd xmm1, xmmword ptr [rsp+0xC0] + paddd xmm2, xmmword ptr [rsp+0xE0] + paddd xmm3, xmmword ptr [rsp+0xD0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x70] + paddd xmm1, xmmword ptr [rsp+0x90] + paddd xmm2, xmmword ptr [rsp+0x30] + paddd xmm3, xmmword ptr [rsp+0xF0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x40] + paddd xmm1, xmmword ptr [rsp+0xB0] + paddd xmm2, xmmword ptr [rsp+0x50] + paddd xmm3, xmmword ptr [rsp+0x10] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp] + paddd xmm1, xmmword ptr [rsp+0x20] + paddd xmm2, xmmword ptr [rsp+0x80] + paddd xmm3, xmmword ptr [rsp+0x60] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0xC0] + paddd xmm1, xmmword ptr [rsp+0x90] + paddd xmm2, xmmword ptr [rsp+0xF0] + paddd xmm3, xmmword ptr [rsp+0xE0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0xD0] + paddd xmm1, xmmword ptr [rsp+0xB0] + paddd xmm2, xmmword ptr [rsp+0xA0] + paddd xmm3, xmmword ptr [rsp+0x80] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0x70] + paddd xmm1, xmmword ptr [rsp+0x50] + paddd xmm2, xmmword ptr [rsp] + paddd xmm3, xmmword ptr [rsp+0x60] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x20] + paddd xmm1, xmmword ptr [rsp+0x30] + paddd xmm2, xmmword ptr [rsp+0x10] + paddd xmm3, xmmword ptr [rsp+0x40] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x90] + paddd xmm1, xmmword ptr [rsp+0xB0] + paddd xmm2, xmmword ptr [rsp+0x80] + paddd xmm3, xmmword ptr [rsp+0xF0] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0xE0] + paddd xmm1, xmmword ptr [rsp+0x50] + paddd xmm2, xmmword ptr [rsp+0xC0] + paddd xmm3, xmmword ptr [rsp+0x10] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0xD0] + paddd xmm1, xmmword ptr [rsp] + paddd xmm2, xmmword ptr [rsp+0x20] + paddd xmm3, xmmword ptr [rsp+0x40] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0x30] + paddd xmm1, xmmword ptr [rsp+0xA0] + paddd xmm2, xmmword ptr [rsp+0x60] + paddd xmm3, xmmword ptr [rsp+0x70] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0xB0] + paddd xmm1, xmmword ptr [rsp+0x50] + paddd xmm2, xmmword ptr [rsp+0x10] + paddd xmm3, xmmword ptr [rsp+0x80] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0xF0] + paddd xmm1, xmmword ptr [rsp] + paddd xmm2, xmmword ptr [rsp+0x90] + paddd xmm3, xmmword ptr [rsp+0x60] + paddd xmm0, xmm4 + paddd xmm1, xmm5 + paddd xmm2, xmm6 + paddd xmm3, xmm7 + pxor xmm12, xmm0 + pxor xmm13, xmm1 + pxor xmm14, xmm2 + pxor xmm15, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + pshufb xmm15, xmm8 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm12 + paddd xmm9, xmm13 + paddd xmm10, xmm14 + paddd xmm11, xmm15 + pxor xmm4, xmm8 + pxor xmm5, xmm9 + pxor xmm6, xmm10 + pxor xmm7, xmm11 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + paddd xmm0, xmmword ptr [rsp+0xE0] + paddd xmm1, xmmword ptr [rsp+0x20] + paddd xmm2, xmmword ptr [rsp+0x30] + paddd xmm3, xmmword ptr [rsp+0x70] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT16+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + movdqa xmmword ptr [rsp+0x100], xmm8 + movdqa xmm8, xmm5 + psrld xmm8, 12 + pslld xmm5, 20 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 12 + pslld xmm6, 20 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 12 + pslld xmm7, 20 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 12 + pslld xmm4, 20 + por xmm4, xmm8 + paddd xmm0, xmmword ptr [rsp+0xA0] + paddd xmm1, xmmword ptr [rsp+0xC0] + paddd xmm2, xmmword ptr [rsp+0x40] + paddd xmm3, xmmword ptr [rsp+0xD0] + paddd xmm0, xmm5 + paddd xmm1, xmm6 + paddd xmm2, xmm7 + paddd xmm3, xmm4 + pxor xmm15, xmm0 + pxor xmm12, xmm1 + pxor xmm13, xmm2 + pxor xmm14, xmm3 + movdqa xmm8, xmmword ptr [ROT8+rip] + pshufb xmm15, xmm8 + pshufb xmm12, xmm8 + pshufb xmm13, xmm8 + pshufb xmm14, xmm8 + paddd xmm10, xmm15 + paddd xmm11, xmm12 + movdqa xmm8, xmmword ptr [rsp+0x100] + paddd xmm8, xmm13 + paddd xmm9, xmm14 + pxor xmm5, xmm10 + pxor xmm6, xmm11 + pxor xmm7, xmm8 + pxor xmm4, xmm9 + pxor xmm0, xmm8 + pxor xmm1, xmm9 + pxor xmm2, xmm10 + pxor xmm3, xmm11 + movdqa xmm8, xmm5 + psrld xmm8, 7 + pslld xmm5, 25 + por xmm5, xmm8 + movdqa xmm8, xmm6 + psrld xmm8, 7 + pslld xmm6, 25 + por xmm6, xmm8 + movdqa xmm8, xmm7 + psrld xmm8, 7 + pslld xmm7, 25 + por xmm7, xmm8 + movdqa xmm8, xmm4 + psrld xmm8, 7 + pslld xmm4, 25 + por xmm4, xmm8 + pxor xmm4, xmm12 + pxor xmm5, xmm13 + pxor xmm6, xmm14 + pxor xmm7, xmm15 + mov eax, r13d + jne 9b + movdqa xmm9, xmm0 + punpckldq xmm0, xmm1 + punpckhdq xmm9, xmm1 + movdqa xmm11, xmm2 + punpckldq xmm2, xmm3 + punpckhdq xmm11, xmm3 + movdqa xmm1, xmm0 + punpcklqdq xmm0, xmm2 + punpckhqdq xmm1, xmm2 + movdqa xmm3, xmm9 + punpcklqdq xmm9, xmm11 + punpckhqdq xmm3, xmm11 + movdqu xmmword ptr [rbx], xmm0 + movdqu xmmword ptr [rbx+0x20], xmm1 + movdqu xmmword ptr [rbx+0x40], xmm9 + movdqu xmmword ptr [rbx+0x60], xmm3 + movdqa xmm9, xmm4 + punpckldq xmm4, xmm5 + punpckhdq xmm9, xmm5 + movdqa xmm11, xmm6 + punpckldq xmm6, xmm7 + punpckhdq xmm11, xmm7 + movdqa xmm5, xmm4 + punpcklqdq xmm4, xmm6 + punpckhqdq xmm5, xmm6 + movdqa xmm7, xmm9 + punpcklqdq xmm9, xmm11 + punpckhqdq xmm7, xmm11 + movdqu xmmword ptr [rbx+0x10], xmm4 + movdqu xmmword ptr [rbx+0x30], xmm5 + movdqu xmmword ptr [rbx+0x50], xmm9 + movdqu xmmword ptr [rbx+0x70], xmm7 + movdqa xmm1, xmmword ptr [rsp+0x110] + movdqa xmm0, xmm1 + paddd xmm1, xmmword ptr [rsp+0x150] + movdqa xmmword ptr [rsp+0x110], xmm1 + pxor xmm0, xmmword ptr [CMP_MSB_MASK+rip] + pxor xmm1, xmmword ptr [CMP_MSB_MASK+rip] + pcmpgtd xmm0, xmm1 + movdqa xmm1, xmmword ptr [rsp+0x120] + psubd xmm1, xmm0 + movdqa xmmword ptr [rsp+0x120], xmm1 + add rbx, 128 + add rdi, 32 + sub rsi, 4 + cmp rsi, 4 + jnc 2b + test rsi, rsi + jne 3f +4: + movdqa xmm6, xmmword ptr [rsp+0x170] + movdqa xmm7, xmmword ptr [rsp+0x180] + movdqa xmm8, xmmword ptr [rsp+0x190] + movdqa xmm9, xmmword ptr [rsp+0x1A0] + movdqa xmm10, xmmword ptr [rsp+0x1B0] + movdqa xmm11, xmmword ptr [rsp+0x1C0] + movdqa xmm12, xmmword ptr [rsp+0x1D0] + movdqa xmm13, xmmword ptr [rsp+0x1E0] + movdqa xmm14, xmmword ptr [rsp+0x1F0] + movdqa xmm15, xmmword ptr [rsp+0x200] + mov rsp, rbp + pop rbp + pop rbx + pop rdi + pop rsi + pop r12 + pop r13 + pop r14 + pop r15 + ret +.p2align 5 +3: + test esi, 0x2 + je 3f + movups xmm0, xmmword ptr [rcx] + movups xmm1, xmmword ptr [rcx+0x10] + movaps xmm8, xmm0 + movaps xmm9, xmm1 + movd xmm13, dword ptr [rsp+0x110] + pinsrd xmm13, dword ptr [rsp+0x120], 1 + pinsrd xmm13, dword ptr [BLAKE3_BLOCK_LEN+rip], 2 + movaps xmmword ptr [rsp], xmm13 + movd xmm14, dword ptr [rsp+0x114] + pinsrd xmm14, dword ptr [rsp+0x124], 1 + pinsrd xmm14, dword ptr [BLAKE3_BLOCK_LEN+rip], 2 + movaps xmmword ptr [rsp+0x10], xmm14 + mov r8, qword ptr [rdi] + mov r9, qword ptr [rdi+0x8] + movzx eax, byte ptr [rbp+0x80] + or eax, r13d + xor edx, edx +2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + movaps xmm2, xmmword ptr [BLAKE3_IV+rip] + movaps xmm10, xmm2 + movups xmm4, xmmword ptr [r8+rdx-0x40] + movups xmm5, xmmword ptr [r8+rdx-0x30] + movaps xmm3, xmm4 + shufps xmm4, xmm5, 136 + shufps xmm3, xmm5, 221 + movaps xmm5, xmm3 + movups xmm6, xmmword ptr [r8+rdx-0x20] + movups xmm7, xmmword ptr [r8+rdx-0x10] + movaps xmm3, xmm6 + shufps xmm6, xmm7, 136 + pshufd xmm6, xmm6, 0x93 + shufps xmm3, xmm7, 221 + pshufd xmm7, xmm3, 0x93 + movups xmm12, xmmword ptr [r9+rdx-0x40] + movups xmm13, xmmword ptr [r9+rdx-0x30] + movaps xmm11, xmm12 + shufps xmm12, xmm13, 136 + shufps xmm11, xmm13, 221 + movaps xmm13, xmm11 + movups xmm14, xmmword ptr [r9+rdx-0x20] + movups xmm15, xmmword ptr [r9+rdx-0x10] + movaps xmm11, xmm14 + shufps xmm14, xmm15, 136 + pshufd xmm14, xmm14, 0x93 + shufps xmm11, xmm15, 221 + pshufd xmm15, xmm11, 0x93 + movaps xmm3, xmmword ptr [rsp] + movaps xmm11, xmmword ptr [rsp+0x10] + pinsrd xmm3, eax, 3 + pinsrd xmm11, eax, 3 + mov al, 7 +9: + paddd xmm0, xmm4 + paddd xmm8, xmm12 + movaps xmmword ptr [rsp+0x20], xmm4 + movaps xmmword ptr [rsp+0x30], xmm12 + paddd xmm0, xmm1 + paddd xmm8, xmm9 + pxor xmm3, xmm0 + pxor xmm11, xmm8 + movaps xmm12, xmmword ptr [ROT16+rip] + pshufb xmm3, xmm12 + pshufb xmm11, xmm12 + paddd xmm2, xmm3 + paddd xmm10, xmm11 + pxor xmm1, xmm2 + pxor xmm9, xmm10 + movdqa xmm4, xmm1 + pslld xmm1, 20 + psrld xmm4, 12 + por xmm1, xmm4 + movdqa xmm4, xmm9 + pslld xmm9, 20 + psrld xmm4, 12 + por xmm9, xmm4 + paddd xmm0, xmm5 + paddd xmm8, xmm13 + movaps xmmword ptr [rsp+0x40], xmm5 + movaps xmmword ptr [rsp+0x50], xmm13 + paddd xmm0, xmm1 + paddd xmm8, xmm9 + pxor xmm3, xmm0 + pxor xmm11, xmm8 + movaps xmm13, xmmword ptr [ROT8+rip] + pshufb xmm3, xmm13 + pshufb xmm11, xmm13 + paddd xmm2, xmm3 + paddd xmm10, xmm11 + pxor xmm1, xmm2 + pxor xmm9, xmm10 + movdqa xmm4, xmm1 + pslld xmm1, 25 + psrld xmm4, 7 + por xmm1, xmm4 + movdqa xmm4, xmm9 + pslld xmm9, 25 + psrld xmm4, 7 + por xmm9, xmm4 + pshufd xmm0, xmm0, 0x93 + pshufd xmm8, xmm8, 0x93 + pshufd xmm3, xmm3, 0x4E + pshufd xmm11, xmm11, 0x4E + pshufd xmm2, xmm2, 0x39 + pshufd xmm10, xmm10, 0x39 + paddd xmm0, xmm6 + paddd xmm8, xmm14 + paddd xmm0, xmm1 + paddd xmm8, xmm9 + pxor xmm3, xmm0 + pxor xmm11, xmm8 + pshufb xmm3, xmm12 + pshufb xmm11, xmm12 + paddd xmm2, xmm3 + paddd xmm10, xmm11 + pxor xmm1, xmm2 + pxor xmm9, xmm10 + movdqa xmm4, xmm1 + pslld xmm1, 20 + psrld xmm4, 12 + por xmm1, xmm4 + movdqa xmm4, xmm9 + pslld xmm9, 20 + psrld xmm4, 12 + por xmm9, xmm4 + paddd xmm0, xmm7 + paddd xmm8, xmm15 + paddd xmm0, xmm1 + paddd xmm8, xmm9 + pxor xmm3, xmm0 + pxor xmm11, xmm8 + pshufb xmm3, xmm13 + pshufb xmm11, xmm13 + paddd xmm2, xmm3 + paddd xmm10, xmm11 + pxor xmm1, xmm2 + pxor xmm9, xmm10 + movdqa xmm4, xmm1 + pslld xmm1, 25 + psrld xmm4, 7 + por xmm1, xmm4 + movdqa xmm4, xmm9 + pslld xmm9, 25 + psrld xmm4, 7 + por xmm9, xmm4 + pshufd xmm0, xmm0, 0x39 + pshufd xmm8, xmm8, 0x39 + pshufd xmm3, xmm3, 0x4E + pshufd xmm11, xmm11, 0x4E + pshufd xmm2, xmm2, 0x93 + pshufd xmm10, xmm10, 0x93 + dec al + je 9f + movdqa xmm12, xmmword ptr [rsp+0x20] + movdqa xmm5, xmmword ptr [rsp+0x40] + pshufd xmm13, xmm12, 0x0F + shufps xmm12, xmm5, 214 + pshufd xmm4, xmm12, 0x39 + movdqa xmm12, xmm6 + shufps xmm12, xmm7, 250 + pblendw xmm13, xmm12, 0xCC + movdqa xmm12, xmm7 + punpcklqdq xmm12, xmm5 + pblendw xmm12, xmm6, 0xC0 + pshufd xmm12, xmm12, 0x78 + punpckhdq xmm5, xmm7 + punpckldq xmm6, xmm5 + pshufd xmm7, xmm6, 0x1E + movdqa xmmword ptr [rsp+0x20], xmm13 + movdqa xmmword ptr [rsp+0x40], xmm12 + movdqa xmm5, xmmword ptr [rsp+0x30] + movdqa xmm13, xmmword ptr [rsp+0x50] + pshufd xmm6, xmm5, 0x0F + shufps xmm5, xmm13, 214 + pshufd xmm12, xmm5, 0x39 + movdqa xmm5, xmm14 + shufps xmm5, xmm15, 250 + pblendw xmm6, xmm5, 0xCC + movdqa xmm5, xmm15 + punpcklqdq xmm5, xmm13 + pblendw xmm5, xmm14, 0xC0 + pshufd xmm5, xmm5, 0x78 + punpckhdq xmm13, xmm15 + punpckldq xmm14, xmm13 + pshufd xmm15, xmm14, 0x1E + movdqa xmm13, xmm6 + movdqa xmm14, xmm5 + movdqa xmm5, xmmword ptr [rsp+0x20] + movdqa xmm6, xmmword ptr [rsp+0x40] + jmp 9b +9: + pxor xmm0, xmm2 + pxor xmm1, xmm3 + pxor xmm8, xmm10 + pxor xmm9, xmm11 + mov eax, r13d + cmp rdx, r15 + jne 2b + movups xmmword ptr [rbx], xmm0 + movups xmmword ptr [rbx+0x10], xmm1 + movups xmmword ptr [rbx+0x20], xmm8 + movups xmmword ptr [rbx+0x30], xmm9 + movdqa xmm0, xmmword ptr [rsp+0x130] + movdqa xmm1, xmmword ptr [rsp+0x110] + movdqa xmm2, xmmword ptr [rsp+0x120] + movdqu xmm3, xmmword ptr [rsp+0x118] + movdqu xmm4, xmmword ptr [rsp+0x128] + blendvps xmm1, xmm3, xmm0 + blendvps xmm2, xmm4, xmm0 + movdqa xmmword ptr [rsp+0x110], xmm1 + movdqa xmmword ptr [rsp+0x120], xmm2 + add rdi, 16 + add rbx, 64 + sub rsi, 2 +3: + test esi, 0x1 + je 4b + movups xmm0, xmmword ptr [rcx] + movups xmm1, xmmword ptr [rcx+0x10] + movd xmm13, dword ptr [rsp+0x110] + pinsrd xmm13, dword ptr [rsp+0x120], 1 + pinsrd xmm13, dword ptr [BLAKE3_BLOCK_LEN+rip], 2 + movaps xmm14, xmmword ptr [ROT8+rip] + movaps xmm15, xmmword ptr [ROT16+rip] + mov r8, qword ptr [rdi] + movzx eax, byte ptr [rbp+0x80] + or eax, r13d + xor edx, edx +2: + mov r14d, eax + or eax, r12d + add rdx, 64 + cmp rdx, r15 + cmovne eax, r14d + movaps xmm2, xmmword ptr [BLAKE3_IV+rip] + movaps xmm3, xmm13 + pinsrd xmm3, eax, 3 + movups xmm4, xmmword ptr [r8+rdx-0x40] + movups xmm5, xmmword ptr [r8+rdx-0x30] + movaps xmm8, xmm4 + shufps xmm4, xmm5, 136 + shufps xmm8, xmm5, 221 + movaps xmm5, xmm8 + movups xmm6, xmmword ptr [r8+rdx-0x20] + movups xmm7, xmmword ptr [r8+rdx-0x10] + movaps xmm8, xmm6 + shufps xmm6, xmm7, 136 + pshufd xmm6, xmm6, 0x93 + shufps xmm8, xmm7, 221 + pshufd xmm7, xmm8, 0x93 + mov al, 7 +9: + paddd xmm0, xmm4 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm15 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm5 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x93 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x39 + paddd xmm0, xmm6 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm15 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm7 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x39 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x93 + dec al + jz 9f + movdqa xmm8, xmm4 + shufps xmm8, xmm5, 214 + pshufd xmm9, xmm4, 0x0F + pshufd xmm4, xmm8, 0x39 + movdqa xmm8, xmm6 + shufps xmm8, xmm7, 250 + pblendw xmm9, xmm8, 0xCC + movdqa xmm8, xmm7 + punpcklqdq xmm8, xmm5 + pblendw xmm8, xmm6, 0xC0 + pshufd xmm8, xmm8, 0x78 + punpckhdq xmm5, xmm7 + punpckldq xmm6, xmm5 + pshufd xmm7, xmm6, 0x1E + movdqa xmm5, xmm9 + movdqa xmm6, xmm8 + jmp 9b +9: + pxor xmm0, xmm2 + pxor xmm1, xmm3 + mov eax, r13d + cmp rdx, r15 + jne 2b + movups xmmword ptr [rbx], xmm0 + movups xmmword ptr [rbx+0x10], xmm1 + jmp 4b + +.p2align 6 +blake3_compress_in_place_sse41: +_blake3_compress_in_place_sse41: + sub rsp, 120 + movdqa xmmword ptr [rsp], xmm6 + movdqa xmmword ptr [rsp+0x10], xmm7 + movdqa xmmword ptr [rsp+0x20], xmm8 + movdqa xmmword ptr [rsp+0x30], xmm9 + movdqa xmmword ptr [rsp+0x40], xmm11 + movdqa xmmword ptr [rsp+0x50], xmm14 + movdqa xmmword ptr [rsp+0x60], xmm15 + movups xmm0, xmmword ptr [rcx] + movups xmm1, xmmword ptr [rcx+0x10] + movaps xmm2, xmmword ptr [BLAKE3_IV+rip] + movzx eax, byte ptr [rsp+0xA0] + movzx r8d, r8b + shl rax, 32 + add r8, rax + movq xmm3, r9 + movq xmm4, r8 + punpcklqdq xmm3, xmm4 + movups xmm4, xmmword ptr [rdx] + movups xmm5, xmmword ptr [rdx+0x10] + movaps xmm8, xmm4 + shufps xmm4, xmm5, 136 + shufps xmm8, xmm5, 221 + movaps xmm5, xmm8 + movups xmm6, xmmword ptr [rdx+0x20] + movups xmm7, xmmword ptr [rdx+0x30] + movaps xmm8, xmm6 + shufps xmm6, xmm7, 136 + pshufd xmm6, xmm6, 0x93 + shufps xmm8, xmm7, 221 + pshufd xmm7, xmm8, 0x93 + movaps xmm14, xmmword ptr [ROT8+rip] + movaps xmm15, xmmword ptr [ROT16+rip] + mov al, 7 +9: + paddd xmm0, xmm4 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm15 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm5 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x93 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x39 + paddd xmm0, xmm6 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm15 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm7 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x39 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x93 + dec al + jz 9f + movdqa xmm8, xmm4 + shufps xmm8, xmm5, 214 + pshufd xmm9, xmm4, 0x0F + pshufd xmm4, xmm8, 0x39 + movdqa xmm8, xmm6 + shufps xmm8, xmm7, 250 + pblendw xmm9, xmm8, 0xCC + movdqa xmm8, xmm7 + punpcklqdq xmm8, xmm5 + pblendw xmm8, xmm6, 0xC0 + pshufd xmm8, xmm8, 0x78 + punpckhdq xmm5, xmm7 + punpckldq xmm6, xmm5 + pshufd xmm7, xmm6, 0x1E + movdqa xmm5, xmm9 + movdqa xmm6, xmm8 + jmp 9b +9: + pxor xmm0, xmm2 + pxor xmm1, xmm3 + movups xmmword ptr [rcx], xmm0 + movups xmmword ptr [rcx+0x10], xmm1 + movdqa xmm6, xmmword ptr [rsp] + movdqa xmm7, xmmword ptr [rsp+0x10] + movdqa xmm8, xmmword ptr [rsp+0x20] + movdqa xmm9, xmmword ptr [rsp+0x30] + movdqa xmm11, xmmword ptr [rsp+0x40] + movdqa xmm14, xmmword ptr [rsp+0x50] + movdqa xmm15, xmmword ptr [rsp+0x60] + add rsp, 120 + ret + + +.p2align 6 +_blake3_compress_xof_sse41: +blake3_compress_xof_sse41: + sub rsp, 120 + movdqa xmmword ptr [rsp], xmm6 + movdqa xmmword ptr [rsp+0x10], xmm7 + movdqa xmmword ptr [rsp+0x20], xmm8 + movdqa xmmword ptr [rsp+0x30], xmm9 + movdqa xmmword ptr [rsp+0x40], xmm11 + movdqa xmmword ptr [rsp+0x50], xmm14 + movdqa xmmword ptr [rsp+0x60], xmm15 + movups xmm0, xmmword ptr [rcx] + movups xmm1, xmmword ptr [rcx+0x10] + movaps xmm2, xmmword ptr [BLAKE3_IV+rip] + movzx eax, byte ptr [rsp+0xA0] + movzx r8d, r8b + mov r10, qword ptr [rsp+0xA8] + shl rax, 32 + add r8, rax + movq xmm3, r9 + movq xmm4, r8 + punpcklqdq xmm3, xmm4 + movups xmm4, xmmword ptr [rdx] + movups xmm5, xmmword ptr [rdx+0x10] + movaps xmm8, xmm4 + shufps xmm4, xmm5, 136 + shufps xmm8, xmm5, 221 + movaps xmm5, xmm8 + movups xmm6, xmmword ptr [rdx+0x20] + movups xmm7, xmmword ptr [rdx+0x30] + movaps xmm8, xmm6 + shufps xmm6, xmm7, 136 + pshufd xmm6, xmm6, 0x93 + shufps xmm8, xmm7, 221 + pshufd xmm7, xmm8, 0x93 + movaps xmm14, xmmword ptr [ROT8+rip] + movaps xmm15, xmmword ptr [ROT16+rip] + mov al, 7 +9: + paddd xmm0, xmm4 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm15 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm5 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x93 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x39 + paddd xmm0, xmm6 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm15 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 20 + psrld xmm11, 12 + por xmm1, xmm11 + paddd xmm0, xmm7 + paddd xmm0, xmm1 + pxor xmm3, xmm0 + pshufb xmm3, xmm14 + paddd xmm2, xmm3 + pxor xmm1, xmm2 + movdqa xmm11, xmm1 + pslld xmm1, 25 + psrld xmm11, 7 + por xmm1, xmm11 + pshufd xmm0, xmm0, 0x39 + pshufd xmm3, xmm3, 0x4E + pshufd xmm2, xmm2, 0x93 + dec al + jz 9f + movdqa xmm8, xmm4 + shufps xmm8, xmm5, 214 + pshufd xmm9, xmm4, 0x0F + pshufd xmm4, xmm8, 0x39 + movdqa xmm8, xmm6 + shufps xmm8, xmm7, 250 + pblendw xmm9, xmm8, 0xCC + movdqa xmm8, xmm7 + punpcklqdq xmm8, xmm5 + pblendw xmm8, xmm6, 0xC0 + pshufd xmm8, xmm8, 0x78 + punpckhdq xmm5, xmm7 + punpckldq xmm6, xmm5 + pshufd xmm7, xmm6, 0x1E + movdqa xmm5, xmm9 + movdqa xmm6, xmm8 + jmp 9b +9: + movdqu xmm4, xmmword ptr [rcx] + movdqu xmm5, xmmword ptr [rcx+0x10] + pxor xmm0, xmm2 + pxor xmm1, xmm3 + pxor xmm2, xmm4 + pxor xmm3, xmm5 + movups xmmword ptr [r10], xmm0 + movups xmmword ptr [r10+0x10], xmm1 + movups xmmword ptr [r10+0x20], xmm2 + movups xmmword ptr [r10+0x30], xmm3 + movdqa xmm6, xmmword ptr [rsp] + movdqa xmm7, xmmword ptr [rsp+0x10] + movdqa xmm8, xmmword ptr [rsp+0x20] + movdqa xmm9, xmmword ptr [rsp+0x30] + movdqa xmm11, xmmword ptr [rsp+0x40] + movdqa xmm14, xmmword ptr [rsp+0x50] + movdqa xmm15, xmmword ptr [rsp+0x60] + add rsp, 120 + ret + + +.section .rodata +.p2align 6 +BLAKE3_IV: + .long 0x6A09E667, 0xBB67AE85 + .long 0x3C6EF372, 0xA54FF53A +ROT16: + .byte 2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13 +ROT8: + .byte 1, 2, 3, 0, 5, 6, 7, 4, 9, 10, 11, 8, 13, 14, 15, 12 +ADD0: + .long 0, 1, 2, 3 +ADD1: + .long 4, 4, 4, 4 +BLAKE3_IV_0: + .long 0x6A09E667, 0x6A09E667, 0x6A09E667, 0x6A09E667 +BLAKE3_IV_1: + .long 0xBB67AE85, 0xBB67AE85, 0xBB67AE85, 0xBB67AE85 +BLAKE3_IV_2: + .long 0x3C6EF372, 0x3C6EF372, 0x3C6EF372, 0x3C6EF372 +BLAKE3_IV_3: + .long 0xA54FF53A, 0xA54FF53A, 0xA54FF53A, 0xA54FF53A +BLAKE3_BLOCK_LEN: + .long 64, 64, 64, 64 +CMP_MSB_MASK: + .long 0x80000000, 0x80000000, 0x80000000, 0x80000000 diff --git a/src/third_party/doctest.h b/src/third_party/doctest.h new file mode 100644 index 0000000..9444698 --- /dev/null +++ b/src/third_party/doctest.h @@ -0,0 +1,6205 @@ +// ====================================================================== lgtm [cpp/missing-header-guard] +// == DO NOT MODIFY THIS FILE BY HAND - IT IS AUTO GENERATED BY CMAKE! == +// ====================================================================== +// +// doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD +// +// Copyright (c) 2016-2019 Viktor Kirilov +// +// Distributed under the MIT Software License +// See accompanying file LICENSE.txt or copy at +// https://opensource.org/licenses/MIT +// +// The documentation can be found at the library's page: +// https://github.com/onqtam/doctest/blob/master/doc/markdown/readme.md +// +// ================================================================================================= +// ================================================================================================= +// ================================================================================================= +// +// The library is heavily influenced by Catch - https://github.com/catchorg/Catch2 +// which uses the Boost Software License - Version 1.0 +// see here - https://github.com/catchorg/Catch2/blob/master/LICENSE.txt +// +// The concept of subcases (sections in Catch) and expression decomposition are from there. +// Some parts of the code are taken directly: +// - stringification - the detection of "ostream& operator<<(ostream&, const T&)" and StringMaker<> +// - the Approx() helper class for floating point comparison +// - colors in the console +// - breaking into a debugger +// - signal / SEH handling +// - timer +// - XmlWriter class - thanks to Phil Nash for allowing the direct reuse (AKA copy/paste) +// +// The expression decomposing templates are taken from lest - https://github.com/martinmoene/lest +// which uses the Boost Software License - Version 1.0 +// see here - https://github.com/martinmoene/lest/blob/master/LICENSE.txt +// +// ================================================================================================= +// ================================================================================================= +// ================================================================================================= + +#ifndef DOCTEST_LIBRARY_INCLUDED +#define DOCTEST_LIBRARY_INCLUDED + +// ================================================================================================= +// == VERSION ====================================================================================== +// ================================================================================================= + +#define DOCTEST_VERSION_MAJOR 2 +#define DOCTEST_VERSION_MINOR 4 +#define DOCTEST_VERSION_PATCH 0 +#define DOCTEST_VERSION_STR "2.4.0" + +#define DOCTEST_VERSION \ + (DOCTEST_VERSION_MAJOR * 10000 + DOCTEST_VERSION_MINOR * 100 + DOCTEST_VERSION_PATCH) + +// ================================================================================================= +// == COMPILER VERSION ============================================================================= +// ================================================================================================= + +// ideas for the version stuff are taken from here: https://github.com/cxxstuff/cxx_detect + +#define DOCTEST_COMPILER(MAJOR, MINOR, PATCH) ((MAJOR)*10000000 + (MINOR)*100000 + (PATCH)) + +// GCC/Clang and GCC/MSVC are mutually exclusive, but Clang/MSVC are not because of clang-cl... +#if defined(_MSC_VER) && defined(_MSC_FULL_VER) +#if _MSC_VER == _MSC_FULL_VER / 10000 +#define DOCTEST_MSVC DOCTEST_COMPILER(_MSC_VER / 100, _MSC_VER % 100, _MSC_FULL_VER % 10000) +#else // MSVC +#define DOCTEST_MSVC \ + DOCTEST_COMPILER(_MSC_VER / 100, (_MSC_FULL_VER / 100000) % 100, _MSC_FULL_VER % 100000) +#endif // MSVC +#endif // MSVC +#if defined(__clang__) && defined(__clang_minor__) +#define DOCTEST_CLANG DOCTEST_COMPILER(__clang_major__, __clang_minor__, __clang_patchlevel__) +#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && \ + !defined(__INTEL_COMPILER) +#define DOCTEST_GCC DOCTEST_COMPILER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#endif // GCC + +#ifndef DOCTEST_MSVC +#define DOCTEST_MSVC 0 +#endif // DOCTEST_MSVC +#ifndef DOCTEST_CLANG +#define DOCTEST_CLANG 0 +#endif // DOCTEST_CLANG +#ifndef DOCTEST_GCC +#define DOCTEST_GCC 0 +#endif // DOCTEST_GCC + +// ================================================================================================= +// == COMPILER WARNINGS HELPERS ==================================================================== +// ================================================================================================= + +#if DOCTEST_CLANG +#define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x) +#define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH _Pragma("clang diagnostic push") +#define DOCTEST_CLANG_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(clang diagnostic ignored w) +#define DOCTEST_CLANG_SUPPRESS_WARNING_POP _Pragma("clang diagnostic pop") +#define DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(w) \ + DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING(w) +#else // DOCTEST_CLANG +#define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH +#define DOCTEST_CLANG_SUPPRESS_WARNING(w) +#define DOCTEST_CLANG_SUPPRESS_WARNING_POP +#define DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(w) +#endif // DOCTEST_CLANG + +#if DOCTEST_GCC +#define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x) +#define DOCTEST_GCC_SUPPRESS_WARNING_PUSH _Pragma("GCC diagnostic push") +#define DOCTEST_GCC_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(GCC diagnostic ignored w) +#define DOCTEST_GCC_SUPPRESS_WARNING_POP _Pragma("GCC diagnostic pop") +#define DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH(w) \ + DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING(w) +#else // DOCTEST_GCC +#define DOCTEST_GCC_SUPPRESS_WARNING_PUSH +#define DOCTEST_GCC_SUPPRESS_WARNING(w) +#define DOCTEST_GCC_SUPPRESS_WARNING_POP +#define DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH(w) +#endif // DOCTEST_GCC + +#if DOCTEST_MSVC +#define DOCTEST_MSVC_SUPPRESS_WARNING_PUSH __pragma(warning(push)) +#define DOCTEST_MSVC_SUPPRESS_WARNING(w) __pragma(warning(disable : w)) +#define DOCTEST_MSVC_SUPPRESS_WARNING_POP __pragma(warning(pop)) +#define DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(w) \ + DOCTEST_MSVC_SUPPRESS_WARNING_PUSH DOCTEST_MSVC_SUPPRESS_WARNING(w) +#else // DOCTEST_MSVC +#define DOCTEST_MSVC_SUPPRESS_WARNING_PUSH +#define DOCTEST_MSVC_SUPPRESS_WARNING(w) +#define DOCTEST_MSVC_SUPPRESS_WARNING_POP +#define DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(w) +#endif // DOCTEST_MSVC + +// ================================================================================================= +// == COMPILER WARNINGS ============================================================================ +// ================================================================================================= + +DOCTEST_CLANG_SUPPRESS_WARNING_PUSH +DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wnon-virtual-dtor") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wdeprecated") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") + +DOCTEST_GCC_SUPPRESS_WARNING_PUSH +DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas") +DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas") +DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++") +DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") +DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing") +DOCTEST_GCC_SUPPRESS_WARNING("-Wctor-dtor-privacy") +DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") +DOCTEST_GCC_SUPPRESS_WARNING("-Wnon-virtual-dtor") +DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs") +DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") +DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") +DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-promo") + +DOCTEST_MSVC_SUPPRESS_WARNING_PUSH +DOCTEST_MSVC_SUPPRESS_WARNING(4616) // invalid compiler warning +DOCTEST_MSVC_SUPPRESS_WARNING(4619) // invalid compiler warning +DOCTEST_MSVC_SUPPRESS_WARNING(4996) // The compiler encountered a deprecated declaration +DOCTEST_MSVC_SUPPRESS_WARNING(4706) // assignment within conditional expression +DOCTEST_MSVC_SUPPRESS_WARNING(4512) // 'class' : assignment operator could not be generated +DOCTEST_MSVC_SUPPRESS_WARNING(4127) // conditional expression is constant +DOCTEST_MSVC_SUPPRESS_WARNING(4820) // padding +DOCTEST_MSVC_SUPPRESS_WARNING(4625) // copy constructor was implicitly defined as deleted +DOCTEST_MSVC_SUPPRESS_WARNING(4626) // assignment operator was implicitly defined as deleted +DOCTEST_MSVC_SUPPRESS_WARNING(5027) // move assignment operator was implicitly defined as deleted +DOCTEST_MSVC_SUPPRESS_WARNING(5026) // move constructor was implicitly defined as deleted +DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly defined as deleted +DOCTEST_MSVC_SUPPRESS_WARNING(4640) // construction of local static object is not thread-safe +// static analysis +DOCTEST_MSVC_SUPPRESS_WARNING(26439) // This kind of function may not throw. Declare it 'noexcept' +DOCTEST_MSVC_SUPPRESS_WARNING(26495) // Always initialize a member variable +DOCTEST_MSVC_SUPPRESS_WARNING(26451) // Arithmetic overflow ... +DOCTEST_MSVC_SUPPRESS_WARNING(26444) // Avoid unnamed objects with custom construction and dtr... +DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum' + +// 4548 - expression before comma has no effect; expected expression with side - effect +// 4265 - class has virtual functions, but destructor is not virtual +// 4986 - exception specification does not match previous declaration +// 4350 - behavior change: 'member1' called instead of 'member2' +// 4668 - 'x' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' +// 4365 - conversion from 'int' to 'unsigned long', signed/unsigned mismatch +// 4774 - format string expected in argument 'x' is not a string literal +// 4820 - padding in structs + +// only 4 should be disabled globally: +// - 4514 # unreferenced inline function has been removed +// - 4571 # SEH related +// - 4710 # function not inlined +// - 4711 # function 'x' selected for automatic inline expansion + +#define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN \ + DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \ + DOCTEST_MSVC_SUPPRESS_WARNING(4548) \ + DOCTEST_MSVC_SUPPRESS_WARNING(4265) \ + DOCTEST_MSVC_SUPPRESS_WARNING(4986) \ + DOCTEST_MSVC_SUPPRESS_WARNING(4350) \ + DOCTEST_MSVC_SUPPRESS_WARNING(4668) \ + DOCTEST_MSVC_SUPPRESS_WARNING(4365) \ + DOCTEST_MSVC_SUPPRESS_WARNING(4774) \ + DOCTEST_MSVC_SUPPRESS_WARNING(4820) \ + DOCTEST_MSVC_SUPPRESS_WARNING(4625) \ + DOCTEST_MSVC_SUPPRESS_WARNING(4626) \ + DOCTEST_MSVC_SUPPRESS_WARNING(5027) \ + DOCTEST_MSVC_SUPPRESS_WARNING(5026) \ + DOCTEST_MSVC_SUPPRESS_WARNING(4623) \ + DOCTEST_MSVC_SUPPRESS_WARNING(5039) \ + DOCTEST_MSVC_SUPPRESS_WARNING(5045) \ + DOCTEST_MSVC_SUPPRESS_WARNING(5105) + +#define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END DOCTEST_MSVC_SUPPRESS_WARNING_POP + +// ================================================================================================= +// == FEATURE DETECTION ============================================================================ +// ================================================================================================= + +// general compiler feature support table: https://en.cppreference.com/w/cpp/compiler_support +// MSVC C++11 feature support table: https://msdn.microsoft.com/en-us/library/hh567368.aspx +// GCC C++11 feature support table: https://gcc.gnu.org/projects/cxx-status.html +// MSVC version table: +// https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B#Internal_version_numbering +// MSVC++ 14.2 (16) _MSC_VER == 1920 (Visual Studio 2019) +// MSVC++ 14.1 (15) _MSC_VER == 1910 (Visual Studio 2017) +// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) +// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) +// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) +// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) +// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) +// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) + +#if DOCTEST_MSVC && !defined(DOCTEST_CONFIG_WINDOWS_SEH) +#define DOCTEST_CONFIG_WINDOWS_SEH +#endif // MSVC +#if defined(DOCTEST_CONFIG_NO_WINDOWS_SEH) && defined(DOCTEST_CONFIG_WINDOWS_SEH) +#undef DOCTEST_CONFIG_WINDOWS_SEH +#endif // DOCTEST_CONFIG_NO_WINDOWS_SEH + +#if !defined(_WIN32) && !defined(__QNX__) && !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && \ + !defined(__EMSCRIPTEN__) +#define DOCTEST_CONFIG_POSIX_SIGNALS +#endif // _WIN32 +#if defined(DOCTEST_CONFIG_NO_POSIX_SIGNALS) && defined(DOCTEST_CONFIG_POSIX_SIGNALS) +#undef DOCTEST_CONFIG_POSIX_SIGNALS +#endif // DOCTEST_CONFIG_NO_POSIX_SIGNALS + +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS +#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND) +#define DOCTEST_CONFIG_NO_EXCEPTIONS +#endif // no exceptions +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + +#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS +#define DOCTEST_CONFIG_NO_EXCEPTIONS +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS + +#if defined(DOCTEST_CONFIG_NO_EXCEPTIONS) && !defined(DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS) +#define DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS && !DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS + +#if defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) && !defined(DOCTEST_CONFIG_IMPLEMENT) +#define DOCTEST_CONFIG_IMPLEMENT +#endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN + +#if defined(_WIN32) || defined(__CYGWIN__) +#if DOCTEST_MSVC +#define DOCTEST_SYMBOL_EXPORT __declspec(dllexport) +#define DOCTEST_SYMBOL_IMPORT __declspec(dllimport) +#else // MSVC +#define DOCTEST_SYMBOL_EXPORT __attribute__((dllexport)) +#define DOCTEST_SYMBOL_IMPORT __attribute__((dllimport)) +#endif // MSVC +#else // _WIN32 +#define DOCTEST_SYMBOL_EXPORT __attribute__((visibility("default"))) +#define DOCTEST_SYMBOL_IMPORT +#endif // _WIN32 + +#ifdef DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL +#ifdef DOCTEST_CONFIG_IMPLEMENT +#define DOCTEST_INTERFACE DOCTEST_SYMBOL_EXPORT +#else // DOCTEST_CONFIG_IMPLEMENT +#define DOCTEST_INTERFACE DOCTEST_SYMBOL_IMPORT +#endif // DOCTEST_CONFIG_IMPLEMENT +#else // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL +#define DOCTEST_INTERFACE +#endif // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL + +#define DOCTEST_EMPTY + +#if DOCTEST_MSVC +#define DOCTEST_NOINLINE __declspec(noinline) +#define DOCTEST_UNUSED +#define DOCTEST_ALIGNMENT(x) +#else // MSVC +#define DOCTEST_NOINLINE __attribute__((noinline)) +#define DOCTEST_UNUSED __attribute__((unused)) +#define DOCTEST_ALIGNMENT(x) __attribute__((aligned(x))) +#endif // MSVC + +#ifndef DOCTEST_NORETURN +#define DOCTEST_NORETURN [[noreturn]] +#endif // DOCTEST_NORETURN + +#ifndef DOCTEST_NOEXCEPT +#define DOCTEST_NOEXCEPT noexcept +#endif // DOCTEST_NOEXCEPT + +// ================================================================================================= +// == FEATURE DETECTION END ======================================================================== +// ================================================================================================= + +// internal macros for string concatenation and anonymous variable name generation +#define DOCTEST_CAT_IMPL(s1, s2) s1##s2 +#define DOCTEST_CAT(s1, s2) DOCTEST_CAT_IMPL(s1, s2) +#ifdef __COUNTER__ // not standard and may be missing for some compilers +#define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __COUNTER__) +#else // __COUNTER__ +#define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __LINE__) +#endif // __COUNTER__ + +#define DOCTEST_TOSTR(x) #x + +#ifndef DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE +#define DOCTEST_REF_WRAP(x) x& +#else // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE +#define DOCTEST_REF_WRAP(x) x +#endif // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE + +// not using __APPLE__ because... this is how Catch does it +#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED +#define DOCTEST_PLATFORM_MAC +#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#define DOCTEST_PLATFORM_IPHONE +#elif defined(_WIN32) +#define DOCTEST_PLATFORM_WINDOWS +#else // DOCTEST_PLATFORM +#define DOCTEST_PLATFORM_LINUX +#endif // DOCTEST_PLATFORM + +#define DOCTEST_GLOBAL_NO_WARNINGS(var) \ + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors") \ + DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-variable") \ + static int var DOCTEST_UNUSED // NOLINT(fuchsia-statically-constructed-objects,cert-err58-cpp) +#define DOCTEST_GLOBAL_NO_WARNINGS_END() DOCTEST_CLANG_SUPPRESS_WARNING_POP + +#ifndef DOCTEST_BREAK_INTO_DEBUGGER +// should probably take a look at https://github.com/scottt/debugbreak +#ifdef DOCTEST_PLATFORM_MAC +#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) +#elif DOCTEST_MSVC +#define DOCTEST_BREAK_INTO_DEBUGGER() __debugbreak() +#elif defined(__MINGW32__) +DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wredundant-decls") +extern "C" __declspec(dllimport) void __stdcall DebugBreak(); +DOCTEST_GCC_SUPPRESS_WARNING_POP +#define DOCTEST_BREAK_INTO_DEBUGGER() ::DebugBreak() +#else // linux +#define DOCTEST_BREAK_INTO_DEBUGGER() ((void)0) +#endif // linux +#endif // DOCTEST_BREAK_INTO_DEBUGGER + +// this is kept here for backwards compatibility since the config option was changed +#ifdef DOCTEST_CONFIG_USE_IOSFWD +#define DOCTEST_CONFIG_USE_STD_HEADERS +#endif // DOCTEST_CONFIG_USE_IOSFWD + +#ifdef DOCTEST_CONFIG_USE_STD_HEADERS +#include +#include +#include +#else // DOCTEST_CONFIG_USE_STD_HEADERS + +#if DOCTEST_CLANG +// to detect if libc++ is being used with clang (the _LIBCPP_VERSION identifier) +#include +#endif // clang + +#ifdef _LIBCPP_VERSION +#define DOCTEST_STD_NAMESPACE_BEGIN _LIBCPP_BEGIN_NAMESPACE_STD +#define DOCTEST_STD_NAMESPACE_END _LIBCPP_END_NAMESPACE_STD +#else // _LIBCPP_VERSION +#define DOCTEST_STD_NAMESPACE_BEGIN namespace std { +#define DOCTEST_STD_NAMESPACE_END } +#endif // _LIBCPP_VERSION + +// Forward declaring 'X' in namespace std is not permitted by the C++ Standard. +DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4643) + +DOCTEST_STD_NAMESPACE_BEGIN // NOLINT (cert-dcl58-cpp) +typedef decltype(nullptr) nullptr_t; +template +struct char_traits; +template <> +struct char_traits; +template +class basic_ostream; +typedef basic_ostream> ostream; +template +class tuple; +#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) +// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183 +template +class allocator; +template +class basic_string; +using string = basic_string, allocator>; +#endif // VS 2019 +DOCTEST_STD_NAMESPACE_END + +DOCTEST_MSVC_SUPPRESS_WARNING_POP + +#endif // DOCTEST_CONFIG_USE_STD_HEADERS + +#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS +#include +#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + +namespace doctest { + +DOCTEST_INTERFACE extern bool is_running_in_test; + +// A 24 byte string class (can be as small as 17 for x64 and 13 for x86) that can hold strings with length +// of up to 23 chars on the stack before going on the heap - the last byte of the buffer is used for: +// - "is small" bit - the highest bit - if "0" then it is small - otherwise its "1" (128) +// - if small - capacity left before going on the heap - using the lowest 5 bits +// - if small - 2 bits are left unused - the second and third highest ones +// - if small - acts as a null terminator if strlen() is 23 (24 including the null terminator) +// and the "is small" bit remains "0" ("as well as the capacity left") so its OK +// Idea taken from this lecture about the string implementation of facebook/folly - fbstring +// https://www.youtube.com/watch?v=kPR8h4-qZdk +// TODO: +// - optimizations - like not deleting memory unnecessarily in operator= and etc. +// - resize/reserve/clear +// - substr +// - replace +// - back/front +// - iterator stuff +// - find & friends +// - push_back/pop_back +// - assign/insert/erase +// - relational operators as free functions - taking const char* as one of the params +class DOCTEST_INTERFACE String +{ + static const unsigned len = 24; //!OCLINT avoid private static members + static const unsigned last = len - 1; //!OCLINT avoid private static members + + struct view // len should be more than sizeof(view) - because of the final byte for flags + { + char* ptr; + unsigned size; + unsigned capacity; + }; + + union + { + char buf[len]; + view data; + }; + + bool isOnStack() const { return (buf[last] & 128) == 0; } + void setOnHeap(); + void setLast(unsigned in = last); + + void copy(const String& other); + +public: + String(); + ~String(); + + // cppcheck-suppress noExplicitConstructor + String(const char* in); + String(const char* in, unsigned in_size); + + String(const String& other); + String& operator=(const String& other); + + String& operator+=(const String& other); + String operator+(const String& other) const; + + String(String&& other); + String& operator=(String&& other); + + char operator[](unsigned i) const; + char& operator[](unsigned i); + + // the only functions I'm willing to leave in the interface - available for inlining + const char* c_str() const { return const_cast(this)->c_str(); } // NOLINT + char* c_str() { + if(isOnStack()) + return reinterpret_cast(buf); + return data.ptr; + } + + unsigned size() const; + unsigned capacity() const; + + int compare(const char* other, bool no_case = false) const; + int compare(const String& other, bool no_case = false) const; +}; + +DOCTEST_INTERFACE bool operator==(const String& lhs, const String& rhs); +DOCTEST_INTERFACE bool operator!=(const String& lhs, const String& rhs); +DOCTEST_INTERFACE bool operator<(const String& lhs, const String& rhs); +DOCTEST_INTERFACE bool operator>(const String& lhs, const String& rhs); +DOCTEST_INTERFACE bool operator<=(const String& lhs, const String& rhs); +DOCTEST_INTERFACE bool operator>=(const String& lhs, const String& rhs); + +DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, const String& in); + +namespace Color { + enum Enum + { + None = 0, + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, + + Bright = 0x10, + + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White + }; + + DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, Color::Enum code); +} // namespace Color + +namespace assertType { + enum Enum + { + // macro traits + + is_warn = 1, + is_check = 2 * is_warn, + is_require = 2 * is_check, + + is_normal = 2 * is_require, + is_throws = 2 * is_normal, + is_throws_as = 2 * is_throws, + is_throws_with = 2 * is_throws_as, + is_nothrow = 2 * is_throws_with, + + is_false = 2 * is_nothrow, + is_unary = 2 * is_false, // not checked anywhere - used just to distinguish the types + + is_eq = 2 * is_unary, + is_ne = 2 * is_eq, + + is_lt = 2 * is_ne, + is_gt = 2 * is_lt, + + is_ge = 2 * is_gt, + is_le = 2 * is_ge, + + // macro types + + DT_WARN = is_normal | is_warn, + DT_CHECK = is_normal | is_check, + DT_REQUIRE = is_normal | is_require, + + DT_WARN_FALSE = is_normal | is_false | is_warn, + DT_CHECK_FALSE = is_normal | is_false | is_check, + DT_REQUIRE_FALSE = is_normal | is_false | is_require, + + DT_WARN_THROWS = is_throws | is_warn, + DT_CHECK_THROWS = is_throws | is_check, + DT_REQUIRE_THROWS = is_throws | is_require, + + DT_WARN_THROWS_AS = is_throws_as | is_warn, + DT_CHECK_THROWS_AS = is_throws_as | is_check, + DT_REQUIRE_THROWS_AS = is_throws_as | is_require, + + DT_WARN_THROWS_WITH = is_throws_with | is_warn, + DT_CHECK_THROWS_WITH = is_throws_with | is_check, + DT_REQUIRE_THROWS_WITH = is_throws_with | is_require, + + DT_WARN_THROWS_WITH_AS = is_throws_with | is_throws_as | is_warn, + DT_CHECK_THROWS_WITH_AS = is_throws_with | is_throws_as | is_check, + DT_REQUIRE_THROWS_WITH_AS = is_throws_with | is_throws_as | is_require, + + DT_WARN_NOTHROW = is_nothrow | is_warn, + DT_CHECK_NOTHROW = is_nothrow | is_check, + DT_REQUIRE_NOTHROW = is_nothrow | is_require, + + DT_WARN_EQ = is_normal | is_eq | is_warn, + DT_CHECK_EQ = is_normal | is_eq | is_check, + DT_REQUIRE_EQ = is_normal | is_eq | is_require, + + DT_WARN_NE = is_normal | is_ne | is_warn, + DT_CHECK_NE = is_normal | is_ne | is_check, + DT_REQUIRE_NE = is_normal | is_ne | is_require, + + DT_WARN_GT = is_normal | is_gt | is_warn, + DT_CHECK_GT = is_normal | is_gt | is_check, + DT_REQUIRE_GT = is_normal | is_gt | is_require, + + DT_WARN_LT = is_normal | is_lt | is_warn, + DT_CHECK_LT = is_normal | is_lt | is_check, + DT_REQUIRE_LT = is_normal | is_lt | is_require, + + DT_WARN_GE = is_normal | is_ge | is_warn, + DT_CHECK_GE = is_normal | is_ge | is_check, + DT_REQUIRE_GE = is_normal | is_ge | is_require, + + DT_WARN_LE = is_normal | is_le | is_warn, + DT_CHECK_LE = is_normal | is_le | is_check, + DT_REQUIRE_LE = is_normal | is_le | is_require, + + DT_WARN_UNARY = is_normal | is_unary | is_warn, + DT_CHECK_UNARY = is_normal | is_unary | is_check, + DT_REQUIRE_UNARY = is_normal | is_unary | is_require, + + DT_WARN_UNARY_FALSE = is_normal | is_false | is_unary | is_warn, + DT_CHECK_UNARY_FALSE = is_normal | is_false | is_unary | is_check, + DT_REQUIRE_UNARY_FALSE = is_normal | is_false | is_unary | is_require, + }; +} // namespace assertType + +DOCTEST_INTERFACE const char* assertString(assertType::Enum at); +DOCTEST_INTERFACE const char* failureString(assertType::Enum at); +DOCTEST_INTERFACE const char* skipPathFromFilename(const char* file); + +struct DOCTEST_INTERFACE TestCaseData +{ + String m_file; // the file in which the test was registered + unsigned m_line; // the line where the test was registered + const char* m_name; // name of the test case + const char* m_test_suite; // the test suite in which the test was added + const char* m_description; + bool m_skip; + bool m_may_fail; + bool m_should_fail; + int m_expected_failures; + double m_timeout; +}; + +struct DOCTEST_INTERFACE AssertData +{ + // common - for all asserts + const TestCaseData* m_test_case; + assertType::Enum m_at; + const char* m_file; + int m_line; + const char* m_expr; + bool m_failed; + + // exception-related - for all asserts + bool m_threw; + String m_exception; + + // for normal asserts + String m_decomp; + + // for specific exception-related asserts + bool m_threw_as; + const char* m_exception_type; + const char* m_exception_string; +}; + +struct DOCTEST_INTERFACE MessageData +{ + String m_string; + const char* m_file; + int m_line; + assertType::Enum m_severity; +}; + +struct DOCTEST_INTERFACE SubcaseSignature +{ + String m_name; + const char* m_file; + int m_line; + + bool operator<(const SubcaseSignature& other) const; +}; + +struct DOCTEST_INTERFACE IContextScope +{ + IContextScope(); + virtual ~IContextScope(); + virtual void stringify(std::ostream*) const = 0; +}; + +struct ContextOptions //!OCLINT too many fields +{ + std::ostream* cout; // stdout stream - std::cout by default + std::ostream* cerr; // stderr stream - std::cerr by default + String binary_name; // the test binary name + + // == parameters from the command line + String out; // output filename + String order_by; // how tests should be ordered + unsigned rand_seed; // the seed for rand ordering + + unsigned first; // the first (matching) test to be executed + unsigned last; // the last (matching) test to be executed + + int abort_after; // stop tests after this many failed assertions + int subcase_filter_levels; // apply the subcase filters for the first N levels + + bool success; // include successful assertions in output + bool case_sensitive; // if filtering should be case sensitive + bool exit; // if the program should be exited after the tests are ran/whatever + bool duration; // print the time duration of each test case + bool no_throw; // to skip exceptions-related assertion macros + bool no_exitcode; // if the framework should return 0 as the exitcode + bool no_run; // to not run the tests at all (can be done with an "*" exclude) + bool no_version; // to not print the version of the framework + bool no_colors; // if output to the console should be colorized + bool force_colors; // forces the use of colors even when a tty cannot be detected + bool no_breaks; // to not break into the debugger + bool no_skip; // don't skip test cases which are marked to be skipped + bool gnu_file_line; // if line numbers should be surrounded with :x: and not (x): + bool no_path_in_filenames; // if the path to files should be removed from the output + bool no_line_numbers; // if source code line numbers should be omitted from the output + bool no_skipped_summary; // don't print "skipped" in the summary !!! UNDOCUMENTED !!! + bool no_time_in_output; // omit any time/timestamps from output !!! UNDOCUMENTED !!! + + bool help; // to print the help + bool version; // to print the version + bool count; // if only the count of matching tests is to be retrieved + bool list_test_cases; // to list all tests matching the filters + bool list_test_suites; // to list all suites matching the filters + bool list_reporters; // lists all registered reporters +}; + +namespace detail { +#if defined(DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING) || defined(DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS) + template + struct enable_if + {}; + + template + struct enable_if + { typedef TYPE type; }; +#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING) || DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + + // clang-format off + template struct remove_reference { typedef T type; }; + template struct remove_reference { typedef T type; }; + template struct remove_reference { typedef T type; }; + + template struct remove_const { typedef T type; }; + template struct remove_const { typedef T type; }; + // clang-format on + + template + struct deferred_false + // cppcheck-suppress unusedStructMember + { static const bool value = false; }; + + namespace has_insertion_operator_impl { + std::ostream &os(); + template + DOCTEST_REF_WRAP(T) val(); + + template + struct check { + static constexpr auto value = false; + }; + + template + struct check(), void())> { + static constexpr auto value = true; + }; + } // namespace has_insertion_operator_impl + + template + using has_insertion_operator = has_insertion_operator_impl::check; + + DOCTEST_INTERFACE void my_memcpy(void* dest, const void* src, unsigned num); + + DOCTEST_INTERFACE std::ostream* getTlsOss(); // returns a thread-local ostringstream + DOCTEST_INTERFACE String getTlsOssResult(); + + template + struct StringMakerBase + { + template + static String convert(const DOCTEST_REF_WRAP(T)) { + return "{?}"; + } + }; + + template <> + struct StringMakerBase + { + template + static String convert(const DOCTEST_REF_WRAP(T) in) { + *getTlsOss() << in; + return getTlsOssResult(); + } + }; + + DOCTEST_INTERFACE String rawMemoryToString(const void* object, unsigned size); + + template + String rawMemoryToString(const DOCTEST_REF_WRAP(T) object) { + return rawMemoryToString(&object, sizeof(object)); + } + + template + const char* type_to_string() { + return "<>"; + } +} // namespace detail + +template +struct StringMaker : public detail::StringMakerBase::value> +{}; + +template +struct StringMaker +{ + template + static String convert(U* p) { + if(p) + return detail::rawMemoryToString(p); + return "NULL"; + } +}; + +template +struct StringMaker +{ + static String convert(R C::*p) { + if(p) + return detail::rawMemoryToString(p); + return "NULL"; + } +}; + +template +String toString(const DOCTEST_REF_WRAP(T) value) { + return StringMaker::convert(value); +} + +#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +DOCTEST_INTERFACE String toString(char* in); +DOCTEST_INTERFACE String toString(const char* in); +#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +DOCTEST_INTERFACE String toString(bool in); +DOCTEST_INTERFACE String toString(float in); +DOCTEST_INTERFACE String toString(double in); +DOCTEST_INTERFACE String toString(double long in); + +DOCTEST_INTERFACE String toString(char in); +DOCTEST_INTERFACE String toString(char signed in); +DOCTEST_INTERFACE String toString(char unsigned in); +DOCTEST_INTERFACE String toString(int short in); +DOCTEST_INTERFACE String toString(int short unsigned in); +DOCTEST_INTERFACE String toString(int in); +DOCTEST_INTERFACE String toString(int unsigned in); +DOCTEST_INTERFACE String toString(int long in); +DOCTEST_INTERFACE String toString(int long unsigned in); +DOCTEST_INTERFACE String toString(int long long in); +DOCTEST_INTERFACE String toString(int long long unsigned in); +DOCTEST_INTERFACE String toString(std::nullptr_t in); + +#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) +// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183 +DOCTEST_INTERFACE String toString(const std::string& in); +#endif // VS 2019 + +class DOCTEST_INTERFACE Approx +{ +public: + explicit Approx(double value); + + Approx operator()(double value) const; + +#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + template + explicit Approx(const T& value, + typename detail::enable_if::value>::type* = + static_cast(nullptr)) { + *this = Approx(static_cast(value)); + } +#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + + Approx& epsilon(double newEpsilon); + +#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + template + typename detail::enable_if::value, Approx&>::type epsilon( + const T& newEpsilon) { + m_epsilon = static_cast(newEpsilon); + return *this; + } +#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + + Approx& scale(double newScale); + +#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + template + typename detail::enable_if::value, Approx&>::type scale( + const T& newScale) { + m_scale = static_cast(newScale); + return *this; + } +#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + + // clang-format off + DOCTEST_INTERFACE friend bool operator==(double lhs, const Approx & rhs); + DOCTEST_INTERFACE friend bool operator==(const Approx & lhs, double rhs); + DOCTEST_INTERFACE friend bool operator!=(double lhs, const Approx & rhs); + DOCTEST_INTERFACE friend bool operator!=(const Approx & lhs, double rhs); + DOCTEST_INTERFACE friend bool operator<=(double lhs, const Approx & rhs); + DOCTEST_INTERFACE friend bool operator<=(const Approx & lhs, double rhs); + DOCTEST_INTERFACE friend bool operator>=(double lhs, const Approx & rhs); + DOCTEST_INTERFACE friend bool operator>=(const Approx & lhs, double rhs); + DOCTEST_INTERFACE friend bool operator< (double lhs, const Approx & rhs); + DOCTEST_INTERFACE friend bool operator< (const Approx & lhs, double rhs); + DOCTEST_INTERFACE friend bool operator> (double lhs, const Approx & rhs); + DOCTEST_INTERFACE friend bool operator> (const Approx & lhs, double rhs); + + DOCTEST_INTERFACE friend String toString(const Approx& in); + +#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS +#define DOCTEST_APPROX_PREFIX \ + template friend typename detail::enable_if::value, bool>::type + + DOCTEST_APPROX_PREFIX operator==(const T& lhs, const Approx& rhs) { return operator==(double(lhs), rhs); } + DOCTEST_APPROX_PREFIX operator==(const Approx& lhs, const T& rhs) { return operator==(rhs, lhs); } + DOCTEST_APPROX_PREFIX operator!=(const T& lhs, const Approx& rhs) { return !operator==(lhs, rhs); } + DOCTEST_APPROX_PREFIX operator!=(const Approx& lhs, const T& rhs) { return !operator==(rhs, lhs); } + DOCTEST_APPROX_PREFIX operator<=(const T& lhs, const Approx& rhs) { return double(lhs) < rhs.m_value || lhs == rhs; } + DOCTEST_APPROX_PREFIX operator<=(const Approx& lhs, const T& rhs) { return lhs.m_value < double(rhs) || lhs == rhs; } + DOCTEST_APPROX_PREFIX operator>=(const T& lhs, const Approx& rhs) { return double(lhs) > rhs.m_value || lhs == rhs; } + DOCTEST_APPROX_PREFIX operator>=(const Approx& lhs, const T& rhs) { return lhs.m_value > double(rhs) || lhs == rhs; } + DOCTEST_APPROX_PREFIX operator< (const T& lhs, const Approx& rhs) { return double(lhs) < rhs.m_value && lhs != rhs; } + DOCTEST_APPROX_PREFIX operator< (const Approx& lhs, const T& rhs) { return lhs.m_value < double(rhs) && lhs != rhs; } + DOCTEST_APPROX_PREFIX operator> (const T& lhs, const Approx& rhs) { return double(lhs) > rhs.m_value && lhs != rhs; } + DOCTEST_APPROX_PREFIX operator> (const Approx& lhs, const T& rhs) { return lhs.m_value > double(rhs) && lhs != rhs; } +#undef DOCTEST_APPROX_PREFIX +#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + + // clang-format on + +private: + double m_epsilon; + double m_scale; + double m_value; +}; + +DOCTEST_INTERFACE String toString(const Approx& in); + +DOCTEST_INTERFACE const ContextOptions* getContextOptions(); + +#if !defined(DOCTEST_CONFIG_DISABLE) + +namespace detail { + // clang-format off +#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + template struct decay_array { typedef T type; }; + template struct decay_array { typedef T* type; }; + template struct decay_array { typedef T* type; }; + + template struct not_char_pointer { enum { value = 1 }; }; + template<> struct not_char_pointer { enum { value = 0 }; }; + template<> struct not_char_pointer { enum { value = 0 }; }; + + template struct can_use_op : public not_char_pointer::type> {}; +#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + // clang-format on + + struct DOCTEST_INTERFACE TestFailureException + { + }; + + DOCTEST_INTERFACE bool checkIfShouldThrow(assertType::Enum at); + +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + DOCTEST_NORETURN +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + DOCTEST_INTERFACE void throwException(); + + struct DOCTEST_INTERFACE Subcase + { + SubcaseSignature m_signature; + bool m_entered = false; + + Subcase(const String& name, const char* file, int line); + ~Subcase(); + + operator bool() const; + }; + + template + String stringifyBinaryExpr(const DOCTEST_REF_WRAP(L) lhs, const char* op, + const DOCTEST_REF_WRAP(R) rhs) { + return toString(lhs) + op + toString(rhs); + } + +#define DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(op, op_str, op_macro) \ + template \ + DOCTEST_NOINLINE Result operator op(const DOCTEST_REF_WRAP(R) rhs) { \ + bool res = op_macro(lhs, rhs); \ + if(m_at & assertType::is_false) \ + res = !res; \ + if(!res || doctest::getContextOptions()->success) \ + return Result(res, stringifyBinaryExpr(lhs, op_str, rhs)); \ + return Result(res); \ + } + + // more checks could be added - like in Catch: + // https://github.com/catchorg/Catch2/pull/1480/files + // https://github.com/catchorg/Catch2/pull/1481/files +#define DOCTEST_FORBIT_EXPRESSION(rt, op) \ + template \ + rt& operator op(const R&) { \ + static_assert(deferred_false::value, \ + "Expression Too Complex Please Rewrite As Binary Comparison!"); \ + return *this; \ + } + + struct DOCTEST_INTERFACE Result + { + bool m_passed; + String m_decomp; + + Result(bool passed, const String& decomposition = String()); + + // forbidding some expressions based on this table: https://en.cppreference.com/w/cpp/language/operator_precedence + DOCTEST_FORBIT_EXPRESSION(Result, &) + DOCTEST_FORBIT_EXPRESSION(Result, ^) + DOCTEST_FORBIT_EXPRESSION(Result, |) + DOCTEST_FORBIT_EXPRESSION(Result, &&) + DOCTEST_FORBIT_EXPRESSION(Result, ||) + DOCTEST_FORBIT_EXPRESSION(Result, ==) + DOCTEST_FORBIT_EXPRESSION(Result, !=) + DOCTEST_FORBIT_EXPRESSION(Result, <) + DOCTEST_FORBIT_EXPRESSION(Result, >) + DOCTEST_FORBIT_EXPRESSION(Result, <=) + DOCTEST_FORBIT_EXPRESSION(Result, >=) + DOCTEST_FORBIT_EXPRESSION(Result, =) + DOCTEST_FORBIT_EXPRESSION(Result, +=) + DOCTEST_FORBIT_EXPRESSION(Result, -=) + DOCTEST_FORBIT_EXPRESSION(Result, *=) + DOCTEST_FORBIT_EXPRESSION(Result, /=) + DOCTEST_FORBIT_EXPRESSION(Result, %=) + DOCTEST_FORBIT_EXPRESSION(Result, <<=) + DOCTEST_FORBIT_EXPRESSION(Result, >>=) + DOCTEST_FORBIT_EXPRESSION(Result, &=) + DOCTEST_FORBIT_EXPRESSION(Result, ^=) + DOCTEST_FORBIT_EXPRESSION(Result, |=) + }; + +#ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION + + DOCTEST_CLANG_SUPPRESS_WARNING_PUSH + DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion") + DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-compare") + //DOCTEST_CLANG_SUPPRESS_WARNING("-Wdouble-promotion") + //DOCTEST_CLANG_SUPPRESS_WARNING("-Wconversion") + //DOCTEST_CLANG_SUPPRESS_WARNING("-Wfloat-equal") + + DOCTEST_GCC_SUPPRESS_WARNING_PUSH + DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion") + DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-compare") + //DOCTEST_GCC_SUPPRESS_WARNING("-Wdouble-promotion") + //DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion") + //DOCTEST_GCC_SUPPRESS_WARNING("-Wfloat-equal") + + DOCTEST_MSVC_SUPPRESS_WARNING_PUSH + // https://stackoverflow.com/questions/39479163 what's the difference between 4018 and 4389 + DOCTEST_MSVC_SUPPRESS_WARNING(4388) // signed/unsigned mismatch + DOCTEST_MSVC_SUPPRESS_WARNING(4389) // 'operator' : signed/unsigned mismatch + DOCTEST_MSVC_SUPPRESS_WARNING(4018) // 'expression' : signed/unsigned mismatch + //DOCTEST_MSVC_SUPPRESS_WARNING(4805) // 'operation' : unsafe mix of type 'type' and type 'type' in operation + +#endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION + + // clang-format off +#ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +#define DOCTEST_COMPARISON_RETURN_TYPE bool +#else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +#define DOCTEST_COMPARISON_RETURN_TYPE typename enable_if::value || can_use_op::value, bool>::type + inline bool eq(const char* lhs, const char* rhs) { return String(lhs) == String(rhs); } + inline bool ne(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); } + inline bool lt(const char* lhs, const char* rhs) { return String(lhs) < String(rhs); } + inline bool gt(const char* lhs, const char* rhs) { return String(lhs) > String(rhs); } + inline bool le(const char* lhs, const char* rhs) { return String(lhs) <= String(rhs); } + inline bool ge(const char* lhs, const char* rhs) { return String(lhs) >= String(rhs); } +#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + // clang-format on + +#define DOCTEST_RELATIONAL_OP(name, op) \ + template \ + DOCTEST_COMPARISON_RETURN_TYPE name(const DOCTEST_REF_WRAP(L) lhs, \ + const DOCTEST_REF_WRAP(R) rhs) { \ + return lhs op rhs; \ + } + + DOCTEST_RELATIONAL_OP(eq, ==) + DOCTEST_RELATIONAL_OP(ne, !=) + DOCTEST_RELATIONAL_OP(lt, <) + DOCTEST_RELATIONAL_OP(gt, >) + DOCTEST_RELATIONAL_OP(le, <=) + DOCTEST_RELATIONAL_OP(ge, >=) + +#ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +#define DOCTEST_CMP_EQ(l, r) l == r +#define DOCTEST_CMP_NE(l, r) l != r +#define DOCTEST_CMP_GT(l, r) l > r +#define DOCTEST_CMP_LT(l, r) l < r +#define DOCTEST_CMP_GE(l, r) l >= r +#define DOCTEST_CMP_LE(l, r) l <= r +#else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +#define DOCTEST_CMP_EQ(l, r) eq(l, r) +#define DOCTEST_CMP_NE(l, r) ne(l, r) +#define DOCTEST_CMP_GT(l, r) gt(l, r) +#define DOCTEST_CMP_LT(l, r) lt(l, r) +#define DOCTEST_CMP_GE(l, r) ge(l, r) +#define DOCTEST_CMP_LE(l, r) le(l, r) +#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + + template + // cppcheck-suppress copyCtorAndEqOperator + struct Expression_lhs + { + L lhs; + assertType::Enum m_at; + + explicit Expression_lhs(L in, assertType::Enum at) + : lhs(in) + , m_at(at) {} + + DOCTEST_NOINLINE operator Result() { + bool res = !!lhs; + if(m_at & assertType::is_false) //!OCLINT bitwise operator in conditional + res = !res; + + if(!res || getContextOptions()->success) + return Result(res, toString(lhs)); + return Result(res); + } + + // clang-format off + DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(==, " == ", DOCTEST_CMP_EQ) //!OCLINT bitwise operator in conditional + DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(!=, " != ", DOCTEST_CMP_NE) //!OCLINT bitwise operator in conditional + DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(>, " > ", DOCTEST_CMP_GT) //!OCLINT bitwise operator in conditional + DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<, " < ", DOCTEST_CMP_LT) //!OCLINT bitwise operator in conditional + DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(>=, " >= ", DOCTEST_CMP_GE) //!OCLINT bitwise operator in conditional + DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<=, " <= ", DOCTEST_CMP_LE) //!OCLINT bitwise operator in conditional + // clang-format on + + // forbidding some expressions based on this table: https://en.cppreference.com/w/cpp/language/operator_precedence + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ^) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, |) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &&) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ||) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, =) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, +=) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, -=) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, *=) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, /=) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, %=) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, <<=) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, >>=) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, &=) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, ^=) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, |=) + // these 2 are unfortunate because they should be allowed - they have higher precedence over the comparisons, but the + // ExpressionDecomposer class uses the left shift operator to capture the left operand of the binary expression... + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, <<) + DOCTEST_FORBIT_EXPRESSION(Expression_lhs, >>) + }; + +#ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION + + DOCTEST_CLANG_SUPPRESS_WARNING_POP + DOCTEST_MSVC_SUPPRESS_WARNING_POP + DOCTEST_GCC_SUPPRESS_WARNING_POP + +#endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION + + struct DOCTEST_INTERFACE ExpressionDecomposer + { + assertType::Enum m_at; + + ExpressionDecomposer(assertType::Enum at); + + // The right operator for capturing expressions is "<=" instead of "<<" (based on the operator precedence table) + // but then there will be warnings from GCC about "-Wparentheses" and since "_Pragma()" is problematic this will stay for now... + // https://github.com/catchorg/Catch2/issues/870 + // https://github.com/catchorg/Catch2/issues/565 + template + Expression_lhs operator<<(const DOCTEST_REF_WRAP(L) operand) { + return Expression_lhs(operand, m_at); + } + }; + + struct DOCTEST_INTERFACE TestSuite + { + const char* m_test_suite; + const char* m_description; + bool m_skip; + bool m_may_fail; + bool m_should_fail; + int m_expected_failures; + double m_timeout; + + TestSuite& operator*(const char* in); + + template + TestSuite& operator*(const T& in) { + in.fill(*this); + return *this; + } + }; + + typedef void (*funcType)(); + + struct DOCTEST_INTERFACE TestCase : public TestCaseData + { + funcType m_test; // a function pointer to the test case + + const char* m_type; // for templated test cases - gets appended to the real name + int m_template_id; // an ID used to distinguish between the different versions of a templated test case + String m_full_name; // contains the name (only for templated test cases!) + the template type + + TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite, + const char* type = "", int template_id = -1); + + TestCase(const TestCase& other); + + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function + TestCase& operator=(const TestCase& other); + DOCTEST_MSVC_SUPPRESS_WARNING_POP + + TestCase& operator*(const char* in); + + template + TestCase& operator*(const T& in) { + in.fill(*this); + return *this; + } + + bool operator<(const TestCase& other) const; + }; + + // forward declarations of functions used by the macros + DOCTEST_INTERFACE int regTest(const TestCase& tc); + DOCTEST_INTERFACE int setTestSuite(const TestSuite& ts); + DOCTEST_INTERFACE bool isDebuggerActive(); + + template + int instantiationHelper(const T&) { return 0; } + + namespace binaryAssertComparison { + enum Enum + { + eq = 0, + ne, + gt, + lt, + ge, + le + }; + } // namespace binaryAssertComparison + + // clang-format off + template struct RelationalComparator { bool operator()(const DOCTEST_REF_WRAP(L), const DOCTEST_REF_WRAP(R) ) const { return false; } }; + +#define DOCTEST_BINARY_RELATIONAL_OP(n, op) \ + template struct RelationalComparator { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return op(lhs, rhs); } }; + // clang-format on + + DOCTEST_BINARY_RELATIONAL_OP(0, eq) + DOCTEST_BINARY_RELATIONAL_OP(1, ne) + DOCTEST_BINARY_RELATIONAL_OP(2, gt) + DOCTEST_BINARY_RELATIONAL_OP(3, lt) + DOCTEST_BINARY_RELATIONAL_OP(4, ge) + DOCTEST_BINARY_RELATIONAL_OP(5, le) + + struct DOCTEST_INTERFACE ResultBuilder : public AssertData + { + ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, + const char* exception_type = "", const char* exception_string = ""); + + void setResult(const Result& res); + + template + DOCTEST_NOINLINE void binary_assert(const DOCTEST_REF_WRAP(L) lhs, + const DOCTEST_REF_WRAP(R) rhs) { + m_failed = !RelationalComparator()(lhs, rhs); + if(m_failed || getContextOptions()->success) + m_decomp = stringifyBinaryExpr(lhs, ", ", rhs); + } + + template + DOCTEST_NOINLINE void unary_assert(const DOCTEST_REF_WRAP(L) val) { + m_failed = !val; + + if(m_at & assertType::is_false) //!OCLINT bitwise operator in conditional + m_failed = !m_failed; + + if(m_failed || getContextOptions()->success) + m_decomp = toString(val); + } + + void translateException(); + + bool log(); + void react() const; + }; + + namespace assertAction { + enum Enum + { + nothing = 0, + dbgbreak = 1, + shouldthrow = 2 + }; + } // namespace assertAction + + DOCTEST_INTERFACE void failed_out_of_a_testing_context(const AssertData& ad); + + DOCTEST_INTERFACE void decomp_assert(assertType::Enum at, const char* file, int line, + const char* expr, Result result); + +#define DOCTEST_ASSERT_OUT_OF_TESTS(decomp) \ + do { \ + if(!is_running_in_test) { \ + if(failed) { \ + ResultBuilder rb(at, file, line, expr); \ + rb.m_failed = failed; \ + rb.m_decomp = decomp; \ + failed_out_of_a_testing_context(rb); \ + if(isDebuggerActive() && !getContextOptions()->no_breaks) \ + DOCTEST_BREAK_INTO_DEBUGGER(); \ + if(checkIfShouldThrow(at)) \ + throwException(); \ + } \ + return; \ + } \ + } while(false) + +#define DOCTEST_ASSERT_IN_TESTS(decomp) \ + ResultBuilder rb(at, file, line, expr); \ + rb.m_failed = failed; \ + if(rb.m_failed || getContextOptions()->success) \ + rb.m_decomp = decomp; \ + if(rb.log()) \ + DOCTEST_BREAK_INTO_DEBUGGER(); \ + if(rb.m_failed && checkIfShouldThrow(at)) \ + throwException() + + template + DOCTEST_NOINLINE void binary_assert(assertType::Enum at, const char* file, int line, + const char* expr, const DOCTEST_REF_WRAP(L) lhs, + const DOCTEST_REF_WRAP(R) rhs) { + bool failed = !RelationalComparator()(lhs, rhs); + + // ################################################################################### + // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT + // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED + // ################################################################################### + DOCTEST_ASSERT_OUT_OF_TESTS(stringifyBinaryExpr(lhs, ", ", rhs)); + DOCTEST_ASSERT_IN_TESTS(stringifyBinaryExpr(lhs, ", ", rhs)); + } + + template + DOCTEST_NOINLINE void unary_assert(assertType::Enum at, const char* file, int line, + const char* expr, const DOCTEST_REF_WRAP(L) val) { + bool failed = !val; + + if(at & assertType::is_false) //!OCLINT bitwise operator in conditional + failed = !failed; + + // ################################################################################### + // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT + // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED + // ################################################################################### + DOCTEST_ASSERT_OUT_OF_TESTS(toString(val)); + DOCTEST_ASSERT_IN_TESTS(toString(val)); + } + + struct DOCTEST_INTERFACE IExceptionTranslator + { + IExceptionTranslator(); + virtual ~IExceptionTranslator(); + virtual bool translate(String&) const = 0; + }; + + template + class ExceptionTranslator : public IExceptionTranslator //!OCLINT destructor of virtual class + { + public: + explicit ExceptionTranslator(String (*translateFunction)(T)) + : m_translateFunction(translateFunction) {} + + bool translate(String& res) const override { +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + try { + throw; // lgtm [cpp/rethrow-no-exception] + // cppcheck-suppress catchExceptionByValue + } catch(T ex) { // NOLINT + res = m_translateFunction(ex); //!OCLINT parameter reassignment + return true; + } catch(...) {} //!OCLINT - empty catch statement +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + ((void)res); // to silence -Wunused-parameter + return false; + } + + private: + String (*m_translateFunction)(T); + }; + + DOCTEST_INTERFACE void registerExceptionTranslatorImpl(const IExceptionTranslator* et); + + template + struct StringStreamBase + { + template + static void convert(std::ostream* s, const T& in) { + *s << toString(in); + } + + // always treat char* as a string in this context - no matter + // if DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING is defined + static void convert(std::ostream* s, const char* in) { *s << String(in); } + }; + + template <> + struct StringStreamBase + { + template + static void convert(std::ostream* s, const T& in) { + *s << in; + } + }; + + template + struct StringStream : public StringStreamBase::value> + {}; + + template + void toStream(std::ostream* s, const T& value) { + StringStream::convert(s, value); + } + +#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + DOCTEST_INTERFACE void toStream(std::ostream* s, char* in); + DOCTEST_INTERFACE void toStream(std::ostream* s, const char* in); +#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + DOCTEST_INTERFACE void toStream(std::ostream* s, bool in); + DOCTEST_INTERFACE void toStream(std::ostream* s, float in); + DOCTEST_INTERFACE void toStream(std::ostream* s, double in); + DOCTEST_INTERFACE void toStream(std::ostream* s, double long in); + + DOCTEST_INTERFACE void toStream(std::ostream* s, char in); + DOCTEST_INTERFACE void toStream(std::ostream* s, char signed in); + DOCTEST_INTERFACE void toStream(std::ostream* s, char unsigned in); + DOCTEST_INTERFACE void toStream(std::ostream* s, int short in); + DOCTEST_INTERFACE void toStream(std::ostream* s, int short unsigned in); + DOCTEST_INTERFACE void toStream(std::ostream* s, int in); + DOCTEST_INTERFACE void toStream(std::ostream* s, int unsigned in); + DOCTEST_INTERFACE void toStream(std::ostream* s, int long in); + DOCTEST_INTERFACE void toStream(std::ostream* s, int long unsigned in); + DOCTEST_INTERFACE void toStream(std::ostream* s, int long long in); + DOCTEST_INTERFACE void toStream(std::ostream* s, int long long unsigned in); + + // ContextScope base class used to allow implementing methods of ContextScope + // that don't depend on the template parameter in doctest.cpp. + class DOCTEST_INTERFACE ContextScopeBase : public IContextScope { + protected: + ContextScopeBase(); + + void destroy(); + }; + + template class ContextScope : public ContextScopeBase + { + const L &lambda_; + + public: + explicit ContextScope(const L &lambda) : lambda_(lambda) {} + + ContextScope(ContextScope &&other) : lambda_(other.lambda_) {} + + void stringify(std::ostream* s) const override { lambda_(s); } + + ~ContextScope() override { destroy(); } + }; + + struct DOCTEST_INTERFACE MessageBuilder : public MessageData + { + std::ostream* m_stream; + + MessageBuilder(const char* file, int line, assertType::Enum severity); + MessageBuilder() = delete; + ~MessageBuilder(); + + template + MessageBuilder& operator<<(const T& in) { + toStream(m_stream, in); + return *this; + } + + bool log(); + void react(); + }; + + template + ContextScope MakeContextScope(const L &lambda) { + return ContextScope(lambda); + } +} // namespace detail + +#define DOCTEST_DEFINE_DECORATOR(name, type, def) \ + struct name \ + { \ + type data; \ + name(type in = def) \ + : data(in) {} \ + void fill(detail::TestCase& state) const { state.DOCTEST_CAT(m_, name) = data; } \ + void fill(detail::TestSuite& state) const { state.DOCTEST_CAT(m_, name) = data; } \ + } + +DOCTEST_DEFINE_DECORATOR(test_suite, const char*, ""); +DOCTEST_DEFINE_DECORATOR(description, const char*, ""); +DOCTEST_DEFINE_DECORATOR(skip, bool, true); +DOCTEST_DEFINE_DECORATOR(timeout, double, 0); +DOCTEST_DEFINE_DECORATOR(may_fail, bool, true); +DOCTEST_DEFINE_DECORATOR(should_fail, bool, true); +DOCTEST_DEFINE_DECORATOR(expected_failures, int, 0); + +template +int registerExceptionTranslator(String (*translateFunction)(T)) { + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") + static detail::ExceptionTranslator exceptionTranslator(translateFunction); + DOCTEST_CLANG_SUPPRESS_WARNING_POP + detail::registerExceptionTranslatorImpl(&exceptionTranslator); + return 0; +} + +} // namespace doctest + +// in a separate namespace outside of doctest because the DOCTEST_TEST_SUITE macro +// introduces an anonymous namespace in which getCurrentTestSuite gets overridden +namespace doctest_detail_test_suite_ns { +DOCTEST_INTERFACE doctest::detail::TestSuite& getCurrentTestSuite(); +} // namespace doctest_detail_test_suite_ns + +namespace doctest { +#else // DOCTEST_CONFIG_DISABLE +template +int registerExceptionTranslator(String (*)(T)) { + return 0; +} +#endif // DOCTEST_CONFIG_DISABLE + +namespace detail { + typedef void (*assert_handler)(const AssertData&); + struct ContextState; +} // namespace detail + +class DOCTEST_INTERFACE Context +{ + detail::ContextState* p; + + void parseArgs(int argc, const char* const* argv, bool withDefaults = false); + +public: + explicit Context(int argc = 0, const char* const* argv = nullptr); + + ~Context(); + + void applyCommandLine(int argc, const char* const* argv); + + void addFilter(const char* filter, const char* value); + void clearFilters(); + void setOption(const char* option, int value); + void setOption(const char* option, const char* value); + + bool shouldExit(); + + void setAsDefaultForAssertsOutOfTestCases(); + + void setAssertHandler(detail::assert_handler ah); + + int run(); +}; + +namespace TestCaseFailureReason { + enum Enum + { + None = 0, + AssertFailure = 1, // an assertion has failed in the test case + Exception = 2, // test case threw an exception + Crash = 4, // a crash... + TooManyFailedAsserts = 8, // the abort-after option + Timeout = 16, // see the timeout decorator + ShouldHaveFailedButDidnt = 32, // see the should_fail decorator + ShouldHaveFailedAndDid = 64, // see the should_fail decorator + DidntFailExactlyNumTimes = 128, // see the expected_failures decorator + FailedExactlyNumTimes = 256, // see the expected_failures decorator + CouldHaveFailedAndDid = 512 // see the may_fail decorator + }; +} // namespace TestCaseFailureReason + +struct DOCTEST_INTERFACE CurrentTestCaseStats +{ + int numAssertsCurrentTest; + int numAssertsFailedCurrentTest; + double seconds; + int failure_flags; // use TestCaseFailureReason::Enum +}; + +struct DOCTEST_INTERFACE TestCaseException +{ + String error_string; + bool is_crash; +}; + +struct DOCTEST_INTERFACE TestRunStats +{ + unsigned numTestCases; + unsigned numTestCasesPassingFilters; + unsigned numTestSuitesPassingFilters; + unsigned numTestCasesFailed; + int numAsserts; + int numAssertsFailed; +}; + +struct QueryData +{ + const TestRunStats* run_stats = nullptr; + const TestCaseData** data = nullptr; + unsigned num_data = 0; +}; + +struct DOCTEST_INTERFACE IReporter +{ + // The constructor has to accept "const ContextOptions&" as a single argument + // which has most of the options for the run + a pointer to the stdout stream + // Reporter(const ContextOptions& in) + + // called when a query should be reported (listing test cases, printing the version, etc.) + virtual void report_query(const QueryData&) = 0; + + // called when the whole test run starts + virtual void test_run_start() = 0; + // called when the whole test run ends (caching a pointer to the input doesn't make sense here) + virtual void test_run_end(const TestRunStats&) = 0; + + // called when a test case is started (safe to cache a pointer to the input) + virtual void test_case_start(const TestCaseData&) = 0; + // called when a test case is reentered because of unfinished subcases (safe to cache a pointer to the input) + virtual void test_case_reenter(const TestCaseData&) = 0; + // called when a test case has ended + virtual void test_case_end(const CurrentTestCaseStats&) = 0; + + // called when an exception is thrown from the test case (or it crashes) + virtual void test_case_exception(const TestCaseException&) = 0; + + // called whenever a subcase is entered (don't cache pointers to the input) + virtual void subcase_start(const SubcaseSignature&) = 0; + // called whenever a subcase is exited (don't cache pointers to the input) + virtual void subcase_end() = 0; + + // called for each assert (don't cache pointers to the input) + virtual void log_assert(const AssertData&) = 0; + // called for each message (don't cache pointers to the input) + virtual void log_message(const MessageData&) = 0; + + // called when a test case is skipped either because it doesn't pass the filters, has a skip decorator + // or isn't in the execution range (between first and last) (safe to cache a pointer to the input) + virtual void test_case_skipped(const TestCaseData&) = 0; + + // doctest will not be managing the lifetimes of reporters given to it but this would still be nice to have + virtual ~IReporter(); + + // can obtain all currently active contexts and stringify them if one wishes to do so + static int get_num_active_contexts(); + static const IContextScope* const* get_active_contexts(); + + // can iterate through contexts which have been stringified automatically in their destructors when an exception has been thrown + static int get_num_stringified_contexts(); + static const String* get_stringified_contexts(); +}; + +namespace detail { + typedef IReporter* (*reporterCreatorFunc)(const ContextOptions&); + + DOCTEST_INTERFACE void registerReporterImpl(const char* name, int prio, reporterCreatorFunc c, bool isReporter); + + template + IReporter* reporterCreator(const ContextOptions& o) { + return new Reporter(o); + } +} // namespace detail + +template +int registerReporter(const char* name, int priority, bool isReporter) { + detail::registerReporterImpl(name, priority, detail::reporterCreator, isReporter); + return 0; +} +} // namespace doctest + +// if registering is not disabled +#if !defined(DOCTEST_CONFIG_DISABLE) + +// common code in asserts - for convenience +#define DOCTEST_ASSERT_LOG_AND_REACT(b) \ + if(b.log()) \ + DOCTEST_BREAK_INTO_DEBUGGER(); \ + b.react() + +#ifdef DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS +#define DOCTEST_WRAP_IN_TRY(x) x; +#else // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS +#define DOCTEST_WRAP_IN_TRY(x) \ + try { \ + x; \ + } catch(...) { _DOCTEST_RB.translateException(); } +#endif // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS + +#ifdef DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS +#define DOCTEST_CAST_TO_VOID(...) \ + DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wuseless-cast") \ + static_cast(__VA_ARGS__); \ + DOCTEST_GCC_SUPPRESS_WARNING_POP +#else // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS +#define DOCTEST_CAST_TO_VOID(...) __VA_ARGS__; +#endif // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS + +// registers the test by initializing a dummy var with a function +#define DOCTEST_REGISTER_FUNCTION(global_prefix, f, decorators) \ + global_prefix DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \ + doctest::detail::regTest( \ + doctest::detail::TestCase( \ + f, __FILE__, __LINE__, \ + doctest_detail_test_suite_ns::getCurrentTestSuite()) * \ + decorators); \ + DOCTEST_GLOBAL_NO_WARNINGS_END() + +#define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, decorators) \ + namespace { \ + struct der : public base \ + { \ + void f(); \ + }; \ + static void func() { \ + der v; \ + v.f(); \ + } \ + DOCTEST_REGISTER_FUNCTION(DOCTEST_EMPTY, func, decorators) \ + } \ + inline DOCTEST_NOINLINE void der::f() + +#define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, decorators) \ + static void f(); \ + DOCTEST_REGISTER_FUNCTION(DOCTEST_EMPTY, f, decorators) \ + static void f() + +#define DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(f, proxy, decorators) \ + static doctest::detail::funcType proxy() { return f; } \ + DOCTEST_REGISTER_FUNCTION(inline const, proxy(), decorators) \ + static void f() + +// for registering tests +#define DOCTEST_TEST_CASE(decorators) \ + DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), decorators) + +// for registering tests in classes - requires C++17 for inline variables! +#if __cplusplus >= 201703L || (DOCTEST_MSVC >= DOCTEST_COMPILER(19, 12, 0) && _MSVC_LANG >= 201703L) +#define DOCTEST_TEST_CASE_CLASS(decorators) \ + DOCTEST_CREATE_AND_REGISTER_FUNCTION_IN_CLASS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), \ + DOCTEST_ANONYMOUS(_DOCTEST_ANON_PROXY_), \ + decorators) +#else // DOCTEST_TEST_CASE_CLASS +#define DOCTEST_TEST_CASE_CLASS(...) \ + TEST_CASES_CAN_BE_REGISTERED_IN_CLASSES_ONLY_IN_CPP17_MODE_OR_WITH_VS_2017_OR_NEWER +#endif // DOCTEST_TEST_CASE_CLASS + +// for registering tests with a fixture +#define DOCTEST_TEST_CASE_FIXTURE(c, decorators) \ + DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(_DOCTEST_ANON_CLASS_), c, \ + DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), decorators) + +// for converting types to strings without the header and demangling +#define DOCTEST_TYPE_TO_STRING_IMPL(...) \ + template <> \ + inline const char* type_to_string<__VA_ARGS__>() { \ + return "<" #__VA_ARGS__ ">"; \ + } +#define DOCTEST_TYPE_TO_STRING(...) \ + namespace doctest { namespace detail { \ + DOCTEST_TYPE_TO_STRING_IMPL(__VA_ARGS__) \ + } \ + } \ + typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) + +#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, iter, func) \ + template \ + static void func(); \ + namespace { \ + template \ + struct iter; \ + template \ + struct iter> \ + { \ + iter(const char* file, unsigned line, int index) { \ + doctest::detail::regTest(doctest::detail::TestCase(func, file, line, \ + doctest_detail_test_suite_ns::getCurrentTestSuite(), \ + doctest::detail::type_to_string(), \ + int(line) * 1000 + index) \ + * dec); \ + iter>(file, line, index + 1); \ + } \ + }; \ + template <> \ + struct iter> \ + { \ + iter(const char*, unsigned, int) {} \ + }; \ + } \ + template \ + static void func() + +#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(dec, T, id) \ + DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(id, ITERATOR), \ + DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)) + +#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, anon, ...) \ + DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY)) = \ + doctest::detail::instantiationHelper(DOCTEST_CAT(id, ITERATOR)<__VA_ARGS__>(__FILE__, __LINE__, 0));\ + DOCTEST_GLOBAL_NO_WARNINGS_END() + +#define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) \ + DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_), std::tuple<__VA_ARGS__>) \ + typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) + +#define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) \ + DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_), __VA_ARGS__) \ + typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) + +#define DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, anon, ...) \ + DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(dec, T, DOCTEST_CAT(anon, ITERATOR), anon); \ + DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(anon, anon, std::tuple<__VA_ARGS__>) \ + template \ + static void anon() + +#define DOCTEST_TEST_CASE_TEMPLATE(dec, T, ...) \ + DOCTEST_TEST_CASE_TEMPLATE_IMPL(dec, T, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_), __VA_ARGS__) + +// for subcases +#define DOCTEST_SUBCASE(name) \ + if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUBCASE_) DOCTEST_UNUSED = \ + doctest::detail::Subcase(name, __FILE__, __LINE__)) + +// for grouping tests in test suites by using code blocks +#define DOCTEST_TEST_SUITE_IMPL(decorators, ns_name) \ + namespace ns_name { namespace doctest_detail_test_suite_ns { \ + static DOCTEST_NOINLINE doctest::detail::TestSuite& getCurrentTestSuite() { \ + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4640) \ + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") \ + static doctest::detail::TestSuite data; \ + static bool inited = false; \ + DOCTEST_MSVC_SUPPRESS_WARNING_POP \ + DOCTEST_CLANG_SUPPRESS_WARNING_POP \ + if(!inited) { \ + data* decorators; \ + inited = true; \ + } \ + return data; \ + } \ + } \ + } \ + namespace ns_name + +#define DOCTEST_TEST_SUITE(decorators) \ + DOCTEST_TEST_SUITE_IMPL(decorators, DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUITE_)) + +// for starting a testsuite block +#define DOCTEST_TEST_SUITE_BEGIN(decorators) \ + DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \ + doctest::detail::setTestSuite(doctest::detail::TestSuite() * decorators); \ + DOCTEST_GLOBAL_NO_WARNINGS_END() \ + typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) + +// for ending a testsuite block +#define DOCTEST_TEST_SUITE_END \ + DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \ + doctest::detail::setTestSuite(doctest::detail::TestSuite() * ""); \ + DOCTEST_GLOBAL_NO_WARNINGS_END() \ + typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) + +// for registering exception translators +#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(translatorName, signature) \ + inline doctest::String translatorName(signature); \ + DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)) = \ + doctest::registerExceptionTranslator(translatorName); \ + DOCTEST_GLOBAL_NO_WARNINGS_END() \ + doctest::String translatorName(signature) + +#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \ + DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_), \ + signature) + +// for registering reporters +#define DOCTEST_REGISTER_REPORTER(name, priority, reporter) \ + DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_REPORTER_)) = \ + doctest::registerReporter(name, priority, true); \ + DOCTEST_GLOBAL_NO_WARNINGS_END() typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) + +// for registering listeners +#define DOCTEST_REGISTER_LISTENER(name, priority, reporter) \ + DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_REPORTER_)) = \ + doctest::registerReporter(name, priority, false); \ + DOCTEST_GLOBAL_NO_WARNINGS_END() typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) + +// for logging +#define DOCTEST_INFO(expression) \ + DOCTEST_INFO_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), \ + DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), expression) + +#define DOCTEST_INFO_IMPL(lambda_name, mb_name, s_name, expression) \ + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4626) \ + auto lambda_name = [&](std::ostream* s_name) { \ + doctest::detail::MessageBuilder mb_name(__FILE__, __LINE__, doctest::assertType::is_warn); \ + mb_name.m_stream = s_name; \ + mb_name << expression; \ + }; \ + DOCTEST_MSVC_SUPPRESS_WARNING_POP \ + auto DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope(lambda_name) + +#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := " << x) + +#define DOCTEST_ADD_AT_IMPL(type, file, line, mb, x) \ + do { \ + doctest::detail::MessageBuilder mb(file, line, doctest::assertType::type); \ + mb << x; \ + DOCTEST_ASSERT_LOG_AND_REACT(mb); \ + } while(false) + +// clang-format off +#define DOCTEST_ADD_MESSAGE_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x) +#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x) +#define DOCTEST_ADD_FAIL_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x) +// clang-format on + +#define DOCTEST_MESSAGE(x) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, x) +#define DOCTEST_FAIL_CHECK(x) DOCTEST_ADD_FAIL_CHECK_AT(__FILE__, __LINE__, x) +#define DOCTEST_FAIL(x) DOCTEST_ADD_FAIL_AT(__FILE__, __LINE__, x) + +#define DOCTEST_TO_LVALUE(...) __VA_ARGS__ // Not removed to keep backwards compatibility. + +#ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS + +#define DOCTEST_ASSERT_IMPLEMENT_2(assert_type, ...) \ + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \ + doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ + __LINE__, #__VA_ARGS__); \ + DOCTEST_WRAP_IN_TRY(_DOCTEST_RB.setResult( \ + doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \ + << __VA_ARGS__)) \ + DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB) \ + DOCTEST_CLANG_SUPPRESS_WARNING_POP + +#define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \ + do { \ + DOCTEST_ASSERT_IMPLEMENT_2(assert_type, __VA_ARGS__); \ + } while(false) + +#else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS + +// necessary for _MESSAGE +#define DOCTEST_ASSERT_IMPLEMENT_2 DOCTEST_ASSERT_IMPLEMENT_1 + +#define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \ + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \ + doctest::detail::decomp_assert( \ + doctest::assertType::assert_type, __FILE__, __LINE__, #__VA_ARGS__, \ + doctest::detail::ExpressionDecomposer(doctest::assertType::assert_type) \ + << __VA_ARGS__) DOCTEST_CLANG_SUPPRESS_WARNING_POP + +#endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS + +#define DOCTEST_WARN(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_WARN, __VA_ARGS__) +#define DOCTEST_CHECK(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_CHECK, __VA_ARGS__) +#define DOCTEST_REQUIRE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE, __VA_ARGS__) +#define DOCTEST_WARN_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_WARN_FALSE, __VA_ARGS__) +#define DOCTEST_CHECK_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_CHECK_FALSE, __VA_ARGS__) +#define DOCTEST_REQUIRE_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE_FALSE, __VA_ARGS__) + +// clang-format off +#define DOCTEST_WARN_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN, cond); } while(false) +#define DOCTEST_CHECK_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK, cond); } while(false) +#define DOCTEST_REQUIRE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE, cond); } while(false) +#define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN_FALSE, cond); } while(false) +#define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK_FALSE, cond); } while(false) +#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE_FALSE, cond); } while(false) +// clang-format on + +#define DOCTEST_ASSERT_THROWS_AS(expr, assert_type, message, ...) \ + do { \ + if(!doctest::getContextOptions()->no_throw) { \ + doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ + __LINE__, #expr, #__VA_ARGS__, message); \ + try { \ + DOCTEST_CAST_TO_VOID(expr) \ + } catch(const doctest::detail::remove_const< \ + doctest::detail::remove_reference<__VA_ARGS__>::type>::type&) { \ + _DOCTEST_RB.translateException(); \ + _DOCTEST_RB.m_threw_as = true; \ + } catch(...) { _DOCTEST_RB.translateException(); } \ + DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ + } \ + } while(false) + +#define DOCTEST_ASSERT_THROWS_WITH(expr, expr_str, assert_type, ...) \ + do { \ + if(!doctest::getContextOptions()->no_throw) { \ + doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ + __LINE__, expr_str, "", __VA_ARGS__); \ + try { \ + DOCTEST_CAST_TO_VOID(expr) \ + } catch(...) { _DOCTEST_RB.translateException(); } \ + DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ + } \ + } while(false) + +#define DOCTEST_ASSERT_NOTHROW(assert_type, ...) \ + do { \ + doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ + __LINE__, #__VA_ARGS__); \ + try { \ + DOCTEST_CAST_TO_VOID(__VA_ARGS__) \ + } catch(...) { _DOCTEST_RB.translateException(); } \ + DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ + } while(false) + +// clang-format off +#define DOCTEST_WARN_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_WARN_THROWS, "") +#define DOCTEST_CHECK_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_CHECK_THROWS, "") +#define DOCTEST_REQUIRE_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_REQUIRE_THROWS, "") + +#define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_AS, "", __VA_ARGS__) +#define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_AS, "", __VA_ARGS__) +#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_AS, "", __VA_ARGS__) + +#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_WARN_THROWS_WITH, __VA_ARGS__) +#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_CHECK_THROWS_WITH, __VA_ARGS__) +#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_REQUIRE_THROWS_WITH, __VA_ARGS__) + +#define DOCTEST_WARN_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_WITH_AS, message, __VA_ARGS__) +#define DOCTEST_CHECK_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_WITH_AS, message, __VA_ARGS__) +#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_WITH_AS, message, __VA_ARGS__) + +#define DOCTEST_WARN_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_WARN_NOTHROW, __VA_ARGS__) +#define DOCTEST_CHECK_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_CHECK_NOTHROW, __VA_ARGS__) +#define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_REQUIRE_NOTHROW, __VA_ARGS__) + +#define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS(expr); } while(false) +#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS(expr); } while(false) +#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS(expr); } while(false) +#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS_AS(expr, ex); } while(false) +#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS_AS(expr, ex); } while(false) +#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS_AS(expr, ex); } while(false) +#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS_WITH(expr, with); } while(false) +#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS_WITH(expr, with); } while(false) +#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS_WITH(expr, with); } while(false) +#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex); } while(false) +#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex); } while(false) +#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex); } while(false) +#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_NOTHROW(expr); } while(false) +#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_NOTHROW(expr); } while(false) +#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_NOTHROW(expr); } while(false) +// clang-format on + +#ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS + +#define DOCTEST_BINARY_ASSERT(assert_type, comp, ...) \ + do { \ + doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ + __LINE__, #__VA_ARGS__); \ + DOCTEST_WRAP_IN_TRY( \ + _DOCTEST_RB.binary_assert( \ + __VA_ARGS__)) \ + DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ + } while(false) + +#define DOCTEST_UNARY_ASSERT(assert_type, ...) \ + do { \ + doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ + __LINE__, #__VA_ARGS__); \ + DOCTEST_WRAP_IN_TRY(_DOCTEST_RB.unary_assert(__VA_ARGS__)) \ + DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ + } while(false) + +#else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS + +#define DOCTEST_BINARY_ASSERT(assert_type, comparison, ...) \ + doctest::detail::binary_assert( \ + doctest::assertType::assert_type, __FILE__, __LINE__, #__VA_ARGS__, __VA_ARGS__) + +#define DOCTEST_UNARY_ASSERT(assert_type, ...) \ + doctest::detail::unary_assert(doctest::assertType::assert_type, __FILE__, __LINE__, \ + #__VA_ARGS__, __VA_ARGS__) + +#endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS + +#define DOCTEST_WARN_EQ(...) DOCTEST_BINARY_ASSERT(DT_WARN_EQ, eq, __VA_ARGS__) +#define DOCTEST_CHECK_EQ(...) DOCTEST_BINARY_ASSERT(DT_CHECK_EQ, eq, __VA_ARGS__) +#define DOCTEST_REQUIRE_EQ(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_EQ, eq, __VA_ARGS__) +#define DOCTEST_WARN_NE(...) DOCTEST_BINARY_ASSERT(DT_WARN_NE, ne, __VA_ARGS__) +#define DOCTEST_CHECK_NE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_NE, ne, __VA_ARGS__) +#define DOCTEST_REQUIRE_NE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_NE, ne, __VA_ARGS__) +#define DOCTEST_WARN_GT(...) DOCTEST_BINARY_ASSERT(DT_WARN_GT, gt, __VA_ARGS__) +#define DOCTEST_CHECK_GT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GT, gt, __VA_ARGS__) +#define DOCTEST_REQUIRE_GT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GT, gt, __VA_ARGS__) +#define DOCTEST_WARN_LT(...) DOCTEST_BINARY_ASSERT(DT_WARN_LT, lt, __VA_ARGS__) +#define DOCTEST_CHECK_LT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LT, lt, __VA_ARGS__) +#define DOCTEST_REQUIRE_LT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LT, lt, __VA_ARGS__) +#define DOCTEST_WARN_GE(...) DOCTEST_BINARY_ASSERT(DT_WARN_GE, ge, __VA_ARGS__) +#define DOCTEST_CHECK_GE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GE, ge, __VA_ARGS__) +#define DOCTEST_REQUIRE_GE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GE, ge, __VA_ARGS__) +#define DOCTEST_WARN_LE(...) DOCTEST_BINARY_ASSERT(DT_WARN_LE, le, __VA_ARGS__) +#define DOCTEST_CHECK_LE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LE, le, __VA_ARGS__) +#define DOCTEST_REQUIRE_LE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LE, le, __VA_ARGS__) + +#define DOCTEST_WARN_UNARY(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY, __VA_ARGS__) +#define DOCTEST_CHECK_UNARY(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY, __VA_ARGS__) +#define DOCTEST_REQUIRE_UNARY(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY, __VA_ARGS__) +#define DOCTEST_WARN_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY_FALSE, __VA_ARGS__) +#define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY_FALSE, __VA_ARGS__) +#define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY_FALSE, __VA_ARGS__) + +#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS + +#undef DOCTEST_WARN_THROWS +#undef DOCTEST_CHECK_THROWS +#undef DOCTEST_REQUIRE_THROWS +#undef DOCTEST_WARN_THROWS_AS +#undef DOCTEST_CHECK_THROWS_AS +#undef DOCTEST_REQUIRE_THROWS_AS +#undef DOCTEST_WARN_THROWS_WITH +#undef DOCTEST_CHECK_THROWS_WITH +#undef DOCTEST_REQUIRE_THROWS_WITH +#undef DOCTEST_WARN_THROWS_WITH_AS +#undef DOCTEST_CHECK_THROWS_WITH_AS +#undef DOCTEST_REQUIRE_THROWS_WITH_AS +#undef DOCTEST_WARN_NOTHROW +#undef DOCTEST_CHECK_NOTHROW +#undef DOCTEST_REQUIRE_NOTHROW + +#undef DOCTEST_WARN_THROWS_MESSAGE +#undef DOCTEST_CHECK_THROWS_MESSAGE +#undef DOCTEST_REQUIRE_THROWS_MESSAGE +#undef DOCTEST_WARN_THROWS_AS_MESSAGE +#undef DOCTEST_CHECK_THROWS_AS_MESSAGE +#undef DOCTEST_REQUIRE_THROWS_AS_MESSAGE +#undef DOCTEST_WARN_THROWS_WITH_MESSAGE +#undef DOCTEST_CHECK_THROWS_WITH_MESSAGE +#undef DOCTEST_REQUIRE_THROWS_WITH_MESSAGE +#undef DOCTEST_WARN_THROWS_WITH_AS_MESSAGE +#undef DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE +#undef DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE +#undef DOCTEST_WARN_NOTHROW_MESSAGE +#undef DOCTEST_CHECK_NOTHROW_MESSAGE +#undef DOCTEST_REQUIRE_NOTHROW_MESSAGE + +#ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS + +#define DOCTEST_WARN_THROWS(...) ((void)0) +#define DOCTEST_CHECK_THROWS(...) ((void)0) +#define DOCTEST_REQUIRE_THROWS(...) ((void)0) +#define DOCTEST_WARN_THROWS_AS(expr, ...) ((void)0) +#define DOCTEST_CHECK_THROWS_AS(expr, ...) ((void)0) +#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ((void)0) +#define DOCTEST_WARN_THROWS_WITH(expr, ...) ((void)0) +#define DOCTEST_CHECK_THROWS_WITH(expr, ...) ((void)0) +#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) ((void)0) +#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) ((void)0) +#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) ((void)0) +#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) ((void)0) +#define DOCTEST_WARN_NOTHROW(...) ((void)0) +#define DOCTEST_CHECK_NOTHROW(...) ((void)0) +#define DOCTEST_REQUIRE_NOTHROW(...) ((void)0) + +#define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) ((void)0) +#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) ((void)0) +#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) ((void)0) +#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) +#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) +#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) +#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0) +#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0) +#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0) +#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0) +#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0) +#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0) +#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) ((void)0) +#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) ((void)0) +#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) ((void)0) + +#else // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS + +#undef DOCTEST_REQUIRE +#undef DOCTEST_REQUIRE_FALSE +#undef DOCTEST_REQUIRE_MESSAGE +#undef DOCTEST_REQUIRE_FALSE_MESSAGE +#undef DOCTEST_REQUIRE_EQ +#undef DOCTEST_REQUIRE_NE +#undef DOCTEST_REQUIRE_GT +#undef DOCTEST_REQUIRE_LT +#undef DOCTEST_REQUIRE_GE +#undef DOCTEST_REQUIRE_LE +#undef DOCTEST_REQUIRE_UNARY +#undef DOCTEST_REQUIRE_UNARY_FALSE + +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS + +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + +// ================================================================================================= +// == WHAT FOLLOWS IS VERSIONS OF THE MACROS THAT DO NOT DO ANY REGISTERING! == +// == THIS CAN BE ENABLED BY DEFINING DOCTEST_CONFIG_DISABLE GLOBALLY! == +// ================================================================================================= +#else // DOCTEST_CONFIG_DISABLE + +#define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, name) \ + namespace { \ + template \ + struct der : public base \ + { void f(); }; \ + } \ + template \ + inline void der::f() + +#define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, name) \ + template \ + static inline void f() + +// for registering tests +#define DOCTEST_TEST_CASE(name) \ + DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name) + +// for registering tests in classes +#define DOCTEST_TEST_CASE_CLASS(name) \ + DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name) + +// for registering tests with a fixture +#define DOCTEST_TEST_CASE_FIXTURE(x, name) \ + DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(_DOCTEST_ANON_CLASS_), x, \ + DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name) + +// for converting types to strings without the header and demangling +#define DOCTEST_TYPE_TO_STRING(...) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) +#define DOCTEST_TYPE_TO_STRING_IMPL(...) + +// for typed tests +#define DOCTEST_TEST_CASE_TEMPLATE(name, type, ...) \ + template \ + inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)() + +#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, type, id) \ + template \ + inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)() + +#define DOCTEST_TEST_CASE_TEMPLATE_INVOKE(id, ...) \ + typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) + +#define DOCTEST_TEST_CASE_TEMPLATE_APPLY(id, ...) \ + typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) + +// for subcases +#define DOCTEST_SUBCASE(name) + +// for a testsuite block +#define DOCTEST_TEST_SUITE(name) namespace + +// for starting a testsuite block +#define DOCTEST_TEST_SUITE_BEGIN(name) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) + +// for ending a testsuite block +#define DOCTEST_TEST_SUITE_END typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) + +#define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \ + template \ + static inline doctest::String DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)(signature) + +#define DOCTEST_REGISTER_REPORTER(name, priority, reporter) +#define DOCTEST_REGISTER_LISTENER(name, priority, reporter) + +#define DOCTEST_INFO(x) ((void)0) +#define DOCTEST_CAPTURE(x) ((void)0) +#define DOCTEST_ADD_MESSAGE_AT(file, line, x) ((void)0) +#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, x) ((void)0) +#define DOCTEST_ADD_FAIL_AT(file, line, x) ((void)0) +#define DOCTEST_MESSAGE(x) ((void)0) +#define DOCTEST_FAIL_CHECK(x) ((void)0) +#define DOCTEST_FAIL(x) ((void)0) + +#define DOCTEST_WARN(...) ((void)0) +#define DOCTEST_CHECK(...) ((void)0) +#define DOCTEST_REQUIRE(...) ((void)0) +#define DOCTEST_WARN_FALSE(...) ((void)0) +#define DOCTEST_CHECK_FALSE(...) ((void)0) +#define DOCTEST_REQUIRE_FALSE(...) ((void)0) + +#define DOCTEST_WARN_MESSAGE(cond, msg) ((void)0) +#define DOCTEST_CHECK_MESSAGE(cond, msg) ((void)0) +#define DOCTEST_REQUIRE_MESSAGE(cond, msg) ((void)0) +#define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) ((void)0) +#define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) ((void)0) +#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) ((void)0) + +#define DOCTEST_WARN_THROWS(...) ((void)0) +#define DOCTEST_CHECK_THROWS(...) ((void)0) +#define DOCTEST_REQUIRE_THROWS(...) ((void)0) +#define DOCTEST_WARN_THROWS_AS(expr, ...) ((void)0) +#define DOCTEST_CHECK_THROWS_AS(expr, ...) ((void)0) +#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ((void)0) +#define DOCTEST_WARN_THROWS_WITH(expr, ...) ((void)0) +#define DOCTEST_CHECK_THROWS_WITH(expr, ...) ((void)0) +#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) ((void)0) +#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) ((void)0) +#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) ((void)0) +#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) ((void)0) +#define DOCTEST_WARN_NOTHROW(...) ((void)0) +#define DOCTEST_CHECK_NOTHROW(...) ((void)0) +#define DOCTEST_REQUIRE_NOTHROW(...) ((void)0) + +#define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) ((void)0) +#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) ((void)0) +#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) ((void)0) +#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) +#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) +#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) +#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0) +#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0) +#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0) +#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0) +#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0) +#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0) +#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) ((void)0) +#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) ((void)0) +#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) ((void)0) + +#define DOCTEST_WARN_EQ(...) ((void)0) +#define DOCTEST_CHECK_EQ(...) ((void)0) +#define DOCTEST_REQUIRE_EQ(...) ((void)0) +#define DOCTEST_WARN_NE(...) ((void)0) +#define DOCTEST_CHECK_NE(...) ((void)0) +#define DOCTEST_REQUIRE_NE(...) ((void)0) +#define DOCTEST_WARN_GT(...) ((void)0) +#define DOCTEST_CHECK_GT(...) ((void)0) +#define DOCTEST_REQUIRE_GT(...) ((void)0) +#define DOCTEST_WARN_LT(...) ((void)0) +#define DOCTEST_CHECK_LT(...) ((void)0) +#define DOCTEST_REQUIRE_LT(...) ((void)0) +#define DOCTEST_WARN_GE(...) ((void)0) +#define DOCTEST_CHECK_GE(...) ((void)0) +#define DOCTEST_REQUIRE_GE(...) ((void)0) +#define DOCTEST_WARN_LE(...) ((void)0) +#define DOCTEST_CHECK_LE(...) ((void)0) +#define DOCTEST_REQUIRE_LE(...) ((void)0) + +#define DOCTEST_WARN_UNARY(...) ((void)0) +#define DOCTEST_CHECK_UNARY(...) ((void)0) +#define DOCTEST_REQUIRE_UNARY(...) ((void)0) +#define DOCTEST_WARN_UNARY_FALSE(...) ((void)0) +#define DOCTEST_CHECK_UNARY_FALSE(...) ((void)0) +#define DOCTEST_REQUIRE_UNARY_FALSE(...) ((void)0) + +#endif // DOCTEST_CONFIG_DISABLE + +// clang-format off +// KEPT FOR BACKWARDS COMPATIBILITY - FORWARDING TO THE RIGHT MACROS +#define DOCTEST_FAST_WARN_EQ DOCTEST_WARN_EQ +#define DOCTEST_FAST_CHECK_EQ DOCTEST_CHECK_EQ +#define DOCTEST_FAST_REQUIRE_EQ DOCTEST_REQUIRE_EQ +#define DOCTEST_FAST_WARN_NE DOCTEST_WARN_NE +#define DOCTEST_FAST_CHECK_NE DOCTEST_CHECK_NE +#define DOCTEST_FAST_REQUIRE_NE DOCTEST_REQUIRE_NE +#define DOCTEST_FAST_WARN_GT DOCTEST_WARN_GT +#define DOCTEST_FAST_CHECK_GT DOCTEST_CHECK_GT +#define DOCTEST_FAST_REQUIRE_GT DOCTEST_REQUIRE_GT +#define DOCTEST_FAST_WARN_LT DOCTEST_WARN_LT +#define DOCTEST_FAST_CHECK_LT DOCTEST_CHECK_LT +#define DOCTEST_FAST_REQUIRE_LT DOCTEST_REQUIRE_LT +#define DOCTEST_FAST_WARN_GE DOCTEST_WARN_GE +#define DOCTEST_FAST_CHECK_GE DOCTEST_CHECK_GE +#define DOCTEST_FAST_REQUIRE_GE DOCTEST_REQUIRE_GE +#define DOCTEST_FAST_WARN_LE DOCTEST_WARN_LE +#define DOCTEST_FAST_CHECK_LE DOCTEST_CHECK_LE +#define DOCTEST_FAST_REQUIRE_LE DOCTEST_REQUIRE_LE + +#define DOCTEST_FAST_WARN_UNARY DOCTEST_WARN_UNARY +#define DOCTEST_FAST_CHECK_UNARY DOCTEST_CHECK_UNARY +#define DOCTEST_FAST_REQUIRE_UNARY DOCTEST_REQUIRE_UNARY +#define DOCTEST_FAST_WARN_UNARY_FALSE DOCTEST_WARN_UNARY_FALSE +#define DOCTEST_FAST_CHECK_UNARY_FALSE DOCTEST_CHECK_UNARY_FALSE +#define DOCTEST_FAST_REQUIRE_UNARY_FALSE DOCTEST_REQUIRE_UNARY_FALSE + +#define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE DOCTEST_TEST_CASE_TEMPLATE_INVOKE +// clang-format on + +// BDD style macros +// clang-format off +#define DOCTEST_SCENARIO(name) DOCTEST_TEST_CASE(" Scenario: " name) +#define DOCTEST_SCENARIO_CLASS(name) DOCTEST_TEST_CASE_CLASS(" Scenario: " name) +#define DOCTEST_SCENARIO_TEMPLATE(name, T, ...) DOCTEST_TEST_CASE_TEMPLATE(" Scenario: " name, T, __VA_ARGS__) +#define DOCTEST_SCENARIO_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(" Scenario: " name, T, id) + +#define DOCTEST_GIVEN(name) DOCTEST_SUBCASE(" Given: " name) +#define DOCTEST_WHEN(name) DOCTEST_SUBCASE(" When: " name) +#define DOCTEST_AND_WHEN(name) DOCTEST_SUBCASE("And when: " name) +#define DOCTEST_THEN(name) DOCTEST_SUBCASE(" Then: " name) +#define DOCTEST_AND_THEN(name) DOCTEST_SUBCASE(" And: " name) +// clang-format on + +// == SHORT VERSIONS OF THE MACROS +#if !defined(DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES) + +#define TEST_CASE DOCTEST_TEST_CASE +#define TEST_CASE_CLASS DOCTEST_TEST_CASE_CLASS +#define TEST_CASE_FIXTURE DOCTEST_TEST_CASE_FIXTURE +#define TYPE_TO_STRING DOCTEST_TYPE_TO_STRING +#define TEST_CASE_TEMPLATE DOCTEST_TEST_CASE_TEMPLATE +#define TEST_CASE_TEMPLATE_DEFINE DOCTEST_TEST_CASE_TEMPLATE_DEFINE +#define TEST_CASE_TEMPLATE_INVOKE DOCTEST_TEST_CASE_TEMPLATE_INVOKE +#define TEST_CASE_TEMPLATE_APPLY DOCTEST_TEST_CASE_TEMPLATE_APPLY +#define SUBCASE DOCTEST_SUBCASE +#define TEST_SUITE DOCTEST_TEST_SUITE +#define TEST_SUITE_BEGIN DOCTEST_TEST_SUITE_BEGIN +#define TEST_SUITE_END DOCTEST_TEST_SUITE_END +#define REGISTER_EXCEPTION_TRANSLATOR DOCTEST_REGISTER_EXCEPTION_TRANSLATOR +#define REGISTER_REPORTER DOCTEST_REGISTER_REPORTER +#define REGISTER_LISTENER DOCTEST_REGISTER_LISTENER +#define INFO DOCTEST_INFO +#define CAPTURE DOCTEST_CAPTURE +#define ADD_MESSAGE_AT DOCTEST_ADD_MESSAGE_AT +#define ADD_FAIL_CHECK_AT DOCTEST_ADD_FAIL_CHECK_AT +#define ADD_FAIL_AT DOCTEST_ADD_FAIL_AT +#define MESSAGE DOCTEST_MESSAGE +#define FAIL_CHECK DOCTEST_FAIL_CHECK +#define FAIL DOCTEST_FAIL +#define TO_LVALUE DOCTEST_TO_LVALUE + +#define WARN DOCTEST_WARN +#define WARN_FALSE DOCTEST_WARN_FALSE +#define WARN_THROWS DOCTEST_WARN_THROWS +#define WARN_THROWS_AS DOCTEST_WARN_THROWS_AS +#define WARN_THROWS_WITH DOCTEST_WARN_THROWS_WITH +#define WARN_THROWS_WITH_AS DOCTEST_WARN_THROWS_WITH_AS +#define WARN_NOTHROW DOCTEST_WARN_NOTHROW +#define CHECK DOCTEST_CHECK +#define CHECK_FALSE DOCTEST_CHECK_FALSE +#define CHECK_THROWS DOCTEST_CHECK_THROWS +#define CHECK_THROWS_AS DOCTEST_CHECK_THROWS_AS +#define CHECK_THROWS_WITH DOCTEST_CHECK_THROWS_WITH +#define CHECK_THROWS_WITH_AS DOCTEST_CHECK_THROWS_WITH_AS +#define CHECK_NOTHROW DOCTEST_CHECK_NOTHROW +#define REQUIRE DOCTEST_REQUIRE +#define REQUIRE_FALSE DOCTEST_REQUIRE_FALSE +#define REQUIRE_THROWS DOCTEST_REQUIRE_THROWS +#define REQUIRE_THROWS_AS DOCTEST_REQUIRE_THROWS_AS +#define REQUIRE_THROWS_WITH DOCTEST_REQUIRE_THROWS_WITH +#define REQUIRE_THROWS_WITH_AS DOCTEST_REQUIRE_THROWS_WITH_AS +#define REQUIRE_NOTHROW DOCTEST_REQUIRE_NOTHROW + +#define WARN_MESSAGE DOCTEST_WARN_MESSAGE +#define WARN_FALSE_MESSAGE DOCTEST_WARN_FALSE_MESSAGE +#define WARN_THROWS_MESSAGE DOCTEST_WARN_THROWS_MESSAGE +#define WARN_THROWS_AS_MESSAGE DOCTEST_WARN_THROWS_AS_MESSAGE +#define WARN_THROWS_WITH_MESSAGE DOCTEST_WARN_THROWS_WITH_MESSAGE +#define WARN_THROWS_WITH_AS_MESSAGE DOCTEST_WARN_THROWS_WITH_AS_MESSAGE +#define WARN_NOTHROW_MESSAGE DOCTEST_WARN_NOTHROW_MESSAGE +#define CHECK_MESSAGE DOCTEST_CHECK_MESSAGE +#define CHECK_FALSE_MESSAGE DOCTEST_CHECK_FALSE_MESSAGE +#define CHECK_THROWS_MESSAGE DOCTEST_CHECK_THROWS_MESSAGE +#define CHECK_THROWS_AS_MESSAGE DOCTEST_CHECK_THROWS_AS_MESSAGE +#define CHECK_THROWS_WITH_MESSAGE DOCTEST_CHECK_THROWS_WITH_MESSAGE +#define CHECK_THROWS_WITH_AS_MESSAGE DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE +#define CHECK_NOTHROW_MESSAGE DOCTEST_CHECK_NOTHROW_MESSAGE +#define REQUIRE_MESSAGE DOCTEST_REQUIRE_MESSAGE +#define REQUIRE_FALSE_MESSAGE DOCTEST_REQUIRE_FALSE_MESSAGE +#define REQUIRE_THROWS_MESSAGE DOCTEST_REQUIRE_THROWS_MESSAGE +#define REQUIRE_THROWS_AS_MESSAGE DOCTEST_REQUIRE_THROWS_AS_MESSAGE +#define REQUIRE_THROWS_WITH_MESSAGE DOCTEST_REQUIRE_THROWS_WITH_MESSAGE +#define REQUIRE_THROWS_WITH_AS_MESSAGE DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE +#define REQUIRE_NOTHROW_MESSAGE DOCTEST_REQUIRE_NOTHROW_MESSAGE + +#define SCENARIO DOCTEST_SCENARIO +#define SCENARIO_CLASS DOCTEST_SCENARIO_CLASS +#define SCENARIO_TEMPLATE DOCTEST_SCENARIO_TEMPLATE +#define SCENARIO_TEMPLATE_DEFINE DOCTEST_SCENARIO_TEMPLATE_DEFINE +#define GIVEN DOCTEST_GIVEN +#define WHEN DOCTEST_WHEN +#define AND_WHEN DOCTEST_AND_WHEN +#define THEN DOCTEST_THEN +#define AND_THEN DOCTEST_AND_THEN + +#define WARN_EQ DOCTEST_WARN_EQ +#define CHECK_EQ DOCTEST_CHECK_EQ +#define REQUIRE_EQ DOCTEST_REQUIRE_EQ +#define WARN_NE DOCTEST_WARN_NE +#define CHECK_NE DOCTEST_CHECK_NE +#define REQUIRE_NE DOCTEST_REQUIRE_NE +#define WARN_GT DOCTEST_WARN_GT +#define CHECK_GT DOCTEST_CHECK_GT +#define REQUIRE_GT DOCTEST_REQUIRE_GT +#define WARN_LT DOCTEST_WARN_LT +#define CHECK_LT DOCTEST_CHECK_LT +#define REQUIRE_LT DOCTEST_REQUIRE_LT +#define WARN_GE DOCTEST_WARN_GE +#define CHECK_GE DOCTEST_CHECK_GE +#define REQUIRE_GE DOCTEST_REQUIRE_GE +#define WARN_LE DOCTEST_WARN_LE +#define CHECK_LE DOCTEST_CHECK_LE +#define REQUIRE_LE DOCTEST_REQUIRE_LE +#define WARN_UNARY DOCTEST_WARN_UNARY +#define CHECK_UNARY DOCTEST_CHECK_UNARY +#define REQUIRE_UNARY DOCTEST_REQUIRE_UNARY +#define WARN_UNARY_FALSE DOCTEST_WARN_UNARY_FALSE +#define CHECK_UNARY_FALSE DOCTEST_CHECK_UNARY_FALSE +#define REQUIRE_UNARY_FALSE DOCTEST_REQUIRE_UNARY_FALSE + +// KEPT FOR BACKWARDS COMPATIBILITY +#define FAST_WARN_EQ DOCTEST_FAST_WARN_EQ +#define FAST_CHECK_EQ DOCTEST_FAST_CHECK_EQ +#define FAST_REQUIRE_EQ DOCTEST_FAST_REQUIRE_EQ +#define FAST_WARN_NE DOCTEST_FAST_WARN_NE +#define FAST_CHECK_NE DOCTEST_FAST_CHECK_NE +#define FAST_REQUIRE_NE DOCTEST_FAST_REQUIRE_NE +#define FAST_WARN_GT DOCTEST_FAST_WARN_GT +#define FAST_CHECK_GT DOCTEST_FAST_CHECK_GT +#define FAST_REQUIRE_GT DOCTEST_FAST_REQUIRE_GT +#define FAST_WARN_LT DOCTEST_FAST_WARN_LT +#define FAST_CHECK_LT DOCTEST_FAST_CHECK_LT +#define FAST_REQUIRE_LT DOCTEST_FAST_REQUIRE_LT +#define FAST_WARN_GE DOCTEST_FAST_WARN_GE +#define FAST_CHECK_GE DOCTEST_FAST_CHECK_GE +#define FAST_REQUIRE_GE DOCTEST_FAST_REQUIRE_GE +#define FAST_WARN_LE DOCTEST_FAST_WARN_LE +#define FAST_CHECK_LE DOCTEST_FAST_CHECK_LE +#define FAST_REQUIRE_LE DOCTEST_FAST_REQUIRE_LE + +#define FAST_WARN_UNARY DOCTEST_FAST_WARN_UNARY +#define FAST_CHECK_UNARY DOCTEST_FAST_CHECK_UNARY +#define FAST_REQUIRE_UNARY DOCTEST_FAST_REQUIRE_UNARY +#define FAST_WARN_UNARY_FALSE DOCTEST_FAST_WARN_UNARY_FALSE +#define FAST_CHECK_UNARY_FALSE DOCTEST_FAST_CHECK_UNARY_FALSE +#define FAST_REQUIRE_UNARY_FALSE DOCTEST_FAST_REQUIRE_UNARY_FALSE + +#define TEST_CASE_TEMPLATE_INSTANTIATE DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE + +#endif // DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES + +#if !defined(DOCTEST_CONFIG_DISABLE) + +// this is here to clear the 'current test suite' for the current translation unit - at the top +DOCTEST_TEST_SUITE_END(); + +// add stringification for primitive/fundamental types +namespace doctest { namespace detail { + DOCTEST_TYPE_TO_STRING_IMPL(bool) + DOCTEST_TYPE_TO_STRING_IMPL(float) + DOCTEST_TYPE_TO_STRING_IMPL(double) + DOCTEST_TYPE_TO_STRING_IMPL(long double) + DOCTEST_TYPE_TO_STRING_IMPL(char) + DOCTEST_TYPE_TO_STRING_IMPL(signed char) + DOCTEST_TYPE_TO_STRING_IMPL(unsigned char) +#if !DOCTEST_MSVC || defined(_NATIVE_WCHAR_T_DEFINED) + DOCTEST_TYPE_TO_STRING_IMPL(wchar_t) +#endif // not MSVC or wchar_t support enabled + DOCTEST_TYPE_TO_STRING_IMPL(short int) + DOCTEST_TYPE_TO_STRING_IMPL(unsigned short int) + DOCTEST_TYPE_TO_STRING_IMPL(int) + DOCTEST_TYPE_TO_STRING_IMPL(unsigned int) + DOCTEST_TYPE_TO_STRING_IMPL(long int) + DOCTEST_TYPE_TO_STRING_IMPL(unsigned long int) + DOCTEST_TYPE_TO_STRING_IMPL(long long int) + DOCTEST_TYPE_TO_STRING_IMPL(unsigned long long int) +}} // namespace doctest::detail + +#endif // DOCTEST_CONFIG_DISABLE + +DOCTEST_CLANG_SUPPRESS_WARNING_POP +DOCTEST_MSVC_SUPPRESS_WARNING_POP +DOCTEST_GCC_SUPPRESS_WARNING_POP + +#endif // DOCTEST_LIBRARY_INCLUDED + +#ifndef DOCTEST_SINGLE_HEADER +#define DOCTEST_SINGLE_HEADER +#endif // DOCTEST_SINGLE_HEADER + +#if defined(DOCTEST_CONFIG_IMPLEMENT) || !defined(DOCTEST_SINGLE_HEADER) + +#ifndef DOCTEST_SINGLE_HEADER +#include "doctest_fwd.h" +#endif // DOCTEST_SINGLE_HEADER + +DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-macros") + +#ifndef DOCTEST_LIBRARY_IMPLEMENTATION +#define DOCTEST_LIBRARY_IMPLEMENTATION + +DOCTEST_CLANG_SUPPRESS_WARNING_POP + +DOCTEST_CLANG_SUPPRESS_WARNING_PUSH +DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wglobal-constructors") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wshorten-64-to-32") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-variable-declarations") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch-enum") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wcovered-switch-default") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-noreturn") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wdisabled-macro-expansion") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-braces") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-field-initializers") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") +DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-member-function") + +DOCTEST_GCC_SUPPRESS_WARNING_PUSH +DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas") +DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas") +DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion") +DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++") +DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion") +DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") +DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing") +DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-field-initializers") +DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-braces") +DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") +DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch") +DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-enum") +DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-default") +DOCTEST_GCC_SUPPRESS_WARNING("-Wunsafe-loop-optimizations") +DOCTEST_GCC_SUPPRESS_WARNING("-Wold-style-cast") +DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs") +DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") +DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-function") +DOCTEST_GCC_SUPPRESS_WARNING("-Wmultiple-inheritance") +DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") +DOCTEST_GCC_SUPPRESS_WARNING("-Wsuggest-attribute") + +DOCTEST_MSVC_SUPPRESS_WARNING_PUSH +DOCTEST_MSVC_SUPPRESS_WARNING(4616) // invalid compiler warning +DOCTEST_MSVC_SUPPRESS_WARNING(4619) // invalid compiler warning +DOCTEST_MSVC_SUPPRESS_WARNING(4996) // The compiler encountered a deprecated declaration +DOCTEST_MSVC_SUPPRESS_WARNING(4267) // 'var' : conversion from 'x' to 'y', possible loss of data +DOCTEST_MSVC_SUPPRESS_WARNING(4706) // assignment within conditional expression +DOCTEST_MSVC_SUPPRESS_WARNING(4512) // 'class' : assignment operator could not be generated +DOCTEST_MSVC_SUPPRESS_WARNING(4127) // conditional expression is constant +DOCTEST_MSVC_SUPPRESS_WARNING(4530) // C++ exception handler used, but unwind semantics not enabled +DOCTEST_MSVC_SUPPRESS_WARNING(4577) // 'noexcept' used with no exception handling mode specified +DOCTEST_MSVC_SUPPRESS_WARNING(4774) // format string expected in argument is not a string literal +DOCTEST_MSVC_SUPPRESS_WARNING(4365) // conversion from 'int' to 'unsigned', signed/unsigned mismatch +DOCTEST_MSVC_SUPPRESS_WARNING(4820) // padding in structs +DOCTEST_MSVC_SUPPRESS_WARNING(4640) // construction of local static object is not thread-safe +DOCTEST_MSVC_SUPPRESS_WARNING(5039) // pointer to potentially throwing function passed to extern C +DOCTEST_MSVC_SUPPRESS_WARNING(5045) // Spectre mitigation stuff +DOCTEST_MSVC_SUPPRESS_WARNING(4626) // assignment operator was implicitly defined as deleted +DOCTEST_MSVC_SUPPRESS_WARNING(5027) // move assignment operator was implicitly defined as deleted +DOCTEST_MSVC_SUPPRESS_WARNING(5026) // move constructor was implicitly defined as deleted +DOCTEST_MSVC_SUPPRESS_WARNING(4625) // copy constructor was implicitly defined as deleted +DOCTEST_MSVC_SUPPRESS_WARNING(4800) // forcing value to bool 'true' or 'false' (performance warning) +// static analysis +DOCTEST_MSVC_SUPPRESS_WARNING(26439) // This kind of function may not throw. Declare it 'noexcept' +DOCTEST_MSVC_SUPPRESS_WARNING(26495) // Always initialize a member variable +DOCTEST_MSVC_SUPPRESS_WARNING(26451) // Arithmetic overflow ... +DOCTEST_MSVC_SUPPRESS_WARNING(26444) // Avoid unnamed objects with custom construction and dtor... +DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum' + +DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN + +// required includes - will go only in one translation unit! +#include +#include +#include +// borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/onqtam/doctest/pull/37 +#ifdef __BORLANDC__ +#include +#endif // __BORLANDC__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef DOCTEST_CONFIG_POSIX_SIGNALS +#include +#endif // DOCTEST_CONFIG_POSIX_SIGNALS +#include +#include +#include + +#ifdef DOCTEST_PLATFORM_MAC +#include +#include +#include +#endif // DOCTEST_PLATFORM_MAC + +#ifdef DOCTEST_PLATFORM_WINDOWS + +// defines for a leaner windows.h +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif // WIN32_LEAN_AND_MEAN +#ifndef NOMINMAX +#define NOMINMAX +#endif // NOMINMAX + +// not sure what AfxWin.h is for - here I do what Catch does +#ifdef __AFXDLL +#include +#else +#if defined(__MINGW32__) || defined(__MINGW64__) +#include +#else // MINGW +#include +#endif // MINGW +#endif +#include + +#else // DOCTEST_PLATFORM_WINDOWS + +#include +#include + +#endif // DOCTEST_PLATFORM_WINDOWS + +// this is a fix for https://github.com/onqtam/doctest/issues/348 +// https://mail.gnome.org/archives/xml/2012-January/msg00000.html +#if !defined(HAVE_UNISTD_H) && !defined(STDOUT_FILENO) +#define STDOUT_FILENO fileno(stdout) +#endif // HAVE_UNISTD_H + +DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END + +// counts the number of elements in a C array +#define DOCTEST_COUNTOF(x) (sizeof(x) / sizeof(x[0])) + +#ifdef DOCTEST_CONFIG_DISABLE +#define DOCTEST_BRANCH_ON_DISABLED(if_disabled, if_not_disabled) if_disabled +#else // DOCTEST_CONFIG_DISABLE +#define DOCTEST_BRANCH_ON_DISABLED(if_disabled, if_not_disabled) if_not_disabled +#endif // DOCTEST_CONFIG_DISABLE + +#ifndef DOCTEST_CONFIG_OPTIONS_PREFIX +#define DOCTEST_CONFIG_OPTIONS_PREFIX "dt-" +#endif + +#ifndef DOCTEST_THREAD_LOCAL +#define DOCTEST_THREAD_LOCAL thread_local +#endif + +#ifdef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS +#define DOCTEST_OPTIONS_PREFIX_DISPLAY DOCTEST_CONFIG_OPTIONS_PREFIX +#else +#define DOCTEST_OPTIONS_PREFIX_DISPLAY "" +#endif + +namespace doctest { + +bool is_running_in_test = false; + +namespace { + using namespace detail; + // case insensitive strcmp + int stricmp(const char* a, const char* b) { + for(;; a++, b++) { + const int d = tolower(*a) - tolower(*b); + if(d != 0 || !*a) + return d; + } + } + + template + String fpToString(T value, int precision) { + std::ostringstream oss; + oss << std::setprecision(precision) << std::fixed << value; + std::string d = oss.str(); + size_t i = d.find_last_not_of('0'); + if(i != std::string::npos && i != d.size() - 1) { + if(d[i] == '.') + i++; + d = d.substr(0, i + 1); + } + return d.c_str(); + } + + struct Endianness + { + enum Arch + { + Big, + Little + }; + + static Arch which() { + int x = 1; + // casting any data pointer to char* is allowed + auto ptr = reinterpret_cast(&x); + if(*ptr) + return Little; + return Big; + } + }; +} // namespace + +namespace detail { + void my_memcpy(void* dest, const void* src, unsigned num) { memcpy(dest, src, num); } + + String rawMemoryToString(const void* object, unsigned size) { + // Reverse order for little endian architectures + int i = 0, end = static_cast(size), inc = 1; + if(Endianness::which() == Endianness::Little) { + i = end - 1; + end = inc = -1; + } + + unsigned const char* bytes = static_cast(object); + std::ostringstream oss; + oss << "0x" << std::setfill('0') << std::hex; + for(; i != end; i += inc) + oss << std::setw(2) << static_cast(bytes[i]); + return oss.str().c_str(); + } + + DOCTEST_THREAD_LOCAL std::ostringstream g_oss; // NOLINT(cert-err58-cpp) + + std::ostream* getTlsOss() { + g_oss.clear(); // there shouldn't be anything worth clearing in the flags + g_oss.str(""); // the slow way of resetting a string stream + //g_oss.seekp(0); // optimal reset - as seen here: https://stackoverflow.com/a/624291/3162383 + return &g_oss; + } + + String getTlsOssResult() { + //g_oss << std::ends; // needed - as shown here: https://stackoverflow.com/a/624291/3162383 + return g_oss.str().c_str(); + } + +#ifndef DOCTEST_CONFIG_DISABLE + +namespace timer_large_integer +{ + +#if defined(DOCTEST_PLATFORM_WINDOWS) + typedef ULONGLONG type; +#else // DOCTEST_PLATFORM_WINDOWS + using namespace std; + typedef uint64_t type; +#endif // DOCTEST_PLATFORM_WINDOWS +} + +typedef timer_large_integer::type ticks_t; + +#ifdef DOCTEST_CONFIG_GETCURRENTTICKS + ticks_t getCurrentTicks() { return DOCTEST_CONFIG_GETCURRENTTICKS(); } +#elif defined(DOCTEST_PLATFORM_WINDOWS) + ticks_t getCurrentTicks() { + static LARGE_INTEGER hz = {0}, hzo = {0}; + if(!hz.QuadPart) { + QueryPerformanceFrequency(&hz); + QueryPerformanceCounter(&hzo); + } + LARGE_INTEGER t; + QueryPerformanceCounter(&t); + return ((t.QuadPart - hzo.QuadPart) * LONGLONG(1000000)) / hz.QuadPart; + } +#else // DOCTEST_PLATFORM_WINDOWS + ticks_t getCurrentTicks() { + timeval t; + gettimeofday(&t, nullptr); + return static_cast(t.tv_sec) * 1000000 + static_cast(t.tv_usec); + } +#endif // DOCTEST_PLATFORM_WINDOWS + + struct Timer + { + void start() { m_ticks = getCurrentTicks(); } + unsigned int getElapsedMicroseconds() const { + return static_cast(getCurrentTicks() - m_ticks); + } + //unsigned int getElapsedMilliseconds() const { + // return static_cast(getElapsedMicroseconds() / 1000); + //} + double getElapsedSeconds() const { return static_cast(getCurrentTicks() - m_ticks) / 1000000.0; } + + private: + ticks_t m_ticks = 0; + }; + + // this holds both parameters from the command line and runtime data for tests + struct ContextState : ContextOptions, TestRunStats, CurrentTestCaseStats + { + std::atomic numAssertsCurrentTest_atomic; + std::atomic numAssertsFailedCurrentTest_atomic; + + std::vector> filters = decltype(filters)(9); // 9 different filters + + std::vector reporters_currently_used; + + const TestCase* currentTest = nullptr; + + assert_handler ah = nullptr; + + Timer timer; + + std::vector stringifiedContexts; // logging from INFO() due to an exception + + // stuff for subcases + std::vector subcasesStack; + std::set subcasesPassed; + int subcasesCurrentMaxLevel; + bool should_reenter; + std::atomic shouldLogCurrentException; + + void resetRunData() { + numTestCases = 0; + numTestCasesPassingFilters = 0; + numTestSuitesPassingFilters = 0; + numTestCasesFailed = 0; + numAsserts = 0; + numAssertsFailed = 0; + numAssertsCurrentTest = 0; + numAssertsFailedCurrentTest = 0; + } + + void finalizeTestCaseData() { + seconds = timer.getElapsedSeconds(); + + // update the non-atomic counters + numAsserts += numAssertsCurrentTest_atomic; + numAssertsFailed += numAssertsFailedCurrentTest_atomic; + numAssertsCurrentTest = numAssertsCurrentTest_atomic; + numAssertsFailedCurrentTest = numAssertsFailedCurrentTest_atomic; + + if(numAssertsFailedCurrentTest) + failure_flags |= TestCaseFailureReason::AssertFailure; + + if(Approx(currentTest->m_timeout).epsilon(DBL_EPSILON) != 0 && + Approx(seconds).epsilon(DBL_EPSILON) > currentTest->m_timeout) + failure_flags |= TestCaseFailureReason::Timeout; + + if(currentTest->m_should_fail) { + if(failure_flags) { + failure_flags |= TestCaseFailureReason::ShouldHaveFailedAndDid; + } else { + failure_flags |= TestCaseFailureReason::ShouldHaveFailedButDidnt; + } + } else if(failure_flags && currentTest->m_may_fail) { + failure_flags |= TestCaseFailureReason::CouldHaveFailedAndDid; + } else if(currentTest->m_expected_failures > 0) { + if(numAssertsFailedCurrentTest == currentTest->m_expected_failures) { + failure_flags |= TestCaseFailureReason::FailedExactlyNumTimes; + } else { + failure_flags |= TestCaseFailureReason::DidntFailExactlyNumTimes; + } + } + + bool ok_to_fail = (TestCaseFailureReason::ShouldHaveFailedAndDid & failure_flags) || + (TestCaseFailureReason::CouldHaveFailedAndDid & failure_flags) || + (TestCaseFailureReason::FailedExactlyNumTimes & failure_flags); + + // if any subcase has failed - the whole test case has failed + if(failure_flags && !ok_to_fail) + numTestCasesFailed++; + } + }; + + ContextState* g_cs = nullptr; + + // used to avoid locks for the debug output + // TODO: figure out if this is indeed necessary/correct - seems like either there still + // could be a race or that there wouldn't be a race even if using the context directly + DOCTEST_THREAD_LOCAL bool g_no_colors; + +#endif // DOCTEST_CONFIG_DISABLE +} // namespace detail + +void String::setOnHeap() { *reinterpret_cast(&buf[last]) = 128; } +void String::setLast(unsigned in) { buf[last] = char(in); } + +void String::copy(const String& other) { + using namespace std; + if(other.isOnStack()) { + memcpy(buf, other.buf, len); + } else { + setOnHeap(); + data.size = other.data.size; + data.capacity = data.size + 1; + data.ptr = new char[data.capacity]; + memcpy(data.ptr, other.data.ptr, data.size + 1); + } +} + +String::String() { + buf[0] = '\0'; + setLast(); +} + +String::~String() { + if(!isOnStack()) + delete[] data.ptr; +} + +String::String(const char* in) + : String(in, strlen(in)) {} + +String::String(const char* in, unsigned in_size) { + using namespace std; + if(in_size <= last) { + memcpy(buf, in, in_size + 1); + setLast(last - in_size); + } else { + setOnHeap(); + data.size = in_size; + data.capacity = data.size + 1; + data.ptr = new char[data.capacity]; + memcpy(data.ptr, in, in_size + 1); + } +} + +String::String(const String& other) { copy(other); } + +String& String::operator=(const String& other) { + if(this != &other) { + if(!isOnStack()) + delete[] data.ptr; + + copy(other); + } + + return *this; +} + +String& String::operator+=(const String& other) { + const unsigned my_old_size = size(); + const unsigned other_size = other.size(); + const unsigned total_size = my_old_size + other_size; + using namespace std; + if(isOnStack()) { + if(total_size < len) { + // append to the current stack space + memcpy(buf + my_old_size, other.c_str(), other_size + 1); + setLast(last - total_size); + } else { + // alloc new chunk + char* temp = new char[total_size + 1]; + // copy current data to new location before writing in the union + memcpy(temp, buf, my_old_size); // skip the +1 ('\0') for speed + // update data in union + setOnHeap(); + data.size = total_size; + data.capacity = data.size + 1; + data.ptr = temp; + // transfer the rest of the data + memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); + } + } else { + if(data.capacity > total_size) { + // append to the current heap block + data.size = total_size; + memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); + } else { + // resize + data.capacity *= 2; + if(data.capacity <= total_size) + data.capacity = total_size + 1; + // alloc new chunk + char* temp = new char[data.capacity]; + // copy current data to new location before releasing it + memcpy(temp, data.ptr, my_old_size); // skip the +1 ('\0') for speed + // release old chunk + delete[] data.ptr; + // update the rest of the union members + data.size = total_size; + data.ptr = temp; + // transfer the rest of the data + memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); + } + } + + return *this; +} + +String String::operator+(const String& other) const { return String(*this) += other; } + +String::String(String&& other) { + using namespace std; + memcpy(buf, other.buf, len); + other.buf[0] = '\0'; + other.setLast(); +} + +String& String::operator=(String&& other) { + using namespace std; + if(this != &other) { + if(!isOnStack()) + delete[] data.ptr; + memcpy(buf, other.buf, len); + other.buf[0] = '\0'; + other.setLast(); + } + return *this; +} + +char String::operator[](unsigned i) const { + return const_cast(this)->operator[](i); // NOLINT +} + +char& String::operator[](unsigned i) { + if(isOnStack()) + return reinterpret_cast(buf)[i]; + return data.ptr[i]; +} + +DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmaybe-uninitialized") +unsigned String::size() const { + if(isOnStack()) + return last - (unsigned(buf[last]) & 31); // using "last" would work only if "len" is 32 + return data.size; +} +DOCTEST_GCC_SUPPRESS_WARNING_POP + +unsigned String::capacity() const { + if(isOnStack()) + return len; + return data.capacity; +} + +int String::compare(const char* other, bool no_case) const { + if(no_case) + return doctest::stricmp(c_str(), other); + return std::strcmp(c_str(), other); +} + +int String::compare(const String& other, bool no_case) const { + return compare(other.c_str(), no_case); +} + +// clang-format off +bool operator==(const String& lhs, const String& rhs) { return lhs.compare(rhs) == 0; } +bool operator!=(const String& lhs, const String& rhs) { return lhs.compare(rhs) != 0; } +bool operator< (const String& lhs, const String& rhs) { return lhs.compare(rhs) < 0; } +bool operator> (const String& lhs, const String& rhs) { return lhs.compare(rhs) > 0; } +bool operator<=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) < 0 : true; } +bool operator>=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) > 0 : true; } +// clang-format on + +std::ostream& operator<<(std::ostream& s, const String& in) { return s << in.c_str(); } + +namespace { + void color_to_stream(std::ostream&, Color::Enum) DOCTEST_BRANCH_ON_DISABLED({}, ;) +} // namespace + +namespace Color { + std::ostream& operator<<(std::ostream& s, Color::Enum code) { + color_to_stream(s, code); + return s; + } +} // namespace Color + +// clang-format off +const char* assertString(assertType::Enum at) { + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4062) // enum 'x' in switch of enum 'y' is not handled + switch(at) { //!OCLINT missing default in switch statements + case assertType::DT_WARN : return "WARN"; + case assertType::DT_CHECK : return "CHECK"; + case assertType::DT_REQUIRE : return "REQUIRE"; + + case assertType::DT_WARN_FALSE : return "WARN_FALSE"; + case assertType::DT_CHECK_FALSE : return "CHECK_FALSE"; + case assertType::DT_REQUIRE_FALSE : return "REQUIRE_FALSE"; + + case assertType::DT_WARN_THROWS : return "WARN_THROWS"; + case assertType::DT_CHECK_THROWS : return "CHECK_THROWS"; + case assertType::DT_REQUIRE_THROWS : return "REQUIRE_THROWS"; + + case assertType::DT_WARN_THROWS_AS : return "WARN_THROWS_AS"; + case assertType::DT_CHECK_THROWS_AS : return "CHECK_THROWS_AS"; + case assertType::DT_REQUIRE_THROWS_AS : return "REQUIRE_THROWS_AS"; + + case assertType::DT_WARN_THROWS_WITH : return "WARN_THROWS_WITH"; + case assertType::DT_CHECK_THROWS_WITH : return "CHECK_THROWS_WITH"; + case assertType::DT_REQUIRE_THROWS_WITH : return "REQUIRE_THROWS_WITH"; + + case assertType::DT_WARN_THROWS_WITH_AS : return "WARN_THROWS_WITH_AS"; + case assertType::DT_CHECK_THROWS_WITH_AS : return "CHECK_THROWS_WITH_AS"; + case assertType::DT_REQUIRE_THROWS_WITH_AS : return "REQUIRE_THROWS_WITH_AS"; + + case assertType::DT_WARN_NOTHROW : return "WARN_NOTHROW"; + case assertType::DT_CHECK_NOTHROW : return "CHECK_NOTHROW"; + case assertType::DT_REQUIRE_NOTHROW : return "REQUIRE_NOTHROW"; + + case assertType::DT_WARN_EQ : return "WARN_EQ"; + case assertType::DT_CHECK_EQ : return "CHECK_EQ"; + case assertType::DT_REQUIRE_EQ : return "REQUIRE_EQ"; + case assertType::DT_WARN_NE : return "WARN_NE"; + case assertType::DT_CHECK_NE : return "CHECK_NE"; + case assertType::DT_REQUIRE_NE : return "REQUIRE_NE"; + case assertType::DT_WARN_GT : return "WARN_GT"; + case assertType::DT_CHECK_GT : return "CHECK_GT"; + case assertType::DT_REQUIRE_GT : return "REQUIRE_GT"; + case assertType::DT_WARN_LT : return "WARN_LT"; + case assertType::DT_CHECK_LT : return "CHECK_LT"; + case assertType::DT_REQUIRE_LT : return "REQUIRE_LT"; + case assertType::DT_WARN_GE : return "WARN_GE"; + case assertType::DT_CHECK_GE : return "CHECK_GE"; + case assertType::DT_REQUIRE_GE : return "REQUIRE_GE"; + case assertType::DT_WARN_LE : return "WARN_LE"; + case assertType::DT_CHECK_LE : return "CHECK_LE"; + case assertType::DT_REQUIRE_LE : return "REQUIRE_LE"; + + case assertType::DT_WARN_UNARY : return "WARN_UNARY"; + case assertType::DT_CHECK_UNARY : return "CHECK_UNARY"; + case assertType::DT_REQUIRE_UNARY : return "REQUIRE_UNARY"; + case assertType::DT_WARN_UNARY_FALSE : return "WARN_UNARY_FALSE"; + case assertType::DT_CHECK_UNARY_FALSE : return "CHECK_UNARY_FALSE"; + case assertType::DT_REQUIRE_UNARY_FALSE : return "REQUIRE_UNARY_FALSE"; + } + DOCTEST_MSVC_SUPPRESS_WARNING_POP + return ""; +} +// clang-format on + +const char* failureString(assertType::Enum at) { + if(at & assertType::is_warn) //!OCLINT bitwise operator in conditional + return "WARNING"; + if(at & assertType::is_check) //!OCLINT bitwise operator in conditional + return "ERROR"; + if(at & assertType::is_require) //!OCLINT bitwise operator in conditional + return "FATAL ERROR"; + return ""; +} + +DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wnull-dereference") +DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wnull-dereference") +// depending on the current options this will remove the path of filenames +const char* skipPathFromFilename(const char* file) { + if(getContextOptions()->no_path_in_filenames) { + auto back = std::strrchr(file, '\\'); + auto forward = std::strrchr(file, '/'); + if(back || forward) { + if(back > forward) + forward = back; + return forward + 1; + } + } + return file; +} +DOCTEST_CLANG_SUPPRESS_WARNING_POP +DOCTEST_GCC_SUPPRESS_WARNING_POP + +bool SubcaseSignature::operator<(const SubcaseSignature& other) const { + if(m_line != other.m_line) + return m_line < other.m_line; + if(std::strcmp(m_file, other.m_file) != 0) + return std::strcmp(m_file, other.m_file) < 0; + return m_name.compare(other.m_name) < 0; +} + +IContextScope::IContextScope() = default; +IContextScope::~IContextScope() = default; + +#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +String toString(char* in) { return toString(static_cast(in)); } +String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; } +#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING +String toString(bool in) { return in ? "true" : "false"; } +String toString(float in) { return fpToString(in, 5) + "f"; } +String toString(double in) { return fpToString(in, 10); } +String toString(double long in) { return fpToString(in, 15); } + +#define DOCTEST_TO_STRING_OVERLOAD(type, fmt) \ + String toString(type in) { \ + char buf[64]; \ + std::sprintf(buf, fmt, in); \ + return buf; \ + } + +DOCTEST_TO_STRING_OVERLOAD(char, "%d") +DOCTEST_TO_STRING_OVERLOAD(char signed, "%d") +DOCTEST_TO_STRING_OVERLOAD(char unsigned, "%u") +DOCTEST_TO_STRING_OVERLOAD(int short, "%d") +DOCTEST_TO_STRING_OVERLOAD(int short unsigned, "%u") +DOCTEST_TO_STRING_OVERLOAD(int, "%d") +DOCTEST_TO_STRING_OVERLOAD(unsigned, "%u") +DOCTEST_TO_STRING_OVERLOAD(int long, "%ld") +DOCTEST_TO_STRING_OVERLOAD(int long unsigned, "%lu") +DOCTEST_TO_STRING_OVERLOAD(int long long, "%lld") +DOCTEST_TO_STRING_OVERLOAD(int long long unsigned, "%llu") + +String toString(std::nullptr_t) { return "NULL"; } + +#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) +// see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183 +String toString(const std::string& in) { return in.c_str(); } +#endif // VS 2019 + +Approx::Approx(double value) + : m_epsilon(static_cast(std::numeric_limits::epsilon()) * 100) + , m_scale(1.0) + , m_value(value) {} + +Approx Approx::operator()(double value) const { + Approx approx(value); + approx.epsilon(m_epsilon); + approx.scale(m_scale); + return approx; +} + +Approx& Approx::epsilon(double newEpsilon) { + m_epsilon = newEpsilon; + return *this; +} +Approx& Approx::scale(double newScale) { + m_scale = newScale; + return *this; +} + +bool operator==(double lhs, const Approx& rhs) { + // Thanks to Richard Harris for his help refining this formula + return std::fabs(lhs - rhs.m_value) < + rhs.m_epsilon * (rhs.m_scale + std::max(std::fabs(lhs), std::fabs(rhs.m_value))); +} +bool operator==(const Approx& lhs, double rhs) { return operator==(rhs, lhs); } +bool operator!=(double lhs, const Approx& rhs) { return !operator==(lhs, rhs); } +bool operator!=(const Approx& lhs, double rhs) { return !operator==(rhs, lhs); } +bool operator<=(double lhs, const Approx& rhs) { return lhs < rhs.m_value || lhs == rhs; } +bool operator<=(const Approx& lhs, double rhs) { return lhs.m_value < rhs || lhs == rhs; } +bool operator>=(double lhs, const Approx& rhs) { return lhs > rhs.m_value || lhs == rhs; } +bool operator>=(const Approx& lhs, double rhs) { return lhs.m_value > rhs || lhs == rhs; } +bool operator<(double lhs, const Approx& rhs) { return lhs < rhs.m_value && lhs != rhs; } +bool operator<(const Approx& lhs, double rhs) { return lhs.m_value < rhs && lhs != rhs; } +bool operator>(double lhs, const Approx& rhs) { return lhs > rhs.m_value && lhs != rhs; } +bool operator>(const Approx& lhs, double rhs) { return lhs.m_value > rhs && lhs != rhs; } + +String toString(const Approx& in) { + return String("Approx( ") + doctest::toString(in.m_value) + " )"; +} +const ContextOptions* getContextOptions() { return DOCTEST_BRANCH_ON_DISABLED(nullptr, g_cs); } + +} // namespace doctest + +#ifdef DOCTEST_CONFIG_DISABLE +namespace doctest { +Context::Context(int, const char* const*) {} +Context::~Context() = default; +void Context::applyCommandLine(int, const char* const*) {} +void Context::addFilter(const char*, const char*) {} +void Context::clearFilters() {} +void Context::setOption(const char*, int) {} +void Context::setOption(const char*, const char*) {} +bool Context::shouldExit() { return false; } +void Context::setAsDefaultForAssertsOutOfTestCases() {} +void Context::setAssertHandler(detail::assert_handler) {} +int Context::run() { return 0; } + +IReporter::~IReporter() = default; + +int IReporter::get_num_active_contexts() { return 0; } +const IContextScope* const* IReporter::get_active_contexts() { return nullptr; } +int IReporter::get_num_stringified_contexts() { return 0; } +const String* IReporter::get_stringified_contexts() { return nullptr; } + +int registerReporter(const char*, int, IReporter*) { return 0; } + +} // namespace doctest +#else // DOCTEST_CONFIG_DISABLE + +#if !defined(DOCTEST_CONFIG_COLORS_NONE) +#if !defined(DOCTEST_CONFIG_COLORS_WINDOWS) && !defined(DOCTEST_CONFIG_COLORS_ANSI) +#ifdef DOCTEST_PLATFORM_WINDOWS +#define DOCTEST_CONFIG_COLORS_WINDOWS +#else // linux +#define DOCTEST_CONFIG_COLORS_ANSI +#endif // platform +#endif // DOCTEST_CONFIG_COLORS_WINDOWS && DOCTEST_CONFIG_COLORS_ANSI +#endif // DOCTEST_CONFIG_COLORS_NONE + +namespace doctest_detail_test_suite_ns { +// holds the current test suite +doctest::detail::TestSuite& getCurrentTestSuite() { + static doctest::detail::TestSuite data; + return data; +} +} // namespace doctest_detail_test_suite_ns + +namespace doctest { +namespace { + // the int (priority) is part of the key for automatic sorting - sadly one can register a + // reporter with a duplicate name and a different priority but hopefully that won't happen often :| + typedef std::map, reporterCreatorFunc> reporterMap; + + reporterMap& getReporters() { + static reporterMap data; + return data; + } + reporterMap& getListeners() { + static reporterMap data; + return data; + } +} // namespace +namespace detail { +#define DOCTEST_ITERATE_THROUGH_REPORTERS(function, ...) \ + for(auto& curr_rep : g_cs->reporters_currently_used) \ + curr_rep->function(__VA_ARGS__) + + bool checkIfShouldThrow(assertType::Enum at) { + if(at & assertType::is_require) //!OCLINT bitwise operator in conditional + return true; + + if((at & assertType::is_check) //!OCLINT bitwise operator in conditional + && getContextOptions()->abort_after > 0 && + (g_cs->numAssertsFailed + g_cs->numAssertsFailedCurrentTest_atomic) >= + getContextOptions()->abort_after) + return true; + + return false; + } + +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + DOCTEST_NORETURN void throwException() { + g_cs->shouldLogCurrentException = false; + throw TestFailureException(); + } // NOLINT(cert-err60-cpp) +#else // DOCTEST_CONFIG_NO_EXCEPTIONS + void throwException() {} +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS +} // namespace detail + +namespace { + using namespace detail; + // matching of a string against a wildcard mask (case sensitivity configurable) taken from + // https://www.codeproject.com/Articles/1088/Wildcard-string-compare-globbing + int wildcmp(const char* str, const char* wild, bool caseSensitive) { + const char* cp = str; + const char* mp = wild; + + while((*str) && (*wild != '*')) { + if((caseSensitive ? (*wild != *str) : (tolower(*wild) != tolower(*str))) && + (*wild != '?')) { + return 0; + } + wild++; + str++; + } + + while(*str) { + if(*wild == '*') { + if(!*++wild) { + return 1; + } + mp = wild; + cp = str + 1; + } else if((caseSensitive ? (*wild == *str) : (tolower(*wild) == tolower(*str))) || + (*wild == '?')) { + wild++; + str++; + } else { + wild = mp; //!OCLINT parameter reassignment + str = cp++; //!OCLINT parameter reassignment + } + } + + while(*wild == '*') { + wild++; + } + return !*wild; + } + + //// C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html + //unsigned hashStr(unsigned const char* str) { + // unsigned long hash = 5381; + // char c; + // while((c = *str++)) + // hash = ((hash << 5) + hash) + c; // hash * 33 + c + // return hash; + //} + + // checks if the name matches any of the filters (and can be configured what to do when empty) + bool matchesAny(const char* name, const std::vector& filters, bool matchEmpty, + bool caseSensitive) { + if(filters.empty() && matchEmpty) + return true; + for(auto& curr : filters) + if(wildcmp(name, curr.c_str(), caseSensitive)) + return true; + return false; + } +} // namespace +namespace detail { + + Subcase::Subcase(const String& name, const char* file, int line) + : m_signature({name, file, line}) { + ContextState* s = g_cs; + + // check subcase filters + if(s->subcasesStack.size() < size_t(s->subcase_filter_levels)) { + if(!matchesAny(m_signature.m_name.c_str(), s->filters[6], true, s->case_sensitive)) + return; + if(matchesAny(m_signature.m_name.c_str(), s->filters[7], false, s->case_sensitive)) + return; + } + + // if a Subcase on the same level has already been entered + if(s->subcasesStack.size() < size_t(s->subcasesCurrentMaxLevel)) { + s->should_reenter = true; + return; + } + + // push the current signature to the stack so we can check if the + // current stack + the current new subcase have been traversed + s->subcasesStack.push_back(m_signature); + if(s->subcasesPassed.count(s->subcasesStack) != 0) { + // pop - revert to previous stack since we've already passed this + s->subcasesStack.pop_back(); + return; + } + + s->subcasesCurrentMaxLevel = s->subcasesStack.size(); + m_entered = true; + + DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature); + } + + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 + DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") + + Subcase::~Subcase() { + if(m_entered) { + // only mark the subcase stack as passed if no subcases have been skipped + if(g_cs->should_reenter == false) + g_cs->subcasesPassed.insert(g_cs->subcasesStack); + g_cs->subcasesStack.pop_back(); + +#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L + if(std::uncaught_exceptions() > 0 +#else + if(std::uncaught_exception() +#endif + && g_cs->shouldLogCurrentException) { + DOCTEST_ITERATE_THROUGH_REPORTERS( + test_case_exception, {"exception thrown in subcase - will translate later " + "when the whole test case has been exited (cannot " + "translate while there is an active exception)", + false}); + g_cs->shouldLogCurrentException = false; + } + DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY); + } + } + + DOCTEST_CLANG_SUPPRESS_WARNING_POP + DOCTEST_GCC_SUPPRESS_WARNING_POP + DOCTEST_MSVC_SUPPRESS_WARNING_POP + + Subcase::operator bool() const { return m_entered; } + + Result::Result(bool passed, const String& decomposition) + : m_passed(passed) + , m_decomp(decomposition) {} + + ExpressionDecomposer::ExpressionDecomposer(assertType::Enum at) + : m_at(at) {} + + TestSuite& TestSuite::operator*(const char* in) { + m_test_suite = in; + // clear state + m_description = nullptr; + m_skip = false; + m_may_fail = false; + m_should_fail = false; + m_expected_failures = 0; + m_timeout = 0; + return *this; + } + + TestCase::TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite, + const char* type, int template_id) { + m_file = file; + m_line = line; + m_name = nullptr; // will be later overridden in operator* + m_test_suite = test_suite.m_test_suite; + m_description = test_suite.m_description; + m_skip = test_suite.m_skip; + m_may_fail = test_suite.m_may_fail; + m_should_fail = test_suite.m_should_fail; + m_expected_failures = test_suite.m_expected_failures; + m_timeout = test_suite.m_timeout; + + m_test = test; + m_type = type; + m_template_id = template_id; + } + + TestCase::TestCase(const TestCase& other) + : TestCaseData() { + *this = other; + } + + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(26434) // hides a non-virtual function + DOCTEST_MSVC_SUPPRESS_WARNING(26437) // Do not slice + TestCase& TestCase::operator=(const TestCase& other) { + static_cast(*this) = static_cast(other); + + m_test = other.m_test; + m_type = other.m_type; + m_template_id = other.m_template_id; + m_full_name = other.m_full_name; + + if(m_template_id != -1) + m_name = m_full_name.c_str(); + return *this; + } + DOCTEST_MSVC_SUPPRESS_WARNING_POP + + TestCase& TestCase::operator*(const char* in) { + m_name = in; + // make a new name with an appended type for templated test case + if(m_template_id != -1) { + m_full_name = String(m_name) + m_type; + // redirect the name to point to the newly constructed full name + m_name = m_full_name.c_str(); + } + return *this; + } + + bool TestCase::operator<(const TestCase& other) const { + if(m_line != other.m_line) + return m_line < other.m_line; + const int file_cmp = m_file.compare(other.m_file); + if(file_cmp != 0) + return file_cmp < 0; + return m_template_id < other.m_template_id; + } +} // namespace detail +namespace { + using namespace detail; + // for sorting tests by file/line + bool fileOrderComparator(const TestCase* lhs, const TestCase* rhs) { + // this is needed because MSVC gives different case for drive letters + // for __FILE__ when evaluated in a header and a source file + const int res = lhs->m_file.compare(rhs->m_file, bool(DOCTEST_MSVC)); + if(res != 0) + return res < 0; + if(lhs->m_line != rhs->m_line) + return lhs->m_line < rhs->m_line; + return lhs->m_template_id < rhs->m_template_id; + } + + // for sorting tests by suite/file/line + bool suiteOrderComparator(const TestCase* lhs, const TestCase* rhs) { + const int res = std::strcmp(lhs->m_test_suite, rhs->m_test_suite); + if(res != 0) + return res < 0; + return fileOrderComparator(lhs, rhs); + } + + // for sorting tests by name/suite/file/line + bool nameOrderComparator(const TestCase* lhs, const TestCase* rhs) { + const int res = std::strcmp(lhs->m_name, rhs->m_name); + if(res != 0) + return res < 0; + return suiteOrderComparator(lhs, rhs); + } + + // all the registered tests + std::set& getRegisteredTests() { + static std::set data; + return data; + } + +#ifdef DOCTEST_CONFIG_COLORS_WINDOWS + HANDLE g_stdoutHandle; + WORD g_origFgAttrs; + WORD g_origBgAttrs; + bool g_attrsInitted = false; + + int colors_init() { + if(!g_attrsInitted) { + g_stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); + g_attrsInitted = true; + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo(g_stdoutHandle, &csbiInfo); + g_origFgAttrs = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED | + BACKGROUND_BLUE | BACKGROUND_INTENSITY); + g_origBgAttrs = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED | + FOREGROUND_BLUE | FOREGROUND_INTENSITY); + } + return 0; + } + + int dumy_init_console_colors = colors_init(); +#endif // DOCTEST_CONFIG_COLORS_WINDOWS + + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") + void color_to_stream(std::ostream& s, Color::Enum code) { + ((void)s); // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS + ((void)code); // for DOCTEST_CONFIG_COLORS_NONE +#ifdef DOCTEST_CONFIG_COLORS_ANSI + if(g_no_colors || + (isatty(STDOUT_FILENO) == false && getContextOptions()->force_colors == false)) + return; + + auto col = ""; + // clang-format off + switch(code) { //!OCLINT missing break in switch statement / unnecessary default statement in covered switch statement + case Color::Red: col = "[0;31m"; break; + case Color::Green: col = "[0;32m"; break; + case Color::Blue: col = "[0;34m"; break; + case Color::Cyan: col = "[0;36m"; break; + case Color::Yellow: col = "[0;33m"; break; + case Color::Grey: col = "[1;30m"; break; + case Color::LightGrey: col = "[0;37m"; break; + case Color::BrightRed: col = "[1;31m"; break; + case Color::BrightGreen: col = "[1;32m"; break; + case Color::BrightWhite: col = "[1;37m"; break; + case Color::Bright: // invalid + case Color::None: + case Color::White: + default: col = "[0m"; + } + // clang-format on + s << "\033" << col; +#endif // DOCTEST_CONFIG_COLORS_ANSI + +#ifdef DOCTEST_CONFIG_COLORS_WINDOWS + if(g_no_colors || + (isatty(fileno(stdout)) == false && getContextOptions()->force_colors == false)) + return; + +#define DOCTEST_SET_ATTR(x) SetConsoleTextAttribute(g_stdoutHandle, x | g_origBgAttrs) + + // clang-format off + switch (code) { + case Color::White: DOCTEST_SET_ATTR(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break; + case Color::Red: DOCTEST_SET_ATTR(FOREGROUND_RED); break; + case Color::Green: DOCTEST_SET_ATTR(FOREGROUND_GREEN); break; + case Color::Blue: DOCTEST_SET_ATTR(FOREGROUND_BLUE); break; + case Color::Cyan: DOCTEST_SET_ATTR(FOREGROUND_BLUE | FOREGROUND_GREEN); break; + case Color::Yellow: DOCTEST_SET_ATTR(FOREGROUND_RED | FOREGROUND_GREEN); break; + case Color::Grey: DOCTEST_SET_ATTR(0); break; + case Color::LightGrey: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY); break; + case Color::BrightRed: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_RED); break; + case Color::BrightGreen: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN); break; + case Color::BrightWhite: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break; + case Color::None: + case Color::Bright: // invalid + default: DOCTEST_SET_ATTR(g_origFgAttrs); + } + // clang-format on +#endif // DOCTEST_CONFIG_COLORS_WINDOWS + } + DOCTEST_CLANG_SUPPRESS_WARNING_POP + + std::vector& getExceptionTranslators() { + static std::vector data; + return data; + } + + String translateActiveException() { +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + String res; + auto& translators = getExceptionTranslators(); + for(auto& curr : translators) + if(curr->translate(res)) + return res; + // clang-format off + DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wcatch-value") + try { + throw; + } catch(std::exception& ex) { + return ex.what(); + } catch(std::string& msg) { + return msg.c_str(); + } catch(const char* msg) { + return msg; + } catch(...) { + return "unknown exception"; + } + DOCTEST_GCC_SUPPRESS_WARNING_POP +// clang-format on +#else // DOCTEST_CONFIG_NO_EXCEPTIONS + return ""; +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + } +} // namespace + +namespace detail { + // used by the macros for registering tests + int regTest(const TestCase& tc) { + getRegisteredTests().insert(tc); + return 0; + } + + // sets the current test suite + int setTestSuite(const TestSuite& ts) { + doctest_detail_test_suite_ns::getCurrentTestSuite() = ts; + return 0; + } + +#ifdef DOCTEST_IS_DEBUGGER_ACTIVE + bool isDebuggerActive() { return DOCTEST_IS_DEBUGGER_ACTIVE(); } +#else // DOCTEST_IS_DEBUGGER_ACTIVE +#ifdef DOCTEST_PLATFORM_MAC + // The following function is taken directly from the following technical note: + // https://developer.apple.com/library/archive/qa/qa1361/_index.html + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive() { + int mib[4]; + kinfo_proc info; + size_t size; + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + info.kp_proc.p_flag = 0; + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + // Call sysctl. + size = sizeof(info); + if(sysctl(mib, DOCTEST_COUNTOF(mib), &info, &size, 0, 0) != 0) { + std::cerr << "\nCall to sysctl failed - unable to determine if debugger is active **\n"; + return false; + } + // We're being debugged if the P_TRACED flag is set. + return ((info.kp_proc.p_flag & P_TRACED) != 0); + } +#elif DOCTEST_MSVC || defined(__MINGW32__) || defined(__MINGW64__) + bool isDebuggerActive() { return ::IsDebuggerPresent() != 0; } +#else + bool isDebuggerActive() { return false; } +#endif // Platform +#endif // DOCTEST_IS_DEBUGGER_ACTIVE + + void registerExceptionTranslatorImpl(const IExceptionTranslator* et) { + if(std::find(getExceptionTranslators().begin(), getExceptionTranslators().end(), et) == + getExceptionTranslators().end()) + getExceptionTranslators().push_back(et); + } + +#ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + void toStream(std::ostream* s, char* in) { *s << in; } + void toStream(std::ostream* s, const char* in) { *s << in; } +#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING + void toStream(std::ostream* s, bool in) { *s << std::boolalpha << in << std::noboolalpha; } + void toStream(std::ostream* s, float in) { *s << in; } + void toStream(std::ostream* s, double in) { *s << in; } + void toStream(std::ostream* s, double long in) { *s << in; } + + void toStream(std::ostream* s, char in) { *s << in; } + void toStream(std::ostream* s, char signed in) { *s << in; } + void toStream(std::ostream* s, char unsigned in) { *s << in; } + void toStream(std::ostream* s, int short in) { *s << in; } + void toStream(std::ostream* s, int short unsigned in) { *s << in; } + void toStream(std::ostream* s, int in) { *s << in; } + void toStream(std::ostream* s, int unsigned in) { *s << in; } + void toStream(std::ostream* s, int long in) { *s << in; } + void toStream(std::ostream* s, int long unsigned in) { *s << in; } + void toStream(std::ostream* s, int long long in) { *s << in; } + void toStream(std::ostream* s, int long long unsigned in) { *s << in; } + + DOCTEST_THREAD_LOCAL std::vector g_infoContexts; // for logging with INFO() + + ContextScopeBase::ContextScopeBase() { + g_infoContexts.push_back(this); + } + + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 + DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") + + // destroy cannot be inlined into the destructor because that would mean calling stringify after + // ContextScope has been destroyed (base class destructors run after derived class destructors). + // Instead, ContextScope calls this method directly from its destructor. + void ContextScopeBase::destroy() { +#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L + if(std::uncaught_exceptions() > 0) { +#else + if(std::uncaught_exception()) { +#endif + std::ostringstream s; + this->stringify(&s); + g_cs->stringifiedContexts.push_back(s.str().c_str()); + } + g_infoContexts.pop_back(); + } + + DOCTEST_CLANG_SUPPRESS_WARNING_POP + DOCTEST_GCC_SUPPRESS_WARNING_POP + DOCTEST_MSVC_SUPPRESS_WARNING_POP +} // namespace detail +namespace { + using namespace detail; + +#if !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && !defined(DOCTEST_CONFIG_WINDOWS_SEH) + struct FatalConditionHandler + { + void reset() {} + }; +#else // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH + + void reportFatal(const std::string&); + +#ifdef DOCTEST_PLATFORM_WINDOWS + + struct SignalDefs + { + DWORD id; + const char* name; + }; + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + SignalDefs signalDefs[] = { + {EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal"}, + {EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow"}, + {EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal"}, + {EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error"}, + }; + + struct FatalConditionHandler + { + static LONG CALLBACK handleException(PEXCEPTION_POINTERS ExceptionInfo) { + for(size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { + if(ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { + reportFatal(signalDefs[i].name); + break; + } + } + // If its not an exception we care about, pass it along. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; + } + + FatalConditionHandler() { + isSet = true; + // 32k seems enough for doctest to handle stack overflow, + // but the value was found experimentally, so there is no strong guarantee + guaranteeSize = 32 * 1024; + // Register an unhandled exception filter + previousTop = SetUnhandledExceptionFilter(handleException); + // Pass in guarantee size to be filled + SetThreadStackGuarantee(&guaranteeSize); + } + + static void reset() { + if(isSet) { + // Unregister handler and restore the old guarantee + SetUnhandledExceptionFilter(previousTop); + SetThreadStackGuarantee(&guaranteeSize); + previousTop = nullptr; + isSet = false; + } + } + + ~FatalConditionHandler() { reset(); } + + private: + static bool isSet; + static ULONG guaranteeSize; + static LPTOP_LEVEL_EXCEPTION_FILTER previousTop; + }; + + bool FatalConditionHandler::isSet = false; + ULONG FatalConditionHandler::guaranteeSize = 0; + LPTOP_LEVEL_EXCEPTION_FILTER FatalConditionHandler::previousTop = nullptr; + +#else // DOCTEST_PLATFORM_WINDOWS + + struct SignalDefs + { + int id; + const char* name; + }; + SignalDefs signalDefs[] = {{SIGINT, "SIGINT - Terminal interrupt signal"}, + {SIGILL, "SIGILL - Illegal instruction signal"}, + {SIGFPE, "SIGFPE - Floating point error signal"}, + {SIGSEGV, "SIGSEGV - Segmentation violation signal"}, + {SIGTERM, "SIGTERM - Termination request signal"}, + {SIGABRT, "SIGABRT - Abort (abnormal termination) signal"}}; + + struct FatalConditionHandler + { + static bool isSet; + static struct sigaction oldSigActions[DOCTEST_COUNTOF(signalDefs)]; + static stack_t oldSigStack; + static char altStackMem[4 * SIGSTKSZ]; + + static void handleSignal(int sig) { + const char* name = ""; + for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { + SignalDefs& def = signalDefs[i]; + if(sig == def.id) { + name = def.name; + break; + } + } + reset(); + reportFatal(name); + raise(sig); + } + + FatalConditionHandler() { + isSet = true; + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = sizeof(altStackMem); + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = {}; + sa.sa_handler = handleSignal; // NOLINT + sa.sa_flags = SA_ONSTACK; + for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); + } + } + + ~FatalConditionHandler() { reset(); } + static void reset() { + if(isSet) { + // Set signals back to previous values -- hopefully nobody overwrote them in the meantime + for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { + sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); + } + // Return the old stack + sigaltstack(&oldSigStack, nullptr); + isSet = false; + } + } + }; + + bool FatalConditionHandler::isSet = false; + struct sigaction FatalConditionHandler::oldSigActions[DOCTEST_COUNTOF(signalDefs)] = {}; + stack_t FatalConditionHandler::oldSigStack = {}; + char FatalConditionHandler::altStackMem[] = {}; + +#endif // DOCTEST_PLATFORM_WINDOWS +#endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH + +} // namespace + +namespace { + using namespace detail; + +#ifdef DOCTEST_PLATFORM_WINDOWS +#define DOCTEST_OUTPUT_DEBUG_STRING(text) ::OutputDebugStringA(text) +#else + // TODO: integration with XCode and other IDEs +#define DOCTEST_OUTPUT_DEBUG_STRING(text) // NOLINT(clang-diagnostic-unused-macros) +#endif // Platform + + void addAssert(assertType::Enum at) { + if((at & assertType::is_warn) == 0) //!OCLINT bitwise operator in conditional + g_cs->numAssertsCurrentTest_atomic++; + } + + void addFailedAssert(assertType::Enum at) { + if((at & assertType::is_warn) == 0) //!OCLINT bitwise operator in conditional + g_cs->numAssertsFailedCurrentTest_atomic++; + } + +#if defined(DOCTEST_CONFIG_POSIX_SIGNALS) || defined(DOCTEST_CONFIG_WINDOWS_SEH) + void reportFatal(const std::string& message) { + g_cs->failure_flags |= TestCaseFailureReason::Crash; + + DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception, {message.c_str(), true}); + + while(g_cs->subcasesStack.size()) { + g_cs->subcasesStack.pop_back(); + DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_end, DOCTEST_EMPTY); + } + + g_cs->finalizeTestCaseData(); + + DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_end, *g_cs); + + DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_end, *g_cs); + } +#endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH +} // namespace +namespace detail { + + ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, + const char* exception_type, const char* exception_string) { + m_test_case = g_cs->currentTest; + m_at = at; + m_file = file; + m_line = line; + m_expr = expr; + m_failed = true; + m_threw = false; + m_threw_as = false; + m_exception_type = exception_type; + m_exception_string = exception_string; +#if DOCTEST_MSVC + if(m_expr[0] == ' ') // this happens when variadic macros are disabled under MSVC + ++m_expr; +#endif // MSVC + } + + void ResultBuilder::setResult(const Result& res) { + m_decomp = res.m_decomp; + m_failed = !res.m_passed; + } + + void ResultBuilder::translateException() { + m_threw = true; + m_exception = translateActiveException(); + } + + bool ResultBuilder::log() { + if(m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional + m_failed = !m_threw; + } else if((m_at & assertType::is_throws_as) && (m_at & assertType::is_throws_with)) { //!OCLINT + m_failed = !m_threw_as || (m_exception != m_exception_string); + } else if(m_at & assertType::is_throws_as) { //!OCLINT bitwise operator in conditional + m_failed = !m_threw_as; + } else if(m_at & assertType::is_throws_with) { //!OCLINT bitwise operator in conditional + m_failed = m_exception != m_exception_string; + } else if(m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional + m_failed = m_threw; + } + + if(m_exception.size()) + m_exception = String("\"") + m_exception + "\""; + + if(is_running_in_test) { + addAssert(m_at); + DOCTEST_ITERATE_THROUGH_REPORTERS(log_assert, *this); + + if(m_failed) + addFailedAssert(m_at); + } else if(m_failed) { + failed_out_of_a_testing_context(*this); + } + + return m_failed && isDebuggerActive() && + !getContextOptions()->no_breaks; // break into debugger + } + + void ResultBuilder::react() const { + if(m_failed && checkIfShouldThrow(m_at)) + throwException(); + } + + void failed_out_of_a_testing_context(const AssertData& ad) { + if(g_cs->ah) + g_cs->ah(ad); + else + std::abort(); + } + + void decomp_assert(assertType::Enum at, const char* file, int line, const char* expr, + Result result) { + bool failed = !result.m_passed; + + // ################################################################################### + // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK FOR THE FAILING ASSERT + // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED + // ################################################################################### + DOCTEST_ASSERT_OUT_OF_TESTS(result.m_decomp); + DOCTEST_ASSERT_IN_TESTS(result.m_decomp); + } + + MessageBuilder::MessageBuilder(const char* file, int line, assertType::Enum severity) { + m_stream = getTlsOss(); + m_file = file; + m_line = line; + m_severity = severity; + } + + IExceptionTranslator::IExceptionTranslator() = default; + IExceptionTranslator::~IExceptionTranslator() = default; + + bool MessageBuilder::log() { + m_string = getTlsOssResult(); + DOCTEST_ITERATE_THROUGH_REPORTERS(log_message, *this); + + const bool isWarn = m_severity & assertType::is_warn; + + // warn is just a message in this context so we don't treat it as an assert + if(!isWarn) { + addAssert(m_severity); + addFailedAssert(m_severity); + } + + return isDebuggerActive() && !getContextOptions()->no_breaks && !isWarn; // break + } + + void MessageBuilder::react() { + if(m_severity & assertType::is_require) //!OCLINT bitwise operator in conditional + throwException(); + } + + MessageBuilder::~MessageBuilder() = default; +} // namespace detail +namespace { + using namespace detail; + + template + DOCTEST_NORETURN void throw_exception(Ex const& e) { +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + throw e; +#else // DOCTEST_CONFIG_NO_EXCEPTIONS + std::cerr << "doctest will terminate because it needed to throw an exception.\n" + << "The message was: " << e.what() << '\n'; + std::terminate(); +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + } + +#ifndef DOCTEST_INTERNAL_ERROR +#define DOCTEST_INTERNAL_ERROR(msg) \ + throw_exception(std::logic_error( \ + __FILE__ ":" DOCTEST_TOSTR(__LINE__) ": Internal doctest error: " msg)) +#endif // DOCTEST_INTERNAL_ERROR + + // clang-format off + +// ================================================================================================= +// The following code has been taken verbatim from Catch2/include/internal/catch_xmlwriter.h/cpp +// This is done so cherry-picking bug fixes is trivial - even the style/formatting is untouched. +// ================================================================================================= + + class XmlEncode { + public: + enum ForWhat { ForTextNodes, ForAttributes }; + + XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ); + + void encodeTo( std::ostream& os ) const; + + friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ); + + private: + std::string m_str; + ForWhat m_forWhat; + }; + + class XmlWriter { + public: + + class ScopedElement { + public: + ScopedElement( XmlWriter* writer ); + + ScopedElement( ScopedElement&& other ) DOCTEST_NOEXCEPT; + ScopedElement& operator=( ScopedElement&& other ) DOCTEST_NOEXCEPT; + + ~ScopedElement(); + + ScopedElement& writeText( std::string const& text, bool indent = true ); + + template + ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { + m_writer->writeAttribute( name, attribute ); + return *this; + } + + private: + mutable XmlWriter* m_writer = nullptr; + }; + + XmlWriter( std::ostream& os = std::cout ); + ~XmlWriter(); + + XmlWriter( XmlWriter const& ) = delete; + XmlWriter& operator=( XmlWriter const& ) = delete; + + XmlWriter& startElement( std::string const& name ); + + ScopedElement scopedElement( std::string const& name ); + + XmlWriter& endElement(); + + XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); + + XmlWriter& writeAttribute( std::string const& name, const char* attribute ); + + XmlWriter& writeAttribute( std::string const& name, bool attribute ); + + template + XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { + std::stringstream rss; + rss << attribute; + return writeAttribute( name, rss.str() ); + } + + XmlWriter& writeText( std::string const& text, bool indent = true ); + + //XmlWriter& writeComment( std::string const& text ); + + //void writeStylesheetRef( std::string const& url ); + + //XmlWriter& writeBlankLine(); + + void ensureTagClosed(); + + private: + + void writeDeclaration(); + + void newlineIfNecessary(); + + bool m_tagIsOpen = false; + bool m_needsNewline = false; + std::vector m_tags; + std::string m_indent; + std::ostream& m_os; + }; + +// ================================================================================================= +// The following code has been taken verbatim from Catch2/include/internal/catch_xmlwriter.h/cpp +// This is done so cherry-picking bug fixes is trivial - even the style/formatting is untouched. +// ================================================================================================= + +using uchar = unsigned char; + +namespace { + + size_t trailingBytes(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return 2; + } + if ((c & 0xF0) == 0xE0) { + return 3; + } + if ((c & 0xF8) == 0xF0) { + return 4; + } + DOCTEST_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + uint32_t headerValue(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return c & 0x1F; + } + if ((c & 0xF0) == 0xE0) { + return c & 0x0F; + } + if ((c & 0xF8) == 0xF0) { + return c & 0x07; + } + DOCTEST_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + void hexEscapeChar(std::ostream& os, unsigned char c) { + std::ios_base::fmtflags f(os.flags()); + os << "\\x" + << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast(c); + os.flags(f); + } + +} // anonymous namespace + + XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) + : m_str( str ), + m_forWhat( forWhat ) + {} + + void XmlEncode::encodeTo( std::ostream& os ) const { + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: https://www.w3.org/TR/xml/#syntax) + + for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { + uchar c = m_str[idx]; + switch (c) { + case '<': os << "<"; break; + case '&': os << "&"; break; + + case '>': + // See: https://www.w3.org/TR/xml/#syntax + if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') + os << ">"; + else + os << c; + break; + + case '\"': + if (m_forWhat == ForAttributes) + os << """; + else + os << c; + break; + + default: + // Check for control characters and invalid utf-8 + + // Escape control characters in standard ascii + // see https://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { + hexEscapeChar(os, c); + break; + } + + // Plain ASCII: Write it to stream + if (c < 0x7F) { + os << c; + break; + } + + // UTF-8 territory + // Check if the encoding is valid and if it is not, hex escape bytes. + // Important: We do not check the exact decoded values for validity, only the encoding format + // First check that this bytes is a valid lead byte: + // This means that it is not encoded as 1111 1XXX + // Or as 10XX XXXX + if (c < 0xC0 || + c >= 0xF8) { + hexEscapeChar(os, c); + break; + } + + auto encBytes = trailingBytes(c); + // Are there enough bytes left to avoid accessing out-of-bounds memory? + if (idx + encBytes - 1 >= m_str.size()) { + hexEscapeChar(os, c); + break; + } + // The header is valid, check data + // The next encBytes bytes must together be a valid utf-8 + // This means: bitpattern 10XX XXXX and the extracted value is sane (ish) + bool valid = true; + uint32_t value = headerValue(c); + for (std::size_t n = 1; n < encBytes; ++n) { + uchar nc = m_str[idx + n]; + valid &= ((nc & 0xC0) == 0x80); + value = (value << 6) | (nc & 0x3F); + } + + if ( + // Wrong bit pattern of following bytes + (!valid) || + // Overlong encodings + (value < 0x80) || + ( value < 0x800 && encBytes > 2) || // removed "0x80 <= value &&" because redundant + (0x800 < value && value < 0x10000 && encBytes > 3) || + // Encoded value out of range + (value >= 0x110000) + ) { + hexEscapeChar(os, c); + break; + } + + // If we got here, this is in fact a valid(ish) utf-8 sequence + for (std::size_t n = 0; n < encBytes; ++n) { + os << m_str[idx + n]; + } + idx += encBytes - 1; + break; + } + } + } + + std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { + xmlEncode.encodeTo( os ); + return os; + } + + XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer ) + : m_writer( writer ) + {} + + XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) DOCTEST_NOEXCEPT + : m_writer( other.m_writer ){ + other.m_writer = nullptr; + } + XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) DOCTEST_NOEXCEPT { + if ( m_writer ) { + m_writer->endElement(); + } + m_writer = other.m_writer; + other.m_writer = nullptr; + return *this; + } + + + XmlWriter::ScopedElement::~ScopedElement() { + if( m_writer ) + m_writer->endElement(); + } + + XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) { + m_writer->writeText( text, indent ); + return *this; + } + + XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) + { + writeDeclaration(); + } + + XmlWriter::~XmlWriter() { + while( !m_tags.empty() ) + endElement(); + } + + XmlWriter& XmlWriter::startElement( std::string const& name ) { + ensureTagClosed(); + newlineIfNecessary(); + m_os << m_indent << '<' << name; + m_tags.push_back( name ); + m_indent += " "; + m_tagIsOpen = true; + return *this; + } + + XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) { + ScopedElement scoped( this ); + startElement( name ); + return scoped; + } + + XmlWriter& XmlWriter::endElement() { + newlineIfNecessary(); + m_indent = m_indent.substr( 0, m_indent.size()-2 ); + if( m_tagIsOpen ) { + m_os << "/>"; + m_tagIsOpen = false; + } + else { + m_os << m_indent << ""; + } + m_os << std::endl; + m_tags.pop_back(); + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) { + if( !name.empty() && !attribute.empty() ) + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, const char* attribute ) { + if( !name.empty() && attribute && attribute[0] != '\0' ) + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) { + m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) { + if( !text.empty() ){ + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if( tagWasOpen && indent ) + m_os << m_indent; + m_os << XmlEncode( text ); + m_needsNewline = true; + } + return *this; + } + + //XmlWriter& XmlWriter::writeComment( std::string const& text ) { + // ensureTagClosed(); + // m_os << m_indent << ""; + // m_needsNewline = true; + // return *this; + //} + + //void XmlWriter::writeStylesheetRef( std::string const& url ) { + // m_os << "\n"; + //} + + //XmlWriter& XmlWriter::writeBlankLine() { + // ensureTagClosed(); + // m_os << '\n'; + // return *this; + //} + + void XmlWriter::ensureTagClosed() { + if( m_tagIsOpen ) { + m_os << ">" << std::endl; + m_tagIsOpen = false; + } + } + + void XmlWriter::writeDeclaration() { + m_os << "\n"; + } + + void XmlWriter::newlineIfNecessary() { + if( m_needsNewline ) { + m_os << std::endl; + m_needsNewline = false; + } + } + +// ================================================================================================= +// End of copy-pasted code from Catch +// ================================================================================================= + + // clang-format on + + struct XmlReporter : public IReporter + { + XmlWriter xml; + std::mutex mutex; + + // caching pointers/references to objects of these types - safe to do + const ContextOptions& opt; + const TestCaseData* tc = nullptr; + + XmlReporter(const ContextOptions& co) + : xml(*co.cout) + , opt(co) {} + + void log_contexts() { + int num_contexts = get_num_active_contexts(); + if(num_contexts) { + auto contexts = get_active_contexts(); + std::stringstream ss; + for(int i = 0; i < num_contexts; ++i) { + contexts[i]->stringify(&ss); + xml.scopedElement("Info").writeText(ss.str()); + ss.str(""); + } + } + } + + unsigned line(unsigned l) const { return opt.no_line_numbers ? 0 : l; } + + void test_case_start_impl(const TestCaseData& in) { + bool open_ts_tag = false; + if(tc != nullptr) { // we have already opened a test suite + if(std::strcmp(tc->m_test_suite, in.m_test_suite) != 0) { + xml.endElement(); + open_ts_tag = true; + } + } + else { + open_ts_tag = true; // first test case ==> first test suite + } + + if(open_ts_tag) { + xml.startElement("TestSuite"); + xml.writeAttribute("name", in.m_test_suite); + } + + tc = ∈ + xml.startElement("TestCase") + .writeAttribute("name", in.m_name) + .writeAttribute("filename", skipPathFromFilename(in.m_file.c_str())) + .writeAttribute("line", line(in.m_line)) + .writeAttribute("description", in.m_description); + + if(Approx(in.m_timeout) != 0) + xml.writeAttribute("timeout", in.m_timeout); + if(in.m_may_fail) + xml.writeAttribute("may_fail", true); + if(in.m_should_fail) + xml.writeAttribute("should_fail", true); + } + + // ========================================================================================= + // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE + // ========================================================================================= + + void report_query(const QueryData& in) override { + test_run_start(); + if(opt.list_reporters) { + for(auto& curr : getListeners()) + xml.scopedElement("Listener") + .writeAttribute("priority", curr.first.first) + .writeAttribute("name", curr.first.second); + for(auto& curr : getReporters()) + xml.scopedElement("Reporter") + .writeAttribute("priority", curr.first.first) + .writeAttribute("name", curr.first.second); + } else if(opt.count || opt.list_test_cases) { + for(unsigned i = 0; i < in.num_data; ++i) { + xml.scopedElement("TestCase").writeAttribute("name", in.data[i]->m_name) + .writeAttribute("testsuite", in.data[i]->m_test_suite) + .writeAttribute("filename", skipPathFromFilename(in.data[i]->m_file.c_str())) + .writeAttribute("line", line(in.data[i]->m_line)); + } + xml.scopedElement("OverallResultsTestCases") + .writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters); + } else if(opt.list_test_suites) { + for(unsigned i = 0; i < in.num_data; ++i) + xml.scopedElement("TestSuite").writeAttribute("name", in.data[i]->m_test_suite); + xml.scopedElement("OverallResultsTestCases") + .writeAttribute("unskipped", in.run_stats->numTestCasesPassingFilters); + xml.scopedElement("OverallResultsTestSuites") + .writeAttribute("unskipped", in.run_stats->numTestSuitesPassingFilters); + } + xml.endElement(); + } + + void test_run_start() override { + // remove .exe extension - mainly to have the same output on UNIX and Windows + std::string binary_name = skipPathFromFilename(opt.binary_name.c_str()); +#ifdef DOCTEST_PLATFORM_WINDOWS + if(binary_name.rfind(".exe") != std::string::npos) + binary_name = binary_name.substr(0, binary_name.length() - 4); +#endif // DOCTEST_PLATFORM_WINDOWS + + xml.startElement("doctest").writeAttribute("binary", binary_name); + if(opt.no_version == false) + xml.writeAttribute("version", DOCTEST_VERSION_STR); + + // only the consequential ones (TODO: filters) + xml.scopedElement("Options") + .writeAttribute("order_by", opt.order_by.c_str()) + .writeAttribute("rand_seed", opt.rand_seed) + .writeAttribute("first", opt.first) + .writeAttribute("last", opt.last) + .writeAttribute("abort_after", opt.abort_after) + .writeAttribute("subcase_filter_levels", opt.subcase_filter_levels) + .writeAttribute("case_sensitive", opt.case_sensitive) + .writeAttribute("no_throw", opt.no_throw) + .writeAttribute("no_skip", opt.no_skip); + } + + void test_run_end(const TestRunStats& p) override { + if(tc) // the TestSuite tag - only if there has been at least 1 test case + xml.endElement(); + + xml.scopedElement("OverallResultsAsserts") + .writeAttribute("successes", p.numAsserts - p.numAssertsFailed) + .writeAttribute("failures", p.numAssertsFailed); + + xml.startElement("OverallResultsTestCases") + .writeAttribute("successes", + p.numTestCasesPassingFilters - p.numTestCasesFailed) + .writeAttribute("failures", p.numTestCasesFailed); + if(opt.no_skipped_summary == false) + xml.writeAttribute("skipped", p.numTestCases - p.numTestCasesPassingFilters); + xml.endElement(); + + xml.endElement(); + } + + void test_case_start(const TestCaseData& in) override { + test_case_start_impl(in); + xml.ensureTagClosed(); + } + + void test_case_reenter(const TestCaseData&) override {} + + void test_case_end(const CurrentTestCaseStats& st) override { + xml.startElement("OverallResultsAsserts") + .writeAttribute("successes", + st.numAssertsCurrentTest - st.numAssertsFailedCurrentTest) + .writeAttribute("failures", st.numAssertsFailedCurrentTest); + if(opt.duration) + xml.writeAttribute("duration", st.seconds); + if(tc->m_expected_failures) + xml.writeAttribute("expected_failures", tc->m_expected_failures); + xml.endElement(); + + xml.endElement(); + } + + void test_case_exception(const TestCaseException& e) override { + std::lock_guard lock(mutex); + + xml.scopedElement("Exception") + .writeAttribute("crash", e.is_crash) + .writeText(e.error_string.c_str()); + } + + void subcase_start(const SubcaseSignature& in) override { + std::lock_guard lock(mutex); + + xml.startElement("SubCase") + .writeAttribute("name", in.m_name) + .writeAttribute("filename", skipPathFromFilename(in.m_file)) + .writeAttribute("line", line(in.m_line)); + xml.ensureTagClosed(); + } + + void subcase_end() override { xml.endElement(); } + + void log_assert(const AssertData& rb) override { + if(!rb.m_failed && !opt.success) + return; + + std::lock_guard lock(mutex); + + xml.startElement("Expression") + .writeAttribute("success", !rb.m_failed) + .writeAttribute("type", assertString(rb.m_at)) + .writeAttribute("filename", skipPathFromFilename(rb.m_file)) + .writeAttribute("line", line(rb.m_line)); + + xml.scopedElement("Original").writeText(rb.m_expr); + + if(rb.m_threw) + xml.scopedElement("Exception").writeText(rb.m_exception.c_str()); + + if(rb.m_at & assertType::is_throws_as) + xml.scopedElement("ExpectedException").writeText(rb.m_exception_type); + if(rb.m_at & assertType::is_throws_with) + xml.scopedElement("ExpectedExceptionString").writeText(rb.m_exception_string); + if((rb.m_at & assertType::is_normal) && !rb.m_threw) + xml.scopedElement("Expanded").writeText(rb.m_decomp.c_str()); + + log_contexts(); + + xml.endElement(); + } + + void log_message(const MessageData& mb) override { + std::lock_guard lock(mutex); + + xml.startElement("Message") + .writeAttribute("type", failureString(mb.m_severity)) + .writeAttribute("filename", skipPathFromFilename(mb.m_file)) + .writeAttribute("line", line(mb.m_line)); + + xml.scopedElement("Text").writeText(mb.m_string.c_str()); + + log_contexts(); + + xml.endElement(); + } + + void test_case_skipped(const TestCaseData& in) override { + if(opt.no_skipped_summary == false) { + test_case_start_impl(in); + xml.writeAttribute("skipped", "true"); + xml.endElement(); + } + } + }; + + DOCTEST_REGISTER_REPORTER("xml", 0, XmlReporter); + + void fulltext_log_assert_to_stream(std::ostream& s, const AssertData& rb) { + if((rb.m_at & (assertType::is_throws_as | assertType::is_throws_with)) == + 0) //!OCLINT bitwise operator in conditional + s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << " ) " + << Color::None; + + if(rb.m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional + s << (rb.m_threw ? "threw as expected!" : "did NOT throw at all!") << "\n"; + } else if((rb.m_at & assertType::is_throws_as) && + (rb.m_at & assertType::is_throws_with)) { //!OCLINT + s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \"" + << rb.m_exception_string << "\", " << rb.m_exception_type << " ) " << Color::None; + if(rb.m_threw) { + if(!rb.m_failed) { + s << "threw as expected!\n"; + } else { + s << "threw a DIFFERENT exception! (contents: " << rb.m_exception << ")\n"; + } + } else { + s << "did NOT throw at all!\n"; + } + } else if(rb.m_at & + assertType::is_throws_as) { //!OCLINT bitwise operator in conditional + s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", " + << rb.m_exception_type << " ) " << Color::None + << (rb.m_threw ? (rb.m_threw_as ? "threw as expected!" : + "threw a DIFFERENT exception: ") : + "did NOT throw at all!") + << Color::Cyan << rb.m_exception << "\n"; + } else if(rb.m_at & + assertType::is_throws_with) { //!OCLINT bitwise operator in conditional + s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \"" + << rb.m_exception_string << "\" ) " << Color::None + << (rb.m_threw ? (!rb.m_failed ? "threw as expected!" : + "threw a DIFFERENT exception: ") : + "did NOT throw at all!") + << Color::Cyan << rb.m_exception << "\n"; + } else if(rb.m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional + s << (rb.m_threw ? "THREW exception: " : "didn't throw!") << Color::Cyan + << rb.m_exception << "\n"; + } else { + s << (rb.m_threw ? "THREW exception: " : + (!rb.m_failed ? "is correct!\n" : "is NOT correct!\n")); + if(rb.m_threw) + s << rb.m_exception << "\n"; + else + s << " values: " << assertString(rb.m_at) << "( " << rb.m_decomp << " )\n"; + } + } + + // TODO: + // - log_contexts() + // - log_message() + // - respond to queries + // - honor remaining options + // - more attributes in tags + struct JUnitReporter : public IReporter + { + XmlWriter xml; + std::mutex mutex; + Timer timer; + std::vector deepestSubcaseStackNames; + + struct JUnitTestCaseData + { +DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") // gmtime + static std::string getCurrentTimestamp() { + // Beware, this is not reentrant because of backward compatibility issues + // Also, UTC only, again because of backward compatibility (%z is C++11) + time_t rawtime; + std::time(&rawtime); + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + + std::tm* timeInfo; + timeInfo = std::gmtime(&rawtime); + + char timeStamp[timeStampSize]; + const char* const fmt = "%Y-%m-%dT%H:%M:%SZ"; + + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); + return std::string(timeStamp); + } +DOCTEST_CLANG_SUPPRESS_WARNING_POP + + struct JUnitTestMessage + { + JUnitTestMessage(const std::string& _message, const std::string& _type, const std::string& _details) + : message(_message), type(_type), details(_details) {} + + JUnitTestMessage(const std::string& _message, const std::string& _details) + : message(_message), type(), details(_details) {} + + std::string message, type, details; + }; + + struct JUnitTestCase + { + JUnitTestCase(const std::string& _classname, const std::string& _name) + : classname(_classname), name(_name), time(0), failures() {} + + std::string classname, name; + double time; + std::vector failures, errors; + }; + + void add(const std::string& classname, const std::string& name) { + testcases.emplace_back(classname, name); + } + + void appendSubcaseNamesToLastTestcase(std::vector nameStack) { + for(auto& curr: nameStack) + if(curr.size()) + testcases.back().name += std::string("/") + curr.c_str(); + } + + void addTime(double time) { + if(time < 1e-4) + time = 0; + testcases.back().time = time; + totalSeconds += time; + } + + void addFailure(const std::string& message, const std::string& type, const std::string& details) { + testcases.back().failures.emplace_back(message, type, details); + ++totalFailures; + } + + void addError(const std::string& message, const std::string& details) { + testcases.back().errors.emplace_back(message, details); + ++totalErrors; + } + + std::vector testcases; + double totalSeconds = 0; + int totalErrors = 0, totalFailures = 0; + }; + + JUnitTestCaseData testCaseData; + + // caching pointers/references to objects of these types - safe to do + const ContextOptions& opt; + const TestCaseData* tc = nullptr; + + JUnitReporter(const ContextOptions& co) + : xml(*co.cout) + , opt(co) {} + + unsigned line(unsigned l) const { return opt.no_line_numbers ? 0 : l; } + + // ========================================================================================= + // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE + // ========================================================================================= + + void report_query(const QueryData&) override {} + + void test_run_start() override {} + + void test_run_end(const TestRunStats& p) override { + // remove .exe extension - mainly to have the same output on UNIX and Windows + std::string binary_name = skipPathFromFilename(opt.binary_name.c_str()); +#ifdef DOCTEST_PLATFORM_WINDOWS + if(binary_name.rfind(".exe") != std::string::npos) + binary_name = binary_name.substr(0, binary_name.length() - 4); +#endif // DOCTEST_PLATFORM_WINDOWS + xml.startElement("testsuites"); + xml.startElement("testsuite").writeAttribute("name", binary_name) + .writeAttribute("errors", testCaseData.totalErrors) + .writeAttribute("failures", testCaseData.totalFailures) + .writeAttribute("tests", p.numAsserts); + if(opt.no_time_in_output == false) { + xml.writeAttribute("time", testCaseData.totalSeconds); + xml.writeAttribute("timestamp", JUnitTestCaseData::getCurrentTimestamp()); + } + if(opt.no_version == false) + xml.writeAttribute("doctest_version", DOCTEST_VERSION_STR); + + for(const auto& testCase : testCaseData.testcases) { + xml.startElement("testcase") + .writeAttribute("classname", testCase.classname) + .writeAttribute("name", testCase.name); + if(opt.no_time_in_output == false) + xml.writeAttribute("time", testCase.time); + // This is not ideal, but it should be enough to mimic gtest's junit output. + xml.writeAttribute("status", "run"); + + for(const auto& failure : testCase.failures) { + xml.scopedElement("failure") + .writeAttribute("message", failure.message) + .writeAttribute("type", failure.type) + .writeText(failure.details, false); + } + + for(const auto& error : testCase.errors) { + xml.scopedElement("error") + .writeAttribute("message", error.message) + .writeText(error.details); + } + + xml.endElement(); + } + xml.endElement(); + xml.endElement(); + } + + void test_case_start(const TestCaseData& in) override { + testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name); + timer.start(); + } + + void test_case_reenter(const TestCaseData& in) override { + testCaseData.addTime(timer.getElapsedSeconds()); + testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames); + deepestSubcaseStackNames.clear(); + + timer.start(); + testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name); + } + + void test_case_end(const CurrentTestCaseStats&) override { + testCaseData.addTime(timer.getElapsedSeconds()); + testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames); + deepestSubcaseStackNames.clear(); + } + + void test_case_exception(const TestCaseException& e) override { + std::lock_guard lock(mutex); + testCaseData.addError("exception", e.error_string.c_str()); + } + + void subcase_start(const SubcaseSignature& in) override { + std::lock_guard lock(mutex); + deepestSubcaseStackNames.push_back(in.m_name); + } + + void subcase_end() override {} + + void log_assert(const AssertData& rb) override { + if(!rb.m_failed) // report only failures & ignore the `success` option + return; + + std::lock_guard lock(mutex); + + std::ostringstream os; + os << skipPathFromFilename(rb.m_file) << (opt.gnu_file_line ? ":" : "(") + << line(rb.m_line) << (opt.gnu_file_line ? ":" : "):") << std::endl; + + fulltext_log_assert_to_stream(os, rb); + testCaseData.addFailure(rb.m_decomp.c_str(), assertString(rb.m_at), os.str()); + } + + void log_message(const MessageData&) override {} + + void test_case_skipped(const TestCaseData&) override {} + }; + + DOCTEST_REGISTER_REPORTER("junit", 0, JUnitReporter); + + struct Whitespace + { + int nrSpaces; + explicit Whitespace(int nr) + : nrSpaces(nr) {} + }; + + std::ostream& operator<<(std::ostream& out, const Whitespace& ws) { + if(ws.nrSpaces != 0) + out << std::setw(ws.nrSpaces) << ' '; + return out; + } + + struct ConsoleReporter : public IReporter + { + std::ostream& s; + bool hasLoggedCurrentTestStart; + std::vector subcasesStack; + size_t currentSubcaseLevel; + std::mutex mutex; + + // caching pointers/references to objects of these types - safe to do + const ContextOptions& opt; + const TestCaseData* tc; + + ConsoleReporter(const ContextOptions& co) + : s(*co.cout) + , opt(co) {} + + ConsoleReporter(const ContextOptions& co, std::ostream& ostr) + : s(ostr) + , opt(co) {} + + // ========================================================================================= + // WHAT FOLLOWS ARE HELPERS USED BY THE OVERRIDES OF THE VIRTUAL METHODS OF THE INTERFACE + // ========================================================================================= + + void separator_to_stream() { + s << Color::Yellow + << "===============================================================================" + "\n"; + } + + const char* getSuccessOrFailString(bool success, assertType::Enum at, + const char* success_str) { + if(success) + return success_str; + return failureString(at); + } + + Color::Enum getSuccessOrFailColor(bool success, assertType::Enum at) { + return success ? Color::BrightGreen : + (at & assertType::is_warn) ? Color::Yellow : Color::Red; + } + + void successOrFailColoredStringToStream(bool success, assertType::Enum at, + const char* success_str = "SUCCESS") { + s << getSuccessOrFailColor(success, at) + << getSuccessOrFailString(success, at, success_str) << ": "; + } + + void log_contexts() { + int num_contexts = get_num_active_contexts(); + if(num_contexts) { + auto contexts = get_active_contexts(); + + s << Color::None << " logged: "; + for(int i = 0; i < num_contexts; ++i) { + s << (i == 0 ? "" : " "); + contexts[i]->stringify(&s); + s << "\n"; + } + } + + s << "\n"; + } + + // this was requested to be made virtual so users could override it + virtual void file_line_to_stream(const char* file, int line, + const char* tail = "") { + s << Color::LightGrey << skipPathFromFilename(file) << (opt.gnu_file_line ? ":" : "(") + << (opt.no_line_numbers ? 0 : line) // 0 or the real num depending on the option + << (opt.gnu_file_line ? ":" : "):") << tail; + } + + void logTestStart() { + if(hasLoggedCurrentTestStart) + return; + + separator_to_stream(); + file_line_to_stream(tc->m_file.c_str(), tc->m_line, "\n"); + if(tc->m_description) + s << Color::Yellow << "DESCRIPTION: " << Color::None << tc->m_description << "\n"; + if(tc->m_test_suite && tc->m_test_suite[0] != '\0') + s << Color::Yellow << "TEST SUITE: " << Color::None << tc->m_test_suite << "\n"; + if(strncmp(tc->m_name, " Scenario:", 11) != 0) + s << Color::Yellow << "TEST CASE: "; + s << Color::None << tc->m_name << "\n"; + + for(size_t i = 0; i < currentSubcaseLevel; ++i) { + if(subcasesStack[i].m_name[0] != '\0') + s << " " << subcasesStack[i].m_name << "\n"; + } + + if(currentSubcaseLevel != subcasesStack.size()) { + s << Color::Yellow << "\nDEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):\n" << Color::None; + for(size_t i = 0; i < subcasesStack.size(); ++i) { + if(subcasesStack[i].m_name[0] != '\0') + s << " " << subcasesStack[i].m_name << "\n"; + } + } + + s << "\n"; + + hasLoggedCurrentTestStart = true; + } + + void printVersion() { + if(opt.no_version == false) + s << Color::Cyan << "[doctest] " << Color::None << "doctest version is \"" + << DOCTEST_VERSION_STR << "\"\n"; + } + + void printIntro() { + printVersion(); + s << Color::Cyan << "[doctest] " << Color::None + << "run with \"--" DOCTEST_OPTIONS_PREFIX_DISPLAY "help\" for options\n"; + } + + void printHelp() { + int sizePrefixDisplay = static_cast(strlen(DOCTEST_OPTIONS_PREFIX_DISPLAY)); + printVersion(); + // clang-format off + s << Color::Cyan << "[doctest]\n" << Color::None; + s << Color::Cyan << "[doctest] " << Color::None; + s << "boolean values: \"1/on/yes/true\" or \"0/off/no/false\"\n"; + s << Color::Cyan << "[doctest] " << Color::None; + s << "filter values: \"str1,str2,str3\" (comma separated strings)\n"; + s << Color::Cyan << "[doctest]\n" << Color::None; + s << Color::Cyan << "[doctest] " << Color::None; + s << "filters use wildcards for matching strings\n"; + s << Color::Cyan << "[doctest] " << Color::None; + s << "something passes a filter if any of the strings in a filter matches\n"; +#ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS + s << Color::Cyan << "[doctest]\n" << Color::None; + s << Color::Cyan << "[doctest] " << Color::None; + s << "ALL FLAGS, OPTIONS AND FILTERS ALSO AVAILABLE WITH A \"" DOCTEST_CONFIG_OPTIONS_PREFIX "\" PREFIX!!!\n"; +#endif + s << Color::Cyan << "[doctest]\n" << Color::None; + s << Color::Cyan << "[doctest] " << Color::None; + s << "Query flags - the program quits after them. Available:\n\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "?, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "help, -" DOCTEST_OPTIONS_PREFIX_DISPLAY "h " + << Whitespace(sizePrefixDisplay*0) << "prints this message\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "v, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "version " + << Whitespace(sizePrefixDisplay*1) << "prints the version\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "c, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "count " + << Whitespace(sizePrefixDisplay*1) << "prints the number of matching tests\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ltc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-test-cases " + << Whitespace(sizePrefixDisplay*1) << "lists all matching tests by name\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "lts, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-test-suites " + << Whitespace(sizePrefixDisplay*1) << "lists all matching test suites\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "lr, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "list-reporters " + << Whitespace(sizePrefixDisplay*1) << "lists all registered reporters\n\n"; + // ================================================================================== << 79 + s << Color::Cyan << "[doctest] " << Color::None; + s << "The available / options/filters are:\n\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-case= " + << Whitespace(sizePrefixDisplay*1) << "filters tests by their name\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tce, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-case-exclude= " + << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their name\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sf, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "source-file= " + << Whitespace(sizePrefixDisplay*1) << "filters tests by their file\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sfe, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "source-file-exclude= " + << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their file\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ts, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-suite= " + << Whitespace(sizePrefixDisplay*1) << "filters tests by their test suite\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tse, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-suite-exclude= " + << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their test suite\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase= " + << Whitespace(sizePrefixDisplay*1) << "filters subcases by their name\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sce, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase-exclude= " + << Whitespace(sizePrefixDisplay*1) << "filters OUT subcases by their name\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "r, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "reporters= " + << Whitespace(sizePrefixDisplay*1) << "reporters to use (console is default)\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "o, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "out= " + << Whitespace(sizePrefixDisplay*1) << "output filename\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ob, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "order-by= " + << Whitespace(sizePrefixDisplay*1) << "how the tests should be ordered\n"; + s << Whitespace(sizePrefixDisplay*3) << " - by [file/suite/name/rand]\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "rs, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "rand-seed= " + << Whitespace(sizePrefixDisplay*1) << "seed for random ordering\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "f, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "first= " + << Whitespace(sizePrefixDisplay*1) << "the first test passing the filters to\n"; + s << Whitespace(sizePrefixDisplay*3) << " execute - for range-based execution\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "l, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "last= " + << Whitespace(sizePrefixDisplay*1) << "the last test passing the filters to\n"; + s << Whitespace(sizePrefixDisplay*3) << " execute - for range-based execution\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "aa, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "abort-after= " + << Whitespace(sizePrefixDisplay*1) << "stop after failed assertions\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "scfl,--" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase-filter-levels= " + << Whitespace(sizePrefixDisplay*1) << "apply filters for the first levels\n"; + s << Color::Cyan << "\n[doctest] " << Color::None; + s << "Bool options - can be used like flags and true is assumed. Available:\n\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "s, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "success= " + << Whitespace(sizePrefixDisplay*1) << "include successful assertions in output\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "cs, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "case-sensitive= " + << Whitespace(sizePrefixDisplay*1) << "filters being treated as case sensitive\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "e, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "exit= " + << Whitespace(sizePrefixDisplay*1) << "exits after the tests finish\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "d, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "duration= " + << Whitespace(sizePrefixDisplay*1) << "prints the time duration of each test\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nt, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-throw= " + << Whitespace(sizePrefixDisplay*1) << "skips exceptions-related assert checks\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ne, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-exitcode= " + << Whitespace(sizePrefixDisplay*1) << "returns (or exits) always with success\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nr, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-run= " + << Whitespace(sizePrefixDisplay*1) << "skips all runtime doctest operations\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nv, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-version= " + << Whitespace(sizePrefixDisplay*1) << "omit the framework version in the output\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-colors= " + << Whitespace(sizePrefixDisplay*1) << "disables colors in output\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "fc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "force-colors= " + << Whitespace(sizePrefixDisplay*1) << "use colors even when not in a tty\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nb, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-breaks= " + << Whitespace(sizePrefixDisplay*1) << "disables breakpoints in debuggers\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ns, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-skip= " + << Whitespace(sizePrefixDisplay*1) << "don't skip test cases marked as skip\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "gfl, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "gnu-file-line= " + << Whitespace(sizePrefixDisplay*1) << ":n: vs (n): for line numbers in output\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "npf, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-path-filenames= " + << Whitespace(sizePrefixDisplay*1) << "only filenames and no paths in output\n"; + s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nln, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-line-numbers= " + << Whitespace(sizePrefixDisplay*1) << "0 instead of real line numbers in output\n"; + // ================================================================================== << 79 + // clang-format on + + s << Color::Cyan << "\n[doctest] " << Color::None; + s << "for more information visit the project documentation\n\n"; + } + + void printRegisteredReporters() { + printVersion(); + auto printReporters = [this] (const reporterMap& reporters, const char* type) { + if(reporters.size()) { + s << Color::Cyan << "[doctest] " << Color::None << "listing all registered " << type << "\n"; + for(auto& curr : reporters) + s << "priority: " << std::setw(5) << curr.first.first + << " name: " << curr.first.second << "\n"; + } + }; + printReporters(getListeners(), "listeners"); + printReporters(getReporters(), "reporters"); + } + + void list_query_results() { + separator_to_stream(); + if(opt.count || opt.list_test_cases) { + s << Color::Cyan << "[doctest] " << Color::None + << "unskipped test cases passing the current filters: " + << g_cs->numTestCasesPassingFilters << "\n"; + } else if(opt.list_test_suites) { + s << Color::Cyan << "[doctest] " << Color::None + << "unskipped test cases passing the current filters: " + << g_cs->numTestCasesPassingFilters << "\n"; + s << Color::Cyan << "[doctest] " << Color::None + << "test suites with unskipped test cases passing the current filters: " + << g_cs->numTestSuitesPassingFilters << "\n"; + } + } + + // ========================================================================================= + // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE + // ========================================================================================= + + void report_query(const QueryData& in) override { + if(opt.version) { + printVersion(); + } else if(opt.help) { + printHelp(); + } else if(opt.list_reporters) { + printRegisteredReporters(); + } else if(opt.count || opt.list_test_cases) { + if(opt.list_test_cases) { + s << Color::Cyan << "[doctest] " << Color::None + << "listing all test case names\n"; + separator_to_stream(); + } + + for(unsigned i = 0; i < in.num_data; ++i) + s << Color::None << in.data[i]->m_name << "\n"; + + separator_to_stream(); + + s << Color::Cyan << "[doctest] " << Color::None + << "unskipped test cases passing the current filters: " + << g_cs->numTestCasesPassingFilters << "\n"; + + } else if(opt.list_test_suites) { + s << Color::Cyan << "[doctest] " << Color::None << "listing all test suites\n"; + separator_to_stream(); + + for(unsigned i = 0; i < in.num_data; ++i) + s << Color::None << in.data[i]->m_test_suite << "\n"; + + separator_to_stream(); + + s << Color::Cyan << "[doctest] " << Color::None + << "unskipped test cases passing the current filters: " + << g_cs->numTestCasesPassingFilters << "\n"; + s << Color::Cyan << "[doctest] " << Color::None + << "test suites with unskipped test cases passing the current filters: " + << g_cs->numTestSuitesPassingFilters << "\n"; + } + } + + void test_run_start() override { printIntro(); } + + void test_run_end(const TestRunStats& p) override { + separator_to_stream(); + s << std::dec; + + const bool anythingFailed = p.numTestCasesFailed > 0 || p.numAssertsFailed > 0; + s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(6) + << p.numTestCasesPassingFilters << " | " + << ((p.numTestCasesPassingFilters == 0 || anythingFailed) ? Color::None : + Color::Green) + << std::setw(6) << p.numTestCasesPassingFilters - p.numTestCasesFailed << " passed" + << Color::None << " | " << (p.numTestCasesFailed > 0 ? Color::Red : Color::None) + << std::setw(6) << p.numTestCasesFailed << " failed" << Color::None << " | "; + if(opt.no_skipped_summary == false) { + const int numSkipped = p.numTestCases - p.numTestCasesPassingFilters; + s << (numSkipped == 0 ? Color::None : Color::Yellow) << std::setw(6) << numSkipped + << " skipped" << Color::None; + } + s << "\n"; + s << Color::Cyan << "[doctest] " << Color::None << "assertions: " << std::setw(6) + << p.numAsserts << " | " + << ((p.numAsserts == 0 || anythingFailed) ? Color::None : Color::Green) + << std::setw(6) << (p.numAsserts - p.numAssertsFailed) << " passed" << Color::None + << " | " << (p.numAssertsFailed > 0 ? Color::Red : Color::None) << std::setw(6) + << p.numAssertsFailed << " failed" << Color::None << " |\n"; + s << Color::Cyan << "[doctest] " << Color::None + << "Status: " << (p.numTestCasesFailed > 0 ? Color::Red : Color::Green) + << ((p.numTestCasesFailed > 0) ? "FAILURE!" : "SUCCESS!") << Color::None << std::endl; + } + + void test_case_start(const TestCaseData& in) override { + hasLoggedCurrentTestStart = false; + tc = ∈ + subcasesStack.clear(); + currentSubcaseLevel = 0; + } + + void test_case_reenter(const TestCaseData&) override { + subcasesStack.clear(); + } + + void test_case_end(const CurrentTestCaseStats& st) override { + // log the preamble of the test case only if there is something + // else to print - something other than that an assert has failed + if(opt.duration || + (st.failure_flags && st.failure_flags != TestCaseFailureReason::AssertFailure)) + logTestStart(); + + if(opt.duration) + s << Color::None << std::setprecision(6) << std::fixed << st.seconds + << " s: " << tc->m_name << "\n"; + + if(st.failure_flags & TestCaseFailureReason::Timeout) + s << Color::Red << "Test case exceeded time limit of " << std::setprecision(6) + << std::fixed << tc->m_timeout << "!\n"; + + if(st.failure_flags & TestCaseFailureReason::ShouldHaveFailedButDidnt) { + s << Color::Red << "Should have failed but didn't! Marking it as failed!\n"; + } else if(st.failure_flags & TestCaseFailureReason::ShouldHaveFailedAndDid) { + s << Color::Yellow << "Failed as expected so marking it as not failed\n"; + } else if(st.failure_flags & TestCaseFailureReason::CouldHaveFailedAndDid) { + s << Color::Yellow << "Allowed to fail so marking it as not failed\n"; + } else if(st.failure_flags & TestCaseFailureReason::DidntFailExactlyNumTimes) { + s << Color::Red << "Didn't fail exactly " << tc->m_expected_failures + << " times so marking it as failed!\n"; + } else if(st.failure_flags & TestCaseFailureReason::FailedExactlyNumTimes) { + s << Color::Yellow << "Failed exactly " << tc->m_expected_failures + << " times as expected so marking it as not failed!\n"; + } + if(st.failure_flags & TestCaseFailureReason::TooManyFailedAsserts) { + s << Color::Red << "Aborting - too many failed asserts!\n"; + } + s << Color::None; // lgtm [cpp/useless-expression] + } + + void test_case_exception(const TestCaseException& e) override { + logTestStart(); + + file_line_to_stream(tc->m_file.c_str(), tc->m_line, " "); + successOrFailColoredStringToStream(false, e.is_crash ? assertType::is_require : + assertType::is_check); + s << Color::Red << (e.is_crash ? "test case CRASHED: " : "test case THREW exception: ") + << Color::Cyan << e.error_string << "\n"; + + int num_stringified_contexts = get_num_stringified_contexts(); + if(num_stringified_contexts) { + auto stringified_contexts = get_stringified_contexts(); + s << Color::None << " logged: "; + for(int i = num_stringified_contexts; i > 0; --i) { + s << (i == num_stringified_contexts ? "" : " ") + << stringified_contexts[i - 1] << "\n"; + } + } + s << "\n" << Color::None; + } + + void subcase_start(const SubcaseSignature& subc) override { + std::lock_guard lock(mutex); + subcasesStack.push_back(subc); + ++currentSubcaseLevel; + hasLoggedCurrentTestStart = false; + } + + void subcase_end() override { + std::lock_guard lock(mutex); + --currentSubcaseLevel; + hasLoggedCurrentTestStart = false; + } + + void log_assert(const AssertData& rb) override { + if(!rb.m_failed && !opt.success) + return; + + std::lock_guard lock(mutex); + + logTestStart(); + + file_line_to_stream(rb.m_file, rb.m_line, " "); + successOrFailColoredStringToStream(!rb.m_failed, rb.m_at); + + fulltext_log_assert_to_stream(s, rb); + + log_contexts(); + } + + void log_message(const MessageData& mb) override { + std::lock_guard lock(mutex); + + logTestStart(); + + file_line_to_stream(mb.m_file, mb.m_line, " "); + s << getSuccessOrFailColor(false, mb.m_severity) + << getSuccessOrFailString(mb.m_severity & assertType::is_warn, mb.m_severity, + "MESSAGE") << ": "; + s << Color::None << mb.m_string << "\n"; + log_contexts(); + } + + void test_case_skipped(const TestCaseData&) override {} + }; + + DOCTEST_REGISTER_REPORTER("console", 0, ConsoleReporter); + +#ifdef DOCTEST_PLATFORM_WINDOWS + struct DebugOutputWindowReporter : public ConsoleReporter + { + DOCTEST_THREAD_LOCAL static std::ostringstream oss; + + DebugOutputWindowReporter(const ContextOptions& co) + : ConsoleReporter(co, oss) {} + +#define DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(func, type, arg) \ + void func(type arg) override { \ + bool with_col = g_no_colors; \ + g_no_colors = false; \ + ConsoleReporter::func(arg); \ + DOCTEST_OUTPUT_DEBUG_STRING(oss.str().c_str()); \ + oss.str(""); \ + g_no_colors = with_col; \ + } + + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_run_start, DOCTEST_EMPTY, DOCTEST_EMPTY) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_run_end, const TestRunStats&, in) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_start, const TestCaseData&, in) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_reenter, const TestCaseData&, in) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_end, const CurrentTestCaseStats&, in) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_exception, const TestCaseException&, in) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(subcase_start, const SubcaseSignature&, in) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(subcase_end, DOCTEST_EMPTY, DOCTEST_EMPTY) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(log_assert, const AssertData&, in) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(log_message, const MessageData&, in) + DOCTEST_DEBUG_OUTPUT_REPORTER_OVERRIDE(test_case_skipped, const TestCaseData&, in) + }; + + DOCTEST_THREAD_LOCAL std::ostringstream DebugOutputWindowReporter::oss; +#endif // DOCTEST_PLATFORM_WINDOWS + + // the implementation of parseOption() + bool parseOptionImpl(int argc, const char* const* argv, const char* pattern, String* value) { + // going from the end to the beginning and stopping on the first occurrence from the end + for(int i = argc; i > 0; --i) { + auto index = i - 1; + auto temp = std::strstr(argv[index], pattern); + if(temp && (value || strlen(temp) == strlen(pattern))) { //!OCLINT prefer early exits and continue + // eliminate matches in which the chars before the option are not '-' + bool noBadCharsFound = true; + auto curr = argv[index]; + while(curr != temp) { + if(*curr++ != '-') { + noBadCharsFound = false; + break; + } + } + if(noBadCharsFound && argv[index][0] == '-') { + if(value) { + // parsing the value of an option + temp += strlen(pattern); + const unsigned len = strlen(temp); + if(len) { + *value = temp; + return true; + } + } else { + // just a flag - no value + return true; + } + } + } + } + return false; + } + + // parses an option and returns the string after the '=' character + bool parseOption(int argc, const char* const* argv, const char* pattern, String* value = nullptr, + const String& defaultVal = String()) { + if(value) + *value = defaultVal; +#ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS + // offset (normally 3 for "dt-") to skip prefix + if(parseOptionImpl(argc, argv, pattern + strlen(DOCTEST_CONFIG_OPTIONS_PREFIX), value)) + return true; +#endif // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS + return parseOptionImpl(argc, argv, pattern, value); + } + + // locates a flag on the command line + bool parseFlag(int argc, const char* const* argv, const char* pattern) { + return parseOption(argc, argv, pattern); + } + + // parses a comma separated list of words after a pattern in one of the arguments in argv + bool parseCommaSepArgs(int argc, const char* const* argv, const char* pattern, + std::vector& res) { + String filtersString; + if(parseOption(argc, argv, pattern, &filtersString)) { + // tokenize with "," as a separator + // cppcheck-suppress strtokCalled + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") + auto pch = std::strtok(filtersString.c_str(), ","); // modifies the string + while(pch != nullptr) { + if(strlen(pch)) + res.push_back(pch); + // uses the strtok() internal state to go to the next token + // cppcheck-suppress strtokCalled + pch = std::strtok(nullptr, ","); + } + DOCTEST_CLANG_SUPPRESS_WARNING_POP + return true; + } + return false; + } + + enum optionType + { + option_bool, + option_int + }; + + // parses an int/bool option from the command line + bool parseIntOption(int argc, const char* const* argv, const char* pattern, optionType type, + int& res) { + String parsedValue; + if(!parseOption(argc, argv, pattern, &parsedValue)) + return false; + + if(type == 0) { + // boolean + const char positive[][5] = {"1", "true", "on", "yes"}; // 5 - strlen("true") + 1 + const char negative[][6] = {"0", "false", "off", "no"}; // 6 - strlen("false") + 1 + + // if the value matches any of the positive/negative possibilities + for(unsigned i = 0; i < 4; i++) { + if(parsedValue.compare(positive[i], true) == 0) { + res = 1; //!OCLINT parameter reassignment + return true; + } + if(parsedValue.compare(negative[i], true) == 0) { + res = 0; //!OCLINT parameter reassignment + return true; + } + } + } else { + // integer + // TODO: change this to use std::stoi or something else! currently it uses undefined behavior - assumes '0' on failed parse... + int theInt = std::atoi(parsedValue.c_str()); // NOLINT + if(theInt != 0) { + res = theInt; //!OCLINT parameter reassignment + return true; + } + } + return false; + } +} // namespace + +Context::Context(int argc, const char* const* argv) + : p(new detail::ContextState) { + parseArgs(argc, argv, true); + if(argc) + p->binary_name = argv[0]; +} + +Context::~Context() { + if(g_cs == p) + g_cs = nullptr; + delete p; +} + +void Context::applyCommandLine(int argc, const char* const* argv) { + parseArgs(argc, argv); + if(argc) + p->binary_name = argv[0]; +} + +// parses args +void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) { + using namespace detail; + + // clang-format off + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "source-file=", p->filters[0]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sf=", p->filters[0]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "source-file-exclude=",p->filters[1]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sfe=", p->filters[1]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-suite=", p->filters[2]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "ts=", p->filters[2]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-suite-exclude=", p->filters[3]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tse=", p->filters[3]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-case=", p->filters[4]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tc=", p->filters[4]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "test-case-exclude=", p->filters[5]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "tce=", p->filters[5]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "subcase=", p->filters[6]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sc=", p->filters[6]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "subcase-exclude=", p->filters[7]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "sce=", p->filters[7]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "reporters=", p->filters[8]); + parseCommaSepArgs(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "r=", p->filters[8]); + // clang-format on + + int intRes = 0; + String strRes; + +#define DOCTEST_PARSE_AS_BOOL_OR_FLAG(name, sname, var, default) \ + if(parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", option_bool, intRes) || \ + parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", option_bool, intRes)) \ + p->var = !!intRes; \ + else if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name) || \ + parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname)) \ + p->var = true; \ + else if(withDefaults) \ + p->var = default + +#define DOCTEST_PARSE_INT_OPTION(name, sname, var, default) \ + if(parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", option_int, intRes) || \ + parseIntOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", option_int, intRes)) \ + p->var = intRes; \ + else if(withDefaults) \ + p->var = default + +#define DOCTEST_PARSE_STR_OPTION(name, sname, var, default) \ + if(parseOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX name "=", &strRes, default) || \ + parseOption(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX sname "=", &strRes, default) || \ + withDefaults) \ + p->var = strRes + + // clang-format off + DOCTEST_PARSE_STR_OPTION("out", "o", out, ""); + DOCTEST_PARSE_STR_OPTION("order-by", "ob", order_by, "file"); + DOCTEST_PARSE_INT_OPTION("rand-seed", "rs", rand_seed, 0); + + DOCTEST_PARSE_INT_OPTION("first", "f", first, 0); + DOCTEST_PARSE_INT_OPTION("last", "l", last, UINT_MAX); + + DOCTEST_PARSE_INT_OPTION("abort-after", "aa", abort_after, 0); + DOCTEST_PARSE_INT_OPTION("subcase-filter-levels", "scfl", subcase_filter_levels, INT_MAX); + + DOCTEST_PARSE_AS_BOOL_OR_FLAG("success", "s", success, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("case-sensitive", "cs", case_sensitive, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("exit", "e", exit, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("duration", "d", duration, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-throw", "nt", no_throw, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-exitcode", "ne", no_exitcode, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-run", "nr", no_run, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-version", "nv", no_version, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-colors", "nc", no_colors, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("force-colors", "fc", force_colors, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-breaks", "nb", no_breaks, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-skip", "ns", no_skip, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("gnu-file-line", "gfl", gnu_file_line, !bool(DOCTEST_MSVC)); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-path-filenames", "npf", no_path_in_filenames, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-line-numbers", "nln", no_line_numbers, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-skipped-summary", "nss", no_skipped_summary, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-time-in-output", "ntio", no_time_in_output, false); + // clang-format on + + if(withDefaults) { + p->help = false; + p->version = false; + p->count = false; + p->list_test_cases = false; + p->list_test_suites = false; + p->list_reporters = false; + } + if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "help") || + parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "h") || + parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "?")) { + p->help = true; + p->exit = true; + } + if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "version") || + parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "v")) { + p->version = true; + p->exit = true; + } + if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "count") || + parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "c")) { + p->count = true; + p->exit = true; + } + if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-test-cases") || + parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "ltc")) { + p->list_test_cases = true; + p->exit = true; + } + if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-test-suites") || + parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "lts")) { + p->list_test_suites = true; + p->exit = true; + } + if(parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "list-reporters") || + parseFlag(argc, argv, DOCTEST_CONFIG_OPTIONS_PREFIX "lr")) { + p->list_reporters = true; + p->exit = true; + } +} + +// allows the user to add procedurally to the filters from the command line +void Context::addFilter(const char* filter, const char* value) { setOption(filter, value); } + +// allows the user to clear all filters from the command line +void Context::clearFilters() { + for(auto& curr : p->filters) + curr.clear(); +} + +// allows the user to override procedurally the int/bool options from the command line +void Context::setOption(const char* option, int value) { + setOption(option, toString(value).c_str()); +} + +// allows the user to override procedurally the string options from the command line +void Context::setOption(const char* option, const char* value) { + auto argv = String("-") + option + "=" + value; + auto lvalue = argv.c_str(); + parseArgs(1, &lvalue); +} + +// users should query this in their main() and exit the program if true +bool Context::shouldExit() { return p->exit; } + +void Context::setAsDefaultForAssertsOutOfTestCases() { g_cs = p; } + +void Context::setAssertHandler(detail::assert_handler ah) { p->ah = ah; } + +// the main function that does all the filtering and test running +int Context::run() { + using namespace detail; + + // save the old context state in case such was setup - for using asserts out of a testing context + auto old_cs = g_cs; + // this is the current contest + g_cs = p; + is_running_in_test = true; + + g_no_colors = p->no_colors; + p->resetRunData(); + + // stdout by default + p->cout = &std::cout; + p->cerr = &std::cerr; + + // or to a file if specified + std::fstream fstr; + if(p->out.size()) { + fstr.open(p->out.c_str(), std::fstream::out); + p->cout = &fstr; + } + + auto cleanup_and_return = [&]() { + if(fstr.is_open()) + fstr.close(); + + // restore context + g_cs = old_cs; + is_running_in_test = false; + + // we have to free the reporters which were allocated when the run started + for(auto& curr : p->reporters_currently_used) + delete curr; + p->reporters_currently_used.clear(); + + if(p->numTestCasesFailed && !p->no_exitcode) + return EXIT_FAILURE; + return EXIT_SUCCESS; + }; + + // setup default reporter if none is given through the command line + if(p->filters[8].empty()) + p->filters[8].push_back("console"); + + // check to see if any of the registered reporters has been selected + for(auto& curr : getReporters()) { + if(matchesAny(curr.first.second.c_str(), p->filters[8], false, p->case_sensitive)) + p->reporters_currently_used.push_back(curr.second(*g_cs)); + } + + // TODO: check if there is nothing in reporters_currently_used + + // prepend all listeners + for(auto& curr : getListeners()) + p->reporters_currently_used.insert(p->reporters_currently_used.begin(), curr.second(*g_cs)); + +#ifdef DOCTEST_PLATFORM_WINDOWS + if(isDebuggerActive()) + p->reporters_currently_used.push_back(new DebugOutputWindowReporter(*g_cs)); +#endif // DOCTEST_PLATFORM_WINDOWS + + // handle version, help and no_run + if(p->no_run || p->version || p->help || p->list_reporters) { + DOCTEST_ITERATE_THROUGH_REPORTERS(report_query, QueryData()); + + return cleanup_and_return(); + } + + std::vector testArray; + for(auto& curr : getRegisteredTests()) + testArray.push_back(&curr); + p->numTestCases = testArray.size(); + + // sort the collected records + if(!testArray.empty()) { + if(p->order_by.compare("file", true) == 0) { + std::sort(testArray.begin(), testArray.end(), fileOrderComparator); + } else if(p->order_by.compare("suite", true) == 0) { + std::sort(testArray.begin(), testArray.end(), suiteOrderComparator); + } else if(p->order_by.compare("name", true) == 0) { + std::sort(testArray.begin(), testArray.end(), nameOrderComparator); + } else if(p->order_by.compare("rand", true) == 0) { + std::srand(p->rand_seed); + + // random_shuffle implementation + const auto first = &testArray[0]; + for(size_t i = testArray.size() - 1; i > 0; --i) { + int idxToSwap = std::rand() % (i + 1); // NOLINT + + const auto temp = first[i]; + + first[i] = first[idxToSwap]; + first[idxToSwap] = temp; + } + } + } + + std::set testSuitesPassingFilt; + + bool query_mode = p->count || p->list_test_cases || p->list_test_suites; + std::vector queryResults; + + if(!query_mode) + DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_start, DOCTEST_EMPTY); + + // invoke the registered functions if they match the filter criteria (or just count them) + for(auto& curr : testArray) { + const auto& tc = *curr; + + bool skip_me = false; + if(tc.m_skip && !p->no_skip) + skip_me = true; + + if(!matchesAny(tc.m_file.c_str(), p->filters[0], true, p->case_sensitive)) + skip_me = true; + if(matchesAny(tc.m_file.c_str(), p->filters[1], false, p->case_sensitive)) + skip_me = true; + if(!matchesAny(tc.m_test_suite, p->filters[2], true, p->case_sensitive)) + skip_me = true; + if(matchesAny(tc.m_test_suite, p->filters[3], false, p->case_sensitive)) + skip_me = true; + if(!matchesAny(tc.m_name, p->filters[4], true, p->case_sensitive)) + skip_me = true; + if(matchesAny(tc.m_name, p->filters[5], false, p->case_sensitive)) + skip_me = true; + + if(!skip_me) + p->numTestCasesPassingFilters++; + + // skip the test if it is not in the execution range + if((p->last < p->numTestCasesPassingFilters && p->first <= p->last) || + (p->first > p->numTestCasesPassingFilters)) + skip_me = true; + + if(skip_me) { + if(!query_mode) + DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_skipped, tc); + continue; + } + + // do not execute the test if we are to only count the number of filter passing tests + if(p->count) + continue; + + // print the name of the test and don't execute it + if(p->list_test_cases) { + queryResults.push_back(&tc); + continue; + } + + // print the name of the test suite if not done already and don't execute it + if(p->list_test_suites) { + if((testSuitesPassingFilt.count(tc.m_test_suite) == 0) && tc.m_test_suite[0] != '\0') { + queryResults.push_back(&tc); + testSuitesPassingFilt.insert(tc.m_test_suite); + p->numTestSuitesPassingFilters++; + } + continue; + } + + // execute the test if it passes all the filtering + { + p->currentTest = &tc; + + p->failure_flags = TestCaseFailureReason::None; + p->seconds = 0; + + // reset atomic counters + p->numAssertsFailedCurrentTest_atomic = 0; + p->numAssertsCurrentTest_atomic = 0; + + p->subcasesPassed.clear(); + + DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_start, tc); + + p->timer.start(); + + bool run_test = true; + + do { + // reset some of the fields for subcases (except for the set of fully passed ones) + p->should_reenter = false; + p->subcasesCurrentMaxLevel = 0; + p->subcasesStack.clear(); + + p->shouldLogCurrentException = true; + + // reset stuff for logging with INFO() + p->stringifiedContexts.clear(); + +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + try { +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + FatalConditionHandler fatalConditionHandler; // Handle signals + // execute the test + tc.m_test(); + fatalConditionHandler.reset(); +#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS + } catch(const TestFailureException&) { + p->failure_flags |= TestCaseFailureReason::AssertFailure; + } catch(...) { + DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_exception, + {translateActiveException(), false}); + p->failure_flags |= TestCaseFailureReason::Exception; + } +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + + // exit this loop if enough assertions have failed - even if there are more subcases + if(p->abort_after > 0 && + p->numAssertsFailed + p->numAssertsFailedCurrentTest_atomic >= p->abort_after) { + run_test = false; + p->failure_flags |= TestCaseFailureReason::TooManyFailedAsserts; + } + + if(p->should_reenter && run_test) + DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_reenter, tc); + if(!p->should_reenter) + run_test = false; + } while(run_test); + + p->finalizeTestCaseData(); + + DOCTEST_ITERATE_THROUGH_REPORTERS(test_case_end, *g_cs); + + p->currentTest = nullptr; + + // stop executing tests if enough assertions have failed + if(p->abort_after > 0 && p->numAssertsFailed >= p->abort_after) + break; + } + } + + if(!query_mode) { + DOCTEST_ITERATE_THROUGH_REPORTERS(test_run_end, *g_cs); + } else { + QueryData qdata; + qdata.run_stats = g_cs; + qdata.data = queryResults.data(); + qdata.num_data = unsigned(queryResults.size()); + DOCTEST_ITERATE_THROUGH_REPORTERS(report_query, qdata); + } + + // see these issues on the reasoning for this: + // - https://github.com/onqtam/doctest/issues/143#issuecomment-414418903 + // - https://github.com/onqtam/doctest/issues/126 + auto DOCTEST_FIX_FOR_MACOS_LIBCPP_IOSFWD_STRING_LINK_ERRORS = []() DOCTEST_NOINLINE + { std::cout << std::string(); }; + DOCTEST_FIX_FOR_MACOS_LIBCPP_IOSFWD_STRING_LINK_ERRORS(); + + return cleanup_and_return(); +} + +IReporter::~IReporter() = default; + +int IReporter::get_num_active_contexts() { return detail::g_infoContexts.size(); } +const IContextScope* const* IReporter::get_active_contexts() { + return get_num_active_contexts() ? &detail::g_infoContexts[0] : nullptr; +} + +int IReporter::get_num_stringified_contexts() { return detail::g_cs->stringifiedContexts.size(); } +const String* IReporter::get_stringified_contexts() { + return get_num_stringified_contexts() ? &detail::g_cs->stringifiedContexts[0] : nullptr; +} + +namespace detail { + void registerReporterImpl(const char* name, int priority, reporterCreatorFunc c, bool isReporter) { + if(isReporter) + getReporters().insert(reporterMap::value_type(reporterMap::key_type(priority, name), c)); + else + getListeners().insert(reporterMap::value_type(reporterMap::key_type(priority, name), c)); + } +} // namespace detail + +} // namespace doctest + +#endif // DOCTEST_CONFIG_DISABLE + +#ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007) // 'function' : must be 'attribute' - see issue #182 +int main(int argc, char** argv) { return doctest::Context(argc, argv).run(); } +DOCTEST_MSVC_SUPPRESS_WARNING_POP +#endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN + +DOCTEST_CLANG_SUPPRESS_WARNING_POP +DOCTEST_MSVC_SUPPRESS_WARNING_POP +DOCTEST_GCC_SUPPRESS_WARNING_POP + +#endif // DOCTEST_LIBRARY_IMPLEMENTATION +#endif // DOCTEST_CONFIG_IMPLEMENT diff --git a/src/third_party/fmt/core.h b/src/third_party/fmt/core.h new file mode 100644 index 0000000..031bf86 --- /dev/null +++ b/src/third_party/fmt/core.h @@ -0,0 +1,1882 @@ +// Formatting library for C++ - the core API +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_CORE_H_ +#define FMT_CORE_H_ + +#include // std::FILE +#include +#include +#include +#include +#include +#include +#include + +// The fmt library version in the form major * 10000 + minor * 100 + patch. +#define FMT_VERSION 70003 + +#ifdef __clang__ +# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) +#else +# define FMT_CLANG_VERSION 0 +#endif + +#if defined(__GNUC__) && !defined(__clang__) +# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#else +# define FMT_GCC_VERSION 0 +#endif + +#if defined(__INTEL_COMPILER) +# define FMT_ICC_VERSION __INTEL_COMPILER +#else +# define FMT_ICC_VERSION 0 +#endif + +#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION +#else +# define FMT_HAS_GXX_CXX11 0 +#endif + +#ifdef __NVCC__ +# define FMT_NVCC __NVCC__ +#else +# define FMT_NVCC 0 +#endif + +#ifdef _MSC_VER +# define FMT_MSC_VER _MSC_VER +# define FMT_SUPPRESS_MSC_WARNING(n) __pragma(warning(suppress : n)) +#else +# define FMT_MSC_VER 0 +# define FMT_SUPPRESS_MSC_WARNING(n) +#endif +#ifdef __has_feature +# define FMT_HAS_FEATURE(x) __has_feature(x) +#else +# define FMT_HAS_FEATURE(x) 0 +#endif + +#if defined(__has_include) && !defined(__INTELLISENSE__) && \ + !(FMT_ICC_VERSION && FMT_ICC_VERSION < 1600) +# define FMT_HAS_INCLUDE(x) __has_include(x) +#else +# define FMT_HAS_INCLUDE(x) 0 +#endif + +#ifdef __has_cpp_attribute +# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define FMT_HAS_CPP_ATTRIBUTE(x) 0 +#endif + +#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ + (__cplusplus >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ + (__cplusplus >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +// Check if relaxed C++14 constexpr is supported. +// GCC doesn't allow throw in constexpr until version 6 (bug 67371). +#ifndef FMT_USE_CONSTEXPR +# define FMT_USE_CONSTEXPR \ + (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \ + (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \ + !FMT_NVCC && !FMT_ICC_VERSION +#endif +#if FMT_USE_CONSTEXPR +# define FMT_CONSTEXPR constexpr +# define FMT_CONSTEXPR_DECL constexpr +#else +# define FMT_CONSTEXPR inline +# define FMT_CONSTEXPR_DECL +#endif + +#ifndef FMT_OVERRIDE +# if FMT_HAS_FEATURE(cxx_override_control) || \ + (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 +# define FMT_OVERRIDE override +# else +# define FMT_OVERRIDE +# endif +#endif + +// Check if exceptions are disabled. +#ifndef FMT_EXCEPTIONS +# if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ + FMT_MSC_VER && !_HAS_EXCEPTIONS +# define FMT_EXCEPTIONS 0 +# else +# define FMT_EXCEPTIONS 1 +# endif +#endif + +// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). +#ifndef FMT_USE_NOEXCEPT +# define FMT_USE_NOEXCEPT 0 +#endif + +#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ + (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 +# define FMT_DETECTED_NOEXCEPT noexcept +# define FMT_HAS_CXX11_NOEXCEPT 1 +#else +# define FMT_DETECTED_NOEXCEPT throw() +# define FMT_HAS_CXX11_NOEXCEPT 0 +#endif + +#ifndef FMT_NOEXCEPT +# if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT +# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT +# else +# define FMT_NOEXCEPT +# endif +#endif + +// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code +// warnings. +#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \ + !FMT_NVCC +# define FMT_NORETURN [[noreturn]] +#else +# define FMT_NORETURN +#endif + +#ifndef FMT_DEPRECATED +# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900 +# define FMT_DEPRECATED [[deprecated]] +# else +# if defined(__GNUC__) || defined(__clang__) +# define FMT_DEPRECATED __attribute__((deprecated)) +# elif FMT_MSC_VER +# define FMT_DEPRECATED __declspec(deprecated) +# else +# define FMT_DEPRECATED /* deprecated */ +# endif +# endif +#endif + +// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers. +#if FMT_ICC_VERSION || defined(__PGI) || FMT_NVCC +# define FMT_DEPRECATED_ALIAS +#else +# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED +#endif + +#ifndef FMT_INLINE +# if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_INLINE inline __attribute__((always_inline)) +# else +# define FMT_INLINE inline +# endif +#endif + +#ifndef FMT_BEGIN_NAMESPACE +# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \ + FMT_MSC_VER >= 1900 +# define FMT_INLINE_NAMESPACE inline namespace +# define FMT_END_NAMESPACE \ + } \ + } +# else +# define FMT_INLINE_NAMESPACE namespace +# define FMT_END_NAMESPACE \ + } \ + using namespace v7; \ + } +# endif +# define FMT_BEGIN_NAMESPACE \ + namespace fmt { \ + FMT_INLINE_NAMESPACE v7 { +#endif + +#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) +# define FMT_CLASS_API FMT_SUPPRESS_MSC_WARNING(4275) +# ifdef FMT_EXPORT +# define FMT_API __declspec(dllexport) +# define FMT_EXTERN_TEMPLATE_API FMT_API +# define FMT_EXPORTED +# elif defined(FMT_SHARED) +# define FMT_API __declspec(dllimport) +# define FMT_EXTERN_TEMPLATE_API FMT_API +# endif +#else +# define FMT_CLASS_API +#endif +#ifndef FMT_API +# define FMT_API +#endif +#ifndef FMT_EXTERN_TEMPLATE_API +# define FMT_EXTERN_TEMPLATE_API +#endif +#ifndef FMT_INSTANTIATION_DEF_API +# define FMT_INSTANTIATION_DEF_API FMT_API +#endif + +#ifndef FMT_HEADER_ONLY +# define FMT_EXTERN extern +#else +# define FMT_EXTERN +#endif + +// libc++ supports string_view in pre-c++17. +#if (FMT_HAS_INCLUDE() && \ + (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \ + (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) +# include +# define FMT_USE_STRING_VIEW +#elif FMT_HAS_INCLUDE("experimental/string_view") && __cplusplus >= 201402L +# include +# define FMT_USE_EXPERIMENTAL_STRING_VIEW +#endif + +#ifndef FMT_UNICODE +# define FMT_UNICODE !FMT_MSC_VER +#endif +#if FMT_UNICODE && FMT_MSC_VER +# pragma execution_character_set("utf-8") +#endif + +FMT_BEGIN_NAMESPACE + +// Implementations of enable_if_t and other metafunctions for older systems. +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; +template using bool_constant = std::integral_constant; +template +using remove_reference_t = typename std::remove_reference::type; +template +using remove_const_t = typename std::remove_const::type; +template +using remove_cvref_t = typename std::remove_cv>::type; +template struct type_identity { using type = T; }; +template using type_identity_t = typename type_identity::type; + +struct monostate {}; + +// An enable_if helper to be used in template parameters which results in much +// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed +// to workaround a bug in MSVC 2019 (see #1140 and #1186). +#define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0 + +namespace detail { + +// A helper function to suppress bogus "conditional expression is constant" +// warnings. +template constexpr T const_check(T value) { return value; } + +FMT_NORETURN FMT_API void assert_fail(const char* file, int line, + const char* message); + +#ifndef FMT_ASSERT +# ifdef NDEBUG +// FMT_ASSERT is not empty to avoid -Werror=empty-body. +# define FMT_ASSERT(condition, message) ((void)0) +# else +# define FMT_ASSERT(condition, message) \ + ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ + ? (void)0 \ + : ::fmt::detail::assert_fail(__FILE__, __LINE__, (message))) +# endif +#endif + +#if defined(FMT_USE_STRING_VIEW) +template using std_string_view = std::basic_string_view; +#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) +template +using std_string_view = std::experimental::basic_string_view; +#else +template struct std_string_view {}; +#endif + +#ifdef FMT_USE_INT128 +// Do nothing. +#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && !(FMT_CLANG_VERSION && FMT_MSC_VER) +# define FMT_USE_INT128 1 +using int128_t = __int128_t; +using uint128_t = __uint128_t; +#else +# define FMT_USE_INT128 0 +#endif +#if !FMT_USE_INT128 +struct int128_t {}; +struct uint128_t {}; +#endif + +// Casts a nonnegative integer to unsigned. +template +FMT_CONSTEXPR typename std::make_unsigned::type to_unsigned(Int value) { + FMT_ASSERT(value >= 0, "negative value"); + return static_cast::type>(value); +} + +FMT_SUPPRESS_MSC_WARNING(4566) constexpr unsigned char micro[] = "\u00B5"; + +template constexpr bool is_unicode() { + return FMT_UNICODE || sizeof(Char) != 1 || + (sizeof(micro) == 3 && micro[0] == 0xC2 && micro[1] == 0xB5); +} + +#ifdef __cpp_char8_t +using char8_type = char8_t; +#else +enum char8_type : unsigned char {}; +#endif +} // namespace detail + +#ifdef FMT_USE_INTERNAL +namespace internal = detail; // DEPRECATED +#endif + +/** + An implementation of ``std::basic_string_view`` for pre-C++17. It provides a + subset of the API. ``fmt::basic_string_view`` is used for format strings even + if ``std::string_view`` is available to prevent issues when a library is + compiled with a different ``-std`` option than the client code (which is not + recommended). + */ +template class basic_string_view { + private: + const Char* data_; + size_t size_; + + public: + using value_type = Char; + using iterator = const Char*; + + constexpr basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {} + + /** Constructs a string reference object from a C string and a size. */ + constexpr basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT + : data_(s), + size_(count) {} + + /** + \rst + Constructs a string reference object from a C string computing + the size with ``std::char_traits::length``. + \endrst + */ +#if __cplusplus >= 201703L // C++17's char_traits::length() is constexpr. + FMT_CONSTEXPR +#endif + basic_string_view(const Char* s) + : data_(s), size_(std::char_traits::length(s)) {} + + /** Constructs a string reference from a ``std::basic_string`` object. */ + template + FMT_CONSTEXPR basic_string_view( + const std::basic_string& s) FMT_NOEXCEPT + : data_(s.data()), + size_(s.size()) {} + + template >::value)> + FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()), + size_(s.size()) {} + + /** Returns a pointer to the string data. */ + constexpr const Char* data() const { return data_; } + + /** Returns the string size. */ + constexpr size_t size() const { return size_; } + + constexpr iterator begin() const { return data_; } + constexpr iterator end() const { return data_ + size_; } + + constexpr const Char& operator[](size_t pos) const { return data_[pos]; } + + FMT_CONSTEXPR void remove_prefix(size_t n) { + data_ += n; + size_ -= n; + } + + // Lexicographically compare this string reference to other. + int compare(basic_string_view other) const { + size_t str_size = size_ < other.size_ ? size_ : other.size_; + int result = std::char_traits::compare(data_, other.data_, str_size); + if (result == 0) + result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); + return result; + } + + friend bool operator==(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) == 0; + } + friend bool operator!=(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) != 0; + } + friend bool operator<(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) < 0; + } + friend bool operator<=(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) <= 0; + } + friend bool operator>(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) > 0; + } + friend bool operator>=(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) >= 0; + } +}; + +using string_view = basic_string_view; +using wstring_view = basic_string_view; + +/** Specifies if ``T`` is a character type. Can be specialized by users. */ +template struct is_char : std::false_type {}; +template <> struct is_char : std::true_type {}; +template <> struct is_char : std::true_type {}; +template <> struct is_char : std::true_type {}; +template <> struct is_char : std::true_type {}; +template <> struct is_char : std::true_type {}; + +/** + \rst + Returns a string view of `s`. In order to add custom string type support to + {fmt} provide an overload of `to_string_view` for it in the same namespace as + the type for the argument-dependent lookup to work. + + **Example**:: + + namespace my_ns { + inline string_view to_string_view(const my_string& s) { + return {s.data(), s.length()}; + } + } + std::string message = fmt::format(my_string("The answer is {}"), 42); + \endrst + */ +template ::value)> +inline basic_string_view to_string_view(const Char* s) { + return s; +} + +template +inline basic_string_view to_string_view( + const std::basic_string& s) { + return s; +} + +template +inline basic_string_view to_string_view(basic_string_view s) { + return s; +} + +template >::value)> +inline basic_string_view to_string_view(detail::std_string_view s) { + return s; +} + +// A base class for compile-time strings. It is defined in the fmt namespace to +// make formatting functions visible via ADL, e.g. format(FMT_STRING("{}"), 42). +struct compile_string {}; + +template +struct is_compile_string : std::is_base_of {}; + +template ::value)> +constexpr basic_string_view to_string_view(const S& s) { + return s; +} + +namespace detail { +void to_string_view(...); +using fmt::v7::to_string_view; + +// Specifies whether S is a string type convertible to fmt::basic_string_view. +// It should be a constexpr function but MSVC 2017 fails to compile it in +// enable_if and MSVC 2015 fails to compile it as an alias template. +template +struct is_string : std::is_class()))> { +}; + +template struct char_t_impl {}; +template struct char_t_impl::value>> { + using result = decltype(to_string_view(std::declval())); + using type = typename result::value_type; +}; + +struct error_handler { + constexpr error_handler() = default; + constexpr error_handler(const error_handler&) = default; + + // This function is intentionally not constexpr to give a compile-time error. + FMT_NORETURN FMT_API void on_error(const char* message); +}; +} // namespace detail + +/** String's character type. */ +template using char_t = typename detail::char_t_impl::type; + +/** + \rst + Parsing context consisting of a format string range being parsed and an + argument counter for automatic indexing. + + You can use one of the following type aliases for common character types: + + +-----------------------+-------------------------------------+ + | Type | Definition | + +=======================+=====================================+ + | format_parse_context | basic_format_parse_context | + +-----------------------+-------------------------------------+ + | wformat_parse_context | basic_format_parse_context | + +-----------------------+-------------------------------------+ + \endrst + */ +template +class basic_format_parse_context : private ErrorHandler { + private: + basic_string_view format_str_; + int next_arg_id_; + + public: + using char_type = Char; + using iterator = typename basic_string_view::iterator; + + explicit constexpr basic_format_parse_context( + basic_string_view format_str, ErrorHandler eh = {}) + : ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {} + + /** + Returns an iterator to the beginning of the format string range being + parsed. + */ + constexpr iterator begin() const FMT_NOEXCEPT { return format_str_.begin(); } + + /** + Returns an iterator past the end of the format string range being parsed. + */ + constexpr iterator end() const FMT_NOEXCEPT { return format_str_.end(); } + + /** Advances the begin iterator to ``it``. */ + FMT_CONSTEXPR void advance_to(iterator it) { + format_str_.remove_prefix(detail::to_unsigned(it - begin())); + } + + /** + Reports an error if using the manual argument indexing; otherwise returns + the next argument index and switches to the automatic indexing. + */ + FMT_CONSTEXPR int next_arg_id() { + // Don't check if the argument id is valid to avoid overhead and because it + // will be checked during formatting anyway. + if (next_arg_id_ >= 0) return next_arg_id_++; + on_error("cannot switch from manual to automatic argument indexing"); + return 0; + } + + /** + Reports an error if using the automatic argument indexing; otherwise + switches to the manual indexing. + */ + FMT_CONSTEXPR void check_arg_id(int) { + if (next_arg_id_ > 0) + on_error("cannot switch from automatic to manual argument indexing"); + else + next_arg_id_ = -1; + } + + FMT_CONSTEXPR void check_arg_id(basic_string_view) {} + + FMT_CONSTEXPR void on_error(const char* message) { + ErrorHandler::on_error(message); + } + + constexpr ErrorHandler error_handler() const { return *this; } +}; + +using format_parse_context = basic_format_parse_context; +using wformat_parse_context = basic_format_parse_context; + +template class basic_format_arg; +template class basic_format_args; +template class dynamic_format_arg_store; + +// A formatter for objects of type T. +template +struct formatter { + // A deleted default constructor indicates a disabled formatter. + formatter() = delete; +}; + +// Specifies if T has an enabled formatter specialization. A type can be +// formattable even if it doesn't have a formatter e.g. via a conversion. +template +using has_formatter = + std::is_constructible>; + +namespace detail { + +/** + \rst + A contiguous memory buffer with an optional growing ability. It is an internal + class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`. + \endrst + */ +template class buffer { + private: + T* ptr_; + size_t size_; + size_t capacity_; + + protected: + // Don't initialize ptr_ since it is not accessed to save a few cycles. + FMT_SUPPRESS_MSC_WARNING(26495) + buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {} + + buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) FMT_NOEXCEPT + : ptr_(p), + size_(sz), + capacity_(cap) {} + + /** Sets the buffer data and capacity. */ + void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT { + ptr_ = buf_data; + capacity_ = buf_capacity; + } + + /** Increases the buffer capacity to hold at least *capacity* elements. */ + virtual void grow(size_t capacity) = 0; + + public: + using value_type = T; + using const_reference = const T&; + + buffer(const buffer&) = delete; + void operator=(const buffer&) = delete; + virtual ~buffer() = default; + + T* begin() FMT_NOEXCEPT { return ptr_; } + T* end() FMT_NOEXCEPT { return ptr_ + size_; } + + const T* begin() const FMT_NOEXCEPT { return ptr_; } + const T* end() const FMT_NOEXCEPT { return ptr_ + size_; } + + /** Returns the size of this buffer. */ + size_t size() const FMT_NOEXCEPT { return size_; } + + /** Returns the capacity of this buffer. */ + size_t capacity() const FMT_NOEXCEPT { return capacity_; } + + /** Returns a pointer to the buffer data. */ + T* data() FMT_NOEXCEPT { return ptr_; } + + /** Returns a pointer to the buffer data. */ + const T* data() const FMT_NOEXCEPT { return ptr_; } + + /** + Resizes the buffer. If T is a POD type new elements may not be initialized. + */ + void resize(size_t new_size) { + reserve(new_size); + size_ = new_size; + } + + /** Clears this buffer. */ + void clear() { size_ = 0; } + + /** Reserves space to store at least *capacity* elements. */ + void reserve(size_t new_capacity) { + if (new_capacity > capacity_) grow(new_capacity); + } + + void push_back(const T& value) { + reserve(size_ + 1); + ptr_[size_++] = value; + } + + /** Appends data to the end of the buffer. */ + template void append(const U* begin, const U* end); + + template T& operator[](I index) { return ptr_[index]; } + template const T& operator[](I index) const { + return ptr_[index]; + } +}; + +// A container-backed buffer. +template +class container_buffer : public buffer { + private: + Container& container_; + + protected: + void grow(size_t capacity) FMT_OVERRIDE { + container_.resize(capacity); + this->set(&container_[0], capacity); + } + + public: + explicit container_buffer(Container& c) + : buffer(c.size()), container_(c) {} +}; + +// Extracts a reference to the container from back_insert_iterator. +template +inline Container& get_container(std::back_insert_iterator it) { + using bi_iterator = std::back_insert_iterator; + struct accessor : bi_iterator { + accessor(bi_iterator iter) : bi_iterator(iter) {} + using bi_iterator::container; + }; + return *accessor(it).container; +} + +template +struct fallback_formatter { + fallback_formatter() = delete; +}; + +// Specifies if T has an enabled fallback_formatter specialization. +template +using has_fallback_formatter = + std::is_constructible>; + +struct view {}; + +template struct named_arg : view { + const Char* name; + const T& value; + named_arg(const Char* n, const T& v) : name(n), value(v) {} +}; + +template struct named_arg_info { + const Char* name; + int id; +}; + +template +struct arg_data { + // args_[0].named_args points to named_args_ to avoid bloating format_args. + T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : 1)]; + named_arg_info named_args_[NUM_NAMED_ARGS]; + + template + arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {} + arg_data(const arg_data& other) = delete; + const T* args() const { return args_ + 1; } + named_arg_info* named_args() { return named_args_; } +}; + +template +struct arg_data { + T args_[NUM_ARGS != 0 ? NUM_ARGS : 1]; + + template + FMT_INLINE arg_data(const U&... init) : args_{init...} {} + FMT_INLINE const T* args() const { return args_; } + FMT_INLINE std::nullptr_t named_args() { return nullptr; } +}; + +template +inline void init_named_args(named_arg_info*, int, int) {} + +template +void init_named_args(named_arg_info* named_args, int arg_count, + int named_arg_count, const T&, const Tail&... args) { + init_named_args(named_args, arg_count + 1, named_arg_count, args...); +} + +template +void init_named_args(named_arg_info* named_args, int arg_count, + int named_arg_count, const named_arg& arg, + const Tail&... args) { + named_args[named_arg_count++] = {arg.name, arg_count}; + init_named_args(named_args, arg_count + 1, named_arg_count, args...); +} + +template +FMT_INLINE void init_named_args(std::nullptr_t, int, int, const Args&...) {} + +template struct is_named_arg : std::false_type {}; + +template +struct is_named_arg> : std::true_type {}; + +template constexpr size_t count() { return B ? 1 : 0; } +template constexpr size_t count() { + return (B1 ? 1 : 0) + count(); +} + +template constexpr size_t count_named_args() { + return count::value...>(); +} + +enum class type { + none_type, + // Integer types should go first, + int_type, + uint_type, + long_long_type, + ulong_long_type, + int128_type, + uint128_type, + bool_type, + char_type, + last_integer_type = char_type, + // followed by floating-point types. + float_type, + double_type, + long_double_type, + last_numeric_type = long_double_type, + cstring_type, + string_type, + pointer_type, + custom_type +}; + +// Maps core type T to the corresponding type enum constant. +template +struct type_constant : std::integral_constant {}; + +#define FMT_TYPE_CONSTANT(Type, constant) \ + template \ + struct type_constant \ + : std::integral_constant {} + +FMT_TYPE_CONSTANT(int, int_type); +FMT_TYPE_CONSTANT(unsigned, uint_type); +FMT_TYPE_CONSTANT(long long, long_long_type); +FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); +FMT_TYPE_CONSTANT(int128_t, int128_type); +FMT_TYPE_CONSTANT(uint128_t, uint128_type); +FMT_TYPE_CONSTANT(bool, bool_type); +FMT_TYPE_CONSTANT(Char, char_type); +FMT_TYPE_CONSTANT(float, float_type); +FMT_TYPE_CONSTANT(double, double_type); +FMT_TYPE_CONSTANT(long double, long_double_type); +FMT_TYPE_CONSTANT(const Char*, cstring_type); +FMT_TYPE_CONSTANT(basic_string_view, string_type); +FMT_TYPE_CONSTANT(const void*, pointer_type); + +constexpr bool is_integral_type(type t) { + return t > type::none_type && t <= type::last_integer_type; +} + +constexpr bool is_arithmetic_type(type t) { + return t > type::none_type && t <= type::last_numeric_type; +} + +template struct string_value { + const Char* data; + size_t size; +}; + +template struct named_arg_value { + const named_arg_info* data; + size_t size; +}; + +template struct custom_value { + using parse_context = typename Context::parse_context_type; + const void* value; + void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx); +}; + +// A formatting argument value. +template class value { + public: + using char_type = typename Context::char_type; + + union { + int int_value; + unsigned uint_value; + long long long_long_value; + unsigned long long ulong_long_value; + int128_t int128_value; + uint128_t uint128_value; + bool bool_value; + char_type char_value; + float float_value; + double double_value; + long double long_double_value; + const void* pointer; + string_value string; + custom_value custom; + named_arg_value named_args; + }; + + constexpr FMT_INLINE value(int val = 0) : int_value(val) {} + constexpr FMT_INLINE value(unsigned val) : uint_value(val) {} + FMT_INLINE value(long long val) : long_long_value(val) {} + FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {} + FMT_INLINE value(int128_t val) : int128_value(val) {} + FMT_INLINE value(uint128_t val) : uint128_value(val) {} + FMT_INLINE value(float val) : float_value(val) {} + FMT_INLINE value(double val) : double_value(val) {} + FMT_INLINE value(long double val) : long_double_value(val) {} + FMT_INLINE value(bool val) : bool_value(val) {} + FMT_INLINE value(char_type val) : char_value(val) {} + FMT_INLINE value(const char_type* val) { string.data = val; } + FMT_INLINE value(basic_string_view val) { + string.data = val.data(); + string.size = val.size(); + } + FMT_INLINE value(const void* val) : pointer(val) {} + FMT_INLINE value(const named_arg_info* args, size_t size) + : named_args{args, size} {} + + template FMT_INLINE value(const T& val) { + custom.value = &val; + // Get the formatter type through the context to allow different contexts + // have different extension points, e.g. `formatter` for `format` and + // `printf_formatter` for `printf`. + custom.format = format_custom_arg< + T, conditional_t::value, + typename Context::template formatter_type, + fallback_formatter>>; + } + + private: + // Formats an argument of a custom type, such as a user-defined class. + template + static void format_custom_arg(const void* arg, + typename Context::parse_context_type& parse_ctx, + Context& ctx) { + Formatter f; + parse_ctx.advance_to(f.parse(parse_ctx)); + ctx.advance_to(f.format(*static_cast(arg), ctx)); + } +}; + +template +FMT_CONSTEXPR basic_format_arg make_arg(const T& value); + +// To minimize the number of types we need to deal with, long is translated +// either to int or to long long depending on its size. +enum { long_short = sizeof(long) == sizeof(int) }; +using long_type = conditional_t; +using ulong_type = conditional_t; + +// Maps formatting arguments to core types. +template struct arg_mapper { + using char_type = typename Context::char_type; + + FMT_CONSTEXPR int map(signed char val) { return val; } + FMT_CONSTEXPR unsigned map(unsigned char val) { return val; } + FMT_CONSTEXPR int map(short val) { return val; } + FMT_CONSTEXPR unsigned map(unsigned short val) { return val; } + FMT_CONSTEXPR int map(int val) { return val; } + FMT_CONSTEXPR unsigned map(unsigned val) { return val; } + FMT_CONSTEXPR long_type map(long val) { return val; } + FMT_CONSTEXPR ulong_type map(unsigned long val) { return val; } + FMT_CONSTEXPR long long map(long long val) { return val; } + FMT_CONSTEXPR unsigned long long map(unsigned long long val) { return val; } + FMT_CONSTEXPR int128_t map(int128_t val) { return val; } + FMT_CONSTEXPR uint128_t map(uint128_t val) { return val; } + FMT_CONSTEXPR bool map(bool val) { return val; } + + template ::value)> + FMT_CONSTEXPR char_type map(T val) { + static_assert( + std::is_same::value || std::is_same::value, + "mixing character types is disallowed"); + return val; + } + + FMT_CONSTEXPR float map(float val) { return val; } + FMT_CONSTEXPR double map(double val) { return val; } + FMT_CONSTEXPR long double map(long double val) { return val; } + + FMT_CONSTEXPR const char_type* map(char_type* val) { return val; } + FMT_CONSTEXPR const char_type* map(const char_type* val) { return val; } + template ::value)> + FMT_CONSTEXPR basic_string_view map(const T& val) { + static_assert(std::is_same>::value, + "mixing character types is disallowed"); + return to_string_view(val); + } + template , T>::value && + !is_string::value && !has_formatter::value && + !has_fallback_formatter::value)> + FMT_CONSTEXPR basic_string_view map(const T& val) { + return basic_string_view(val); + } + template < + typename T, + FMT_ENABLE_IF( + std::is_constructible, T>::value && + !std::is_constructible, T>::value && + !is_string::value && !has_formatter::value && + !has_fallback_formatter::value)> + FMT_CONSTEXPR basic_string_view map(const T& val) { + return std_string_view(val); + } + FMT_CONSTEXPR const char* map(const signed char* val) { + static_assert(std::is_same::value, "invalid string type"); + return reinterpret_cast(val); + } + FMT_CONSTEXPR const char* map(const unsigned char* val) { + static_assert(std::is_same::value, "invalid string type"); + return reinterpret_cast(val); + } + FMT_CONSTEXPR const char* map(signed char* val) { + const auto* const_val = val; + return map(const_val); + } + FMT_CONSTEXPR const char* map(unsigned char* val) { + const auto* const_val = val; + return map(const_val); + } + + FMT_CONSTEXPR const void* map(void* val) { return val; } + FMT_CONSTEXPR const void* map(const void* val) { return val; } + FMT_CONSTEXPR const void* map(std::nullptr_t val) { return val; } + template FMT_CONSTEXPR int map(const T*) { + // Formatting of arbitrary pointers is disallowed. If you want to output + // a pointer cast it to "void *" or "const void *". In particular, this + // forbids formatting of "[const] volatile char *" which is printed as bool + // by iostreams. + static_assert(!sizeof(T), "formatting of non-void pointers is disallowed"); + return 0; + } + + template ::value && + !has_formatter::value && + !has_fallback_formatter::value)> + FMT_CONSTEXPR auto map(const T& val) + -> decltype(std::declval().map( + static_cast::type>(val))) { + return map(static_cast::type>(val)); + } + template ::value && !is_char::value && + (has_formatter::value || + has_fallback_formatter::value))> + FMT_CONSTEXPR const T& map(const T& val) { + return val; + } + + template + FMT_CONSTEXPR auto map(const named_arg& val) + -> decltype(std::declval().map(val.value)) { + return map(val.value); + } + + int map(...) { + constexpr bool formattable = sizeof(Context) == 0; + static_assert( + formattable, + "Cannot format argument. To make type T formattable provide a " + "formatter specialization: " + "https://fmt.dev/latest/api.html#formatting-user-defined-types"); + return 0; + } +}; + +// A type constant after applying arg_mapper. +template +using mapped_type_constant = + type_constant().map(std::declval())), + typename Context::char_type>; + +enum { packed_arg_bits = 4 }; +// Maximum number of arguments with packed types. +enum { max_packed_args = 62 / packed_arg_bits }; +enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; +enum : unsigned long long { has_named_args_bit = 1ULL << 62 }; +} // namespace detail + +// A formatting argument. It is a trivially copyable/constructible type to +// allow storage in basic_memory_buffer. +template class basic_format_arg { + private: + detail::value value_; + detail::type type_; + + template + friend FMT_CONSTEXPR basic_format_arg detail::make_arg( + const T& value); + + template + friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis, + const basic_format_arg& arg) + -> decltype(vis(0)); + + friend class basic_format_args; + friend class dynamic_format_arg_store; + + using char_type = typename Context::char_type; + + template + friend struct detail::arg_data; + + basic_format_arg(const detail::named_arg_info* args, size_t size) + : value_(args, size) {} + + public: + class handle { + public: + explicit handle(detail::custom_value custom) : custom_(custom) {} + + void format(typename Context::parse_context_type& parse_ctx, + Context& ctx) const { + custom_.format(custom_.value, parse_ctx, ctx); + } + + private: + detail::custom_value custom_; + }; + + constexpr basic_format_arg() : type_(detail::type::none_type) {} + + constexpr explicit operator bool() const FMT_NOEXCEPT { + return type_ != detail::type::none_type; + } + + detail::type type() const { return type_; } + + bool is_integral() const { return detail::is_integral_type(type_); } + bool is_arithmetic() const { return detail::is_arithmetic_type(type_); } +}; + +/** + \rst + Visits an argument dispatching to the appropriate visit method based on + the argument type. For example, if the argument type is ``double`` then + ``vis(value)`` will be called with the value of type ``double``. + \endrst + */ +template +FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg( + Visitor&& vis, const basic_format_arg& arg) -> decltype(vis(0)) { + using char_type = typename Context::char_type; + switch (arg.type_) { + case detail::type::none_type: + break; + case detail::type::int_type: + return vis(arg.value_.int_value); + case detail::type::uint_type: + return vis(arg.value_.uint_value); + case detail::type::long_long_type: + return vis(arg.value_.long_long_value); + case detail::type::ulong_long_type: + return vis(arg.value_.ulong_long_value); +#if FMT_USE_INT128 + case detail::type::int128_type: + return vis(arg.value_.int128_value); + case detail::type::uint128_type: + return vis(arg.value_.uint128_value); +#else + case detail::type::int128_type: + case detail::type::uint128_type: + break; +#endif + case detail::type::bool_type: + return vis(arg.value_.bool_value); + case detail::type::char_type: + return vis(arg.value_.char_value); + case detail::type::float_type: + return vis(arg.value_.float_value); + case detail::type::double_type: + return vis(arg.value_.double_value); + case detail::type::long_double_type: + return vis(arg.value_.long_double_value); + case detail::type::cstring_type: + return vis(arg.value_.string.data); + case detail::type::string_type: + return vis(basic_string_view(arg.value_.string.data, + arg.value_.string.size)); + case detail::type::pointer_type: + return vis(arg.value_.pointer); + case detail::type::custom_type: + return vis(typename basic_format_arg::handle(arg.value_.custom)); + } + return vis(monostate()); +} + +// Checks whether T is a container with contiguous storage. +template struct is_contiguous : std::false_type {}; +template +struct is_contiguous> : std::true_type {}; +template +struct is_contiguous> : std::true_type {}; + +namespace detail { + +template +struct is_back_insert_iterator : std::false_type {}; +template +struct is_back_insert_iterator> + : std::true_type {}; + +template +struct is_contiguous_back_insert_iterator : std::false_type {}; +template +struct is_contiguous_back_insert_iterator> + : is_contiguous {}; + +// A type-erased reference to an std::locale to avoid heavy include. +class locale_ref { + private: + const void* locale_; // A type-erased pointer to std::locale. + + public: + locale_ref() : locale_(nullptr) {} + template explicit locale_ref(const Locale& loc); + + explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; } + + template Locale get() const; +}; + +template constexpr unsigned long long encode_types() { return 0; } + +template +constexpr unsigned long long encode_types() { + return static_cast(mapped_type_constant::value) | + (encode_types() << packed_arg_bits); +} + +template +FMT_CONSTEXPR basic_format_arg make_arg(const T& value) { + basic_format_arg arg; + arg.type_ = mapped_type_constant::value; + arg.value_ = arg_mapper().map(value); + return arg; +} + +// The type template parameter is there to avoid an ODR violation when using +// a fallback formatter in one translation unit and an implicit conversion in +// another (not recommended). +template +inline value make_arg(const T& val) { + return arg_mapper().map(val); +} + +template +inline basic_format_arg make_arg(const T& value) { + return make_arg(value); +} + +template struct is_reference_wrapper : std::false_type {}; +template +struct is_reference_wrapper> : std::true_type {}; + +template const T& unwrap(const T& v) { return v; } +template const T& unwrap(const std::reference_wrapper& v) { + return static_cast(v); +} + +class dynamic_arg_list { + // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for + // templates it doesn't complain about inability to deduce single translation + // unit for placing vtable. So storage_node_base is made a fake template. + template struct node { + virtual ~node() = default; + std::unique_ptr> next; + }; + + template struct typed_node : node<> { + T value; + + template + FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} + + template + FMT_CONSTEXPR typed_node(const basic_string_view& arg) + : value(arg.data(), arg.size()) {} + }; + + std::unique_ptr> head_; + + public: + template const T& push(const Arg& arg) { + auto new_node = std::unique_ptr>(new typed_node(arg)); + auto& value = new_node->value; + new_node->next = std::move(head_); + head_ = std::move(new_node); + return value; + } +}; +} // namespace detail + +// Formatting context. +template class basic_format_context { + public: + /** The character type for the output. */ + using char_type = Char; + + private: + OutputIt out_; + basic_format_args args_; + detail::locale_ref loc_; + + public: + using iterator = OutputIt; + using format_arg = basic_format_arg; + using parse_context_type = basic_format_parse_context; + template using formatter_type = formatter; + + basic_format_context(const basic_format_context&) = delete; + void operator=(const basic_format_context&) = delete; + /** + Constructs a ``basic_format_context`` object. References to the arguments are + stored in the object so make sure they have appropriate lifetimes. + */ + basic_format_context(OutputIt out, + basic_format_args ctx_args, + detail::locale_ref loc = detail::locale_ref()) + : out_(out), args_(ctx_args), loc_(loc) {} + + format_arg arg(int id) const { return args_.get(id); } + format_arg arg(basic_string_view name) { return args_.get(name); } + int arg_id(basic_string_view name) { return args_.get_id(name); } + const basic_format_args& args() const { return args_; } + + detail::error_handler error_handler() { return {}; } + void on_error(const char* message) { error_handler().on_error(message); } + + // Returns an iterator to the beginning of the output range. + iterator out() { return out_; } + + // Advances the begin iterator to ``it``. + void advance_to(iterator it) { + if (!detail::is_back_insert_iterator()) out_ = it; + } + + detail::locale_ref locale() { return loc_; } +}; + +template +using buffer_context = + basic_format_context>, Char>; +using format_context = buffer_context; +using wformat_context = buffer_context; + +// Workaround a bug in gcc: https://stackoverflow.com/q/62767544/471164. +#define FMT_BUFFER_CONTEXT(Char) \ + basic_format_context>, Char> + +/** + \rst + An array of references to arguments. It can be implicitly converted into + `~fmt::basic_format_args` for passing into type-erased formatting functions + such as `~fmt::vformat`. + \endrst + */ +template +class format_arg_store +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + // Workaround a GCC template argument substitution bug. + : public basic_format_args +#endif +{ + private: + static const size_t num_args = sizeof...(Args); + static const size_t num_named_args = detail::count_named_args(); + static const bool is_packed = num_args <= detail::max_packed_args; + + using value_type = conditional_t, + basic_format_arg>; + + detail::arg_data + data_; + + friend class basic_format_args; + + static constexpr unsigned long long desc = + (is_packed ? detail::encode_types() + : detail::is_unpacked_bit | num_args) | + (num_named_args != 0 + ? static_cast(detail::has_named_args_bit) + : 0); + + public: + format_arg_store(const Args&... args) + : +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + basic_format_args(*this), +#endif + data_{detail::make_arg< + is_packed, Context, + detail::mapped_type_constant::value>(args)...} { + detail::init_named_args(data_.named_args(), 0, 0, args...); + } +}; + +/** + \rst + Constructs an `~fmt::format_arg_store` object that contains references to + arguments and can be implicitly converted to `~fmt::format_args`. `Context` + can be omitted in which case it defaults to `~fmt::context`. + See `~fmt::arg` for lifetime considerations. + \endrst + */ +template +inline format_arg_store make_format_args( + const Args&... args) { + return {args...}; +} + +/** + \rst + Returns a named argument to be used in a formatting function. It should only + be used in a call to a formatting function. + + **Example**:: + + fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); + \endrst + */ +template +inline detail::named_arg arg(const Char* name, const T& arg) { + static_assert(!detail::is_named_arg(), "nested named arguments"); + return {name, arg}; +} + +/** + \rst + A dynamic version of `fmt::format_arg_store`. + It's equipped with a storage to potentially temporary objects which lifetimes + could be shorter than the format arguments object. + + It can be implicitly converted into `~fmt::basic_format_args` for passing + into type-erased formatting functions such as `~fmt::vformat`. + \endrst + */ +template +class dynamic_format_arg_store +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + // Workaround a GCC template argument substitution bug. + : public basic_format_args +#endif +{ + private: + using char_type = typename Context::char_type; + + template struct need_copy { + static constexpr detail::type mapped_type = + detail::mapped_type_constant::value; + + enum { + value = !(detail::is_reference_wrapper::value || + std::is_same>::value || + std::is_same>::value || + (mapped_type != detail::type::cstring_type && + mapped_type != detail::type::string_type && + mapped_type != detail::type::custom_type)) + }; + }; + + template + using stored_type = conditional_t::value, + std::basic_string, T>; + + // Storage of basic_format_arg must be contiguous. + std::vector> data_; + std::vector> named_info_; + + // Storage of arguments not fitting into basic_format_arg must grow + // without relocation because items in data_ refer to it. + detail::dynamic_arg_list dynamic_args_; + + friend class basic_format_args; + + unsigned long long get_types() const { + return detail::is_unpacked_bit | data_.size() | + (named_info_.empty() + ? 0ULL + : static_cast(detail::has_named_args_bit)); + } + + const basic_format_arg* data() const { + return named_info_.empty() ? data_.data() : data_.data() + 1; + } + + template void emplace_arg(const T& arg) { + data_.emplace_back(detail::make_arg(arg)); + } + + template + void emplace_arg(const detail::named_arg& arg) { + if (named_info_.empty()) { + constexpr const detail::named_arg_info* zero_ptr{nullptr}; + data_.insert(data_.begin(), {zero_ptr, 0}); + } + data_.emplace_back(detail::make_arg(detail::unwrap(arg.value))); + auto pop_one = [](std::vector>* data) { + data->pop_back(); + }; + std::unique_ptr>, decltype(pop_one)> + guard{&data_, pop_one}; + named_info_.push_back({arg.name, static_cast(data_.size() - 2u)}); + data_[0].value_.named_args = {named_info_.data(), named_info_.size()}; + guard.release(); + } + + public: + /** + \rst + Adds an argument into the dynamic store for later passing to a formatting + function. + + Note that custom types and string types (but not string views) are copied + into the store dynamically allocating memory if necessary. + + **Example**:: + + fmt::dynamic_format_arg_store store; + store.push_back(42); + store.push_back("abc"); + store.push_back(1.5f); + std::string result = fmt::vformat("{} and {} and {}", store); + \endrst + */ + template void push_back(const T& arg) { + if (detail::const_check(need_copy::value)) + emplace_arg(dynamic_args_.push>(arg)); + else + emplace_arg(detail::unwrap(arg)); + } + + /** + \rst + Adds a reference to the argument into the dynamic store for later passing to + a formatting function. Supports named arguments wrapped in + ``std::reference_wrapper`` via ``std::ref()``/``std::cref()``. + + **Example**:: + + fmt::dynamic_format_arg_store store; + char str[] = "1234567890"; + store.push_back(std::cref(str)); + int a1_val{42}; + auto a1 = fmt::arg("a1_", a1_val); + store.push_back(std::cref(a1)); + + // Changing str affects the output but only for string and custom types. + str[0] = 'X'; + + std::string result = fmt::vformat("{} and {a1_}"); + assert(result == "X234567890 and 42"); + \endrst + */ + template void push_back(std::reference_wrapper arg) { + static_assert( + detail::is_named_arg::type>::value || + need_copy::value, + "objects of built-in types and string views are always copied"); + emplace_arg(arg.get()); + } + + /** + Adds named argument into the dynamic store for later passing to a formatting + function. ``std::reference_wrapper`` is supported to avoid copying of the + argument. + */ + template + void push_back(const detail::named_arg& arg) { + const char_type* arg_name = + dynamic_args_.push>(arg.name).c_str(); + if (detail::const_check(need_copy::value)) { + emplace_arg( + fmt::arg(arg_name, dynamic_args_.push>(arg.value))); + } else { + emplace_arg(fmt::arg(arg_name, arg.value)); + } + } + + /** Erase all elements from the store */ + void clear() { + data_.clear(); + named_info_.clear(); + dynamic_args_ = detail::dynamic_arg_list(); + } + + /** + \rst + Reserves space to store at least *new_cap* arguments including + *new_cap_named* named arguments. + \endrst + */ + void reserve(size_t new_cap, size_t new_cap_named) { + FMT_ASSERT(new_cap >= new_cap_named, + "Set of arguments includes set of named arguments"); + data_.reserve(new_cap); + named_info_.reserve(new_cap_named); + } +}; + +/** + \rst + A view of a collection of formatting arguments. To avoid lifetime issues it + should only be used as a parameter type in type-erased functions such as + ``vformat``:: + + void vlog(string_view format_str, format_args args); // OK + format_args args = make_format_args(42); // Error: dangling reference + \endrst + */ +template class basic_format_args { + public: + using size_type = int; + using format_arg = basic_format_arg; + + private: + // A descriptor that contains information about formatting arguments. + // If the number of arguments is less or equal to max_packed_args then + // argument types are passed in the descriptor. This reduces binary code size + // per formatting function call. + unsigned long long desc_; + union { + // If is_packed() returns true then argument values are stored in values_; + // otherwise they are stored in args_. This is done to improve cache + // locality and reduce compiled code size since storing larger objects + // may require more code (at least on x86-64) even if the same amount of + // data is actually copied to stack. It saves ~10% on the bloat test. + const detail::value* values_; + const format_arg* args_; + }; + + bool is_packed() const { return (desc_ & detail::is_unpacked_bit) == 0; } + bool has_named_args() const { + return (desc_ & detail::has_named_args_bit) != 0; + } + + detail::type type(int index) const { + int shift = index * detail::packed_arg_bits; + unsigned int mask = (1 << detail::packed_arg_bits) - 1; + return static_cast((desc_ >> shift) & mask); + } + + basic_format_args(unsigned long long desc, + const detail::value* values) + : desc_(desc), values_(values) {} + basic_format_args(unsigned long long desc, const format_arg* args) + : desc_(desc), args_(args) {} + + public: + basic_format_args() : desc_(0) {} + + /** + \rst + Constructs a `basic_format_args` object from `~fmt::format_arg_store`. + \endrst + */ + template + FMT_INLINE basic_format_args(const format_arg_store& store) + : basic_format_args(store.desc, store.data_.args()) {} + + /** + \rst + Constructs a `basic_format_args` object from + `~fmt::dynamic_format_arg_store`. + \endrst + */ + FMT_INLINE basic_format_args(const dynamic_format_arg_store& store) + : basic_format_args(store.get_types(), store.data()) {} + + /** + \rst + Constructs a `basic_format_args` object from a dynamic set of arguments. + \endrst + */ + basic_format_args(const format_arg* args, int count) + : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count), + args) {} + + /** Returns the argument with the specified id. */ + format_arg get(int id) const { + format_arg arg; + if (!is_packed()) { + if (id < max_size()) arg = args_[id]; + return arg; + } + if (id >= detail::max_packed_args) return arg; + arg.type_ = type(id); + if (arg.type_ == detail::type::none_type) return arg; + arg.value_ = values_[id]; + return arg; + } + + template format_arg get(basic_string_view name) const { + int id = get_id(name); + return id >= 0 ? get(id) : format_arg(); + } + + template int get_id(basic_string_view name) const { + if (!has_named_args()) return -1; + const auto& named_args = + (is_packed() ? values_[-1] : args_[-1].value_).named_args; + for (size_t i = 0; i < named_args.size; ++i) { + if (named_args.data[i].name == name) return named_args.data[i].id; + } + return -1; + } + + int max_size() const { + unsigned long long max_packed = detail::max_packed_args; + return static_cast(is_packed() ? max_packed + : desc_ & ~detail::is_unpacked_bit); + } +}; + +/** An alias to ``basic_format_args``. */ +// It is a separate type rather than an alias to make symbols readable. +struct format_args : basic_format_args { + template + FMT_INLINE format_args(const Args&... args) : basic_format_args(args...) {} +}; +struct wformat_args : basic_format_args { + using basic_format_args::basic_format_args; +}; + +namespace detail { + +// Reports a compile-time error if S is not a valid format string. +template ::value)> +FMT_INLINE void check_format_string(const S&) { +#ifdef FMT_ENFORCE_COMPILE_STRING + static_assert(is_compile_string::value, + "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " + "FMT_STRING."); +#endif +} +template ::value)> +void check_format_string(S); + +template > +inline format_arg_store, remove_reference_t...> +make_args_checked(const S& format_str, + const remove_reference_t&... args) { + static_assert(count<(std::is_base_of>::value && + std::is_reference::value)...>() == 0, + "passing views as lvalues is disallowed"); + check_format_string(format_str); + return {args...}; +} + +template ::value)> +std::basic_string vformat( + basic_string_view format_str, + basic_format_args>> args); + +FMT_API std::string vformat(string_view format_str, format_args args); + +template +typename FMT_BUFFER_CONTEXT(Char)::iterator vformat_to( + buffer& buf, basic_string_view format_str, + basic_format_args)> args); + +template ::value)> +inline void vprint_mojibake(std::FILE*, basic_string_view, const Args&) {} + +FMT_API void vprint_mojibake(std::FILE*, string_view, format_args); +#ifndef _WIN32 +inline void vprint_mojibake(std::FILE*, string_view, format_args) {} +#endif +} // namespace detail + +/** Formats a string and writes the output to ``out``. */ +// GCC 8 and earlier cannot handle std::back_insert_iterator with +// vformat_to(...) overload, so SFINAE on iterator type instead. +template < + typename OutputIt, typename S, typename Char = char_t, + FMT_ENABLE_IF(detail::is_contiguous_back_insert_iterator::value)> +OutputIt vformat_to( + OutputIt out, const S& format_str, + basic_format_args>> args) { + auto& c = detail::get_container(out); + detail::container_buffer> buf(c); + detail::vformat_to(buf, to_string_view(format_str), args); + return out; +} + +template ::value&& detail::is_string::value)> +inline std::back_insert_iterator format_to( + std::back_insert_iterator out, const S& format_str, + Args&&... args) { + return vformat_to(out, to_string_view(format_str), + detail::make_args_checked(format_str, args...)); +} + +template > +FMT_INLINE std::basic_string vformat( + const S& format_str, + basic_format_args>> args) { + return detail::vformat(to_string_view(format_str), args); +} + +/** + \rst + Formats arguments and returns the result as a string. + + **Example**:: + + #include + std::string message = fmt::format("The answer is {}", 42); + \endrst +*/ +// Pass char_t as a default template parameter instead of using +// std::basic_string> to reduce the symbol size. +template > +FMT_INLINE std::basic_string format(const S& format_str, Args&&... args) { + const auto& vargs = detail::make_args_checked(format_str, args...); + return detail::vformat(to_string_view(format_str), vargs); +} + +FMT_API void vprint(string_view, format_args); +FMT_API void vprint(std::FILE*, string_view, format_args); + +/** + \rst + Formats ``args`` according to specifications in ``format_str`` and writes the + output to the file ``f``. Strings are assumed to be Unicode-encoded unless the + ``FMT_UNICODE`` macro is set to 0. + + **Example**:: + + fmt::print(stderr, "Don't {}!", "panic"); + \endrst + */ +template > +inline void print(std::FILE* f, const S& format_str, Args&&... args) { + const auto& vargs = detail::make_args_checked(format_str, args...); + return detail::is_unicode() + ? vprint(f, to_string_view(format_str), vargs) + : detail::vprint_mojibake(f, to_string_view(format_str), vargs); +} + +/** + \rst + Formats ``args`` according to specifications in ``format_str`` and writes + the output to ``stdout``. Strings are assumed to be Unicode-encoded unless + the ``FMT_UNICODE`` macro is set to 0. + + **Example**:: + + fmt::print("Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ +template > +inline void print(const S& format_str, Args&&... args) { + const auto& vargs = detail::make_args_checked(format_str, args...); + return detail::is_unicode() + ? vprint(to_string_view(format_str), vargs) + : detail::vprint_mojibake(stdout, to_string_view(format_str), + vargs); +} +FMT_END_NAMESPACE + +#endif // FMT_CORE_H_ diff --git a/src/third_party/fmt/format-inl.h b/src/third_party/fmt/format-inl.h new file mode 100644 index 0000000..d8c9c8a --- /dev/null +++ b/src/third_party/fmt/format-inl.h @@ -0,0 +1,1453 @@ +// Formatting library for C++ - implementation +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_FORMAT_INL_H_ +#define FMT_FORMAT_INL_H_ + +#include +#include +#include +#include +#include +#include // for std::memmove +#include +#include + +#include "format.h" +#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) +# include +#endif + +#ifdef _WIN32 +# if !defined(NOMINMAX) && !defined(WIN32_LEAN_AND_MEAN) +# define NOMINMAX +# define WIN32_LEAN_AND_MEAN +# include +# undef WIN32_LEAN_AND_MEAN +# undef NOMINMAX +# else +# include +# endif +# include +#endif + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4702) // unreachable code +#endif + +// Dummy implementations of strerror_r and strerror_s called if corresponding +// system functions are not available. +inline fmt::detail::null<> strerror_r(int, char*, ...) { return {}; } +inline fmt::detail::null<> strerror_s(char*, size_t, ...) { return {}; } + +FMT_BEGIN_NAMESPACE +namespace detail { + +FMT_FUNC void assert_fail(const char* file, int line, const char* message) { + // Use unchecked std::fprintf to avoid triggering another assertion when + // writing to stderr fails + std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); + // Chosen instead of std::abort to satisfy Clang in CUDA mode during device + // code pass. + std::terminate(); +} + +#ifndef _MSC_VER +# define FMT_SNPRINTF snprintf +#else // _MSC_VER +inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) { + va_list args; + va_start(args, format); + int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); + va_end(args); + return result; +} +# define FMT_SNPRINTF fmt_snprintf +#endif // _MSC_VER + +// A portable thread-safe version of strerror. +// Sets buffer to point to a string describing the error code. +// This can be either a pointer to a string stored in buffer, +// or a pointer to some static immutable string. +// Returns one of the following values: +// 0 - success +// ERANGE - buffer is not large enough to store the error message +// other - failure +// Buffer should be at least of size 1. +FMT_FUNC int safe_strerror(int error_code, char*& buffer, + size_t buffer_size) FMT_NOEXCEPT { + FMT_ASSERT(buffer != nullptr && buffer_size != 0, "invalid buffer"); + + class dispatcher { + private: + int error_code_; + char*& buffer_; + size_t buffer_size_; + + // A noop assignment operator to avoid bogus warnings. + void operator=(const dispatcher&) {} + + // Handle the result of XSI-compliant version of strerror_r. + int handle(int result) { + // glibc versions before 2.13 return result in errno. + return result == -1 ? errno : result; + } + + // Handle the result of GNU-specific version of strerror_r. + FMT_MAYBE_UNUSED + int handle(char* message) { + // If the buffer is full then the message is probably truncated. + if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1) + return ERANGE; + buffer_ = message; + return 0; + } + + // Handle the case when strerror_r is not available. + FMT_MAYBE_UNUSED + int handle(detail::null<>) { + return fallback(strerror_s(buffer_, buffer_size_, error_code_)); + } + + // Fallback to strerror_s when strerror_r is not available. + FMT_MAYBE_UNUSED + int fallback(int result) { + // If the buffer is full then the message is probably truncated. + return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE + : result; + } + +#if !FMT_MSC_VER + // Fallback to strerror if strerror_r and strerror_s are not available. + int fallback(detail::null<>) { + errno = 0; + buffer_ = strerror(error_code_); + return errno; + } +#endif + + public: + dispatcher(int err_code, char*& buf, size_t buf_size) + : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} + + int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); } + }; + return dispatcher(error_code, buffer, buffer_size).run(); +} + +FMT_FUNC void format_error_code(detail::buffer& out, int error_code, + string_view message) FMT_NOEXCEPT { + // Report error code making sure that the output fits into + // inline_buffer_size to avoid dynamic memory allocation and potential + // bad_alloc. + out.resize(0); + static const char SEP[] = ": "; + static const char ERROR_STR[] = "error "; + // Subtract 2 to account for terminating null characters in SEP and ERROR_STR. + size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; + auto abs_value = static_cast>(error_code); + if (detail::is_negative(error_code)) { + abs_value = 0 - abs_value; + ++error_code_size; + } + error_code_size += detail::to_unsigned(detail::count_digits(abs_value)); + auto it = std::back_inserter(out); + if (message.size() <= inline_buffer_size - error_code_size) + format_to(it, "{}{}", message, SEP); + format_to(it, "{}{}", ERROR_STR, error_code); + assert(out.size() <= inline_buffer_size); +} + +FMT_FUNC void report_error(format_func func, int error_code, + string_view message) FMT_NOEXCEPT { + memory_buffer full_message; + func(full_message, error_code, message); + // Don't use fwrite_fully because the latter may throw. + (void)std::fwrite(full_message.data(), full_message.size(), 1, stderr); + std::fputc('\n', stderr); +} + +// A wrapper around fwrite that throws on error. +FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count, + FILE* stream) { + size_t written = std::fwrite(ptr, size, count, stream); + if (written < count) FMT_THROW(system_error(errno, "cannot write to file")); +} +} // namespace detail + +#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) +namespace detail { + +template +locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { + static_assert(std::is_same::value, ""); +} + +template Locale locale_ref::get() const { + static_assert(std::is_same::value, ""); + return locale_ ? *static_cast(locale_) : std::locale(); +} + +template FMT_FUNC std::string grouping_impl(locale_ref loc) { + return std::use_facet>(loc.get()).grouping(); +} +template FMT_FUNC Char thousands_sep_impl(locale_ref loc) { + return std::use_facet>(loc.get()) + .thousands_sep(); +} +template FMT_FUNC Char decimal_point_impl(locale_ref loc) { + return std::use_facet>(loc.get()) + .decimal_point(); +} +} // namespace detail +#else +template +FMT_FUNC std::string detail::grouping_impl(locale_ref) { + return "\03"; +} +template FMT_FUNC Char detail::thousands_sep_impl(locale_ref) { + return FMT_STATIC_THOUSANDS_SEPARATOR; +} +template FMT_FUNC Char detail::decimal_point_impl(locale_ref) { + return '.'; +} +#endif + +FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT = default; +FMT_API FMT_FUNC system_error::~system_error() FMT_NOEXCEPT = default; + +FMT_FUNC void system_error::init(int err_code, string_view format_str, + format_args args) { + error_code_ = err_code; + memory_buffer buffer; + format_system_error(buffer, err_code, vformat(format_str, args)); + std::runtime_error& base = *this; + base = std::runtime_error(to_string(buffer)); +} + +namespace detail { + +template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) { + // fallback_uintptr is always stored in little endian. + int i = static_cast(sizeof(void*)) - 1; + while (i > 0 && n.value[i] == 0) --i; + auto char_digits = std::numeric_limits::digits / 4; + return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1; +} + +template +const typename basic_data::digit_pair basic_data::digits[] = { + {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, + {'0', '5'}, {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, + {'1', '0'}, {'1', '1'}, {'1', '2'}, {'1', '3'}, {'1', '4'}, + {'1', '5'}, {'1', '6'}, {'1', '7'}, {'1', '8'}, {'1', '9'}, + {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, {'2', '4'}, + {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'}, + {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, + {'3', '5'}, {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, + {'4', '0'}, {'4', '1'}, {'4', '2'}, {'4', '3'}, {'4', '4'}, + {'4', '5'}, {'4', '6'}, {'4', '7'}, {'4', '8'}, {'4', '9'}, + {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, {'5', '4'}, + {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, + {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, + {'6', '5'}, {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, + {'7', '0'}, {'7', '1'}, {'7', '2'}, {'7', '3'}, {'7', '4'}, + {'7', '5'}, {'7', '6'}, {'7', '7'}, {'7', '8'}, {'7', '9'}, + {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, {'8', '4'}, + {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, + {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, + {'9', '5'}, {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}}; + +template +const char basic_data::hex_digits[] = "0123456789abcdef"; + +#define FMT_POWERS_OF_10(factor) \ + factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ + (factor)*1000000, (factor)*10000000, (factor)*100000000, \ + (factor)*1000000000 + +template +const uint64_t basic_data::powers_of_10_64[] = { + 1, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + +template +const uint32_t basic_data::zero_or_powers_of_10_32[] = {0, + FMT_POWERS_OF_10(1)}; + +template +const uint64_t basic_data::zero_or_powers_of_10_64[] = { + 0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + +// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. +// These are generated by support/compute-powers.py. +template +const uint64_t basic_data::pow10_significands[] = { + 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, + 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, + 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, + 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, + 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, + 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, + 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, + 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, + 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, + 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053, + 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, + 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, + 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, + 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb, + 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, + 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, + 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, + 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, + 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, + 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, + 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, + 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25, + 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, + 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, + 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, + 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, + 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, + 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, + 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b, +}; + +// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding +// to significands above. +template +const int16_t basic_data::pow10_exponents[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, + -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, + -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, + -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, + -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, + 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, + 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, + 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; + +template +const char basic_data::foreground_color[] = "\x1b[38;2;"; +template +const char basic_data::background_color[] = "\x1b[48;2;"; +template const char basic_data::reset_color[] = "\x1b[0m"; +template const wchar_t basic_data::wreset_color[] = L"\x1b[0m"; +template const char basic_data::signs[] = {0, '-', '+', ' '}; +template +const char basic_data::left_padding_shifts[] = {31, 31, 0, 1, 0}; +template +const char basic_data::right_padding_shifts[] = {0, 31, 0, 1, 0}; + +template struct bits { + static FMT_CONSTEXPR_DECL const int value = + static_cast(sizeof(T) * std::numeric_limits::digits); +}; + +class fp; +template fp normalize(fp value); + +// Lower (upper) boundary is a value half way between a floating-point value +// and its predecessor (successor). Boundaries have the same exponent as the +// value so only significands are stored. +struct boundaries { + uint64_t lower; + uint64_t upper; +}; + +// A handmade floating-point number f * pow(2, e). +class fp { + private: + using significand_type = uint64_t; + + public: + significand_type f; + int e; + + // All sizes are in bits. + // Subtract 1 to account for an implicit most significant bit in the + // normalized form. + static FMT_CONSTEXPR_DECL const int double_significand_size = + std::numeric_limits::digits - 1; + static FMT_CONSTEXPR_DECL const uint64_t implicit_bit = + 1ULL << double_significand_size; + static FMT_CONSTEXPR_DECL const int significand_size = + bits::value; + + fp() : f(0), e(0) {} + fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} + + // Constructs fp from an IEEE754 double. It is a template to prevent compile + // errors on platforms where double is not IEEE754. + template explicit fp(Double d) { assign(d); } + + // Assigns d to this and return true iff predecessor is closer than successor. + template + bool assign(Double d) { + // Assume double is in the format [sign][exponent][significand]. + using limits = std::numeric_limits; + const int exponent_size = + bits::value - double_significand_size - 1; // -1 for sign + const uint64_t significand_mask = implicit_bit - 1; + const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask; + const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1; + auto u = bit_cast(d); + f = u & significand_mask; + int biased_e = + static_cast((u & exponent_mask) >> double_significand_size); + // Predecessor is closer if d is a normalized power of 2 (f == 0) other than + // the smallest normalized number (biased_e > 1). + bool is_predecessor_closer = f == 0 && biased_e > 1; + if (biased_e != 0) + f += implicit_bit; + else + biased_e = 1; // Subnormals use biased exponent 1 (min exponent). + e = biased_e - exponent_bias - double_significand_size; + return is_predecessor_closer; + } + + template + bool assign(Double) { + *this = fp(); + return false; + } + + // Assigns d to this together with computing lower and upper boundaries, + // where a boundary is a value half way between the number and its predecessor + // (lower) or successor (upper). The upper boundary is normalized and lower + // has the same exponent but may be not normalized. + template boundaries assign_with_boundaries(Double d) { + bool is_lower_closer = assign(d); + fp lower = + is_lower_closer ? fp((f << 2) - 1, e - 2) : fp((f << 1) - 1, e - 1); + // 1 in normalize accounts for the exponent shift above. + fp upper = normalize<1>(fp((f << 1) + 1, e - 1)); + lower.f <<= lower.e - upper.e; + return boundaries{lower.f, upper.f}; + } + + template boundaries assign_float_with_boundaries(Double d) { + assign(d); + constexpr int min_normal_e = std::numeric_limits::min_exponent - + std::numeric_limits::digits; + significand_type half_ulp = 1 << (std::numeric_limits::digits - + std::numeric_limits::digits - 1); + if (min_normal_e > e) half_ulp <<= min_normal_e - e; + fp upper = normalize<0>(fp(f + half_ulp, e)); + fp lower = fp( + f - (half_ulp >> ((f == implicit_bit && e > min_normal_e) ? 1 : 0)), e); + lower.f <<= lower.e - upper.e; + return boundaries{lower.f, upper.f}; + } +}; + +// Normalizes the value converted from double and multiplied by (1 << SHIFT). +template fp normalize(fp value) { + // Handle subnormals. + const auto shifted_implicit_bit = fp::implicit_bit << SHIFT; + while ((value.f & shifted_implicit_bit) == 0) { + value.f <<= 1; + --value.e; + } + // Subtract 1 to account for hidden bit. + const auto offset = + fp::significand_size - fp::double_significand_size - SHIFT - 1; + value.f <<= offset; + value.e -= offset; + return value; +} + +inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; } + +// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. +inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { +#if FMT_USE_INT128 + auto product = static_cast<__uint128_t>(lhs) * rhs; + auto f = static_cast(product >> 64); + return (static_cast(product) & (1ULL << 63)) != 0 ? f + 1 : f; +#else + // Multiply 32-bit parts of significands. + uint64_t mask = (1ULL << 32) - 1; + uint64_t a = lhs >> 32, b = lhs & mask; + uint64_t c = rhs >> 32, d = rhs & mask; + uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d; + // Compute mid 64-bit of result and round. + uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31); + return ac + (ad >> 32) + (bc >> 32) + (mid >> 32); +#endif +} + +inline fp operator*(fp x, fp y) { return {multiply(x.f, y.f), x.e + y.e + 64}; } + +// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its +// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`. +inline fp get_cached_power(int min_exponent, int& pow10_exponent) { + const int64_t one_over_log2_10 = 0x4d104d42; // round(pow(2, 32) / log2(10)) + int index = static_cast( + ((min_exponent + fp::significand_size - 1) * one_over_log2_10 + + ((int64_t(1) << 32) - 1)) // ceil + >> 32 // arithmetic shift + ); + // Decimal exponent of the first (smallest) cached power of 10. + const int first_dec_exp = -348; + // Difference between 2 consecutive decimal exponents in cached powers of 10. + const int dec_exp_step = 8; + index = (index - first_dec_exp - 1) / dec_exp_step + 1; + pow10_exponent = first_dec_exp + index * dec_exp_step; + return {data::pow10_significands[index], data::pow10_exponents[index]}; +} + +// A simple accumulator to hold the sums of terms in bigint::square if uint128_t +// is not available. +struct accumulator { + uint64_t lower; + uint64_t upper; + + accumulator() : lower(0), upper(0) {} + explicit operator uint32_t() const { return static_cast(lower); } + + void operator+=(uint64_t n) { + lower += n; + if (lower < n) ++upper; + } + void operator>>=(int shift) { + assert(shift == 32); + (void)shift; + lower = (upper << 32) | (lower >> 32); + upper >>= 32; + } +}; + +class bigint { + private: + // A bigint is stored as an array of bigits (big digits), with bigit at index + // 0 being the least significant one. + using bigit = uint32_t; + using double_bigit = uint64_t; + enum { bigits_capacity = 32 }; + basic_memory_buffer bigits_; + int exp_; + + bigit operator[](int index) const { return bigits_[to_unsigned(index)]; } + bigit& operator[](int index) { return bigits_[to_unsigned(index)]; } + + static FMT_CONSTEXPR_DECL const int bigit_bits = bits::value; + + friend struct formatter; + + void subtract_bigits(int index, bigit other, bigit& borrow) { + auto result = static_cast((*this)[index]) - other - borrow; + (*this)[index] = static_cast(result); + borrow = static_cast(result >> (bigit_bits * 2 - 1)); + } + + void remove_leading_zeros() { + int num_bigits = static_cast(bigits_.size()) - 1; + while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; + bigits_.resize(to_unsigned(num_bigits + 1)); + } + + // Computes *this -= other assuming aligned bigints and *this >= other. + void subtract_aligned(const bigint& other) { + FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints"); + FMT_ASSERT(compare(*this, other) >= 0, ""); + bigit borrow = 0; + int i = other.exp_ - exp_; + for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) { + subtract_bigits(i, other.bigits_[j], borrow); + } + while (borrow > 0) subtract_bigits(i, 0, borrow); + remove_leading_zeros(); + } + + void multiply(uint32_t value) { + const double_bigit wide_value = value; + bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + double_bigit result = bigits_[i] * wide_value + carry; + bigits_[i] = static_cast(result); + carry = static_cast(result >> bigit_bits); + } + if (carry != 0) bigits_.push_back(carry); + } + + void multiply(uint64_t value) { + const bigit mask = ~bigit(0); + const double_bigit lower = value & mask; + const double_bigit upper = value >> bigit_bits; + double_bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + double_bigit result = bigits_[i] * lower + (carry & mask); + carry = + bigits_[i] * upper + (result >> bigit_bits) + (carry >> bigit_bits); + bigits_[i] = static_cast(result); + } + while (carry != 0) { + bigits_.push_back(carry & mask); + carry >>= bigit_bits; + } + } + + public: + bigint() : exp_(0) {} + explicit bigint(uint64_t n) { assign(n); } + ~bigint() { assert(bigits_.capacity() <= bigits_capacity); } + + bigint(const bigint&) = delete; + void operator=(const bigint&) = delete; + + void assign(const bigint& other) { + auto size = other.bigits_.size(); + bigits_.resize(size); + auto data = other.bigits_.data(); + std::copy(data, data + size, make_checked(bigits_.data(), size)); + exp_ = other.exp_; + } + + void assign(uint64_t n) { + size_t num_bigits = 0; + do { + bigits_[num_bigits++] = n & ~bigit(0); + n >>= bigit_bits; + } while (n != 0); + bigits_.resize(num_bigits); + exp_ = 0; + } + + int num_bigits() const { return static_cast(bigits_.size()) + exp_; } + + FMT_NOINLINE bigint& operator<<=(int shift) { + assert(shift >= 0); + exp_ += shift / bigit_bits; + shift %= bigit_bits; + if (shift == 0) return *this; + bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + bigit c = bigits_[i] >> (bigit_bits - shift); + bigits_[i] = (bigits_[i] << shift) + carry; + carry = c; + } + if (carry != 0) bigits_.push_back(carry); + return *this; + } + + template bigint& operator*=(Int value) { + FMT_ASSERT(value > 0, ""); + multiply(uint32_or_64_or_128_t(value)); + return *this; + } + + friend int compare(const bigint& lhs, const bigint& rhs) { + int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits(); + if (num_lhs_bigits != num_rhs_bigits) + return num_lhs_bigits > num_rhs_bigits ? 1 : -1; + int i = static_cast(lhs.bigits_.size()) - 1; + int j = static_cast(rhs.bigits_.size()) - 1; + int end = i - j; + if (end < 0) end = 0; + for (; i >= end; --i, --j) { + bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j]; + if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1; + } + if (i != j) return i > j ? 1 : -1; + return 0; + } + + // Returns compare(lhs1 + lhs2, rhs). + friend int add_compare(const bigint& lhs1, const bigint& lhs2, + const bigint& rhs) { + int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits()); + int num_rhs_bigits = rhs.num_bigits(); + if (max_lhs_bigits + 1 < num_rhs_bigits) return -1; + if (max_lhs_bigits > num_rhs_bigits) return 1; + auto get_bigit = [](const bigint& n, int i) -> bigit { + return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0; + }; + double_bigit borrow = 0; + int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_); + for (int i = num_rhs_bigits - 1; i >= min_exp; --i) { + double_bigit sum = + static_cast(get_bigit(lhs1, i)) + get_bigit(lhs2, i); + bigit rhs_bigit = get_bigit(rhs, i); + if (sum > rhs_bigit + borrow) return 1; + borrow = rhs_bigit + borrow - sum; + if (borrow > 1) return -1; + borrow <<= bigit_bits; + } + return borrow != 0 ? -1 : 0; + } + + // Assigns pow(10, exp) to this bigint. + void assign_pow10(int exp) { + assert(exp >= 0); + if (exp == 0) return assign(1); + // Find the top bit. + int bitmask = 1; + while (exp >= bitmask) bitmask <<= 1; + bitmask >>= 1; + // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by + // repeated squaring and multiplication. + assign(5); + bitmask >>= 1; + while (bitmask != 0) { + square(); + if ((exp & bitmask) != 0) *this *= 5; + bitmask >>= 1; + } + *this <<= exp; // Multiply by pow(2, exp) by shifting. + } + + void square() { + basic_memory_buffer n(std::move(bigits_)); + int num_bigits = static_cast(bigits_.size()); + int num_result_bigits = 2 * num_bigits; + bigits_.resize(to_unsigned(num_result_bigits)); + using accumulator_t = conditional_t; + auto sum = accumulator_t(); + for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) { + // Compute bigit at position bigit_index of the result by adding + // cross-product terms n[i] * n[j] such that i + j == bigit_index. + for (int i = 0, j = bigit_index; j >= 0; ++i, --j) { + // Most terms are multiplied twice which can be optimized in the future. + sum += static_cast(n[i]) * n[j]; + } + (*this)[bigit_index] = static_cast(sum); + sum >>= bits::value; // Compute the carry. + } + // Do the same for the top half. + for (int bigit_index = num_bigits; bigit_index < num_result_bigits; + ++bigit_index) { + for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;) + sum += static_cast(n[i++]) * n[j--]; + (*this)[bigit_index] = static_cast(sum); + sum >>= bits::value; + } + --num_result_bigits; + remove_leading_zeros(); + exp_ *= 2; + } + + // Divides this bignum by divisor, assigning the remainder to this and + // returning the quotient. + int divmod_assign(const bigint& divisor) { + FMT_ASSERT(this != &divisor, ""); + if (compare(*this, divisor) < 0) return 0; + int num_bigits = static_cast(bigits_.size()); + FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); + int exp_difference = exp_ - divisor.exp_; + if (exp_difference > 0) { + // Align bigints by adding trailing zeros to simplify subtraction. + bigits_.resize(to_unsigned(num_bigits + exp_difference)); + for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) + bigits_[j] = bigits_[i]; + std::uninitialized_fill_n(bigits_.data(), exp_difference, 0); + exp_ -= exp_difference; + } + int quotient = 0; + do { + subtract_aligned(divisor); + ++quotient; + } while (compare(*this, divisor) >= 0); + return quotient; + } +}; + +enum class round_direction { unknown, up, down }; + +// Given the divisor (normally a power of 10), the remainder = v % divisor for +// some number v and the error, returns whether v should be rounded up, down, or +// whether the rounding direction can't be determined due to error. +// error should be less than divisor / 2. +inline round_direction get_round_direction(uint64_t divisor, uint64_t remainder, + uint64_t error) { + FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow. + FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow. + FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow. + // Round down if (remainder + error) * 2 <= divisor. + if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2) + return round_direction::down; + // Round up if (remainder - error) * 2 >= divisor. + if (remainder >= error && + remainder - error >= divisor - (remainder - error)) { + return round_direction::up; + } + return round_direction::unknown; +} + +namespace digits { +enum result { + more, // Generate more digits. + done, // Done generating digits. + error // Digit generation cancelled due to an error. +}; +} + +// A version of count_digits optimized for grisu_gen_digits. +inline int grisu_count_digits(uint32_t n) { + if (n < 10) return 1; + if (n < 100) return 2; + if (n < 1000) return 3; + if (n < 10000) return 4; + if (n < 100000) return 5; + if (n < 1000000) return 6; + if (n < 10000000) return 7; + if (n < 100000000) return 8; + if (n < 1000000000) return 9; + return 10; +} + +// Generates output using the Grisu digit-gen algorithm. +// error: the size of the region (lower, upper) outside of which numbers +// definitely do not round to value (Delta in Grisu3). +template +FMT_ALWAYS_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, + int& exp, Handler& handler) { + const fp one(1ULL << -value.e, value.e); + // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be + // zero because it contains a product of two 64-bit numbers with MSB set (due + // to normalization) - 1, shifted right by at most 60 bits. + auto integral = static_cast(value.f >> -one.e); + FMT_ASSERT(integral != 0, ""); + FMT_ASSERT(integral == value.f >> -one.e, ""); + // The fractional part of scaled value (p2 in Grisu) c = value % one. + uint64_t fractional = value.f & (one.f - 1); + exp = grisu_count_digits(integral); // kappa in Grisu. + // Divide by 10 to prevent overflow. + auto result = handler.on_start(data::powers_of_10_64[exp - 1] << -one.e, + value.f / 10, error * 10, exp); + if (result != digits::more) return result; + // Generate digits for the integral part. This can produce up to 10 digits. + do { + uint32_t digit = 0; + auto divmod_integral = [&](uint32_t divisor) { + digit = integral / divisor; + integral %= divisor; + }; + // This optimization by Milo Yip reduces the number of integer divisions by + // one per iteration. + switch (exp) { + case 10: + divmod_integral(1000000000); + break; + case 9: + divmod_integral(100000000); + break; + case 8: + divmod_integral(10000000); + break; + case 7: + divmod_integral(1000000); + break; + case 6: + divmod_integral(100000); + break; + case 5: + divmod_integral(10000); + break; + case 4: + divmod_integral(1000); + break; + case 3: + divmod_integral(100); + break; + case 2: + divmod_integral(10); + break; + case 1: + digit = integral; + integral = 0; + break; + default: + FMT_ASSERT(false, "invalid number of digits"); + } + --exp; + uint64_t remainder = + (static_cast(integral) << -one.e) + fractional; + result = handler.on_digit(static_cast('0' + digit), + data::powers_of_10_64[exp] << -one.e, remainder, + error, exp, true); + if (result != digits::more) return result; + } while (exp > 0); + // Generate digits for the fractional part. + for (;;) { + fractional *= 10; + error *= 10; + char digit = + static_cast('0' + static_cast(fractional >> -one.e)); + fractional &= one.f - 1; + --exp; + result = handler.on_digit(digit, one.f, fractional, error, exp, false); + if (result != digits::more) return result; + } +} + +// The fixed precision digit handler. +struct fixed_handler { + char* buf; + int size; + int precision; + int exp10; + bool fixed; + + digits::result on_start(uint64_t divisor, uint64_t remainder, uint64_t error, + int& exp) { + // Non-fixed formats require at least one digit and no precision adjustment. + if (!fixed) return digits::more; + // Adjust fixed precision by exponent because it is relative to decimal + // point. + precision += exp + exp10; + // Check if precision is satisfied just by leading zeros, e.g. + // format("{:.2f}", 0.001) gives "0.00" without generating any digits. + if (precision > 0) return digits::more; + if (precision < 0) return digits::done; + auto dir = get_round_direction(divisor, remainder, error); + if (dir == round_direction::unknown) return digits::error; + buf[size++] = dir == round_direction::up ? '1' : '0'; + return digits::done; + } + + digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder, + uint64_t error, int, bool integral) { + FMT_ASSERT(remainder < divisor, ""); + buf[size++] = digit; + if (size < precision) return digits::more; + if (!integral) { + // Check if error * 2 < divisor with overflow prevention. + // The check is not needed for the integral part because error = 1 + // and divisor > (1 << 32) there. + if (error >= divisor || error >= divisor - error) return digits::error; + } else { + FMT_ASSERT(error == 1 && divisor > 2, ""); + } + auto dir = get_round_direction(divisor, remainder, error); + if (dir != round_direction::up) + return dir == round_direction::down ? digits::done : digits::error; + ++buf[size - 1]; + for (int i = size - 1; i > 0 && buf[i] > '9'; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] > '9') { + buf[0] = '1'; + buf[size++] = '0'; + } + return digits::done; + } +}; + +// The shortest representation digit handler. +struct grisu_shortest_handler { + char* buf; + int size; + // Distance between scaled value and upper bound (wp_W in Grisu3). + uint64_t diff; + + digits::result on_start(uint64_t, uint64_t, uint64_t, int&) { + return digits::more; + } + + // Decrement the generated number approaching value from above. + void round(uint64_t d, uint64_t divisor, uint64_t& remainder, + uint64_t error) { + while ( + remainder < d && error - remainder >= divisor && + (remainder + divisor < d || d - remainder >= remainder + divisor - d)) { + --buf[size - 1]; + remainder += divisor; + } + } + + // Implements Grisu's round_weed. + digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder, + uint64_t error, int exp, bool integral) { + buf[size++] = digit; + if (remainder >= error) return digits::more; + uint64_t unit = integral ? 1 : data::powers_of_10_64[-exp]; + uint64_t up = (diff - 1) * unit; // wp_Wup + round(up, divisor, remainder, error); + uint64_t down = (diff + 1) * unit; // wp_Wdown + if (remainder < down && error - remainder >= divisor && + (remainder + divisor < down || + down - remainder > remainder + divisor - down)) { + return digits::error; + } + return 2 * unit <= remainder && remainder <= error - 4 * unit + ? digits::done + : digits::error; + } +}; + +// Formats value using a variation of the Fixed-Precision Positive +// Floating-Point Printout ((FPP)^2) algorithm by Steele & White: +// https://fmt.dev/p372-steele.pdf. +template +void fallback_format(Double d, buffer& buf, int& exp10) { + bigint numerator; // 2 * R in (FPP)^2. + bigint denominator; // 2 * S in (FPP)^2. + // lower and upper are differences between value and corresponding boundaries. + bigint lower; // (M^- in (FPP)^2). + bigint upper_store; // upper's value if different from lower. + bigint* upper = nullptr; // (M^+ in (FPP)^2). + fp value; + // Shift numerator and denominator by an extra bit or two (if lower boundary + // is closer) to make lower and upper integers. This eliminates multiplication + // by 2 during later computations. + // TODO: handle float + int shift = value.assign(d) ? 2 : 1; + uint64_t significand = value.f << shift; + if (value.e >= 0) { + numerator.assign(significand); + numerator <<= value.e; + lower.assign(1); + lower <<= value.e; + if (shift != 1) { + upper_store.assign(1); + upper_store <<= value.e + 1; + upper = &upper_store; + } + denominator.assign_pow10(exp10); + denominator <<= 1; + } else if (exp10 < 0) { + numerator.assign_pow10(-exp10); + lower.assign(numerator); + if (shift != 1) { + upper_store.assign(numerator); + upper_store <<= 1; + upper = &upper_store; + } + numerator *= significand; + denominator.assign(1); + denominator <<= shift - value.e; + } else { + numerator.assign(significand); + denominator.assign_pow10(exp10); + denominator <<= shift - value.e; + lower.assign(1); + if (shift != 1) { + upper_store.assign(1ULL << 1); + upper = &upper_store; + } + } + if (!upper) upper = &lower; + // Invariant: value == (numerator / denominator) * pow(10, exp10). + bool even = (value.f & 1) == 0; + int num_digits = 0; + char* data = buf.data(); + for (;;) { + int digit = numerator.divmod_assign(denominator); + bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower. + // numerator + upper >[=] pow10: + bool high = add_compare(numerator, *upper, denominator) + even > 0; + data[num_digits++] = static_cast('0' + digit); + if (low || high) { + if (!low) { + ++data[num_digits - 1]; + } else if (high) { + int result = add_compare(numerator, numerator, denominator); + // Round half to even. + if (result > 0 || (result == 0 && (digit % 2) != 0)) + ++data[num_digits - 1]; + } + buf.resize(to_unsigned(num_digits)); + exp10 -= num_digits - 1; + return; + } + numerator *= 10; + lower *= 10; + if (upper != &lower) *upper *= 10; + } +} + +// Formats value using the Grisu algorithm +// (https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf) +// if T is a IEEE754 binary32 or binary64 and snprintf otherwise. +template +int format_float(T value, int precision, float_specs specs, buffer& buf) { + static_assert(!std::is_same::value, ""); + FMT_ASSERT(value >= 0, "value is negative"); + + const bool fixed = specs.format == float_format::fixed; + if (value <= 0) { // <= instead of == to silence a warning. + if (precision <= 0 || !fixed) { + buf.push_back('0'); + return 0; + } + buf.resize(to_unsigned(precision)); + std::uninitialized_fill_n(buf.data(), precision, '0'); + return -precision; + } + + if (!specs.use_grisu) return snprintf_float(value, precision, specs, buf); + + int exp = 0; + const int min_exp = -60; // alpha in Grisu. + int cached_exp10 = 0; // K in Grisu. + if (precision < 0) { + fp fp_value; + auto boundaries = specs.binary32 + ? fp_value.assign_float_with_boundaries(value) + : fp_value.assign_with_boundaries(value); + fp_value = normalize(fp_value); + // Find a cached power of 10 such that multiplying value by it will bring + // the exponent in the range [min_exp, -32]. + const fp cached_pow = get_cached_power( + min_exp - (fp_value.e + fp::significand_size), cached_exp10); + // Multiply value and boundaries by the cached power of 10. + fp_value = fp_value * cached_pow; + boundaries.lower = multiply(boundaries.lower, cached_pow.f); + boundaries.upper = multiply(boundaries.upper, cached_pow.f); + assert(min_exp <= fp_value.e && fp_value.e <= -32); + --boundaries.lower; // \tilde{M}^- - 1 ulp -> M^-_{\downarrow}. + ++boundaries.upper; // \tilde{M}^+ + 1 ulp -> M^+_{\uparrow}. + // Numbers outside of (lower, upper) definitely do not round to value. + grisu_shortest_handler handler{buf.data(), 0, + boundaries.upper - fp_value.f}; + auto result = + grisu_gen_digits(fp(boundaries.upper, fp_value.e), + boundaries.upper - boundaries.lower, exp, handler); + if (result == digits::error) { + exp += handler.size - cached_exp10 - 1; + fallback_format(value, buf, exp); + return exp; + } + buf.resize(to_unsigned(handler.size)); + } else { + if (precision > 17) return snprintf_float(value, precision, specs, buf); + fp normalized = normalize(fp(value)); + const auto cached_pow = get_cached_power( + min_exp - (normalized.e + fp::significand_size), cached_exp10); + normalized = normalized * cached_pow; + fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed}; + if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error) + return snprintf_float(value, precision, specs, buf); + int num_digits = handler.size; + if (!fixed) { + // Remove trailing zeros. + while (num_digits > 0 && buf[num_digits - 1] == '0') { + --num_digits; + ++exp; + } + } + buf.resize(to_unsigned(num_digits)); + } + return exp - cached_exp10; +} + +template +int snprintf_float(T value, int precision, float_specs specs, + buffer& buf) { + // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. + FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer"); + static_assert(!std::is_same::value, ""); + + // Subtract 1 to account for the difference in precision since we use %e for + // both general and exponent format. + if (specs.format == float_format::general || + specs.format == float_format::exp) + precision = (precision >= 0 ? precision : 6) - 1; + + // Build the format string. + enum { max_format_size = 7 }; // The longest format is "%#.*Le". + char format[max_format_size]; + char* format_ptr = format; + *format_ptr++ = '%'; + if (specs.showpoint && specs.format == float_format::hex) *format_ptr++ = '#'; + if (precision >= 0) { + *format_ptr++ = '.'; + *format_ptr++ = '*'; + } + if (std::is_same()) *format_ptr++ = 'L'; + *format_ptr++ = specs.format != float_format::hex + ? (specs.format == float_format::fixed ? 'f' : 'e') + : (specs.upper ? 'A' : 'a'); + *format_ptr = '\0'; + + // Format using snprintf. + auto offset = buf.size(); + for (;;) { + auto begin = buf.data() + offset; + auto capacity = buf.capacity() - offset; +#ifdef FMT_FUZZ + if (precision > 100000) + throw std::runtime_error( + "fuzz mode - avoid large allocation inside snprintf"); +#endif + // Suppress the warning about a nonliteral format string. + // Cannot use auto because of a bug in MinGW (#1532). + int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; + int result = precision >= 0 + ? snprintf_ptr(begin, capacity, format, precision, value) + : snprintf_ptr(begin, capacity, format, value); + if (result < 0) { + buf.reserve(buf.capacity() + 1); // The buffer will grow exponentially. + continue; + } + auto size = to_unsigned(result); + // Size equal to capacity means that the last character was truncated. + if (size >= capacity) { + buf.reserve(size + offset + 1); // Add 1 for the terminating '\0'. + continue; + } + auto is_digit = [](char c) { return c >= '0' && c <= '9'; }; + if (specs.format == float_format::fixed) { + if (precision == 0) { + buf.resize(size); + return 0; + } + // Find and remove the decimal point. + auto end = begin + size, p = end; + do { + --p; + } while (is_digit(*p)); + int fraction_size = static_cast(end - p - 1); + std::memmove(p, p + 1, to_unsigned(fraction_size)); + buf.resize(size - 1); + return -fraction_size; + } + if (specs.format == float_format::hex) { + buf.resize(size + offset); + return 0; + } + // Find and parse the exponent. + auto end = begin + size, exp_pos = end; + do { + --exp_pos; + } while (*exp_pos != 'e'); + char sign = exp_pos[1]; + assert(sign == '+' || sign == '-'); + int exp = 0; + auto p = exp_pos + 2; // Skip 'e' and sign. + do { + assert(is_digit(*p)); + exp = exp * 10 + (*p++ - '0'); + } while (p != end); + if (sign == '-') exp = -exp; + int fraction_size = 0; + if (exp_pos != begin + 1) { + // Remove trailing zeros. + auto fraction_end = exp_pos - 1; + while (*fraction_end == '0') --fraction_end; + // Move the fractional part left to get rid of the decimal point. + fraction_size = static_cast(fraction_end - begin - 1); + std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size)); + } + buf.resize(to_unsigned(fraction_size) + offset + 1); + return exp - fraction_size; + } +} + +// A public domain branchless UTF-8 decoder by Christopher Wellons: +// https://github.com/skeeto/branchless-utf8 +/* Decode the next character, c, from buf, reporting errors in e. + * + * Since this is a branchless decoder, four bytes will be read from the + * buffer regardless of the actual length of the next character. This + * means the buffer _must_ have at least three bytes of zero padding + * following the end of the data stream. + * + * Errors are reported in e, which will be non-zero if the parsed + * character was somehow invalid: invalid byte sequence, non-canonical + * encoding, or a surrogate half. + * + * The function returns a pointer to the next character. When an error + * occurs, this pointer will be a guess that depends on the particular + * error, but it will always advance at least one byte. + */ +FMT_FUNC const char* utf8_decode(const char* buf, uint32_t* c, int* e) { + static const char lengths[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 2, 2, 2, 3, 3, 4, 0}; + static const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; + static const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; + static const int shiftc[] = {0, 18, 12, 6, 0}; + static const int shifte[] = {0, 6, 4, 2, 0}; + + auto s = reinterpret_cast(buf); + int len = lengths[s[0] >> 3]; + + // Compute the pointer to the next character early so that the next + // iteration can start working on the next character. Neither Clang + // nor GCC figure out this reordering on their own. + const char* next = buf + len + !len; + + // Assume a four-byte character and load four bytes. Unused bits are + // shifted out. + *c = uint32_t(s[0] & masks[len]) << 18; + *c |= uint32_t(s[1] & 0x3f) << 12; + *c |= uint32_t(s[2] & 0x3f) << 6; + *c |= uint32_t(s[3] & 0x3f) << 0; + *c >>= shiftc[len]; + + // Accumulate the various error conditions. + *e = (*c < mins[len]) << 6; // non-canonical encoding + *e |= ((*c >> 11) == 0x1b) << 7; // surrogate half? + *e |= (*c > 0x10FFFF) << 8; // out of range? + *e |= (s[1] & 0xc0) >> 2; + *e |= (s[2] & 0xc0) >> 4; + *e |= (s[3]) >> 6; + *e ^= 0x2a; // top two bits of each tail byte correct? + *e >>= shifte[len]; + + return next; +} +} // namespace detail + +template <> struct formatter { + format_parse_context::iterator parse(format_parse_context& ctx) { + return ctx.begin(); + } + + format_context::iterator format(const detail::bigint& n, + format_context& ctx) { + auto out = ctx.out(); + bool first = true; + for (auto i = n.bigits_.size(); i > 0; --i) { + auto value = n.bigits_[i - 1u]; + if (first) { + out = format_to(out, "{:x}", value); + first = false; + continue; + } + out = format_to(out, "{:08x}", value); + } + if (n.exp_ > 0) + out = format_to(out, "p{}", n.exp_ * detail::bigint::bigit_bits); + return out; + } +}; + +FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) { + auto transcode = [this](const char* p) { + auto cp = uint32_t(); + auto error = 0; + p = utf8_decode(p, &cp, &error); + if (error != 0) FMT_THROW(std::runtime_error("invalid utf8")); + if (cp <= 0xFFFF) { + buffer_.push_back(static_cast(cp)); + } else { + cp -= 0x10000; + buffer_.push_back(static_cast(0xD800 + (cp >> 10))); + buffer_.push_back(static_cast(0xDC00 + (cp & 0x3FF))); + } + return p; + }; + auto p = s.data(); + const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. + if (s.size() >= block_size) { + for (auto end = p + s.size() - block_size + 1; p < end;) p = transcode(p); + } + if (auto num_chars_left = s.data() + s.size() - p) { + char buf[2 * block_size - 1] = {}; + memcpy(buf, p, to_unsigned(num_chars_left)); + p = buf; + do { + p = transcode(p); + } while (p - buf < num_chars_left); + } + buffer_.push_back(0); +} + +FMT_FUNC void format_system_error(detail::buffer& out, int error_code, + string_view message) FMT_NOEXCEPT { + FMT_TRY { + memory_buffer buf; + buf.resize(inline_buffer_size); + for (;;) { + char* system_message = &buf[0]; + int result = + detail::safe_strerror(error_code, system_message, buf.size()); + if (result == 0) { + format_to(std::back_inserter(out), "{}: {}", message, system_message); + return; + } + if (result != ERANGE) + break; // Can't get error message, report error code instead. + buf.resize(buf.size() * 2); + } + } + FMT_CATCH(...) {} + format_error_code(out, error_code, message); +} + +FMT_FUNC void detail::error_handler::on_error(const char* message) { + FMT_THROW(format_error(message)); +} + +FMT_FUNC void report_system_error(int error_code, + fmt::string_view message) FMT_NOEXCEPT { + report_error(format_system_error, error_code, message); +} + +struct stringifier { + template FMT_INLINE std::string operator()(T value) const { + return to_string(value); + } + std::string operator()(basic_format_arg::handle h) const { + memory_buffer buf; + detail::buffer& base = buf; + format_parse_context parse_ctx({}); + format_context format_ctx(std::back_inserter(base), {}, {}); + h.format(parse_ctx, format_ctx); + return to_string(buf); + } +}; + +FMT_FUNC std::string detail::vformat(string_view format_str, format_args args) { + if (format_str.size() == 2 && equal2(format_str.data(), "{}")) { + auto arg = args.get(0); + if (!arg) error_handler().on_error("argument not found"); + return visit_format_arg(stringifier(), arg); + } + memory_buffer buffer; + detail::vformat_to(buffer, format_str, args); + return to_string(buffer); +} + +FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { + memory_buffer buffer; + detail::vformat_to(buffer, format_str, + basic_format_args>(args)); +#ifdef _WIN32 + auto fd = _fileno(f); + if (_isatty(fd)) { + detail::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size())); + auto written = DWORD(); + if (!WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), + u16.c_str(), static_cast(u16.size()), &written, + nullptr)) { + FMT_THROW(format_error("failed to write to console")); + } + return; + } +#endif + detail::fwrite_fully(buffer.data(), 1, buffer.size(), f); +} + +#ifdef _WIN32 +// Print assuming legacy (non-Unicode) encoding. +FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str, + format_args args) { + memory_buffer buffer; + detail::vformat_to(buffer, format_str, + basic_format_args>(args)); + fwrite_fully(buffer.data(), 1, buffer.size(), f); +} +#endif + +FMT_FUNC void vprint(string_view format_str, format_args args) { + vprint(stdout, format_str, args); +} + +FMT_END_NAMESPACE + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +#endif // FMT_FORMAT_INL_H_ diff --git a/src/third_party/fmt/format.h b/src/third_party/fmt/format.h new file mode 100644 index 0000000..17509b7 --- /dev/null +++ b/src/third_party/fmt/format.h @@ -0,0 +1,3729 @@ +/* + Formatting library for C++ + + Copyright (c) 2012 - present, Victor Zverovich + + 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. + + --- Optional exception to the license --- + + As an exception, if, as a result of your compiling your source code, portions + of this Software are embedded into a machine-executable object form of such + source code, you may redistribute such embedded portions in such object form + without including the above copyright and permission notices. + */ + +#ifndef FMT_FORMAT_H_ +#define FMT_FORMAT_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" + +#ifdef __INTEL_COMPILER +# define FMT_ICC_VERSION __INTEL_COMPILER +#elif defined(__ICL) +# define FMT_ICC_VERSION __ICL +#else +# define FMT_ICC_VERSION 0 +#endif + +#ifdef __NVCC__ +# define FMT_CUDA_VERSION (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__) +#else +# define FMT_CUDA_VERSION 0 +#endif + +#ifdef __has_builtin +# define FMT_HAS_BUILTIN(x) __has_builtin(x) +#else +# define FMT_HAS_BUILTIN(x) 0 +#endif + +#if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_NOINLINE __attribute__((noinline)) +#else +# define FMT_NOINLINE +#endif + +#if __cplusplus == 201103L || __cplusplus == 201402L +# if defined(__clang__) +# define FMT_FALLTHROUGH [[clang::fallthrough]] +# elif FMT_GCC_VERSION >= 700 && !defined(__PGI) && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) +# define FMT_FALLTHROUGH [[gnu::fallthrough]] +# else +# define FMT_FALLTHROUGH +# endif +#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) || \ + (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define FMT_FALLTHROUGH [[fallthrough]] +#else +# define FMT_FALLTHROUGH +#endif + +#ifndef FMT_MAYBE_UNUSED +# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused) +# define FMT_MAYBE_UNUSED [[maybe_unused]] +# else +# define FMT_MAYBE_UNUSED +# endif +#endif + +#ifndef FMT_THROW +# if FMT_EXCEPTIONS +# if FMT_MSC_VER || FMT_NVCC +FMT_BEGIN_NAMESPACE +namespace detail { +template inline void do_throw(const Exception& x) { + // Silence unreachable code warnings in MSVC and NVCC because these + // are nearly impossible to fix in a generic code. + volatile bool b = true; + if (b) throw x; +} +} // namespace detail +FMT_END_NAMESPACE +# define FMT_THROW(x) detail::do_throw(x) +# else +# define FMT_THROW(x) throw x +# endif +# else +# define FMT_THROW(x) \ + do { \ + static_cast(sizeof(x)); \ + FMT_ASSERT(false, ""); \ + } while (false) +# endif +#endif + +#if FMT_EXCEPTIONS +# define FMT_TRY try +# define FMT_CATCH(x) catch (x) +#else +# define FMT_TRY if (true) +# define FMT_CATCH(x) if (false) +#endif + +#ifndef FMT_USE_USER_DEFINED_LITERALS +// EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs. +# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \ + FMT_MSC_VER >= 1900) && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480) +# define FMT_USE_USER_DEFINED_LITERALS 1 +# else +# define FMT_USE_USER_DEFINED_LITERALS 0 +# endif +#endif + +#ifndef FMT_USE_UDL_TEMPLATE +// EDG frontend based compilers (icc, nvcc, etc) and GCC < 6.4 do not properly +// support UDL templates and GCC >= 9 warns about them. +# if FMT_USE_USER_DEFINED_LITERALS && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 501) && \ + ((FMT_GCC_VERSION >= 604 && __cplusplus >= 201402L) || \ + FMT_CLANG_VERSION >= 304) +# define FMT_USE_UDL_TEMPLATE 1 +# else +# define FMT_USE_UDL_TEMPLATE 0 +# endif +#endif + +#ifndef FMT_USE_FLOAT +# define FMT_USE_FLOAT 1 +#endif + +#ifndef FMT_USE_DOUBLE +# define FMT_USE_DOUBLE 1 +#endif + +#ifndef FMT_USE_LONG_DOUBLE +# define FMT_USE_LONG_DOUBLE 1 +#endif + +// __builtin_clz is broken in clang with Microsoft CodeGen: +// https://github.com/fmtlib/fmt/issues/519 +#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz)) && !FMT_MSC_VER +# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) +#endif +#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clzll)) && !FMT_MSC_VER +# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) +#endif + +// Some compilers masquerade as both MSVC and GCC-likes or otherwise support +// __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the +// MSVC intrinsics if the clz and clzll builtins are not available. +#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && !defined(_MANAGED) +# include // _BitScanReverse, _BitScanReverse64 + +FMT_BEGIN_NAMESPACE +namespace detail { +// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. +# ifndef __clang__ +# pragma intrinsic(_BitScanReverse) +# endif +inline uint32_t clz(uint32_t x) { + unsigned long r = 0; + _BitScanReverse(&r, x); + + FMT_ASSERT(x != 0, ""); + // Static analysis complains about using uninitialized data + // "r", but the only way that can happen is if "x" is 0, + // which the callers guarantee to not happen. + FMT_SUPPRESS_MSC_WARNING(6102) + return 31 - r; +} +# define FMT_BUILTIN_CLZ(n) detail::clz(n) + +# if defined(_WIN64) && !defined(__clang__) +# pragma intrinsic(_BitScanReverse64) +# endif + +inline uint32_t clzll(uint64_t x) { + unsigned long r = 0; +# ifdef _WIN64 + _BitScanReverse64(&r, x); +# else + // Scan the high 32 bits. + if (_BitScanReverse(&r, static_cast(x >> 32))) return 63 - (r + 32); + + // Scan the low 32 bits. + _BitScanReverse(&r, static_cast(x)); +# endif + + FMT_ASSERT(x != 0, ""); + // Static analysis complains about using uninitialized data + // "r", but the only way that can happen is if "x" is 0, + // which the callers guarantee to not happen. + FMT_SUPPRESS_MSC_WARNING(6102) + return 63 - r; +} +# define FMT_BUILTIN_CLZLL(n) detail::clzll(n) +} // namespace detail +FMT_END_NAMESPACE +#endif + +// Enable the deprecated numeric alignment. +#ifndef FMT_DEPRECATED_NUMERIC_ALIGN +# define FMT_DEPRECATED_NUMERIC_ALIGN 0 +#endif + +FMT_BEGIN_NAMESPACE +namespace detail { + +// An equivalent of `*reinterpret_cast(&source)` that doesn't have +// undefined behavior (e.g. due to type aliasing). +// Example: uint64_t d = bit_cast(2.718); +template +inline Dest bit_cast(const Source& source) { + static_assert(sizeof(Dest) == sizeof(Source), "size mismatch"); + Dest dest; + std::memcpy(&dest, &source, sizeof(dest)); + return dest; +} + +inline bool is_big_endian() { + const auto u = 1u; + struct bytes { + char data[sizeof(u)]; + }; + return bit_cast(u).data[0] == 0; +} + +// A fallback implementation of uintptr_t for systems that lack it. +struct fallback_uintptr { + unsigned char value[sizeof(void*)]; + + fallback_uintptr() = default; + explicit fallback_uintptr(const void* p) { + *this = bit_cast(p); + if (is_big_endian()) { + for (size_t i = 0, j = sizeof(void*) - 1; i < j; ++i, --j) + std::swap(value[i], value[j]); + } + } +}; +#ifdef UINTPTR_MAX +using uintptr_t = ::uintptr_t; +inline uintptr_t to_uintptr(const void* p) { return bit_cast(p); } +#else +using uintptr_t = fallback_uintptr; +inline fallback_uintptr to_uintptr(const void* p) { + return fallback_uintptr(p); +} +#endif + +// Returns the largest possible value for type T. Same as +// std::numeric_limits::max() but shorter and not affected by the max macro. +template constexpr T max_value() { + return (std::numeric_limits::max)(); +} +template constexpr int num_bits() { + return std::numeric_limits::digits; +} +// std::numeric_limits::digits may return 0 for 128-bit ints. +template <> constexpr int num_bits() { return 128; } +template <> constexpr int num_bits() { return 128; } +template <> constexpr int num_bits() { + return static_cast(sizeof(void*) * + std::numeric_limits::digits); +} + +FMT_INLINE void assume(bool condition) { + (void)condition; +#if FMT_HAS_BUILTIN(__builtin_assume) + __builtin_assume(condition); +#endif +} + +// A workaround for gcc 4.8 to make void_t work in a SFINAE context. +template struct void_t_impl { using type = void; }; + +template +using void_t = typename detail::void_t_impl::type; + +// An approximation of iterator_t for pre-C++20 systems. +template +using iterator_t = decltype(std::begin(std::declval())); +template using sentinel_t = decltype(std::end(std::declval())); + +// Detect the iterator category of *any* given type in a SFINAE-friendly way. +// Unfortunately, older implementations of std::iterator_traits are not safe +// for use in a SFINAE-context. +template +struct iterator_category : std::false_type {}; + +template struct iterator_category { + using type = std::random_access_iterator_tag; +}; + +template +struct iterator_category> { + using type = typename It::iterator_category; +}; + +// Detect if *any* given type models the OutputIterator concept. +template class is_output_iterator { + // Check for mutability because all iterator categories derived from + // std::input_iterator_tag *may* also meet the requirements of an + // OutputIterator, thereby falling into the category of 'mutable iterators' + // [iterator.requirements.general] clause 4. The compiler reveals this + // property only at the point of *actually dereferencing* the iterator! + template + static decltype(*(std::declval())) test(std::input_iterator_tag); + template static char& test(std::output_iterator_tag); + template static const char& test(...); + + using type = decltype(test(typename iterator_category::type{})); + + public: + enum { value = !std::is_const>::value }; +}; + +// A workaround for std::string not having mutable data() until C++17. +template inline Char* get_data(std::basic_string& s) { + return &s[0]; +} +template +inline typename Container::value_type* get_data(Container& c) { + return c.data(); +} + +#if defined(_SECURE_SCL) && _SECURE_SCL +// Make a checked iterator to avoid MSVC warnings. +template using checked_ptr = stdext::checked_array_iterator; +template checked_ptr make_checked(T* p, size_t size) { + return {p, size}; +} +#else +template using checked_ptr = T*; +template inline T* make_checked(T* p, size_t) { return p; } +#endif + +template ::value)> +#if FMT_CLANG_VERSION +__attribute__((no_sanitize("undefined"))) +#endif +inline checked_ptr +reserve(std::back_insert_iterator it, size_t n) { + Container& c = get_container(it); + size_t size = c.size(); + c.resize(size + n); + return make_checked(get_data(c) + size, n); +} + +template inline Iterator& reserve(Iterator& it, size_t) { + return it; +} + +template ::value)> +inline std::back_insert_iterator base_iterator( + std::back_insert_iterator& it, + checked_ptr) { + return it; +} + +template +inline Iterator base_iterator(Iterator, Iterator it) { + return it; +} + +// An output iterator that counts the number of objects written to it and +// discards them. +class counting_iterator { + private: + size_t count_; + + public: + using iterator_category = std::output_iterator_tag; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = void; + using _Unchecked_type = counting_iterator; // Mark iterator as checked. + + struct value_type { + template void operator=(const T&) {} + }; + + counting_iterator() : count_(0) {} + + size_t count() const { return count_; } + + counting_iterator& operator++() { + ++count_; + return *this; + } + + counting_iterator operator++(int) { + auto it = *this; + ++*this; + return it; + } + + value_type operator*() const { return {}; } +}; + +template class truncating_iterator_base { + protected: + OutputIt out_; + size_t limit_; + size_t count_; + + truncating_iterator_base(OutputIt out, size_t limit) + : out_(out), limit_(limit), count_(0) {} + + public: + using iterator_category = std::output_iterator_tag; + using value_type = typename std::iterator_traits::value_type; + using difference_type = void; + using pointer = void; + using reference = void; + using _Unchecked_type = + truncating_iterator_base; // Mark iterator as checked. + + OutputIt base() const { return out_; } + size_t count() const { return count_; } +}; + +// An output iterator that truncates the output and counts the number of objects +// written to it. +template ::value_type>::type> +class truncating_iterator; + +template +class truncating_iterator + : public truncating_iterator_base { + mutable typename truncating_iterator_base::value_type blackhole_; + + public: + using value_type = typename truncating_iterator_base::value_type; + + truncating_iterator(OutputIt out, size_t limit) + : truncating_iterator_base(out, limit) {} + + truncating_iterator& operator++() { + if (this->count_++ < this->limit_) ++this->out_; + return *this; + } + + truncating_iterator operator++(int) { + auto it = *this; + ++*this; + return it; + } + + value_type& operator*() const { + return this->count_ < this->limit_ ? *this->out_ : blackhole_; + } +}; + +template +class truncating_iterator + : public truncating_iterator_base { + public: + truncating_iterator(OutputIt out, size_t limit) + : truncating_iterator_base(out, limit) {} + + template truncating_iterator& operator=(T val) { + if (this->count_++ < this->limit_) *this->out_++ = val; + return *this; + } + + truncating_iterator& operator++() { return *this; } + truncating_iterator& operator++(int) { return *this; } + truncating_iterator& operator*() { return *this; } +}; + +template +inline size_t count_code_points(basic_string_view s) { + return s.size(); +} + +// Counts the number of code points in a UTF-8 string. +inline size_t count_code_points(basic_string_view s) { + const char* data = s.data(); + size_t num_code_points = 0; + for (size_t i = 0, size = s.size(); i != size; ++i) { + if ((data[i] & 0xc0) != 0x80) ++num_code_points; + } + return num_code_points; +} + +inline size_t count_code_points(basic_string_view s) { + return count_code_points(basic_string_view( + reinterpret_cast(s.data()), s.size())); +} + +template +inline size_t code_point_index(basic_string_view s, size_t n) { + size_t size = s.size(); + return n < size ? n : size; +} + +// Calculates the index of the nth code point in a UTF-8 string. +inline size_t code_point_index(basic_string_view s, size_t n) { + const char8_type* data = s.data(); + size_t num_code_points = 0; + for (size_t i = 0, size = s.size(); i != size; ++i) { + if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) { + return i; + } + } + return s.size(); +} + +template +using needs_conversion = bool_constant< + std::is_same::value_type, + char>::value && + std::is_same::value>; + +template ::value)> +OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) { + return std::copy(begin, end, it); +} + +template ::value)> +OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) { + return std::transform(begin, end, it, + [](char c) { return static_cast(c); }); +} + +#ifndef FMT_USE_GRISU +# define FMT_USE_GRISU 1 +#endif + +template constexpr bool use_grisu() { + return FMT_USE_GRISU && std::numeric_limits::is_iec559 && + sizeof(T) <= sizeof(double); +} + +template +template +void buffer::append(const U* begin, const U* end) { + size_t new_size = size_ + to_unsigned(end - begin); + reserve(new_size); + std::uninitialized_copy(begin, end, + make_checked(ptr_ + size_, capacity_ - size_)); + size_ = new_size; +} +} // namespace detail + +// The number of characters to store in the basic_memory_buffer object itself +// to avoid dynamic memory allocation. +enum { inline_buffer_size = 500 }; + +/** + \rst + A dynamically growing memory buffer for trivially copyable/constructible types + with the first ``SIZE`` elements stored in the object itself. + + You can use one of the following type aliases for common character types: + + +----------------+------------------------------+ + | Type | Definition | + +================+==============================+ + | memory_buffer | basic_memory_buffer | + +----------------+------------------------------+ + | wmemory_buffer | basic_memory_buffer | + +----------------+------------------------------+ + + **Example**:: + + fmt::memory_buffer out; + format_to(out, "The answer is {}.", 42); + + This will append the following output to the ``out`` object: + + .. code-block:: none + + The answer is 42. + + The output can be converted to an ``std::string`` with ``to_string(out)``. + \endrst + */ +template > +class basic_memory_buffer : public detail::buffer { + private: + T store_[SIZE]; + + // Don't inherit from Allocator avoid generating type_info for it. + Allocator alloc_; + + // Deallocate memory allocated by the buffer. + void deallocate() { + T* data = this->data(); + if (data != store_) alloc_.deallocate(data, this->capacity()); + } + + protected: + void grow(size_t size) FMT_OVERRIDE; + + public: + using value_type = T; + using const_reference = const T&; + + explicit basic_memory_buffer(const Allocator& alloc = Allocator()) + : alloc_(alloc) { + this->set(store_, SIZE); + } + ~basic_memory_buffer() FMT_OVERRIDE { deallocate(); } + + private: + // Move data from other to this buffer. + void move(basic_memory_buffer& other) { + alloc_ = std::move(other.alloc_); + T* data = other.data(); + size_t size = other.size(), capacity = other.capacity(); + if (data == other.store_) { + this->set(store_, capacity); + std::uninitialized_copy(other.store_, other.store_ + size, + detail::make_checked(store_, capacity)); + } else { + this->set(data, capacity); + // Set pointer to the inline array so that delete is not called + // when deallocating. + other.set(other.store_, 0); + } + this->resize(size); + } + + public: + /** + \rst + Constructs a :class:`fmt::basic_memory_buffer` object moving the content + of the other object to it. + \endrst + */ + basic_memory_buffer(basic_memory_buffer&& other) FMT_NOEXCEPT { move(other); } + + /** + \rst + Moves the content of the other ``basic_memory_buffer`` object to this one. + \endrst + */ + basic_memory_buffer& operator=(basic_memory_buffer&& other) FMT_NOEXCEPT { + FMT_ASSERT(this != &other, ""); + deallocate(); + move(other); + return *this; + } + + // Returns a copy of the allocator associated with this buffer. + Allocator get_allocator() const { return alloc_; } +}; + +template +void basic_memory_buffer::grow(size_t size) { +#ifdef FMT_FUZZ + if (size > 5000) throw std::runtime_error("fuzz mode - won't grow that much"); +#endif + size_t old_capacity = this->capacity(); + size_t new_capacity = old_capacity + old_capacity / 2; + if (size > new_capacity) new_capacity = size; + T* old_data = this->data(); + T* new_data = + std::allocator_traits::allocate(alloc_, new_capacity); + // The following code doesn't throw, so the raw pointer above doesn't leak. + std::uninitialized_copy(old_data, old_data + this->size(), + detail::make_checked(new_data, new_capacity)); + this->set(new_data, new_capacity); + // deallocate must not throw according to the standard, but even if it does, + // the buffer already uses the new storage and will deallocate it in + // destructor. + if (old_data != store_) alloc_.deallocate(old_data, old_capacity); +} + +using memory_buffer = basic_memory_buffer; +using wmemory_buffer = basic_memory_buffer; + +template +struct is_contiguous> : std::true_type { +}; + +/** A formatting error such as invalid format string. */ +FMT_CLASS_API +class FMT_API format_error : public std::runtime_error { + public: + explicit format_error(const char* message) : std::runtime_error(message) {} + explicit format_error(const std::string& message) + : std::runtime_error(message) {} + format_error(const format_error&) = default; + format_error& operator=(const format_error&) = default; + format_error(format_error&&) = default; + format_error& operator=(format_error&&) = default; + ~format_error() FMT_NOEXCEPT FMT_OVERRIDE; +}; + +namespace detail { + +template +using is_signed = + std::integral_constant::is_signed || + std::is_same::value>; + +// Returns true if value is negative, false otherwise. +// Same as `value < 0` but doesn't produce warnings if T is an unsigned type. +template ::value)> +FMT_CONSTEXPR bool is_negative(T value) { + return value < 0; +} +template ::value)> +FMT_CONSTEXPR bool is_negative(T) { + return false; +} + +template ::value)> +FMT_CONSTEXPR bool is_supported_floating_point(T) { + return (std::is_same::value && FMT_USE_FLOAT) || + (std::is_same::value && FMT_USE_DOUBLE) || + (std::is_same::value && FMT_USE_LONG_DOUBLE); +} + +// Smallest of uint32_t, uint64_t, uint128_t that is large enough to +// represent all values of T. +template +using uint32_or_64_or_128_t = + conditional_t() <= 32, uint32_t, + conditional_t() <= 64, uint64_t, uint128_t>>; + +// Static data is placed in this class template for the header-only config. +template struct FMT_EXTERN_TEMPLATE_API basic_data { + static const uint64_t powers_of_10_64[]; + static const uint32_t zero_or_powers_of_10_32[]; + static const uint64_t zero_or_powers_of_10_64[]; + static const uint64_t pow10_significands[]; + static const int16_t pow10_exponents[]; + // GCC generates slightly better code for pairs than chars. + using digit_pair = char[2]; + static const digit_pair digits[]; + static const char hex_digits[]; + static const char foreground_color[]; + static const char background_color[]; + static const char reset_color[5]; + static const wchar_t wreset_color[5]; + static const char signs[]; + static const char left_padding_shifts[5]; + static const char right_padding_shifts[5]; +}; + +#ifndef FMT_EXPORTED +FMT_EXTERN template struct basic_data; +#endif + +// This is a struct rather than an alias to avoid shadowing warnings in gcc. +struct data : basic_data<> {}; + +#ifdef FMT_BUILTIN_CLZLL +// Returns the number of decimal digits in n. Leading zeros are not counted +// except for n == 0 in which case count_digits returns 1. +inline int count_digits(uint64_t n) { + // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 + // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits. + int t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; + return t - (n < data::zero_or_powers_of_10_64[t]) + 1; +} +#else +// Fallback version of count_digits used when __builtin_clz is not available. +inline int count_digits(uint64_t n) { + int count = 1; + for (;;) { + // Integer division is slow so do it for a group of four digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + if (n < 10) return count; + if (n < 100) return count + 1; + if (n < 1000) return count + 2; + if (n < 10000) return count + 3; + n /= 10000u; + count += 4; + } +} +#endif + +#if FMT_USE_INT128 +inline int count_digits(uint128_t n) { + int count = 1; + for (;;) { + // Integer division is slow so do it for a group of four digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + if (n < 10) return count; + if (n < 100) return count + 1; + if (n < 1000) return count + 2; + if (n < 10000) return count + 3; + n /= 10000U; + count += 4; + } +} +#endif + +// Counts the number of digits in n. BITS = log2(radix). +template inline int count_digits(UInt n) { + int num_digits = 0; + do { + ++num_digits; + } while ((n >>= BITS) != 0); + return num_digits; +} + +template <> int count_digits<4>(detail::fallback_uintptr n); + +#if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_ALWAYS_INLINE inline __attribute__((always_inline)) +#else +# define FMT_ALWAYS_INLINE +#endif + +#ifdef FMT_BUILTIN_CLZ +// Optional version of count_digits for better performance on 32-bit platforms. +inline int count_digits(uint32_t n) { + int t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; + return t - (n < data::zero_or_powers_of_10_32[t]) + 1; +} +#endif + +template constexpr int digits10() FMT_NOEXCEPT { + return std::numeric_limits::digits10; +} +template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } +template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } + +template FMT_API std::string grouping_impl(locale_ref loc); +template inline std::string grouping(locale_ref loc) { + return grouping_impl(loc); +} +template <> inline std::string grouping(locale_ref loc) { + return grouping_impl(loc); +} + +template FMT_API Char thousands_sep_impl(locale_ref loc); +template inline Char thousands_sep(locale_ref loc) { + return Char(thousands_sep_impl(loc)); +} +template <> inline wchar_t thousands_sep(locale_ref loc) { + return thousands_sep_impl(loc); +} + +template FMT_API Char decimal_point_impl(locale_ref loc); +template inline Char decimal_point(locale_ref loc) { + return Char(decimal_point_impl(loc)); +} +template <> inline wchar_t decimal_point(locale_ref loc) { + return decimal_point_impl(loc); +} + +// Compares two characters for equality. +template bool equal2(const Char* lhs, const char* rhs) { + return lhs[0] == rhs[0] && lhs[1] == rhs[1]; +} +inline bool equal2(const char* lhs, const char* rhs) { + return memcmp(lhs, rhs, 2) == 0; +} + +// Copies two characters from src to dst. +template void copy2(Char* dst, const char* src) { + *dst++ = static_cast(*src++); + *dst = static_cast(*src); +} +inline void copy2(char* dst, const char* src) { memcpy(dst, src, 2); } + +template struct format_decimal_result { + Iterator begin; + Iterator end; +}; + +// Formats a decimal unsigned integer value writing into out pointing to a +// buffer of specified size. The caller must ensure that the buffer is large +// enough. +template +inline format_decimal_result format_decimal(Char* out, UInt value, + int size) { + FMT_ASSERT(size >= count_digits(value), "invalid digit count"); + out += size; + Char* end = out; + while (value >= 100) { + // Integer division is slow so do it for a group of two digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + out -= 2; + copy2(out, data::digits[value % 100]); + value /= 100; + } + if (value < 10) { + *--out = static_cast('0' + value); + return {out, end}; + } + out -= 2; + copy2(out, data::digits[value]); + return {out, end}; +} + +template >::value)> +inline format_decimal_result format_decimal(Iterator out, UInt value, + int num_digits) { + // Buffer should be large enough to hold all digits (<= digits10 + 1). + enum { max_size = digits10() + 1 }; + Char buffer[2 * max_size]; + auto end = format_decimal(buffer, value, num_digits).end; + return {out, detail::copy_str(buffer, end, out)}; +} + +template +inline Char* format_uint(Char* buffer, UInt value, int num_digits, + bool upper = false) { + buffer += num_digits; + Char* end = buffer; + do { + const char* digits = upper ? "0123456789ABCDEF" : data::hex_digits; + unsigned digit = (value & ((1 << BASE_BITS) - 1)); + *--buffer = static_cast(BASE_BITS < 4 ? static_cast('0' + digit) + : digits[digit]); + } while ((value >>= BASE_BITS) != 0); + return end; +} + +template +Char* format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, + bool = false) { + auto char_digits = std::numeric_limits::digits / 4; + int start = (num_digits + char_digits - 1) / char_digits - 1; + if (int start_digits = num_digits % char_digits) { + unsigned value = n.value[start--]; + buffer = format_uint(buffer, value, start_digits); + } + for (; start >= 0; --start) { + unsigned value = n.value[start]; + buffer += char_digits; + auto p = buffer; + for (int i = 0; i < char_digits; ++i) { + unsigned digit = (value & ((1 << BASE_BITS) - 1)); + *--p = static_cast(data::hex_digits[digit]); + value >>= BASE_BITS; + } + } + return buffer; +} + +template +inline It format_uint(It out, UInt value, int num_digits, bool upper = false) { + // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1). + char buffer[num_bits() / BASE_BITS + 1]; + format_uint(buffer, value, num_digits, upper); + return detail::copy_str(buffer, buffer + num_digits, out); +} + +// A converter from UTF-8 to UTF-16. +class utf8_to_utf16 { + private: + wmemory_buffer buffer_; + + public: + FMT_API explicit utf8_to_utf16(string_view s); + operator wstring_view() const { return {&buffer_[0], size()}; } + size_t size() const { return buffer_.size() - 1; } + const wchar_t* c_str() const { return &buffer_[0]; } + std::wstring str() const { return {&buffer_[0], size()}; } +}; + +template struct null {}; + +// Workaround an array initialization issue in gcc 4.8. +template struct fill_t { + private: + enum { max_size = 4 }; + Char data_[max_size]; + unsigned char size_; + + public: + FMT_CONSTEXPR void operator=(basic_string_view s) { + auto size = s.size(); + if (size > max_size) { + FMT_THROW(format_error("invalid fill")); + return; + } + for (size_t i = 0; i < size; ++i) data_[i] = s[i]; + size_ = static_cast(size); + } + + size_t size() const { return size_; } + const Char* data() const { return data_; } + + FMT_CONSTEXPR Char& operator[](size_t index) { return data_[index]; } + FMT_CONSTEXPR const Char& operator[](size_t index) const { + return data_[index]; + } + + static FMT_CONSTEXPR fill_t make() { + auto fill = fill_t(); + fill[0] = Char(' '); + fill.size_ = 1; + return fill; + } +}; +} // namespace detail + +// We cannot use enum classes as bit fields because of a gcc bug +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414. +namespace align { +enum type { none, left, right, center, numeric }; +} +using align_t = align::type; + +namespace sign { +enum type { none, minus, plus, space }; +} +using sign_t = sign::type; + +// Format specifiers for built-in and string types. +template struct basic_format_specs { + int width; + int precision; + char type; + align_t align : 4; + sign_t sign : 3; + bool alt : 1; // Alternate form ('#'). + detail::fill_t fill; + + constexpr basic_format_specs() + : width(0), + precision(-1), + type(0), + align(align::none), + sign(sign::none), + alt(false), + fill(detail::fill_t::make()) {} +}; + +using format_specs = basic_format_specs; + +namespace detail { + +// A floating-point presentation format. +enum class float_format : unsigned char { + general, // General: exponent notation or fixed point based on magnitude. + exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3. + fixed, // Fixed point with the default precision of 6, e.g. 0.0012. + hex +}; + +struct float_specs { + int precision; + float_format format : 8; + sign_t sign : 8; + bool upper : 1; + bool locale : 1; + bool binary32 : 1; + bool use_grisu : 1; + bool showpoint : 1; +}; + +// Writes the exponent exp in the form "[+-]d{2,3}" to buffer. +template It write_exponent(int exp, It it) { + FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); + if (exp < 0) { + *it++ = static_cast('-'); + exp = -exp; + } else { + *it++ = static_cast('+'); + } + if (exp >= 100) { + const char* top = data::digits[exp / 100]; + if (exp >= 1000) *it++ = static_cast(top[0]); + *it++ = static_cast(top[1]); + exp %= 100; + } + const char* d = data::digits[exp]; + *it++ = static_cast(d[0]); + *it++ = static_cast(d[1]); + return it; +} + +template class float_writer { + private: + // The number is given as v = digits_ * pow(10, exp_). + const char* digits_; + int num_digits_; + int exp_; + size_t size_; + float_specs specs_; + Char decimal_point_; + + template It prettify(It it) const { + // pow(10, full_exp - 1) <= v <= pow(10, full_exp). + int full_exp = num_digits_ + exp_; + if (specs_.format == float_format::exp) { + // Insert a decimal point after the first digit and add an exponent. + *it++ = static_cast(*digits_); + int num_zeros = specs_.precision - num_digits_; + if (num_digits_ > 1 || specs_.showpoint) *it++ = decimal_point_; + it = copy_str(digits_ + 1, digits_ + num_digits_, it); + if (num_zeros > 0 && specs_.showpoint) + it = std::fill_n(it, num_zeros, static_cast('0')); + *it++ = static_cast(specs_.upper ? 'E' : 'e'); + return write_exponent(full_exp - 1, it); + } + if (num_digits_ <= full_exp) { + // 1234e7 -> 12340000000[.0+] + it = copy_str(digits_, digits_ + num_digits_, it); + it = std::fill_n(it, full_exp - num_digits_, static_cast('0')); + if (specs_.showpoint || specs_.precision < 0) { + *it++ = decimal_point_; + int num_zeros = specs_.precision - full_exp; + if (num_zeros <= 0) { + if (specs_.format != float_format::fixed) + *it++ = static_cast('0'); + return it; + } +#ifdef FMT_FUZZ + if (num_zeros > 5000) + throw std::runtime_error("fuzz mode - avoiding excessive cpu use"); +#endif + it = std::fill_n(it, num_zeros, static_cast('0')); + } + } else if (full_exp > 0) { + // 1234e-2 -> 12.34[0+] + it = copy_str(digits_, digits_ + full_exp, it); + if (!specs_.showpoint) { + // Remove trailing zeros. + int num_digits = num_digits_; + while (num_digits > full_exp && digits_[num_digits - 1] == '0') + --num_digits; + if (num_digits != full_exp) *it++ = decimal_point_; + return copy_str(digits_ + full_exp, digits_ + num_digits, it); + } + *it++ = decimal_point_; + it = copy_str(digits_ + full_exp, digits_ + num_digits_, it); + if (specs_.precision > num_digits_) { + // Add trailing zeros. + int num_zeros = specs_.precision - num_digits_; + it = std::fill_n(it, num_zeros, static_cast('0')); + } + } else { + // 1234e-6 -> 0.001234 + *it++ = static_cast('0'); + int num_zeros = -full_exp; + int num_digits = num_digits_; + if (num_digits == 0 && specs_.precision >= 0 && + specs_.precision < num_zeros) { + num_zeros = specs_.precision; + } + // Remove trailing zeros. + if (!specs_.showpoint) + while (num_digits > 0 && digits_[num_digits - 1] == '0') --num_digits; + if (num_zeros != 0 || num_digits != 0 || specs_.showpoint) { + *it++ = decimal_point_; + it = std::fill_n(it, num_zeros, static_cast('0')); + it = copy_str(digits_, digits_ + num_digits, it); + } + } + return it; + } + + public: + float_writer(const char* digits, int num_digits, int exp, float_specs specs, + Char decimal_point) + : digits_(digits), + num_digits_(num_digits), + exp_(exp), + specs_(specs), + decimal_point_(decimal_point) { + int full_exp = num_digits + exp - 1; + int precision = specs.precision > 0 ? specs.precision : 16; + if (specs_.format == float_format::general && + !(full_exp >= -4 && full_exp < precision)) { + specs_.format = float_format::exp; + } + size_ = prettify(counting_iterator()).count(); + size_ += specs.sign ? 1 : 0; + } + + size_t size() const { return size_; } + + template It operator()(It it) const { + if (specs_.sign) *it++ = static_cast(data::signs[specs_.sign]); + return prettify(it); + } +}; + +template +int format_float(T value, int precision, float_specs specs, buffer& buf); + +// Formats a floating-point number with snprintf. +template +int snprintf_float(T value, int precision, float_specs specs, + buffer& buf); + +template T promote_float(T value) { return value; } +inline double promote_float(float value) { return static_cast(value); } + +template +FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) { + switch (spec) { + case 0: + case 'd': + handler.on_dec(); + break; + case 'x': + case 'X': + handler.on_hex(); + break; + case 'b': + case 'B': + handler.on_bin(); + break; + case 'o': + handler.on_oct(); + break; +#ifdef FMT_DEPRECATED_N_SPECIFIER + case 'n': +#endif + case 'L': + handler.on_num(); + break; + case 'c': + handler.on_chr(); + break; + default: + handler.on_error(); + } +} + +template +FMT_CONSTEXPR float_specs parse_float_type_spec( + const basic_format_specs& specs, ErrorHandler&& eh = {}) { + auto result = float_specs(); + result.showpoint = specs.alt; + switch (specs.type) { + case 0: + result.format = float_format::general; + result.showpoint |= specs.precision > 0; + break; + case 'G': + result.upper = true; + FMT_FALLTHROUGH; + case 'g': + result.format = float_format::general; + break; + case 'E': + result.upper = true; + FMT_FALLTHROUGH; + case 'e': + result.format = float_format::exp; + result.showpoint |= specs.precision != 0; + break; + case 'F': + result.upper = true; + FMT_FALLTHROUGH; + case 'f': + result.format = float_format::fixed; + result.showpoint |= specs.precision != 0; + break; + case 'A': + result.upper = true; + FMT_FALLTHROUGH; + case 'a': + result.format = float_format::hex; + break; +#ifdef FMT_DEPRECATED_N_SPECIFIER + case 'n': +#endif + case 'L': + result.locale = true; + break; + default: + eh.on_error("invalid type specifier"); + break; + } + return result; +} + +template +FMT_CONSTEXPR void handle_char_specs(const basic_format_specs* specs, + Handler&& handler) { + if (!specs) return handler.on_char(); + if (specs->type && specs->type != 'c') return handler.on_int(); + if (specs->align == align::numeric || specs->sign != sign::none || specs->alt) + handler.on_error("invalid format specifier for char"); + handler.on_char(); +} + +template +FMT_CONSTEXPR void handle_cstring_type_spec(Char spec, Handler&& handler) { + if (spec == 0 || spec == 's') + handler.on_string(); + else if (spec == 'p') + handler.on_pointer(); + else + handler.on_error("invalid type specifier"); +} + +template +FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler&& eh) { + if (spec != 0 && spec != 's') eh.on_error("invalid type specifier"); +} + +template +FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) { + if (spec != 0 && spec != 'p') eh.on_error("invalid type specifier"); +} + +template class int_type_checker : private ErrorHandler { + public: + FMT_CONSTEXPR explicit int_type_checker(ErrorHandler eh) : ErrorHandler(eh) {} + + FMT_CONSTEXPR void on_dec() {} + FMT_CONSTEXPR void on_hex() {} + FMT_CONSTEXPR void on_bin() {} + FMT_CONSTEXPR void on_oct() {} + FMT_CONSTEXPR void on_num() {} + FMT_CONSTEXPR void on_chr() {} + + FMT_CONSTEXPR void on_error() { + ErrorHandler::on_error("invalid type specifier"); + } +}; + +template +class char_specs_checker : public ErrorHandler { + private: + char type_; + + public: + FMT_CONSTEXPR char_specs_checker(char type, ErrorHandler eh) + : ErrorHandler(eh), type_(type) {} + + FMT_CONSTEXPR void on_int() { + handle_int_type_spec(type_, int_type_checker(*this)); + } + FMT_CONSTEXPR void on_char() {} +}; + +template +class cstring_type_checker : public ErrorHandler { + public: + FMT_CONSTEXPR explicit cstring_type_checker(ErrorHandler eh) + : ErrorHandler(eh) {} + + FMT_CONSTEXPR void on_string() {} + FMT_CONSTEXPR void on_pointer() {} +}; + +template +FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t& fill) { + auto fill_size = fill.size(); + if (fill_size == 1) return std::fill_n(it, n, fill[0]); + for (size_t i = 0; i < n; ++i) it = std::copy_n(fill.data(), fill_size, it); + return it; +} + +// Writes the output of f, padded according to format specifications in specs. +// size: output size in code units. +// width: output display width in (terminal) column positions. +template +inline OutputIt write_padded(OutputIt out, + const basic_format_specs& specs, size_t size, + size_t width, const F& f) { + static_assert(align == align::left || align == align::right, ""); + unsigned spec_width = to_unsigned(specs.width); + size_t padding = spec_width > width ? spec_width - width : 0; + auto* shifts = align == align::left ? data::left_padding_shifts + : data::right_padding_shifts; + size_t left_padding = padding >> shifts[specs.align]; + auto it = reserve(out, size + padding * specs.fill.size()); + it = fill(it, left_padding, specs.fill); + it = f(it); + it = fill(it, padding - left_padding, specs.fill); + return base_iterator(out, it); +} + +template +inline OutputIt write_padded(OutputIt out, + const basic_format_specs& specs, size_t size, + const F& f) { + return write_padded(out, specs, size, size, f); +} + +template +OutputIt write_bytes(OutputIt out, string_view bytes, + const basic_format_specs& specs) { + using iterator = remove_reference_t; + return write_padded(out, specs, bytes.size(), [bytes](iterator it) { + const char* data = bytes.data(); + return copy_str(data, data + bytes.size(), it); + }); +} + +// Data for write_int that doesn't depend on output iterator type. It is used to +// avoid template code bloat. +template struct write_int_data { + size_t size; + size_t padding; + + write_int_data(int num_digits, string_view prefix, + const basic_format_specs& specs) + : size(prefix.size() + to_unsigned(num_digits)), padding(0) { + if (specs.align == align::numeric) { + auto width = to_unsigned(specs.width); + if (width > size) { + padding = width - size; + size = width; + } + } else if (specs.precision > num_digits) { + size = prefix.size() + to_unsigned(specs.precision); + padding = to_unsigned(specs.precision - num_digits); + } + } +}; + +// Writes an integer in the format +// +// where are written by f(it). +template +OutputIt write_int(OutputIt out, int num_digits, string_view prefix, + const basic_format_specs& specs, F f) { + auto data = write_int_data(num_digits, prefix, specs); + using iterator = remove_reference_t; + return write_padded(out, specs, data.size, [=](iterator it) { + if (prefix.size() != 0) + it = copy_str(prefix.begin(), prefix.end(), it); + it = std::fill_n(it, data.padding, static_cast('0')); + return f(it); + }); +} + +template +OutputIt write(OutputIt out, basic_string_view s, + const basic_format_specs& specs) { + auto data = s.data(); + auto size = s.size(); + if (specs.precision >= 0 && to_unsigned(specs.precision) < size) + size = code_point_index(s, to_unsigned(specs.precision)); + auto width = specs.width != 0 + ? count_code_points(basic_string_view(data, size)) + : 0; + using iterator = remove_reference_t; + return write_padded(out, specs, size, width, [=](iterator it) { + return copy_str(data, data + size, it); + }); +} + +// The handle_int_type_spec handler that writes an integer. +template struct int_writer { + OutputIt out; + locale_ref locale; + const basic_format_specs& specs; + UInt abs_value; + char prefix[4]; + unsigned prefix_size; + + using iterator = + remove_reference_t(), 0))>; + + string_view get_prefix() const { return string_view(prefix, prefix_size); } + + template + int_writer(OutputIt output, locale_ref loc, Int value, + const basic_format_specs& s) + : out(output), + locale(loc), + specs(s), + abs_value(static_cast(value)), + prefix_size(0) { + static_assert(std::is_same, UInt>::value, ""); + if (is_negative(value)) { + prefix[0] = '-'; + ++prefix_size; + abs_value = 0 - abs_value; + } else if (specs.sign != sign::none && specs.sign != sign::minus) { + prefix[0] = specs.sign == sign::plus ? '+' : ' '; + ++prefix_size; + } + } + + void on_dec() { + auto num_digits = count_digits(abs_value); + out = write_int( + out, num_digits, get_prefix(), specs, [this, num_digits](iterator it) { + return format_decimal(it, abs_value, num_digits).end; + }); + } + + void on_hex() { + if (specs.alt) { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = specs.type; + } + int num_digits = count_digits<4>(abs_value); + out = write_int(out, num_digits, get_prefix(), specs, + [this, num_digits](iterator it) { + return format_uint<4, Char>(it, abs_value, num_digits, + specs.type != 'x'); + }); + } + + void on_bin() { + if (specs.alt) { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = static_cast(specs.type); + } + int num_digits = count_digits<1>(abs_value); + out = write_int(out, num_digits, get_prefix(), specs, + [this, num_digits](iterator it) { + return format_uint<1, Char>(it, abs_value, num_digits); + }); + } + + void on_oct() { + int num_digits = count_digits<3>(abs_value); + if (specs.alt && specs.precision <= num_digits && abs_value != 0) { + // Octal prefix '0' is counted as a digit, so only add it if precision + // is not greater than the number of digits. + prefix[prefix_size++] = '0'; + } + out = write_int(out, num_digits, get_prefix(), specs, + [this, num_digits](iterator it) { + return format_uint<3, Char>(it, abs_value, num_digits); + }); + } + + enum { sep_size = 1 }; + + void on_num() { + std::string groups = grouping(locale); + if (groups.empty()) return on_dec(); + auto sep = thousands_sep(locale); + if (!sep) return on_dec(); + int num_digits = count_digits(abs_value); + int size = num_digits, n = num_digits; + std::string::const_iterator group = groups.cbegin(); + while (group != groups.cend() && n > *group && *group > 0 && + *group != max_value()) { + size += sep_size; + n -= *group; + ++group; + } + if (group == groups.cend()) size += sep_size * ((n - 1) / groups.back()); + char digits[40]; + format_decimal(digits, abs_value, num_digits); + basic_memory_buffer buffer; + size += prefix_size; + buffer.resize(size); + basic_string_view s(&sep, sep_size); + // Index of a decimal digit with the least significant digit having index 0. + int digit_index = 0; + group = groups.cbegin(); + auto p = buffer.data() + size; + for (int i = num_digits - 1; i >= 0; --i) { + *--p = static_cast(digits[i]); + if (*group <= 0 || ++digit_index % *group != 0 || + *group == max_value()) + continue; + if (group + 1 != groups.cend()) { + digit_index = 0; + ++group; + } + p -= s.size(); + std::uninitialized_copy(s.data(), s.data() + s.size(), + make_checked(p, s.size())); + } + if (prefix_size != 0) p[-1] = static_cast('-'); + using iterator = remove_reference_t; + auto data = buffer.data(); + out = write_padded(out, specs, size, size, [=](iterator it) { + return copy_str(data, data + size, it); + }); + } + + void on_chr() { *out++ = static_cast(abs_value); } + + FMT_NORETURN void on_error() { + FMT_THROW(format_error("invalid type specifier")); + } +}; + +template +OutputIt write_nonfinite(OutputIt out, bool isinf, + const basic_format_specs& specs, + const float_specs& fspecs) { + auto str = + isinf ? (fspecs.upper ? "INF" : "inf") : (fspecs.upper ? "NAN" : "nan"); + constexpr size_t str_size = 3; + auto sign = fspecs.sign; + auto size = str_size + (sign ? 1 : 0); + using iterator = remove_reference_t; + return write_padded(out, specs, size, [=](iterator it) { + if (sign) *it++ = static_cast(data::signs[sign]); + return copy_str(str, str + str_size, it); + }); +} + +template ::value)> +OutputIt write(OutputIt out, T value, basic_format_specs specs, + locale_ref loc = {}) { + if (const_check(!is_supported_floating_point(value))) return out; + float_specs fspecs = parse_float_type_spec(specs); + fspecs.sign = specs.sign; + if (std::signbit(value)) { // value < 0 is false for NaN so use signbit. + fspecs.sign = sign::minus; + value = -value; + } else if (fspecs.sign == sign::minus) { + fspecs.sign = sign::none; + } + + if (!std::isfinite(value)) + return write_nonfinite(out, std::isinf(value), specs, fspecs); + + if (specs.align == align::numeric && fspecs.sign) { + auto it = reserve(out, 1); + *it++ = static_cast(data::signs[fspecs.sign]); + out = base_iterator(out, it); + fspecs.sign = sign::none; + if (specs.width != 0) --specs.width; + } + + memory_buffer buffer; + if (fspecs.format == float_format::hex) { + if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]); + snprintf_float(promote_float(value), specs.precision, fspecs, buffer); + return write_bytes(out, {buffer.data(), buffer.size()}, specs); + } + int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6; + if (fspecs.format == float_format::exp) { + if (precision == max_value()) + FMT_THROW(format_error("number is too big")); + else + ++precision; + } + if (const_check(std::is_same())) fspecs.binary32 = true; + fspecs.use_grisu = use_grisu(); + int exp = format_float(promote_float(value), precision, fspecs, buffer); + fspecs.precision = precision; + Char point = + fspecs.locale ? decimal_point(loc) : static_cast('.'); + float_writer w(buffer.data(), static_cast(buffer.size()), exp, + fspecs, point); + return write_padded(out, specs, w.size(), w); +} + +template ::value)> +OutputIt write(OutputIt out, T value) { + if (const_check(!is_supported_floating_point(value))) return out; + auto fspecs = float_specs(); + if (std::signbit(value)) { // value < 0 is false for NaN so use signbit. + fspecs.sign = sign::minus; + value = -value; + } + + auto specs = basic_format_specs(); + if (!std::isfinite(value)) + return write_nonfinite(out, std::isinf(value), specs, fspecs); + + memory_buffer buffer; + int precision = -1; + if (const_check(std::is_same())) fspecs.binary32 = true; + fspecs.use_grisu = use_grisu(); + int exp = format_float(promote_float(value), precision, fspecs, buffer); + fspecs.precision = precision; + float_writer w(buffer.data(), static_cast(buffer.size()), exp, + fspecs, static_cast('.')); + return base_iterator(out, w(reserve(out, w.size()))); +} + +template +OutputIt write_char(OutputIt out, Char value, + const basic_format_specs& specs) { + using iterator = remove_reference_t; + return write_padded(out, specs, 1, [=](iterator it) { + *it++ = value; + return it; + }); +} + +template +OutputIt write_ptr(OutputIt out, UIntPtr value, + const basic_format_specs* specs) { + int num_digits = count_digits<4>(value); + auto size = to_unsigned(num_digits) + size_t(2); + using iterator = remove_reference_t; + auto write = [=](iterator it) { + *it++ = static_cast('0'); + *it++ = static_cast('x'); + return format_uint<4, Char>(it, value, num_digits); + }; + return specs ? write_padded(out, *specs, size, write) + : base_iterator(out, write(reserve(out, size))); +} + +template struct is_integral : std::is_integral {}; +template <> struct is_integral : std::true_type {}; +template <> struct is_integral : std::true_type {}; + +template +OutputIt write(OutputIt out, monostate) { + FMT_ASSERT(false, ""); + return out; +} + +template ::value)> +OutputIt write(OutputIt out, string_view value) { + auto it = reserve(out, value.size()); + it = copy_str(value.begin(), value.end(), it); + return base_iterator(out, it); +} + +template +OutputIt write(OutputIt out, basic_string_view value) { + auto it = reserve(out, value.size()); + it = std::copy(value.begin(), value.end(), it); + return base_iterator(out, it); +} + +template ::value && + !std::is_same::value && + !std::is_same::value)> +OutputIt write(OutputIt out, T value) { + auto abs_value = static_cast>(value); + bool negative = is_negative(value); + // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. + if (negative) abs_value = ~abs_value + 1; + int num_digits = count_digits(abs_value); + auto it = reserve(out, (negative ? 1 : 0) + static_cast(num_digits)); + if (negative) *it++ = static_cast('-'); + it = format_decimal(it, abs_value, num_digits).end; + return base_iterator(out, it); +} + +template +OutputIt write(OutputIt out, bool value) { + return write(out, string_view(value ? "true" : "false")); +} + +template +OutputIt write(OutputIt out, Char value) { + auto it = reserve(out, 1); + *it++ = value; + return base_iterator(out, it); +} + +template +OutputIt write(OutputIt out, const Char* value) { + if (!value) { + FMT_THROW(format_error("string pointer is null")); + } else { + auto length = std::char_traits::length(value); + out = write(out, basic_string_view(value, length)); + } + return out; +} + +template +OutputIt write(OutputIt out, const void* value) { + return write_ptr(out, to_uintptr(value), nullptr); +} + +template +auto write(OutputIt out, const T& value) -> typename std::enable_if< + mapped_type_constant>::value == + type::custom_type, + OutputIt>::type { + basic_format_context ctx(out, {}, {}); + return formatter().format(value, ctx); +} + +// An argument visitor that formats the argument and writes it via the output +// iterator. It's a class and not a generic lambda for compatibility with C++11. +template struct default_arg_formatter { + using context = basic_format_context; + + OutputIt out; + basic_format_args args; + locale_ref loc; + + template OutputIt operator()(T value) { + return write(out, value); + } + + OutputIt operator()(typename basic_format_arg::handle handle) { + basic_format_parse_context parse_ctx({}); + basic_format_context format_ctx(out, args, loc); + handle.format(parse_ctx, format_ctx); + return format_ctx.out(); + } +}; + +template +class arg_formatter_base { + public: + using iterator = OutputIt; + using char_type = Char; + using format_specs = basic_format_specs; + + private: + iterator out_; + locale_ref locale_; + format_specs* specs_; + + // Attempts to reserve space for n extra characters in the output range. + // Returns a pointer to the reserved range or a reference to out_. + auto reserve(size_t n) -> decltype(detail::reserve(out_, n)) { + return detail::reserve(out_, n); + } + + using reserve_iterator = remove_reference_t(), 0))>; + + template void write_int(T value, const format_specs& spec) { + using uint_type = uint32_or_64_or_128_t; + int_writer w(out_, locale_, value, spec); + handle_int_type_spec(spec.type, w); + out_ = w.out; + } + + void write(char value) { + auto&& it = reserve(1); + *it++ = value; + } + + template ::value)> + void write(Ch value) { + out_ = detail::write(out_, value); + } + + void write(string_view value) { + auto&& it = reserve(value.size()); + it = copy_str(value.begin(), value.end(), it); + } + void write(wstring_view value) { + static_assert(std::is_same::value, ""); + auto&& it = reserve(value.size()); + it = std::copy(value.begin(), value.end(), it); + } + + template + void write(const Ch* s, size_t size, const format_specs& specs) { + auto width = specs.width != 0 + ? count_code_points(basic_string_view(s, size)) + : 0; + out_ = write_padded(out_, specs, size, width, [=](reserve_iterator it) { + return copy_str(s, s + size, it); + }); + } + + template + void write(basic_string_view s, const format_specs& specs = {}) { + out_ = detail::write(out_, s, specs); + } + + void write_pointer(const void* p) { + out_ = write_ptr(out_, to_uintptr(p), specs_); + } + + struct char_spec_handler : ErrorHandler { + arg_formatter_base& formatter; + Char value; + + char_spec_handler(arg_formatter_base& f, Char val) + : formatter(f), value(val) {} + + void on_int() { + // char is only formatted as int if there are specs. + formatter.write_int(static_cast(value), *formatter.specs_); + } + void on_char() { + if (formatter.specs_) + formatter.out_ = write_char(formatter.out_, value, *formatter.specs_); + else + formatter.write(value); + } + }; + + struct cstring_spec_handler : error_handler { + arg_formatter_base& formatter; + const Char* value; + + cstring_spec_handler(arg_formatter_base& f, const Char* val) + : formatter(f), value(val) {} + + void on_string() { formatter.write(value); } + void on_pointer() { formatter.write_pointer(value); } + }; + + protected: + iterator out() { return out_; } + format_specs* specs() { return specs_; } + + void write(bool value) { + if (specs_) + write(string_view(value ? "true" : "false"), *specs_); + else + out_ = detail::write(out_, value); + } + + void write(const Char* value) { + if (!value) { + FMT_THROW(format_error("string pointer is null")); + } else { + auto length = std::char_traits::length(value); + basic_string_view sv(value, length); + specs_ ? write(sv, *specs_) : write(sv); + } + } + + public: + arg_formatter_base(OutputIt out, format_specs* s, locale_ref loc) + : out_(out), locale_(loc), specs_(s) {} + + iterator operator()(monostate) { + FMT_ASSERT(false, "invalid argument type"); + return out_; + } + + template ::value)> + FMT_INLINE iterator operator()(T value) { + if (specs_) + write_int(value, *specs_); + else + out_ = detail::write(out_, value); + return out_; + } + + iterator operator()(Char value) { + handle_char_specs(specs_, + char_spec_handler(*this, static_cast(value))); + return out_; + } + + iterator operator()(bool value) { + if (specs_ && specs_->type) return (*this)(value ? 1 : 0); + write(value != 0); + return out_; + } + + template ::value)> + iterator operator()(T value) { + auto specs = specs_ ? *specs_ : format_specs(); + if (const_check(is_supported_floating_point(value))) + out_ = detail::write(out_, value, specs, locale_); + else + FMT_ASSERT(false, "unsupported float argument type"); + return out_; + } + + iterator operator()(const Char* value) { + if (!specs_) return write(value), out_; + handle_cstring_type_spec(specs_->type, cstring_spec_handler(*this, value)); + return out_; + } + + iterator operator()(basic_string_view value) { + if (specs_) { + check_string_type_spec(specs_->type, error_handler()); + write(value, *specs_); + } else { + write(value); + } + return out_; + } + + iterator operator()(const void* value) { + if (specs_) check_pointer_type_spec(specs_->type, error_handler()); + write_pointer(value); + return out_; + } +}; + +template FMT_CONSTEXPR bool is_name_start(Char c) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; +} + +// Parses the range [begin, end) as an unsigned integer. This function assumes +// that the range is non-empty and the first character is a digit. +template +FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end, + ErrorHandler&& eh) { + FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); + unsigned value = 0; + // Convert to unsigned to prevent a warning. + constexpr unsigned max_int = max_value(); + unsigned big = max_int / 10; + do { + // Check for overflow. + if (value > big) { + value = max_int + 1; + break; + } + value = value * 10 + unsigned(*begin - '0'); + ++begin; + } while (begin != end && '0' <= *begin && *begin <= '9'); + if (value > max_int) eh.on_error("number is too big"); + return static_cast(value); +} + +template class custom_formatter { + private: + using char_type = typename Context::char_type; + + basic_format_parse_context& parse_ctx_; + Context& ctx_; + + public: + explicit custom_formatter(basic_format_parse_context& parse_ctx, + Context& ctx) + : parse_ctx_(parse_ctx), ctx_(ctx) {} + + bool operator()(typename basic_format_arg::handle h) const { + h.format(parse_ctx_, ctx_); + return true; + } + + template bool operator()(T) const { return false; } +}; + +template +using is_integer = + bool_constant::value && !std::is_same::value && + !std::is_same::value && + !std::is_same::value>; + +template class width_checker { + public: + explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {} + + template ::value)> + FMT_CONSTEXPR unsigned long long operator()(T value) { + if (is_negative(value)) handler_.on_error("negative width"); + return static_cast(value); + } + + template ::value)> + FMT_CONSTEXPR unsigned long long operator()(T) { + handler_.on_error("width is not integer"); + return 0; + } + + private: + ErrorHandler& handler_; +}; + +template class precision_checker { + public: + explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {} + + template ::value)> + FMT_CONSTEXPR unsigned long long operator()(T value) { + if (is_negative(value)) handler_.on_error("negative precision"); + return static_cast(value); + } + + template ::value)> + FMT_CONSTEXPR unsigned long long operator()(T) { + handler_.on_error("precision is not integer"); + return 0; + } + + private: + ErrorHandler& handler_; +}; + +// A format specifier handler that sets fields in basic_format_specs. +template class specs_setter { + public: + explicit FMT_CONSTEXPR specs_setter(basic_format_specs& specs) + : specs_(specs) {} + + FMT_CONSTEXPR specs_setter(const specs_setter& other) + : specs_(other.specs_) {} + + FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; } + FMT_CONSTEXPR void on_fill(basic_string_view fill) { + specs_.fill = fill; + } + FMT_CONSTEXPR void on_plus() { specs_.sign = sign::plus; } + FMT_CONSTEXPR void on_minus() { specs_.sign = sign::minus; } + FMT_CONSTEXPR void on_space() { specs_.sign = sign::space; } + FMT_CONSTEXPR void on_hash() { specs_.alt = true; } + + FMT_CONSTEXPR void on_zero() { + specs_.align = align::numeric; + specs_.fill[0] = Char('0'); + } + + FMT_CONSTEXPR void on_width(int width) { specs_.width = width; } + FMT_CONSTEXPR void on_precision(int precision) { + specs_.precision = precision; + } + FMT_CONSTEXPR void end_precision() {} + + FMT_CONSTEXPR void on_type(Char type) { + specs_.type = static_cast(type); + } + + protected: + basic_format_specs& specs_; +}; + +template class numeric_specs_checker { + public: + FMT_CONSTEXPR numeric_specs_checker(ErrorHandler& eh, detail::type arg_type) + : error_handler_(eh), arg_type_(arg_type) {} + + FMT_CONSTEXPR void require_numeric_argument() { + if (!is_arithmetic_type(arg_type_)) + error_handler_.on_error("format specifier requires numeric argument"); + } + + FMT_CONSTEXPR void check_sign() { + require_numeric_argument(); + if (is_integral_type(arg_type_) && arg_type_ != type::int_type && + arg_type_ != type::long_long_type && arg_type_ != type::char_type) { + error_handler_.on_error("format specifier requires signed argument"); + } + } + + FMT_CONSTEXPR void check_precision() { + if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type) + error_handler_.on_error("precision not allowed for this argument type"); + } + + private: + ErrorHandler& error_handler_; + detail::type arg_type_; +}; + +// A format specifier handler that checks if specifiers are consistent with the +// argument type. +template class specs_checker : public Handler { + private: + numeric_specs_checker checker_; + + // Suppress an MSVC warning about using this in initializer list. + FMT_CONSTEXPR Handler& error_handler() { return *this; } + + public: + FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type) + : Handler(handler), checker_(error_handler(), arg_type) {} + + FMT_CONSTEXPR specs_checker(const specs_checker& other) + : Handler(other), checker_(error_handler(), other.arg_type_) {} + + FMT_CONSTEXPR void on_align(align_t align) { + if (align == align::numeric) checker_.require_numeric_argument(); + Handler::on_align(align); + } + + FMT_CONSTEXPR void on_plus() { + checker_.check_sign(); + Handler::on_plus(); + } + + FMT_CONSTEXPR void on_minus() { + checker_.check_sign(); + Handler::on_minus(); + } + + FMT_CONSTEXPR void on_space() { + checker_.check_sign(); + Handler::on_space(); + } + + FMT_CONSTEXPR void on_hash() { + checker_.require_numeric_argument(); + Handler::on_hash(); + } + + FMT_CONSTEXPR void on_zero() { + checker_.require_numeric_argument(); + Handler::on_zero(); + } + + FMT_CONSTEXPR void end_precision() { checker_.check_precision(); } +}; + +template