--- /dev/null
+# 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
--- /dev/null
+.git
+**/.vagrant
--- /dev/null
+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
--- /dev/null
+# Run Clang-Format on all code to follow the new code style.
+f5795cdbc0703d80ab21f39c49bb2384ea2429ba
--- /dev/null
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: bug
+assignees: ''
+
+---
+### How to reproduce ###
+<!-- Steps to reproduce the behavior: -->
+
+1.
+2.
+3.
+
+### Actual behavior ###
+<!-- What happened? -->
+
+
+
+### Expected behavior ###
+<!-- What did you expect to happen? -->
+
+
+
+### Environment ###
+<!-- Which ccache version did you use? -->
+
+ccache version X.Y.Z
+
+<!-- If applicable, add more info such as compiler, OS and file system below: -->
+
--- /dev/null
+---
+name: Feature request
+about: Suggest a new feature for this project
+title: ''
+labels: feature
+assignees: ''
+
+---
+<!-- Describe below how you want ccache to work or behave: -->
+
--- /dev/null
+---
+name: Improvement
+about: Suggest an improvement that is neither a bug fix nor a new feature
+title: ''
+labels: improvement
+assignees: ''
+
+---
+### Description ###
+<!-- Please describe your improvement suggestion below: -->
+
--- /dev/null
+---
+name: Question
+about: Ask for support or make an enquiry
+title: ''
+labels: support
+assignees: ''
+
+---
+### Question ###
+<!-- What do you want help with or know about? -->
--- /dev/null
+<!--
+ Thanks for contributing to ccache! Please read
+ https://github.com/ccache/ccache/blob/master/CONTRIBUTING.md#contributing-code
+ before submitting the pull request.
+
+ Please describe what the pull request is about. If it fixes a bug or
+ implements a feature that exists as a ccache issue, state which one. If it
+ implements a feature, please describe what it does and motivate why you think
+ that it would be a good idea for ccache.
+-->
--- /dev/null
+# 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.
--- /dev/null
+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
--- /dev/null
+# 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/
--- /dev/null
+Anders F Björklund <anders.f.bjorklund@gmail.com>
+Anders F Björklund <anders.f.bjorklund@gmail.com> <anders@itension.se>
+Anders F Björklund <anders.f.bjorklund@gmail.com> <anders@psqr.se>
+Andrew Boie <andrew.p.boie@intel.com>
+Arne Hasselbring <arne.hasselbring@googlemail.com>
+Bernhard Bauer <bauerb@chromium.org> <bauerb@google.com>
+Chiaki Ishikawa <ishikawa@yk.rim.or.jp>
+Clemens Rabe <clemens.rabe@gmail.com> <clemens.rabe@clemensrabe.de>
+Clemens Rabe <clemens.rabe@gmail.com> <crabe@gmx.de>
+Doug Anderson <dianders@disordat.com>
+Erik Flodin <erik@ejohansson.se>
+Hongli Lai <hongli@phusion.nl>
+Jonny Yu <yingshen.yu@gmail.com>
+Kona Blend <kona8lend@gmail.com>
+Leanid Chaika <leanid.chaika@gmail.com>
+Luboš Luňák <l.lunak@centrum.cz> <l.lunak@suse.cz>
+Martin Ettl <ettl.martin78@gmail.com>
+Mizuha Himuraki <mocha.java.cchip@gmail.com>
+Paul Bunch <paulbunc@gmail.com>
+Per Nordlöw <per.nordlow@autoliv.com>
+Peter Budai <peterbudai@hotmail.com>
+Ramiro Polla <ramiro.polla@gmail.com>
+Ramiro Polla <ramiro.polla@gmail.com> <ramiro@mac-vbox-ubuntu910.(none)>
+Ryan Brown <ryb@ableton.com>
+Thomas Otto <39962140+totph@users.noreply.github.com>
+Thomas Röfer <Thomas.Roefer@dfki.de>
+Tor Arne Vestbø <tor.arne.vestbo@qt.io> <torarnv@gmail.com>
+Ville Skyttä <ville.skytta@iki.fi>
+Xavier René-Corail <xavier.renecorail@gmail.com>
--- /dev/null
+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 "$<CONFIGURATION>")
+ 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()
## 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?
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 <http://uncrustify.sourceforge.net> 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.
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
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 <http://www.gnu.org/licenses/>.
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
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
-<http://www.gnu.org/licenses/>.
+<https://www.gnu.org/licenses/>.
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
-<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+<https://www.gnu.org/philosophy/why-not-lgpl.html>.
+++ /dev/null
-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.
--- /dev/null
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ 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.
-ccache copyright and license
+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
+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
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
-------------------------------------------------------------------------------
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
+<https://github.com/pmconrad/tinydnssec>.
-------------------------------------------------------------------------------
- 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 <conrad@quisquis.de>
+
+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 <http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------------
-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 <https://github.com/hrydgard/minitrace>.
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
+<https://github.com/martinmoene/optional-lite> 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 <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.
+src/third_party/nonstd/string_view.hpp
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This alternative implementation of `std::string_view` was downloaded from
+<https://github.com/martinmoene/string-view-lite> and has the following license
+text:
- 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.
-------------------------------------------------------------------------------
+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 <http://zlib.net> with the
-following license:
+xxHash - Extremely Fast Hash algorithm. Copied from xxHash v0.8.0 downloaded
+from <https://github.com/Cyan4973/xxHash/releases>.
-------------------------------------------------------------------------------
- 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.
-------------------------------------------------------------------------------
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>\r
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"\r
- "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">\r
-<head>\r
-<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />\r
-<meta name="generator" content="AsciiDoc 9.0.0rc1" />\r
-<title>ccache copyright and license</title>\r
-<style type="text/css">\r
-/* Shared CSS for AsciiDoc xhtml11 and html5 backends */\r
-\r
-/* Default font. */\r
-body {\r
- font-family: Georgia,serif;\r
-}\r
-\r
-/* Title font. */\r
-h1, h2, h3, h4, h5, h6,\r
-div.title, caption.title,\r
-thead, p.table.header,\r
-#toctitle,\r
-#author, #revnumber, #revdate, #revremark,\r
-#footer {\r
- font-family: Arial,Helvetica,sans-serif;\r
-}\r
-\r
-body {\r
- margin: 1em 5% 1em 5%;\r
-}\r
-\r
-a {\r
- color: blue;\r
- text-decoration: underline;\r
-}\r
-a:visited {\r
- color: fuchsia;\r
-}\r
-\r
-em {\r
- font-style: italic;\r
- color: navy;\r
-}\r
-\r
-strong {\r
- font-weight: bold;\r
- color: #083194;\r
-}\r
-\r
-h1, h2, h3, h4, h5, h6 {\r
- color: #527bbd;\r
- margin-top: 1.2em;\r
- margin-bottom: 0.5em;\r
- line-height: 1.3;\r
-}\r
-\r
-h1, h2, h3 {\r
- border-bottom: 2px solid silver;\r
-}\r
-h2 {\r
- padding-top: 0.5em;\r
-}\r
-h3 {\r
- float: left;\r
-}\r
-h3 + * {\r
- clear: left;\r
-}\r
-h5 {\r
- font-size: 1.0em;\r
-}\r
-\r
-div.sectionbody {\r
- margin-left: 0;\r
-}\r
-\r
-hr {\r
- border: 1px solid silver;\r
-}\r
-\r
-p {\r
- margin-top: 0.5em;\r
- margin-bottom: 0.5em;\r
-}\r
-\r
-ul, ol, li > p {\r
- margin-top: 0;\r
-}\r
-ul > li { color: #aaa; }\r
-ul > li > * { color: black; }\r
-\r
-.monospaced, code, pre {\r
- font-family: "Courier New", Courier, monospace;\r
- font-size: inherit;\r
- color: navy;\r
- padding: 0;\r
- margin: 0;\r
-}\r
-pre {\r
- white-space: pre-wrap;\r
-}\r
-\r
-#author {\r
- color: #527bbd;\r
- font-weight: bold;\r
- font-size: 1.1em;\r
-}\r
-#email {\r
-}\r
-#revnumber, #revdate, #revremark {\r
-}\r
-\r
-#footer {\r
- font-size: small;\r
- border-top: 2px solid silver;\r
- padding-top: 0.5em;\r
- margin-top: 4.0em;\r
-}\r
-#footer-text {\r
- float: left;\r
- padding-bottom: 0.5em;\r
-}\r
-#footer-badges {\r
- float: right;\r
- padding-bottom: 0.5em;\r
-}\r
-\r
-#preamble {\r
- margin-top: 1.5em;\r
- margin-bottom: 1.5em;\r
-}\r
-div.imageblock, div.exampleblock, div.verseblock,\r
-div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,\r
-div.admonitionblock {\r
- margin-top: 1.0em;\r
- margin-bottom: 1.5em;\r
-}\r
-div.admonitionblock {\r
- margin-top: 2.0em;\r
- margin-bottom: 2.0em;\r
- margin-right: 10%;\r
- color: #606060;\r
-}\r
-\r
-div.content { /* Block element content. */\r
- padding: 0;\r
-}\r
-\r
-/* Block element titles. */\r
-div.title, caption.title {\r
- color: #527bbd;\r
- font-weight: bold;\r
- text-align: left;\r
- margin-top: 1.0em;\r
- margin-bottom: 0.5em;\r
-}\r
-div.title + * {\r
- margin-top: 0;\r
-}\r
-\r
-td div.title:first-child {\r
- margin-top: 0.0em;\r
-}\r
-div.content div.title:first-child {\r
- margin-top: 0.0em;\r
-}\r
-div.content + div.title {\r
- margin-top: 0.0em;\r
-}\r
-\r
-div.sidebarblock > div.content {\r
- background: #ffffee;\r
- border: 1px solid #dddddd;\r
- border-left: 4px solid #f0f0f0;\r
- padding: 0.5em;\r
-}\r
-\r
-div.listingblock > div.content {\r
- border: 1px solid #dddddd;\r
- border-left: 5px solid #f0f0f0;\r
- background: #f8f8f8;\r
- padding: 0.5em;\r
-}\r
-\r
-div.quoteblock, div.verseblock {\r
- padding-left: 1.0em;\r
- margin-left: 1.0em;\r
- margin-right: 10%;\r
- border-left: 5px solid #f0f0f0;\r
- color: #888;\r
-}\r
-\r
-div.quoteblock > div.attribution {\r
- padding-top: 0.5em;\r
- text-align: right;\r
-}\r
-\r
-div.verseblock > pre.content {\r
- font-family: inherit;\r
- font-size: inherit;\r
-}\r
-div.verseblock > div.attribution {\r
- padding-top: 0.75em;\r
- text-align: left;\r
-}\r
-/* DEPRECATED: Pre version 8.2.7 verse style literal block. */\r
-div.verseblock + div.attribution {\r
- text-align: left;\r
-}\r
-\r
-div.admonitionblock .icon {\r
- vertical-align: top;\r
- font-size: 1.1em;\r
- font-weight: bold;\r
- text-decoration: underline;\r
- color: #527bbd;\r
- padding-right: 0.5em;\r
-}\r
-div.admonitionblock td.content {\r
- padding-left: 0.5em;\r
- border-left: 3px solid #dddddd;\r
-}\r
-\r
-div.exampleblock > div.content {\r
- border-left: 3px solid #dddddd;\r
- padding-left: 0.5em;\r
-}\r
-\r
-div.imageblock div.content { padding-left: 0; }\r
-span.image img { border-style: none; vertical-align: text-bottom; }\r
-a.image:visited { color: white; }\r
-\r
-dl {\r
- margin-top: 0.8em;\r
- margin-bottom: 0.8em;\r
-}\r
-dt {\r
- margin-top: 0.5em;\r
- margin-bottom: 0;\r
- font-style: normal;\r
- color: navy;\r
-}\r
-dd > *:first-child {\r
- margin-top: 0.1em;\r
-}\r
-\r
-ul, ol {\r
- list-style-position: outside;\r
-}\r
-ol.arabic {\r
- list-style-type: decimal;\r
-}\r
-ol.loweralpha {\r
- list-style-type: lower-alpha;\r
-}\r
-ol.upperalpha {\r
- list-style-type: upper-alpha;\r
-}\r
-ol.lowerroman {\r
- list-style-type: lower-roman;\r
-}\r
-ol.upperroman {\r
- list-style-type: upper-roman;\r
-}\r
-\r
-div.compact ul, div.compact ol,\r
-div.compact p, div.compact p,\r
-div.compact div, div.compact div {\r
- margin-top: 0.1em;\r
- margin-bottom: 0.1em;\r
-}\r
-\r
-tfoot {\r
- font-weight: bold;\r
-}\r
-td > div.verse {\r
- white-space: pre;\r
-}\r
-\r
-div.hdlist {\r
- margin-top: 0.8em;\r
- margin-bottom: 0.8em;\r
-}\r
-div.hdlist tr {\r
- padding-bottom: 15px;\r
-}\r
-dt.hdlist1.strong, td.hdlist1.strong {\r
- font-weight: bold;\r
-}\r
-td.hdlist1 {\r
- vertical-align: top;\r
- font-style: normal;\r
- padding-right: 0.8em;\r
- color: navy;\r
-}\r
-td.hdlist2 {\r
- vertical-align: top;\r
-}\r
-div.hdlist.compact tr {\r
- margin: 0;\r
- padding-bottom: 0;\r
-}\r
-\r
-.comment {\r
- background: yellow;\r
-}\r
-\r
-.footnote, .footnoteref {\r
- font-size: 0.8em;\r
-}\r
-\r
-span.footnote, span.footnoteref {\r
- vertical-align: super;\r
-}\r
-\r
-#footnotes {\r
- margin: 20px 0 20px 0;\r
- padding: 7px 0 0 0;\r
-}\r
-\r
-#footnotes div.footnote {\r
- margin: 0 0 5px 0;\r
-}\r
-\r
-#footnotes hr {\r
- border: none;\r
- border-top: 1px solid silver;\r
- height: 1px;\r
- text-align: left;\r
- margin-left: 0;\r
- width: 20%;\r
- min-width: 100px;\r
-}\r
-\r
-div.colist td {\r
- padding-right: 0.5em;\r
- padding-bottom: 0.3em;\r
- vertical-align: top;\r
-}\r
-div.colist td img {\r
- margin-top: 0.3em;\r
-}\r
-\r
-@media print {\r
- #footer-badges { display: none; }\r
-}\r
-\r
-#toc {\r
- margin-bottom: 2.5em;\r
-}\r
-\r
-#toctitle {\r
- color: #527bbd;\r
- font-size: 1.1em;\r
- font-weight: bold;\r
- margin-top: 1.0em;\r
- margin-bottom: 0.1em;\r
-}\r
-\r
-div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {\r
- margin-top: 0;\r
- margin-bottom: 0;\r
-}\r
-div.toclevel2 {\r
- margin-left: 2em;\r
- font-size: 0.9em;\r
-}\r
-div.toclevel3 {\r
- margin-left: 4em;\r
- font-size: 0.9em;\r
-}\r
-div.toclevel4 {\r
- margin-left: 6em;\r
- font-size: 0.9em;\r
-}\r
-\r
-span.aqua { color: aqua; }\r
-span.black { color: black; }\r
-span.blue { color: blue; }\r
-span.fuchsia { color: fuchsia; }\r
-span.gray { color: gray; }\r
-span.green { color: green; }\r
-span.lime { color: lime; }\r
-span.maroon { color: maroon; }\r
-span.navy { color: navy; }\r
-span.olive { color: olive; }\r
-span.purple { color: purple; }\r
-span.red { color: red; }\r
-span.silver { color: silver; }\r
-span.teal { color: teal; }\r
-span.white { color: white; }\r
-span.yellow { color: yellow; }\r
-\r
-span.aqua-background { background: aqua; }\r
-span.black-background { background: black; }\r
-span.blue-background { background: blue; }\r
-span.fuchsia-background { background: fuchsia; }\r
-span.gray-background { background: gray; }\r
-span.green-background { background: green; }\r
-span.lime-background { background: lime; }\r
-span.maroon-background { background: maroon; }\r
-span.navy-background { background: navy; }\r
-span.olive-background { background: olive; }\r
-span.purple-background { background: purple; }\r
-span.red-background { background: red; }\r
-span.silver-background { background: silver; }\r
-span.teal-background { background: teal; }\r
-span.white-background { background: white; }\r
-span.yellow-background { background: yellow; }\r
-\r
-span.big { font-size: 2em; }\r
-span.small { font-size: 0.6em; }\r
-\r
-span.underline { text-decoration: underline; }\r
-span.overline { text-decoration: overline; }\r
-span.line-through { text-decoration: line-through; }\r
-\r
-div.unbreakable { page-break-inside: avoid; }\r
-\r
-\r
-/*\r
- * xhtml11 specific\r
- *\r
- * */\r
-\r
-div.tableblock {\r
- margin-top: 1.0em;\r
- margin-bottom: 1.5em;\r
-}\r
-div.tableblock > table {\r
- border: 3px solid #527bbd;\r
-}\r
-thead, p.table.header {\r
- font-weight: bold;\r
- color: #527bbd;\r
-}\r
-p.table {\r
- margin-top: 0;\r
-}\r
-/* Because the table frame attribute is overridden by CSS in most browsers. */\r
-div.tableblock > table[frame="void"] {\r
- border-style: none;\r
-}\r
-div.tableblock > table[frame="hsides"] {\r
- border-left-style: none;\r
- border-right-style: none;\r
-}\r
-div.tableblock > table[frame="vsides"] {\r
- border-top-style: none;\r
- border-bottom-style: none;\r
-}\r
-\r
-\r
-/*\r
- * html5 specific\r
- *\r
- * */\r
-\r
-table.tableblock {\r
- margin-top: 1.0em;\r
- margin-bottom: 1.5em;\r
-}\r
-thead, p.tableblock.header {\r
- font-weight: bold;\r
- color: #527bbd;\r
-}\r
-p.tableblock {\r
- margin-top: 0;\r
-}\r
-table.tableblock {\r
- border-width: 3px;\r
- border-spacing: 0px;\r
- border-style: solid;\r
- border-color: #527bbd;\r
- border-collapse: collapse;\r
-}\r
-th.tableblock, td.tableblock {\r
- border-width: 1px;\r
- padding: 4px;\r
- border-style: solid;\r
- border-color: #527bbd;\r
-}\r
-\r
-table.tableblock.frame-topbot {\r
- border-left-style: hidden;\r
- border-right-style: hidden;\r
-}\r
-table.tableblock.frame-sides {\r
- border-top-style: hidden;\r
- border-bottom-style: hidden;\r
-}\r
-table.tableblock.frame-none {\r
- border-style: hidden;\r
-}\r
-\r
-th.tableblock.halign-left, td.tableblock.halign-left {\r
- text-align: left;\r
-}\r
-th.tableblock.halign-center, td.tableblock.halign-center {\r
- text-align: center;\r
-}\r
-th.tableblock.halign-right, td.tableblock.halign-right {\r
- text-align: right;\r
-}\r
-\r
-th.tableblock.valign-top, td.tableblock.valign-top {\r
- vertical-align: top;\r
-}\r
-th.tableblock.valign-middle, td.tableblock.valign-middle {\r
- vertical-align: middle;\r
-}\r
-th.tableblock.valign-bottom, td.tableblock.valign-bottom {\r
- vertical-align: bottom;\r
-}\r
-\r
-\r
-/*\r
- * manpage specific\r
- *\r
- * */\r
-\r
-body.manpage h1 {\r
- padding-top: 0.5em;\r
- padding-bottom: 0.5em;\r
- border-top: 2px solid silver;\r
- border-bottom: 2px solid silver;\r
-}\r
-body.manpage h2 {\r
- border-style: none;\r
-}\r
-body.manpage div.sectionbody {\r
- margin-left: 3em;\r
-}\r
-\r
-@media print {\r
- body.manpage div#toc { display: none; }\r
-}\r
-\r
-\r
-</style>\r
-<script type="text/javascript">\r
-/*<+'])');\r
- // Function that scans the DOM tree for header elements (the DOM2\r
- // nodeIterator API would be a better technique but not supported by all\r
- // browsers).\r
- var iterate = function (el) {\r
- for (var i = el.firstChild; i != null; i = i.nextSibling) {\r
- if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {\r
- var mo = re.exec(i.tagName);\r
- if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {\r
- result[result.length] = new TocEntry(i, getText(i), mo[1]-1);\r
- }\r
- iterate(i);\r
- }\r
- }\r
- }\r
- iterate(el);\r
- return result;\r
- }\r
-\r
- var toc = document.getElementById("toc");\r
- if (!toc) {\r
- return;\r
- }\r
-\r
- // Delete existing TOC entries in case we're reloading the TOC.\r
- var tocEntriesToRemove = [];\r
- var i;\r
- for (i = 0; i < toc.childNodes.length; i++) {\r
- var entry = toc.childNodes[i];\r
- if (entry.nodeName.toLowerCase() == 'div'\r
- && entry.getAttribute("class")\r
- && entry.getAttribute("class").match(/^toclevel/))\r
- tocEntriesToRemove.push(entry);\r
- }\r
- for (i = 0; i < tocEntriesToRemove.length; i++) {\r
- toc.removeChild(tocEntriesToRemove[i]);\r
- }\r
-\r
- // Rebuild TOC entries.\r
- var entries = tocEntries(document.getElementById("content"), toclevels);\r
- for (var i = 0; i < entries.length; ++i) {\r
- var entry = entries[i];\r
- if (entry.element.id == "")\r
- entry.element.id = "_toc_" + i;\r
- var a = document.createElement("a");\r
- a.href = "#" + entry.element.id;\r
- a.appendChild(document.createTextNode(entry.text));\r
- var div = document.createElement("div");\r
- div.appendChild(a);\r
- div.className = "toclevel" + entry.toclevel;\r
- toc.appendChild(div);\r
- }\r
- if (entries.length == 0)\r
- toc.parentNode.removeChild(toc);\r
-},\r
-\r
-\r
-/////////////////////////////////////////////////////////////////////\r
-// Footnotes generator\r
-/////////////////////////////////////////////////////////////////////\r
-\r
-/* Based on footnote generation code from:\r
- * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html\r
- */\r
-\r
-footnotes: function () {\r
- // Delete existing footnote entries in case we're reloading the footnodes.\r
- var i;\r
- var noteholder = document.getElementById("footnotes");\r
- if (!noteholder) {\r
- return;\r
- }\r
- var entriesToRemove = [];\r
- for (i = 0; i < noteholder.childNodes.length; i++) {\r
- var entry = noteholder.childNodes[i];\r
- if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")\r
- entriesToRemove.push(entry);\r
- }\r
- for (i = 0; i < entriesToRemove.length; i++) {\r
- noteholder.removeChild(entriesToRemove[i]);\r
- }\r
-\r
- // Rebuild footnote entries.\r
- var cont = document.getElementById("content");\r
- var spans = cont.getElementsByTagName("span");\r
- var refs = {};\r
- var n = 0;\r
- for (i=0; i<spans.length; i++) {\r
- if (spans[i].className == "footnote") {\r
- n++;\r
- var note = spans[i].getAttribute("data-note");\r
- if (!note) {\r
- // Use [\s\S] in place of . so multi-line matches work.\r
- // Because JavaScript has no s (dotall) regex flag.\r
- note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];\r
- spans[i].innerHTML =\r
- "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +\r
- "' title='View footnote' class='footnote'>" + n + "</a>]";\r
- spans[i].setAttribute("data-note", note);\r
- }\r
- noteholder.innerHTML +=\r
- "<div class='footnote' id='_footnote_" + n + "'>" +\r
- "<a href='#_footnoteref_" + n + "' title='Return to text'>" +\r
- n + "</a>. " + note + "</div>";\r
- var id =spans[i].getAttribute("id");\r
- if (id != null) refs["#"+id] = n;\r
- }\r
- }\r
- if (n == 0)\r
- noteholder.parentNode.removeChild(noteholder);\r
- else {\r
- // Process footnoterefs.\r
- for (i=0; i<spans.length; i++) {\r
- if (spans[i].className == "footnoteref") {\r
- var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");\r
- href = href.match(/#.*/)[0]; // Because IE return full URL.\r
- n = refs[href];\r
- spans[i].innerHTML =\r
- "[<a href='#_footnote_" + n +\r
- "' title='View footnote' class='footnote'>" + n + "</a>]";\r
- }\r
- }\r
- }\r
-},\r
-\r
-install: function(toclevels) {\r
- var timerId;\r
-\r
- function reinstall() {\r
- asciidoc.footnotes();\r
- if (toclevels) {\r
- asciidoc.toc(toclevels);\r
- }\r
- }\r
-\r
- function reinstallAndRemoveTimer() {\r
- clearInterval(timerId);\r
- reinstall();\r
- }\r
-\r
- timerId = setInterval(reinstall, 500);\r
- if (document.addEventListener)\r
- document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);\r
- else\r
- window.onload = reinstallAndRemoveTimer;\r
-}\r
-\r
-}\r
-asciidoc.install(2);\r
-/*]]>*/\r
-</script>\r
-</head>\r
-<body class="article">\r
-<div id="header">\r
-<h1>ccache copyright and license</h1>\r
-<span id="revnumber">version 3.7.12</span>\r
-<div id="toc">
- <div id="toctitle">Table of Contents</div>
- <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
-</div>\r
-</div>\r
-<div id="content">\r
-<div class="sect1">\r
-<h2 id="_overall_license">Overall license</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>The license for ccache as a whole is as follows:</p></div>\r
-<div class="listingblock">\r
-<div class="content">\r
-<pre><code> This program is free software; you can redistribute it and/or modify it under\r
- the terms of the GNU General Public License as published by the Free Software\r
- Foundation; either version 3 of the License, or (at your option) any later\r
- version.\r
-\r
- This program is distributed in the hope that it will be useful, but WITHOUT ANY\r
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A\r
- PARTICULAR PURPOSE. See the GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License along with\r
- this program; if not, write to the Free Software Foundation, Inc., 51 Franklin\r
- Street, Fifth Floor, Boston, MA 02110-1301 USA</code></pre>\r
-</div></div>\r
-<div class="paragraph"><p>The full license text can be found in GPL-3.0.txt and at\r
-<a href="http://www.gnu.org/licenses/gpl-3.0.html">http://www.gnu.org/licenses/gpl-3.0.html</a>.</p></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_copyright_and_authors">Copyright and authors</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>ccache is a collective work with contributions from many people, listed in\r
-AUTHORS.adoc and at <a href="https://ccache.dev/credits.html">https://ccache.dev/credits.html</a>. Subsequent additions by\r
-contributing authors are implicitly licensed to the public under the same terms\r
-(GNU GPL version 3 or later), but the contributing authors retain copyrights on\r
-their portions of the work.</p></div>\r
-<div class="paragraph"><p>The copyright for ccache as a whole is as follows:</p></div>\r
-<div class="listingblock">\r
-<div class="content">\r
-<pre><code> Copyright (C) 2002-2007 Andrew Tridgell\r
- Copyright (C) 2009-2020 Joel Rosdahl</code></pre>\r
-</div></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_files_derived_from_other_sources">Files derived from other sources</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>The ccache distribution contain some files from other sources and some have\r
-been modified for use in ccache. These files all carry attribution notices, and\r
-may qualify as “separate and independent works in themselves” for purposes of\r
-the GPL: that is, if separated from the ccache sources, they may be usable\r
-under less restrictive terms.</p></div>\r
-<div class="sect2">\r
-<h3 id="_src_getopt_long_hc">src/getopt_long.[hc]</h3>\r
-<div class="paragraph"><p>This implementation of <code>getopt_long()</code> was copied from\r
-<a href="http://www.postgresql.org">PostgreSQL</a> and has the following license text:</p></div>\r
-<div class="listingblock">\r
-<div class="content">\r
-<pre><code> Portions Copyright (c) 1987, 1993, 1994\r
- The Regents of the University of California. All rights reserved.\r
-\r
- Portions Copyright (c) 2003\r
- PostgreSQL Global Development Group\r
-\r
- Redistribution and use in source and binary forms, with or without\r
- modification, are permitted provided that the following conditions\r
- are met:\r
- 1. Redistributions of source code must retain the above copyright\r
- notice, this list of conditions and the following disclaimer.\r
- 2. Redistributions in binary form must reproduce the above copyright\r
- notice, this list of conditions and the following disclaimer in the\r
- documentation and/or other materials provided with the distribution.\r
- 3. Neither the name of the University nor the names of its contributors\r
- may be used to endorse or promote products derived from this software\r
- without specific prior written permission.\r
-\r
- THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND\r
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
- ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE\r
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\r
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\r
- SUCH DAMAGE.</code></pre>\r
-</div></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_src_hashtable_hc">src/hashtable*.[hc]</h3>\r
-<div class="paragraph"><p>This code comes from <a href="http://www.cl.cam.ac.uk/~cwc22/hashtable/">http://www.cl.cam.ac.uk/~cwc22/hashtable/</a> with the\r
-following license:</p></div>\r
-<div class="listingblock">\r
-<div class="content">\r
-<pre><code> Copyright (c) 2002, 2004, Christopher Clark\r
- All rights reserved.\r
-\r
- Redistribution and use in source and binary forms, with or without\r
- modification, are permitted provided that the following conditions are met:\r
-\r
- * Redistributions of source code must retain the above copyright notice,\r
- this list of conditions and the following disclaimer.\r
-\r
- * Redistributions in binary form must reproduce the above copyright notice,\r
- this list of conditions and the following disclaimer in the documentation\r
- and/or other materials provided with the distribution.\r
-\r
- * Neither the name of the original author; nor the names of any\r
- contributors may be used to endorse or promote products derived from this\r
- software without specific prior written permission.\r
-\r
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\r
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
- POSSIBILITY OF SUCH DAMAGE.</code></pre>\r
-</div></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_m4_feature_macros_m4">m4/feature_macros.m4</h3>\r
-<div class="paragraph"><p>This Autoconf M4 snippet comes from <a href="http://www.python.org">Python</a> 2.6’s\r
-<code>configure.in</code> with the following license:</p></div>\r
-<div class="listingblock">\r
-<div class="content">\r
-<pre><code> A. HISTORY OF THE SOFTWARE\r
- ==========================\r
-\r
- Python was created in the early 1990s by Guido van Rossum at Stichting\r
- Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands\r
- as a successor of a language called ABC. Guido remains Python's\r
- principal author, although it includes many contributions from others.\r
-\r
- In 1995, Guido continued his work on Python at the Corporation for\r
- National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)\r
- in Reston, Virginia where he released several versions of the\r
- software.\r
-\r
- In May 2000, Guido and the Python core development team moved to\r
- BeOpen.com to form the BeOpen PythonLabs team. In October of the same\r
- year, the PythonLabs team moved to Digital Creations (now Zope\r
- Corporation, see http://www.zope.com). In 2001, the Python Software\r
- Foundation (PSF, see http://www.python.org/psf/) was formed, a\r
- non-profit organization created specifically to own Python-related\r
- Intellectual Property. Zope Corporation is a sponsoring member of\r
- the PSF.\r
-\r
- All Python releases are Open Source (see http://www.opensource.org for\r
- the Open Source Definition). Historically, most, but not all, Python\r
- releases have also been GPL-compatible; the table below summarizes\r
- the various releases.\r
-\r
- Release Derived Year Owner GPL-\r
- from compatible? (1)\r
-\r
- 0.9.0 thru 1.2 1991-1995 CWI yes\r
- 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes\r
- 1.6 1.5.2 2000 CNRI no\r
- 2.0 1.6 2000 BeOpen.com no\r
- 1.6.1 1.6 2001 CNRI yes (2)\r
- 2.1 2.0+1.6.1 2001 PSF no\r
- 2.0.1 2.0+1.6.1 2001 PSF yes\r
- 2.1.1 2.1+2.0.1 2001 PSF yes\r
- 2.2 2.1.1 2001 PSF yes\r
- 2.1.2 2.1.1 2002 PSF yes\r
- 2.1.3 2.1.2 2002 PSF yes\r
- 2.2.1 2.2 2002 PSF yes\r
- 2.2.2 2.2.1 2002 PSF yes\r
- 2.2.3 2.2.2 2003 PSF yes\r
- 2.3 2.2.2 2002-2003 PSF yes\r
- 2.3.1 2.3 2002-2003 PSF yes\r
- 2.3.2 2.3.1 2002-2003 PSF yes\r
- 2.3.3 2.3.2 2002-2003 PSF yes\r
- 2.3.4 2.3.3 2004 PSF yes\r
- 2.3.5 2.3.4 2005 PSF yes\r
- 2.4 2.3 2004 PSF yes\r
- 2.4.1 2.4 2005 PSF yes\r
- 2.4.2 2.4.1 2005 PSF yes\r
- 2.4.3 2.4.2 2006 PSF yes\r
- 2.4.4 2.4.3 2006 PSF yes\r
- 2.5 2.4 2006 PSF yes\r
- 2.5.1 2.5 2007 PSF yes\r
- 2.5.2 2.5.1 2008 PSF yes\r
- 2.5.3 2.5.2 2008 PSF yes\r
- 2.6 2.5 2008 PSF yes\r
- 2.6.1 2.6 2008 PSF yes\r
-\r
- Footnotes:\r
-\r
- (1) GPL-compatible doesn't mean that we're distributing Python under\r
- the GPL. All Python licenses, unlike the GPL, let you distribute\r
- a modified version without making your changes open source. The\r
- GPL-compatible licenses make it possible to combine Python with\r
- other software that is released under the GPL; the others don't.\r
-\r
- (2) According to Richard Stallman, 1.6.1 is not GPL-compatible,\r
- because its license has a choice of law clause. According to\r
- CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1\r
- is "not incompatible" with the GPL.\r
-\r
- Thanks to the many outside volunteers who have worked under Guido's\r
- direction to make these releases possible.\r
-\r
-\r
- B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON\r
- ===============================================================\r
-\r
- PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2\r
- --------------------------------------------\r
-\r
- 1. This LICENSE AGREEMENT is between the Python Software Foundation\r
- ("PSF"), and the Individual or Organization ("Licensee") accessing and\r
- otherwise using this software ("Python") in source or binary form and\r
- its associated documentation.\r
-\r
- 2. Subject to the terms and conditions of this License Agreement, PSF hereby\r
- grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,\r
- analyze, test, perform and/or display publicly, prepare derivative works,\r
- distribute, and otherwise use Python alone or in any derivative version,\r
- provided, however, that PSF's License Agreement and PSF's notice of copyright,\r
- i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Python\r
- Software Foundation; All Rights Reserved" are retained in Python alone or in any\r
- derivative version prepared by Licensee.\r
-\r
- 3. In the event Licensee prepares a derivative work that is based on\r
- or incorporates Python or any part thereof, and wants to make\r
- the derivative work available to others as provided herein, then\r
- Licensee hereby agrees to include in any such work a brief summary of\r
- the changes made to Python.\r
-\r
- 4. PSF is making Python available to Licensee on an "AS IS"\r
- basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR\r
- IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND\r
- DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS\r
- FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT\r
- INFRINGE ANY THIRD PARTY RIGHTS.\r
-\r
- 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON\r
- FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS\r
- A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,\r
- OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.\r
-\r
- 6. This License Agreement will automatically terminate upon a material\r
- breach of its terms and conditions.\r
-\r
- 7. Nothing in this License Agreement shall be deemed to create any\r
- relationship of agency, partnership, or joint venture between PSF and\r
- Licensee. This License Agreement does not grant permission to use PSF\r
- trademarks or trade name in a trademark sense to endorse or promote\r
- products or services of Licensee, or any third party.\r
-\r
- 8. By copying, installing or otherwise using Python, Licensee\r
- agrees to be bound by the terms and conditions of this License\r
- Agreement.\r
-\r
-\r
- BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0\r
- -------------------------------------------\r
-\r
- BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1\r
-\r
- 1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an\r
- office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the\r
- Individual or Organization ("Licensee") accessing and otherwise using\r
- this software in source or binary form and its associated\r
- documentation ("the Software").\r
-\r
- 2. Subject to the terms and conditions of this BeOpen Python License\r
- Agreement, BeOpen hereby grants Licensee a non-exclusive,\r
- royalty-free, world-wide license to reproduce, analyze, test, perform\r
- and/or display publicly, prepare derivative works, distribute, and\r
- otherwise use the Software alone or in any derivative version,\r
- provided, however, that the BeOpen Python License is retained in the\r
- Software, alone or in any derivative version prepared by Licensee.\r
-\r
- 3. BeOpen is making the Software available to Licensee on an "AS IS"\r
- basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR\r
- IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND\r
- DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS\r
- FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT\r
- INFRINGE ANY THIRD PARTY RIGHTS.\r
-\r
- 4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE\r
- SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS\r
- AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY\r
- DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.\r
-\r
- 5. This License Agreement will automatically terminate upon a material\r
- breach of its terms and conditions.\r
-\r
- 6. This License Agreement shall be governed by and interpreted in all\r
- respects by the law of the State of California, excluding conflict of\r
- law provisions. Nothing in this License Agreement shall be deemed to\r
- create any relationship of agency, partnership, or joint venture\r
- between BeOpen and Licensee. This License Agreement does not grant\r
- permission to use BeOpen trademarks or trade names in a trademark\r
- sense to endorse or promote products or services of Licensee, or any\r
- third party. As an exception, the "BeOpen Python" logos available at\r
- http://www.pythonlabs.com/logos.html may be used according to the\r
- permissions granted on that web page.\r
-\r
- 7. By copying, installing or otherwise using the software, Licensee\r
- agrees to be bound by the terms and conditions of this License\r
- Agreement.\r
-\r
-\r
- CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1\r
- ---------------------------------------\r
-\r
- 1. This LICENSE AGREEMENT is between the Corporation for National\r
- Research Initiatives, having an office at 1895 Preston White Drive,\r
- Reston, VA 20191 ("CNRI"), and the Individual or Organization\r
- ("Licensee") accessing and otherwise using Python 1.6.1 software in\r
- source or binary form and its associated documentation.\r
-\r
- 2. Subject to the terms and conditions of this License Agreement, CNRI\r
- hereby grants Licensee a nonexclusive, royalty-free, world-wide\r
- license to reproduce, analyze, test, perform and/or display publicly,\r
- prepare derivative works, distribute, and otherwise use Python 1.6.1\r
- alone or in any derivative version, provided, however, that CNRI's\r
- License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)\r
- 1995-2001 Corporation for National Research Initiatives; All Rights\r
- Reserved" are retained in Python 1.6.1 alone or in any derivative\r
- version prepared by Licensee. Alternately, in lieu of CNRI's License\r
- Agreement, Licensee may substitute the following text (omitting the\r
- quotes): "Python 1.6.1 is made available subject to the terms and\r
- conditions in CNRI's License Agreement. This Agreement together with\r
- Python 1.6.1 may be located on the Internet using the following\r
- unique, persistent identifier (known as a handle): 1895.22/1013. This\r
- Agreement may also be obtained from a proxy server on the Internet\r
- using the following URL: http://hdl.handle.net/1895.22/1013".\r
-\r
- 3. In the event Licensee prepares a derivative work that is based on\r
- or incorporates Python 1.6.1 or any part thereof, and wants to make\r
- the derivative work available to others as provided herein, then\r
- Licensee hereby agrees to include in any such work a brief summary of\r
- the changes made to Python 1.6.1.\r
-\r
- 4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"\r
- basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR\r
- IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND\r
- DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS\r
- FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT\r
- INFRINGE ANY THIRD PARTY RIGHTS.\r
-\r
- 5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON\r
- 1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS\r
- A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,\r
- OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.\r
-\r
- 6. This License Agreement will automatically terminate upon a material\r
- breach of its terms and conditions.\r
-\r
- 7. This License Agreement shall be governed by the federal\r
- intellectual property law of the United States, including without\r
- limitation the federal copyright law, and, to the extent such\r
- U.S. federal law does not apply, by the law of the Commonwealth of\r
- Virginia, excluding Virginia's conflict of law provisions.\r
- Notwithstanding the foregoing, with regard to derivative works based\r
- on Python 1.6.1 that incorporate non-separable material that was\r
- previously distributed under the GNU General Public License (GPL), the\r
- law of the Commonwealth of Virginia shall govern this License\r
- Agreement only as to issues arising under or with respect to\r
- Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this\r
- License Agreement shall be deemed to create any relationship of\r
- agency, partnership, or joint venture between CNRI and Licensee. This\r
- License Agreement does not grant permission to use CNRI trademarks or\r
- trade name in a trademark sense to endorse or promote products or\r
- services of Licensee, or any third party.\r
-\r
- 8. By clicking on the "ACCEPT" button where indicated, or by copying,\r
- installing or otherwise using Python 1.6.1, Licensee agrees to be\r
- bound by the terms and conditions of this License Agreement.\r
-\r
- ACCEPT\r
-\r
-\r
- CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2\r
- --------------------------------------------------\r
-\r
- Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,\r
- The Netherlands. All rights reserved.\r
-\r
- Permission to use, copy, modify, and distribute this software and its\r
- documentation for any purpose and without fee is hereby granted,\r
- provided that the above copyright notice appear in all copies and that\r
- both that copyright notice and this permission notice appear in\r
- supporting documentation, and that the name of Stichting Mathematisch\r
- Centrum or CWI not be used in advertising or publicity pertaining to\r
- distribution of the software without specific, written prior\r
- permission.\r
-\r
- STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO\r
- THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\r
- FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE\r
- FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\r
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\r
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT\r
- OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.</code></pre>\r
-</div></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_src_murmurhashneutral2_hc">src/murmurhashneutral2.[hc]</h3>\r
-<div class="paragraph"><p>This fast hash implementation is released to the public domain by Austin\r
-Appleby. See <a href="http://murmurhash.googlepages.com">http://murmurhash.googlepages.com</a>.</p></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_src_minitrace_hc">src/minitrace.[hc]</h3>\r
-<div class="paragraph"><p>A library for producing JSON traces suitable for Chrome’s built-in trace viewer\r
-(chrome://tracing). Downloaded from <a href="https://github.com/hrydgard/minitrace">https://github.com/hrydgard/minitrace</a>.</p></div>\r
-<div class="listingblock">\r
-<div class="content">\r
-<pre><code>The MIT License (MIT)\r
-\r
-Copyright (c) 2014 Henrik Rydgård\r
-\r
-Permission is hereby granted, free of charge, to any person obtaining a copy\r
-of this software and associated documentation files (the "Software"), to deal\r
-in the Software without restriction, including without limitation the rights\r
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
-copies of the Software, and to permit persons to whom the Software is\r
-furnished to do so, subject to the following conditions:\r
-\r
-The above copyright notice and this permission notice shall be included in all\r
-copies or substantial portions of the Software.\r
-\r
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
-SOFTWARE.</code></pre>\r
-</div></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_src_snprintf_c_and_m4_snprintf_m4">src/snprintf.c and m4/snprintf.m4</h3>\r
-<div class="paragraph"><p>This implementation of <code>snprintf()</code> and similar functions was downloaded from\r
-<a href="http://www.jhweiss.de/software/snprintf.html">http://www.jhweiss.de/software/snprintf.html</a> and has the following license:</p></div>\r
-<div class="listingblock">\r
-<div class="content">\r
-<pre><code> Copyright (c) 1995 Patrick Powell.\r
-\r
- This code is based on code written by Patrick Powell <papowell@astart.com>.\r
- It may be used for any purpose as long as this notice remains intact on all\r
- source code distributions.\r
-\r
- Copyright (c) 2008 Holger Weiss.\r
-\r
- This version of the code is maintained by Holger Weiss <holger@jhweiss.de>.\r
- My changes to the code may freely be used, modified and/or redistributed for\r
- any purpose. It would be nice if additions and fixes to this file (including\r
- trivial code cleanups) would be sent back in order to let me include them in\r
- the version available at <http://www.jhweiss.de/software/snprintf.html>.\r
- However, this is not a requirement for using or redistributing (possibly\r
- modified) versions of this file, nor is leaving this notice intact mandatory.</code></pre>\r
-</div></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_src_zlib_hc">src/zlib/*.[hc]</h3>\r
-<div class="paragraph"><p>This is a bundled subset of zlib 1.2.11 from <a href="http://zlib.net">http://zlib.net</a> with the\r
-following license:</p></div>\r
-<div class="listingblock">\r
-<div class="content">\r
-<pre><code> Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler\r
-\r
- This software is provided 'as-is', without any express or implied\r
- warranty. In no event will the authors be held liable for any damages\r
- arising from the use of this software.\r
-\r
- Permission is granted to anyone to use this software for any purpose,\r
- including commercial applications, and to alter it and redistribute it\r
- freely, subject to the following restrictions:\r
-\r
- 1. The origin of this software must not be misrepresented; you must not\r
- claim that you wrote the original software. If you use this software\r
- in a product, an acknowledgment in the product documentation would be\r
- appreciated but is not required.\r
- 2. Altered source versions must be plainly marked as such, and must not be\r
- misrepresented as being the original software.\r
- 3. This notice may not be removed or altered from any source distribution.\r
-\r
- Jean-loup Gailly Mark Adler\r
- jloup@gzip.org madler@alumni.caltech.edu</code></pre>\r
-</div></div>\r
-</div>\r
-</div>\r
-</div>\r
-</div>\r
-<div id="footnotes"><hr /></div>\r
-<div id="footer">\r
-<div id="footer-text">\r
-Version 3.7.12<br />\r
-Last updated\r
- 2020-10-01 14:20:15 CEST\r
-</div>\r
-</div>\r
-</body>\r
-</html>\r
+++ /dev/null
-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@
-ccache – a fast compiler cache
+Ccache – a fast compiler cache
==============================
-[](https://travis-ci.org/ccache/ccache)
+[](https://github.com/ccache/ccache/actions?query=workflow%3A%22Build%22)
[](https://lgtm.com/projects/g/ccache/ccache/context:cpp)
[](https://lgtm.com/projects/g/ccache/ccache/alerts)
+[](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)
* [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)
+++ /dev/null
-#!/bin/sh
-
-set -e
-
-if [ -f dev_mode_disabled ]; then
- cat <<EOF >&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
-<https://ccache.dev/repo.html>.
-EOF
- exit 1
-fi
-
-autoheader
-autoconf
-echo "Now run ./configure and make"
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+CcacheVersion.cmake export-subst
--- /dev/null
+# 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)
--- /dev/null
+# 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)
--- /dev/null
+# 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
+# <https://github.com/ccache/ccache/archive/VERSION.tar.gz>. 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()
--- /dev/null
+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()
--- /dev/null
+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()
--- /dev/null
+# 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")
--- /dev/null
+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 "$<BUILD_INTERFACE:${zstd_dir}/lib>")
+
+ 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")
--- /dev/null
+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)
--- /dev/null
+include(CcacheVersion)
+configure_file(
+ ${CMAKE_SOURCE_DIR}/cmake/version.cpp.in
+ ${CMAKE_BINARY_DIR}/src/version.cpp
+ @ONLY)
+message(STATUS "Ccache version: ${VERSION}")
--- /dev/null
+# 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()
--- /dev/null
+# 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()
--- /dev/null
+#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 <linux/fs.h> 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 <pwd.h> 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 <syslog.h> header file. */
+#cmakedefine HAVE_SYSLOG_H
+
+/* Define to 1 if you have the <sys/clonefile.h> header file. */
+#cmakedefine HAVE_SYS_CLONEFILE_H
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#cmakedefine HAVE_SYS_IOCTL_H
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+#cmakedefine HAVE_SYS_MMAN_H
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#cmakedefine HAVE_SYS_TIME_H
+
+/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
+#cmakedefine HAVE_SYS_WAIT_H
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#cmakedefine HAVE_SYS_FILE_H
+
+/* Define to 1 if you have the <termios.h> header file. */
+#cmakedefine HAVE_TERMIOS_H
+
+/* Define to 1 if you have the <dirent.h> header file. */
+#cmakedefine HAVE_DIRENT_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#cmakedefine HAVE_STRINGS_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#cmakedefine HAVE_UNISTD_H
+
+/* Define to 1 if you have the <utime.h> header file. */
+#cmakedefine HAVE_UTIME_H
+
+/* Define to 1 if you have the <sys/utime.h> header file. */
+#cmakedefine HAVE_SYS_UTIME_H
+
+/* Define to 1 if you have the <varargs.h> 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
--- /dev/null
+extern const char CCACHE_VERSION[];
+const char CCACHE_VERSION[] = "@VERSION@";
+++ /dev/null
-#! /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 <https://www.gnu.org/licenses/>.
-#
-# 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 <config-patches@gnu.org>.
-
-
-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 <config-patches@gnu.org>."
-
-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 <features.h>
- #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 <stdio.h> /* 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 <sys/systemcfg.h>
-
- 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 <stdlib.h>
- #include <unistd.h>
-
- 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 <unistd.h>
- 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' </usr/options/cb.name`
- echo "$UNAME_MACHINE"-pc-isc"$UNAME_REL"
- elif /bin/uname -X 2>/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 <Richard.M.Bartel@ccMail.Census.GOV>
- echo i586-unisys-sysv4
- exit ;;
- *:UNIX_System_V:4*:FTX*)
- # From Gerald Hewes <hewes@openmarket.com>.
- # 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 <<EOF
-
-NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize
-the system type. Please install a C compiler and try again.
-EOF
- ;;
-esac
-
-cat >&2 <<EOF
-
-This script (version $timestamp), has failed to recognize the
-operating system you are using. If your script is old, overwrite *all*
-copies of config.guess and config.sub with the latest versions from:
-
- https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
-and
- https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
-
-If $0 has already been updated, send the following data and any
-information you think might be pertinent to config-patches@gnu.org to
-provide the necessary information to handle your system.
-
-config.guess timestamp = $timestamp
-
-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`
-/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:
+++ /dev/null
-/* 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 <ctype.h> header file. */
-#undef HAVE_CTYPE_H
-
-/* Define to 1 if you have the <dirent.h> 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 <inttypes.h> 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 <locale.h> 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 <memory.h> 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 <ndir.h> 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 <pwd.h> 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 <stdarg.h> header file. */
-#undef HAVE_STDARG_H
-
-/* Define to 1 if you have the <stddef.h> header file. */
-#undef HAVE_STDDEF_H
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#undef HAVE_STDINT_H
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#undef HAVE_STDLIB_H
-
-/* Define to 1 if you have the <strings.h> header file. */
-#undef HAVE_STRINGS_H
-
-/* Define to 1 if you have the <string.h> 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 <sys/dir.h> header file, and it defines `DIR'.
- */
-#undef HAVE_SYS_DIR_H
-
-/* Define to 1 if you have the <sys/mman.h> header file. */
-#undef HAVE_SYS_MMAN_H
-
-/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
- */
-#undef HAVE_SYS_NDIR_H
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#undef HAVE_SYS_STAT_H
-
-/* Define to 1 if you have the <sys/time.h> header file. */
-#undef HAVE_SYS_TIME_H
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#undef HAVE_SYS_TYPES_H
-
-/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
-#undef HAVE_SYS_WAIT_H
-
-/* Define to 1 if you have the <termios.h> 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 <unistd.h> 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 <varargs.h> 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 <sys/time.h> and <time.h>. */
-#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 <stdint.h> and <inttypes.h> do
- not define. */
-#undef intmax_t
-
-/* Define to `unsigned int' if <sys/types.h> does not define. */
-#undef size_t
-
-/* Define to the widest unsigned integer type if <stdint.h> and <inttypes.h>
- 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
+++ /dev/null
-#! /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 <https://www.gnu.org/licenses/>.
-#
-# 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 <config-patches@gnu.org>.
-#
-# 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 <config-patches@gnu.org>."
-
-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:
+++ /dev/null
-#! /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 </dev/null
-exec 6>&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 <stdio.h>
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-#ifdef HAVE_SYS_STAT_H
-# include <sys/stat.h>
-#endif
-#ifdef STDC_HEADERS
-# include <stdlib.h>
-# include <stddef.h>
-#else
-# ifdef HAVE_STDLIB_H
-# include <stdlib.h>
-# endif
-#endif
-#ifdef HAVE_STRING_H
-# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
-# include <memory.h>
-# endif
-# include <string.h>
-#endif
-#ifdef HAVE_STRINGS_H
-# include <strings.h>
-#endif
-#ifdef HAVE_INTTYPES_H
-# include <inttypes.h>
-#endif
-#ifdef HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#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<lib dir> if you have libraries in a
- nonstandard directory <lib dir>
- LIBS libraries to pass to the linker, e.g. -l<library>
- CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
- you have headers in a nonstandard directory <include dir>
- 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 <limits.h> declares $2.
- For example, HP-UX 11i <limits.h> declares gettimeofday. */
-#define $2 innocuous_$2
-
-/* System header to define __stub macros and hopefully few prototypes,
- which can conflict with char $2 (); below.
- Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
- <limits.h> exists even on freestanding compilers. */
-
-#ifdef __STDC__
-# include <limits.h>
-#else
-# include <assert.h>
-#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 <stdio.h>
-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 <stdarg.h>
-#include <stdio.h>
-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 <stdarg.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <wchar.h>
-#include <stdio.h>
-
-// 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 <limits.h> to <assert.h> if __STDC__ is defined, since
- # <limits.h> 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 <limits.h>
-#else
-# include <assert.h>
-#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 <ac_nonexistent.h>
-_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 <limits.h> to <assert.h> if __STDC__ is defined, since
- # <limits.h> 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 <limits.h>
-#else
-# include <assert.h>
-#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 <ac_nonexistent.h>
-_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 <sys/types.h>
-#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 <sys/types.h>
-#include <sys/time.h>
-#include <time.h>
-
-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 <sys/types.h>
-#include <sys/wait.h>
-#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 <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <float.h>
-
-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 <string.h>
-
-_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 <stdlib.h>
-
-_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 <ctype.h>
-#include <stdlib.h>
-#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 <stdlib.h>
-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 <holger@jhweiss.de>.
-#
-# 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 <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.
-
-# HW_HEADER_STDARG_H
-# ------------------
-# Define HAVE_STDARG_H to 1 if <stdarg.h> is available.
-# HW_HEADER_STDARG_H
-
-# HW_HEADER_VARARGS_H
-# -------------------
-# Define HAVE_VARARGS_H to 1 if <varargs.h> 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 <stdarg.h>
- #endif
- #include <stdio.h>
- 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 <locale.h>
-"
-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 <locale.h>
-"
-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 <limits.h>
- #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 <stdio.h>
-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 <stdarg.h>
- #elif HAVE_VARARGS_H
- #include <varargs.h>
- #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 <stdarg.h>
- #elif HAVE_VARARGS_H
- #include <varargs.h>
- #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 <zlib.h>
-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 <sys/types.h>
- #include <sys/param.h>
-
-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 <sys/types.h>
- #include <sys/param.h>
-
-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 <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris).
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <limits.h>
-
-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 <limits.h>
-
-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 2>/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
-' <conf$$subs.awk | sed '
-/^[^""]/{
- N
- s/\n//
-}
-' >>$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 = "\a"
-
-}
-{
- 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
-' <confdefs.h | sed '
-s/'"$ac_delim"'/"\\\
-"/g' >>$CONFIG_STATUS || ac_write_fail=1
-
-cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
- for (key in D) D_is_set[key] = 1
- FS = "\a"
-}
-/^[\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 <<EOF >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 <<EOF >>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;}
+++ /dev/null
-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 <stdlib.h>],
- [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 <zlib.h>],
- [
- #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 <<EOF >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 <<EOF >>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)
+++ /dev/null
-# 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!<literal>(.*?)</literal>!<emphasis role="strong">\1</emphasis>!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/^\*.*/<STDIN> . "\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
+++ /dev/null
-Remove this file to enable developer mode.
-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
* 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
* Martin Pool
* Mathias De Maré
* Matthias Kretz
+* Matt Whitlock
* Melven Roehrig-Zoellner
* Michael Marineau
* Michael Meeks
* 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
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>\r
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"\r
- "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">\r
-<head>\r
-<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />\r
-<meta name="generator" content="AsciiDoc 9.0.0rc1" />\r
-<title>ccache authors</title>\r
-<style type="text/css">\r
-/* Shared CSS for AsciiDoc xhtml11 and html5 backends */\r
-\r
-/* Default font. */\r
-body {\r
- font-family: Georgia,serif;\r
-}\r
-\r
-/* Title font. */\r
-h1, h2, h3, h4, h5, h6,\r
-div.title, caption.title,\r
-thead, p.table.header,\r
-#toctitle,\r
-#author, #revnumber, #revdate, #revremark,\r
-#footer {\r
- font-family: Arial,Helvetica,sans-serif;\r
-}\r
-\r
-body {\r
- margin: 1em 5% 1em 5%;\r
-}\r
-\r
-a {\r
- color: blue;\r
- text-decoration: underline;\r
-}\r
-a:visited {\r
- color: fuchsia;\r
-}\r
-\r
-em {\r
- font-style: italic;\r
- color: navy;\r
-}\r
-\r
-strong {\r
- font-weight: bold;\r
- color: #083194;\r
-}\r
-\r
-h1, h2, h3, h4, h5, h6 {\r
- color: #527bbd;\r
- margin-top: 1.2em;\r
- margin-bottom: 0.5em;\r
- line-height: 1.3;\r
-}\r
-\r
-h1, h2, h3 {\r
- border-bottom: 2px solid silver;\r
-}\r
-h2 {\r
- padding-top: 0.5em;\r
-}\r
-h3 {\r
- float: left;\r
-}\r
-h3 + * {\r
- clear: left;\r
-}\r
-h5 {\r
- font-size: 1.0em;\r
-}\r
-\r
-div.sectionbody {\r
- margin-left: 0;\r
-}\r
-\r
-hr {\r
- border: 1px solid silver;\r
-}\r
-\r
-p {\r
- margin-top: 0.5em;\r
- margin-bottom: 0.5em;\r
-}\r
-\r
-ul, ol, li > p {\r
- margin-top: 0;\r
-}\r
-ul > li { color: #aaa; }\r
-ul > li > * { color: black; }\r
-\r
-.monospaced, code, pre {\r
- font-family: "Courier New", Courier, monospace;\r
- font-size: inherit;\r
- color: navy;\r
- padding: 0;\r
- margin: 0;\r
-}\r
-pre {\r
- white-space: pre-wrap;\r
-}\r
-\r
-#author {\r
- color: #527bbd;\r
- font-weight: bold;\r
- font-size: 1.1em;\r
-}\r
-#email {\r
-}\r
-#revnumber, #revdate, #revremark {\r
-}\r
-\r
-#footer {\r
- font-size: small;\r
- border-top: 2px solid silver;\r
- padding-top: 0.5em;\r
- margin-top: 4.0em;\r
-}\r
-#footer-text {\r
- float: left;\r
- padding-bottom: 0.5em;\r
-}\r
-#footer-badges {\r
- float: right;\r
- padding-bottom: 0.5em;\r
-}\r
-\r
-#preamble {\r
- margin-top: 1.5em;\r
- margin-bottom: 1.5em;\r
-}\r
-div.imageblock, div.exampleblock, div.verseblock,\r
-div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,\r
-div.admonitionblock {\r
- margin-top: 1.0em;\r
- margin-bottom: 1.5em;\r
-}\r
-div.admonitionblock {\r
- margin-top: 2.0em;\r
- margin-bottom: 2.0em;\r
- margin-right: 10%;\r
- color: #606060;\r
-}\r
-\r
-div.content { /* Block element content. */\r
- padding: 0;\r
-}\r
-\r
-/* Block element titles. */\r
-div.title, caption.title {\r
- color: #527bbd;\r
- font-weight: bold;\r
- text-align: left;\r
- margin-top: 1.0em;\r
- margin-bottom: 0.5em;\r
-}\r
-div.title + * {\r
- margin-top: 0;\r
-}\r
-\r
-td div.title:first-child {\r
- margin-top: 0.0em;\r
-}\r
-div.content div.title:first-child {\r
- margin-top: 0.0em;\r
-}\r
-div.content + div.title {\r
- margin-top: 0.0em;\r
-}\r
-\r
-div.sidebarblock > div.content {\r
- background: #ffffee;\r
- border: 1px solid #dddddd;\r
- border-left: 4px solid #f0f0f0;\r
- padding: 0.5em;\r
-}\r
-\r
-div.listingblock > div.content {\r
- border: 1px solid #dddddd;\r
- border-left: 5px solid #f0f0f0;\r
- background: #f8f8f8;\r
- padding: 0.5em;\r
-}\r
-\r
-div.quoteblock, div.verseblock {\r
- padding-left: 1.0em;\r
- margin-left: 1.0em;\r
- margin-right: 10%;\r
- border-left: 5px solid #f0f0f0;\r
- color: #888;\r
-}\r
-\r
-div.quoteblock > div.attribution {\r
- padding-top: 0.5em;\r
- text-align: right;\r
-}\r
-\r
-div.verseblock > pre.content {\r
- font-family: inherit;\r
- font-size: inherit;\r
-}\r
-div.verseblock > div.attribution {\r
- padding-top: 0.75em;\r
- text-align: left;\r
-}\r
-/* DEPRECATED: Pre version 8.2.7 verse style literal block. */\r
-div.verseblock + div.attribution {\r
- text-align: left;\r
-}\r
-\r
-div.admonitionblock .icon {\r
- vertical-align: top;\r
- font-size: 1.1em;\r
- font-weight: bold;\r
- text-decoration: underline;\r
- color: #527bbd;\r
- padding-right: 0.5em;\r
-}\r
-div.admonitionblock td.content {\r
- padding-left: 0.5em;\r
- border-left: 3px solid #dddddd;\r
-}\r
-\r
-div.exampleblock > div.content {\r
- border-left: 3px solid #dddddd;\r
- padding-left: 0.5em;\r
-}\r
-\r
-div.imageblock div.content { padding-left: 0; }\r
-span.image img { border-style: none; vertical-align: text-bottom; }\r
-a.image:visited { color: white; }\r
-\r
-dl {\r
- margin-top: 0.8em;\r
- margin-bottom: 0.8em;\r
-}\r
-dt {\r
- margin-top: 0.5em;\r
- margin-bottom: 0;\r
- font-style: normal;\r
- color: navy;\r
-}\r
-dd > *:first-child {\r
- margin-top: 0.1em;\r
-}\r
-\r
-ul, ol {\r
- list-style-position: outside;\r
-}\r
-ol.arabic {\r
- list-style-type: decimal;\r
-}\r
-ol.loweralpha {\r
- list-style-type: lower-alpha;\r
-}\r
-ol.upperalpha {\r
- list-style-type: upper-alpha;\r
-}\r
-ol.lowerroman {\r
- list-style-type: lower-roman;\r
-}\r
-ol.upperroman {\r
- list-style-type: upper-roman;\r
-}\r
-\r
-div.compact ul, div.compact ol,\r
-div.compact p, div.compact p,\r
-div.compact div, div.compact div {\r
- margin-top: 0.1em;\r
- margin-bottom: 0.1em;\r
-}\r
-\r
-tfoot {\r
- font-weight: bold;\r
-}\r
-td > div.verse {\r
- white-space: pre;\r
-}\r
-\r
-div.hdlist {\r
- margin-top: 0.8em;\r
- margin-bottom: 0.8em;\r
-}\r
-div.hdlist tr {\r
- padding-bottom: 15px;\r
-}\r
-dt.hdlist1.strong, td.hdlist1.strong {\r
- font-weight: bold;\r
-}\r
-td.hdlist1 {\r
- vertical-align: top;\r
- font-style: normal;\r
- padding-right: 0.8em;\r
- color: navy;\r
-}\r
-td.hdlist2 {\r
- vertical-align: top;\r
-}\r
-div.hdlist.compact tr {\r
- margin: 0;\r
- padding-bottom: 0;\r
-}\r
-\r
-.comment {\r
- background: yellow;\r
-}\r
-\r
-.footnote, .footnoteref {\r
- font-size: 0.8em;\r
-}\r
-\r
-span.footnote, span.footnoteref {\r
- vertical-align: super;\r
-}\r
-\r
-#footnotes {\r
- margin: 20px 0 20px 0;\r
- padding: 7px 0 0 0;\r
-}\r
-\r
-#footnotes div.footnote {\r
- margin: 0 0 5px 0;\r
-}\r
-\r
-#footnotes hr {\r
- border: none;\r
- border-top: 1px solid silver;\r
- height: 1px;\r
- text-align: left;\r
- margin-left: 0;\r
- width: 20%;\r
- min-width: 100px;\r
-}\r
-\r
-div.colist td {\r
- padding-right: 0.5em;\r
- padding-bottom: 0.3em;\r
- vertical-align: top;\r
-}\r
-div.colist td img {\r
- margin-top: 0.3em;\r
-}\r
-\r
-@media print {\r
- #footer-badges { display: none; }\r
-}\r
-\r
-#toc {\r
- margin-bottom: 2.5em;\r
-}\r
-\r
-#toctitle {\r
- color: #527bbd;\r
- font-size: 1.1em;\r
- font-weight: bold;\r
- margin-top: 1.0em;\r
- margin-bottom: 0.1em;\r
-}\r
-\r
-div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {\r
- margin-top: 0;\r
- margin-bottom: 0;\r
-}\r
-div.toclevel2 {\r
- margin-left: 2em;\r
- font-size: 0.9em;\r
-}\r
-div.toclevel3 {\r
- margin-left: 4em;\r
- font-size: 0.9em;\r
-}\r
-div.toclevel4 {\r
- margin-left: 6em;\r
- font-size: 0.9em;\r
-}\r
-\r
-span.aqua { color: aqua; }\r
-span.black { color: black; }\r
-span.blue { color: blue; }\r
-span.fuchsia { color: fuchsia; }\r
-span.gray { color: gray; }\r
-span.green { color: green; }\r
-span.lime { color: lime; }\r
-span.maroon { color: maroon; }\r
-span.navy { color: navy; }\r
-span.olive { color: olive; }\r
-span.purple { color: purple; }\r
-span.red { color: red; }\r
-span.silver { color: silver; }\r
-span.teal { color: teal; }\r
-span.white { color: white; }\r
-span.yellow { color: yellow; }\r
-\r
-span.aqua-background { background: aqua; }\r
-span.black-background { background: black; }\r
-span.blue-background { background: blue; }\r
-span.fuchsia-background { background: fuchsia; }\r
-span.gray-background { background: gray; }\r
-span.green-background { background: green; }\r
-span.lime-background { background: lime; }\r
-span.maroon-background { background: maroon; }\r
-span.navy-background { background: navy; }\r
-span.olive-background { background: olive; }\r
-span.purple-background { background: purple; }\r
-span.red-background { background: red; }\r
-span.silver-background { background: silver; }\r
-span.teal-background { background: teal; }\r
-span.white-background { background: white; }\r
-span.yellow-background { background: yellow; }\r
-\r
-span.big { font-size: 2em; }\r
-span.small { font-size: 0.6em; }\r
-\r
-span.underline { text-decoration: underline; }\r
-span.overline { text-decoration: overline; }\r
-span.line-through { text-decoration: line-through; }\r
-\r
-div.unbreakable { page-break-inside: avoid; }\r
-\r
-\r
-/*\r
- * xhtml11 specific\r
- *\r
- * */\r
-\r
-div.tableblock {\r
- margin-top: 1.0em;\r
- margin-bottom: 1.5em;\r
-}\r
-div.tableblock > table {\r
- border: 3px solid #527bbd;\r
-}\r
-thead, p.table.header {\r
- font-weight: bold;\r
- color: #527bbd;\r
-}\r
-p.table {\r
- margin-top: 0;\r
-}\r
-/* Because the table frame attribute is overridden by CSS in most browsers. */\r
-div.tableblock > table[frame="void"] {\r
- border-style: none;\r
-}\r
-div.tableblock > table[frame="hsides"] {\r
- border-left-style: none;\r
- border-right-style: none;\r
-}\r
-div.tableblock > table[frame="vsides"] {\r
- border-top-style: none;\r
- border-bottom-style: none;\r
-}\r
-\r
-\r
-/*\r
- * html5 specific\r
- *\r
- * */\r
-\r
-table.tableblock {\r
- margin-top: 1.0em;\r
- margin-bottom: 1.5em;\r
-}\r
-thead, p.tableblock.header {\r
- font-weight: bold;\r
- color: #527bbd;\r
-}\r
-p.tableblock {\r
- margin-top: 0;\r
-}\r
-table.tableblock {\r
- border-width: 3px;\r
- border-spacing: 0px;\r
- border-style: solid;\r
- border-color: #527bbd;\r
- border-collapse: collapse;\r
-}\r
-th.tableblock, td.tableblock {\r
- border-width: 1px;\r
- padding: 4px;\r
- border-style: solid;\r
- border-color: #527bbd;\r
-}\r
-\r
-table.tableblock.frame-topbot {\r
- border-left-style: hidden;\r
- border-right-style: hidden;\r
-}\r
-table.tableblock.frame-sides {\r
- border-top-style: hidden;\r
- border-bottom-style: hidden;\r
-}\r
-table.tableblock.frame-none {\r
- border-style: hidden;\r
-}\r
-\r
-th.tableblock.halign-left, td.tableblock.halign-left {\r
- text-align: left;\r
-}\r
-th.tableblock.halign-center, td.tableblock.halign-center {\r
- text-align: center;\r
-}\r
-th.tableblock.halign-right, td.tableblock.halign-right {\r
- text-align: right;\r
-}\r
-\r
-th.tableblock.valign-top, td.tableblock.valign-top {\r
- vertical-align: top;\r
-}\r
-th.tableblock.valign-middle, td.tableblock.valign-middle {\r
- vertical-align: middle;\r
-}\r
-th.tableblock.valign-bottom, td.tableblock.valign-bottom {\r
- vertical-align: bottom;\r
-}\r
-\r
-\r
-/*\r
- * manpage specific\r
- *\r
- * */\r
-\r
-body.manpage h1 {\r
- padding-top: 0.5em;\r
- padding-bottom: 0.5em;\r
- border-top: 2px solid silver;\r
- border-bottom: 2px solid silver;\r
-}\r
-body.manpage h2 {\r
- border-style: none;\r
-}\r
-body.manpage div.sectionbody {\r
- margin-left: 3em;\r
-}\r
-\r
-@media print {\r
- body.manpage div#toc { display: none; }\r
-}\r
-\r
-\r
-</style>\r
-<script type="text/javascript">\r
-/*<+'])');\r
- // Function that scans the DOM tree for header elements (the DOM2\r
- // nodeIterator API would be a better technique but not supported by all\r
- // browsers).\r
- var iterate = function (el) {\r
- for (var i = el.firstChild; i != null; i = i.nextSibling) {\r
- if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {\r
- var mo = re.exec(i.tagName);\r
- if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {\r
- result[result.length] = new TocEntry(i, getText(i), mo[1]-1);\r
- }\r
- iterate(i);\r
- }\r
- }\r
- }\r
- iterate(el);\r
- return result;\r
- }\r
-\r
- var toc = document.getElementById("toc");\r
- if (!toc) {\r
- return;\r
- }\r
-\r
- // Delete existing TOC entries in case we're reloading the TOC.\r
- var tocEntriesToRemove = [];\r
- var i;\r
- for (i = 0; i < toc.childNodes.length; i++) {\r
- var entry = toc.childNodes[i];\r
- if (entry.nodeName.toLowerCase() == 'div'\r
- && entry.getAttribute("class")\r
- && entry.getAttribute("class").match(/^toclevel/))\r
- tocEntriesToRemove.push(entry);\r
- }\r
- for (i = 0; i < tocEntriesToRemove.length; i++) {\r
- toc.removeChild(tocEntriesToRemove[i]);\r
- }\r
-\r
- // Rebuild TOC entries.\r
- var entries = tocEntries(document.getElementById("content"), toclevels);\r
- for (var i = 0; i < entries.length; ++i) {\r
- var entry = entries[i];\r
- if (entry.element.id == "")\r
- entry.element.id = "_toc_" + i;\r
- var a = document.createElement("a");\r
- a.href = "#" + entry.element.id;\r
- a.appendChild(document.createTextNode(entry.text));\r
- var div = document.createElement("div");\r
- div.appendChild(a);\r
- div.className = "toclevel" + entry.toclevel;\r
- toc.appendChild(div);\r
- }\r
- if (entries.length == 0)\r
- toc.parentNode.removeChild(toc);\r
-},\r
-\r
-\r
-/////////////////////////////////////////////////////////////////////\r
-// Footnotes generator\r
-/////////////////////////////////////////////////////////////////////\r
-\r
-/* Based on footnote generation code from:\r
- * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html\r
- */\r
-\r
-footnotes: function () {\r
- // Delete existing footnote entries in case we're reloading the footnodes.\r
- var i;\r
- var noteholder = document.getElementById("footnotes");\r
- if (!noteholder) {\r
- return;\r
- }\r
- var entriesToRemove = [];\r
- for (i = 0; i < noteholder.childNodes.length; i++) {\r
- var entry = noteholder.childNodes[i];\r
- if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")\r
- entriesToRemove.push(entry);\r
- }\r
- for (i = 0; i < entriesToRemove.length; i++) {\r
- noteholder.removeChild(entriesToRemove[i]);\r
- }\r
-\r
- // Rebuild footnote entries.\r
- var cont = document.getElementById("content");\r
- var spans = cont.getElementsByTagName("span");\r
- var refs = {};\r
- var n = 0;\r
- for (i=0; i<spans.length; i++) {\r
- if (spans[i].className == "footnote") {\r
- n++;\r
- var note = spans[i].getAttribute("data-note");\r
- if (!note) {\r
- // Use [\s\S] in place of . so multi-line matches work.\r
- // Because JavaScript has no s (dotall) regex flag.\r
- note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];\r
- spans[i].innerHTML =\r
- "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +\r
- "' title='View footnote' class='footnote'>" + n + "</a>]";\r
- spans[i].setAttribute("data-note", note);\r
- }\r
- noteholder.innerHTML +=\r
- "<div class='footnote' id='_footnote_" + n + "'>" +\r
- "<a href='#_footnoteref_" + n + "' title='Return to text'>" +\r
- n + "</a>. " + note + "</div>";\r
- var id =spans[i].getAttribute("id");\r
- if (id != null) refs["#"+id] = n;\r
- }\r
- }\r
- if (n == 0)\r
- noteholder.parentNode.removeChild(noteholder);\r
- else {\r
- // Process footnoterefs.\r
- for (i=0; i<spans.length; i++) {\r
- if (spans[i].className == "footnoteref") {\r
- var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");\r
- href = href.match(/#.*/)[0]; // Because IE return full URL.\r
- n = refs[href];\r
- spans[i].innerHTML =\r
- "[<a href='#_footnote_" + n +\r
- "' title='View footnote' class='footnote'>" + n + "</a>]";\r
- }\r
- }\r
- }\r
-},\r
-\r
-install: function(toclevels) {\r
- var timerId;\r
-\r
- function reinstall() {\r
- asciidoc.footnotes();\r
- if (toclevels) {\r
- asciidoc.toc(toclevels);\r
- }\r
- }\r
-\r
- function reinstallAndRemoveTimer() {\r
- clearInterval(timerId);\r
- reinstall();\r
- }\r
-\r
- timerId = setInterval(reinstall, 500);\r
- if (document.addEventListener)\r
- document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);\r
- else\r
- window.onload = reinstallAndRemoveTimer;\r
-}\r
-\r
-}\r
-asciidoc.install(2);\r
-/*]]>*/\r
-</script>\r
-</head>\r
-<body class="article">\r
-<div id="header">\r
-<h1>ccache authors</h1>\r
-<span id="revnumber">version 3.7.12</span>\r
-<div id="toc">
- <div id="toctitle">Table of Contents</div>
- <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
-</div>\r
-</div>\r
-<div id="content">\r
-<div id="preamble">\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>ccache was originally written by Andrew Tridgell and is currently developed and\r
-maintained by Joel Rosdahl.</p></div>\r
-<div class="paragraph"><p>ccache is a collective work with contributions from many people, including:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Alexander Korsunsky\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Alexander Lanin\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Alexey Tourbin\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Alfred Landrum\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Anders F Björklund\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Andrea Bittau\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Andreas Huber\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-André Klitzing\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Andrew Boie\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Andrew Stubbs\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Andrew Tridgell\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Bernhard Bauer\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Björn Jacke\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Chiaki Ishikawa\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Chris AtLee\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Clemens Rabe\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Cristian Adam\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-David Givone\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Deepak Yadav\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Doug Anderson\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Edward Z. Yang\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Erik Johansson\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Francois Marier\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Gabriel Scherer\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Geert Bosch\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Geert Kloosterman\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Grigory Entin\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Havard Graff\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Hongli Lai\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Ivan Vaigult\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Ivan Volnov\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Jiang Jiang\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Joel Galenson\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Joel Rosdahl\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-John Basila\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-John Coiner\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Jon Bernard\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Jonny Yu\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Jørgen P. Tjernø\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Josh Soref\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Justin Lebar\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Karl Chen\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Kona Blend\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Kovarththanan Rajaratnam\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Lalit Chhabra\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Lars Gustäbel\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Leanid Chaika\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Loïc Yhuel\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Luboš Luňák\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-luzpaz\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Maarten Maathuis\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Mark Starovoytov\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Martin Ettl\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Martin Pool\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Mathias De Maré\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Matthias Kretz\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Melven Roehrig-Zoellner\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Michael Marineau\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Michael Meeks\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Michał Mirosław\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Mihai Serban\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Mike Blumenkrantz\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Mike Frysinger\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Mike Gulick\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Mikhail Kolomeytsev\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Mizuha Himuraki\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Mostyn Bramley-Moore\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Neil Mushell\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Nick Schultz\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Norbert Lange\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Oded Shimon\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Olle Liljenzin\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Orgad Shaneh\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Orion Poplawski\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Owen Mann\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Patrick von Reth\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Paul Griffith\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Pavel Boldin\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Pavol Sakac\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Per Nordlöw\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Peter Budai\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Philippe Proulx\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Rafael Kitover\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Ramiro Polla\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Robert Yang\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Robin H. Johnson\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Rolf Bjarne Kvinge\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Russell King\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-RW\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Ryan Brown\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Sam Gross\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Steffen Dettmer\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Thomas Otto\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Thomas Röfer\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Timofei Kushnir\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Tim Potter\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Tomasz Miąsko\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Tom Hughes\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Tor Arne Vestbø\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Vadim Petrochenkov\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Ville Skyttä\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-William S Fulton\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Wilson Snyder\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Xavier René-Corail\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Yiding Jia\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Yvan Janssens\r
-</p>\r
-</li>\r
-</ul></div>\r
-<div class="paragraph"><p>Thanks!</p></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div id="footnotes"><hr /></div>\r
-<div id="footer">\r
-<div id="footer-text">\r
-Version 3.7.12<br />\r
-Last updated\r
- 2020-10-01 14:20:15 CEST\r
-</div>\r
-</div>\r
-</body>\r
-</html>\r
--- /dev/null
+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!<literal>\(.*?\)</literal>!<emphasis role="strong">\\1</emphasis>!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()
--- /dev/null
+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.
--- /dev/null
+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.
Name
----
-ccache - a fast C/C++ compiler cache
+Ccache - a fast C/C++ compiler cache
Synopsis
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
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
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 `<MD4>-<size>`) 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
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
+ <<config_compression_level,*compression_level*>> 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
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 *_<ccachedir>_/ccache.conf* (typically
- *$HOME/.ccache/ccache.conf*).
-3. The system-wide configuration file *_<sysconfdir>_/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
+ *_<sysconfdir>_/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 <<config_ccache_dir,*ccache_dir*>> (*CCACHE_DIR*) is set then
+ use *<ccache_dir>/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
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 <<config_base_dir,*base_dir*>> 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*::
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.
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
+<<config_file_clone,*file_clone*>> option) or hard linking (the
+<<config_hard_link,*hard_link*>> 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
+ <<config_compression,*compression*>> 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 <<config_hard_link,*hard_link*>>. 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 <<config_hard_link,*hard_link*>> 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
+<<config_hard_link,*hard_link*>> 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
+ <<config_base_dir,*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,COMPILING IN DIFFERENT DIRECTORIES>>.
+
The reason for including the CWD in the hash by default is to prevent a problem
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
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
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 <<config_stats,*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.
+set <<config_temporary_dir,*temporary_dir*>> since ccache will fail to create
+temporary files otherwise. You may also want to set <<config_stats,*stats*>> 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 <<config_read_only,*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 <<config_read_only,*read_only*>>
+ 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
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 *<cache_dir>/tmp*.
+ This option specifies where ccache will put temporary files. The default is
+ */run/user/<UID>/ccache-tmp* if */run/user/<UID>* exists, otherwise
+ *<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.)
+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.
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.
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
+ <<config_limit_multiple,*limit_multiple*>>, <<config_max_size,*max_size*>>
+ and <<config_max_files,*max_files*>> 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
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
+<<config_max_files,*max_files*>> are not exceeded. Note that
+<<config_limit_multiple,*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*.
+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
+<<config_compression,*compression*>> and
+<<config_compression_level,*compression_level*>> 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 <<config_file_clone,*file_clone*>> or
+ <<config_hard_link,*hard_link*>>) or unknown files (for instance files
+ created by older ccache versions).
+* The compression ratio is affected by
+ <<config_compression_level,*compression_level*>>.
+
+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
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
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.
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
+<<config_compiler_check,*compiler_check*>> (*CCACHE_COMPILERCHECK*) failed.
| compiler produced empty output |
The compiler's output file (typically an object file) was empty after
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
+<<config_extra_file_to_hash,*extra_files_to_hash*>> (*CCACHE_EXTRAFILES*).
| files in cache |
Current number of files in the cache.
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.
* 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 <<config_compiler_check,*compiler_check*>>)
* 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 <<config_hash_dir,*hash_dir*>> is enabled)
+* contents of files specified by
+ <<config_extra_file_to_hash,*extra_files_to_hash*>> (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
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:
The direct mode will be disabled if any of the following holds:
-* the configuration setting *direct_mode* is false
+* <<config_direct_mode,*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:
* 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
~~~~~~~~~~~~~~~
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*
+* <<config_depend_mode,*depend_mode*>> is false.
+* <<config_run_second_cpp,*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.
+debug mode via the configuration option <<config_debug,*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:
|==============================================================================
-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
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 <<config_base_dir,*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
+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 <<config_sloppiness,*sloppiness*>> 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
--
+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
+<<config_sloppiness,*sloppiness*>> 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
---------------
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 <<config_hard_link,*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.
+* Set the configuration option <<config_umask,*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
* 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 <<config_temporary_dir,*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
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 <<config_prefix_command,*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
+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 <<config_prefix_command,*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:
+Unless you set <<config_compiler_check,*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*).
+Another minor thing is that if <<config_prefix_command,*prefix_command*>> is
+used, ccache will not invoke the other wrapper when running the preprocessor,
+which increases performance. You can use
+<<config_prefix_command_cpp,*prefix_command_cpp*>> 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,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
~~~~~~~
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 <<config_debug,*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*.
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.
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
+ <<config_base_dir,base directory>>.
** 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 <<config_sloppiness,*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*.
+ <<config_sloppiness,*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
+ past, you can set <<config_sloppiness,*sloppiness*>> 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
+ <<config_sloppiness,*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.
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
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:
+<<config_prefix_command,*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. Remove the bad object file from the build tree.
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
<https://ccache.dev/credits.html> for a list of contributors.
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>\r
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"\r
- "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">\r
-<head>\r
-<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />\r
-<meta name="generator" content="AsciiDoc 9.0.0rc1" />\r
-<title>CCACHE(1)</title>\r
-<style type="text/css">\r
-/* Shared CSS for AsciiDoc xhtml11 and html5 backends */\r
-\r
-/* Default font. */\r
-body {\r
- font-family: Georgia,serif;\r
-}\r
-\r
-/* Title font. */\r
-h1, h2, h3, h4, h5, h6,\r
-div.title, caption.title,\r
-thead, p.table.header,\r
-#toctitle,\r
-#author, #revnumber, #revdate, #revremark,\r
-#footer {\r
- font-family: Arial,Helvetica,sans-serif;\r
-}\r
-\r
-body {\r
- margin: 1em 5% 1em 5%;\r
-}\r
-\r
-a {\r
- color: blue;\r
- text-decoration: underline;\r
-}\r
-a:visited {\r
- color: fuchsia;\r
-}\r
-\r
-em {\r
- font-style: italic;\r
- color: navy;\r
-}\r
-\r
-strong {\r
- font-weight: bold;\r
- color: #083194;\r
-}\r
-\r
-h1, h2, h3, h4, h5, h6 {\r
- color: #527bbd;\r
- margin-top: 1.2em;\r
- margin-bottom: 0.5em;\r
- line-height: 1.3;\r
-}\r
-\r
-h1, h2, h3 {\r
- border-bottom: 2px solid silver;\r
-}\r
-h2 {\r
- padding-top: 0.5em;\r
-}\r
-h3 {\r
- float: left;\r
-}\r
-h3 + * {\r
- clear: left;\r
-}\r
-h5 {\r
- font-size: 1.0em;\r
-}\r
-\r
-div.sectionbody {\r
- margin-left: 0;\r
-}\r
-\r
-hr {\r
- border: 1px solid silver;\r
-}\r
-\r
-p {\r
- margin-top: 0.5em;\r
- margin-bottom: 0.5em;\r
-}\r
-\r
-ul, ol, li > p {\r
- margin-top: 0;\r
-}\r
-ul > li { color: #aaa; }\r
-ul > li > * { color: black; }\r
-\r
-.monospaced, code, pre {\r
- font-family: "Courier New", Courier, monospace;\r
- font-size: inherit;\r
- color: navy;\r
- padding: 0;\r
- margin: 0;\r
-}\r
-pre {\r
- white-space: pre-wrap;\r
-}\r
-\r
-#author {\r
- color: #527bbd;\r
- font-weight: bold;\r
- font-size: 1.1em;\r
-}\r
-#email {\r
-}\r
-#revnumber, #revdate, #revremark {\r
-}\r
-\r
-#footer {\r
- font-size: small;\r
- border-top: 2px solid silver;\r
- padding-top: 0.5em;\r
- margin-top: 4.0em;\r
-}\r
-#footer-text {\r
- float: left;\r
- padding-bottom: 0.5em;\r
-}\r
-#footer-badges {\r
- float: right;\r
- padding-bottom: 0.5em;\r
-}\r
-\r
-#preamble {\r
- margin-top: 1.5em;\r
- margin-bottom: 1.5em;\r
-}\r
-div.imageblock, div.exampleblock, div.verseblock,\r
-div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,\r
-div.admonitionblock {\r
- margin-top: 1.0em;\r
- margin-bottom: 1.5em;\r
-}\r
-div.admonitionblock {\r
- margin-top: 2.0em;\r
- margin-bottom: 2.0em;\r
- margin-right: 10%;\r
- color: #606060;\r
-}\r
-\r
-div.content { /* Block element content. */\r
- padding: 0;\r
-}\r
-\r
-/* Block element titles. */\r
-div.title, caption.title {\r
- color: #527bbd;\r
- font-weight: bold;\r
- text-align: left;\r
- margin-top: 1.0em;\r
- margin-bottom: 0.5em;\r
-}\r
-div.title + * {\r
- margin-top: 0;\r
-}\r
-\r
-td div.title:first-child {\r
- margin-top: 0.0em;\r
-}\r
-div.content div.title:first-child {\r
- margin-top: 0.0em;\r
-}\r
-div.content + div.title {\r
- margin-top: 0.0em;\r
-}\r
-\r
-div.sidebarblock > div.content {\r
- background: #ffffee;\r
- border: 1px solid #dddddd;\r
- border-left: 4px solid #f0f0f0;\r
- padding: 0.5em;\r
-}\r
-\r
-div.listingblock > div.content {\r
- border: 1px solid #dddddd;\r
- border-left: 5px solid #f0f0f0;\r
- background: #f8f8f8;\r
- padding: 0.5em;\r
-}\r
-\r
-div.quoteblock, div.verseblock {\r
- padding-left: 1.0em;\r
- margin-left: 1.0em;\r
- margin-right: 10%;\r
- border-left: 5px solid #f0f0f0;\r
- color: #888;\r
-}\r
-\r
-div.quoteblock > div.attribution {\r
- padding-top: 0.5em;\r
- text-align: right;\r
-}\r
-\r
-div.verseblock > pre.content {\r
- font-family: inherit;\r
- font-size: inherit;\r
-}\r
-div.verseblock > div.attribution {\r
- padding-top: 0.75em;\r
- text-align: left;\r
-}\r
-/* DEPRECATED: Pre version 8.2.7 verse style literal block. */\r
-div.verseblock + div.attribution {\r
- text-align: left;\r
-}\r
-\r
-div.admonitionblock .icon {\r
- vertical-align: top;\r
- font-size: 1.1em;\r
- font-weight: bold;\r
- text-decoration: underline;\r
- color: #527bbd;\r
- padding-right: 0.5em;\r
-}\r
-div.admonitionblock td.content {\r
- padding-left: 0.5em;\r
- border-left: 3px solid #dddddd;\r
-}\r
-\r
-div.exampleblock > div.content {\r
- border-left: 3px solid #dddddd;\r
- padding-left: 0.5em;\r
-}\r
-\r
-div.imageblock div.content { padding-left: 0; }\r
-span.image img { border-style: none; vertical-align: text-bottom; }\r
-a.image:visited { color: white; }\r
-\r
-dl {\r
- margin-top: 0.8em;\r
- margin-bottom: 0.8em;\r
-}\r
-dt {\r
- margin-top: 0.5em;\r
- margin-bottom: 0;\r
- font-style: normal;\r
- color: navy;\r
-}\r
-dd > *:first-child {\r
- margin-top: 0.1em;\r
-}\r
-\r
-ul, ol {\r
- list-style-position: outside;\r
-}\r
-ol.arabic {\r
- list-style-type: decimal;\r
-}\r
-ol.loweralpha {\r
- list-style-type: lower-alpha;\r
-}\r
-ol.upperalpha {\r
- list-style-type: upper-alpha;\r
-}\r
-ol.lowerroman {\r
- list-style-type: lower-roman;\r
-}\r
-ol.upperroman {\r
- list-style-type: upper-roman;\r
-}\r
-\r
-div.compact ul, div.compact ol,\r
-div.compact p, div.compact p,\r
-div.compact div, div.compact div {\r
- margin-top: 0.1em;\r
- margin-bottom: 0.1em;\r
-}\r
-\r
-tfoot {\r
- font-weight: bold;\r
-}\r
-td > div.verse {\r
- white-space: pre;\r
-}\r
-\r
-div.hdlist {\r
- margin-top: 0.8em;\r
- margin-bottom: 0.8em;\r
-}\r
-div.hdlist tr {\r
- padding-bottom: 15px;\r
-}\r
-dt.hdlist1.strong, td.hdlist1.strong {\r
- font-weight: bold;\r
-}\r
-td.hdlist1 {\r
- vertical-align: top;\r
- font-style: normal;\r
- padding-right: 0.8em;\r
- color: navy;\r
-}\r
-td.hdlist2 {\r
- vertical-align: top;\r
-}\r
-div.hdlist.compact tr {\r
- margin: 0;\r
- padding-bottom: 0;\r
-}\r
-\r
-.comment {\r
- background: yellow;\r
-}\r
-\r
-.footnote, .footnoteref {\r
- font-size: 0.8em;\r
-}\r
-\r
-span.footnote, span.footnoteref {\r
- vertical-align: super;\r
-}\r
-\r
-#footnotes {\r
- margin: 20px 0 20px 0;\r
- padding: 7px 0 0 0;\r
-}\r
-\r
-#footnotes div.footnote {\r
- margin: 0 0 5px 0;\r
-}\r
-\r
-#footnotes hr {\r
- border: none;\r
- border-top: 1px solid silver;\r
- height: 1px;\r
- text-align: left;\r
- margin-left: 0;\r
- width: 20%;\r
- min-width: 100px;\r
-}\r
-\r
-div.colist td {\r
- padding-right: 0.5em;\r
- padding-bottom: 0.3em;\r
- vertical-align: top;\r
-}\r
-div.colist td img {\r
- margin-top: 0.3em;\r
-}\r
-\r
-@media print {\r
- #footer-badges { display: none; }\r
-}\r
-\r
-#toc {\r
- margin-bottom: 2.5em;\r
-}\r
-\r
-#toctitle {\r
- color: #527bbd;\r
- font-size: 1.1em;\r
- font-weight: bold;\r
- margin-top: 1.0em;\r
- margin-bottom: 0.1em;\r
-}\r
-\r
-div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {\r
- margin-top: 0;\r
- margin-bottom: 0;\r
-}\r
-div.toclevel2 {\r
- margin-left: 2em;\r
- font-size: 0.9em;\r
-}\r
-div.toclevel3 {\r
- margin-left: 4em;\r
- font-size: 0.9em;\r
-}\r
-div.toclevel4 {\r
- margin-left: 6em;\r
- font-size: 0.9em;\r
-}\r
-\r
-span.aqua { color: aqua; }\r
-span.black { color: black; }\r
-span.blue { color: blue; }\r
-span.fuchsia { color: fuchsia; }\r
-span.gray { color: gray; }\r
-span.green { color: green; }\r
-span.lime { color: lime; }\r
-span.maroon { color: maroon; }\r
-span.navy { color: navy; }\r
-span.olive { color: olive; }\r
-span.purple { color: purple; }\r
-span.red { color: red; }\r
-span.silver { color: silver; }\r
-span.teal { color: teal; }\r
-span.white { color: white; }\r
-span.yellow { color: yellow; }\r
-\r
-span.aqua-background { background: aqua; }\r
-span.black-background { background: black; }\r
-span.blue-background { background: blue; }\r
-span.fuchsia-background { background: fuchsia; }\r
-span.gray-background { background: gray; }\r
-span.green-background { background: green; }\r
-span.lime-background { background: lime; }\r
-span.maroon-background { background: maroon; }\r
-span.navy-background { background: navy; }\r
-span.olive-background { background: olive; }\r
-span.purple-background { background: purple; }\r
-span.red-background { background: red; }\r
-span.silver-background { background: silver; }\r
-span.teal-background { background: teal; }\r
-span.white-background { background: white; }\r
-span.yellow-background { background: yellow; }\r
-\r
-span.big { font-size: 2em; }\r
-span.small { font-size: 0.6em; }\r
-\r
-span.underline { text-decoration: underline; }\r
-span.overline { text-decoration: overline; }\r
-span.line-through { text-decoration: line-through; }\r
-\r
-div.unbreakable { page-break-inside: avoid; }\r
-\r
-\r
-/*\r
- * xhtml11 specific\r
- *\r
- * */\r
-\r
-div.tableblock {\r
- margin-top: 1.0em;\r
- margin-bottom: 1.5em;\r
-}\r
-div.tableblock > table {\r
- border: 3px solid #527bbd;\r
-}\r
-thead, p.table.header {\r
- font-weight: bold;\r
- color: #527bbd;\r
-}\r
-p.table {\r
- margin-top: 0;\r
-}\r
-/* Because the table frame attribute is overridden by CSS in most browsers. */\r
-div.tableblock > table[frame="void"] {\r
- border-style: none;\r
-}\r
-div.tableblock > table[frame="hsides"] {\r
- border-left-style: none;\r
- border-right-style: none;\r
-}\r
-div.tableblock > table[frame="vsides"] {\r
- border-top-style: none;\r
- border-bottom-style: none;\r
-}\r
-\r
-\r
-/*\r
- * html5 specific\r
- *\r
- * */\r
-\r
-table.tableblock {\r
- margin-top: 1.0em;\r
- margin-bottom: 1.5em;\r
-}\r
-thead, p.tableblock.header {\r
- font-weight: bold;\r
- color: #527bbd;\r
-}\r
-p.tableblock {\r
- margin-top: 0;\r
-}\r
-table.tableblock {\r
- border-width: 3px;\r
- border-spacing: 0px;\r
- border-style: solid;\r
- border-color: #527bbd;\r
- border-collapse: collapse;\r
-}\r
-th.tableblock, td.tableblock {\r
- border-width: 1px;\r
- padding: 4px;\r
- border-style: solid;\r
- border-color: #527bbd;\r
-}\r
-\r
-table.tableblock.frame-topbot {\r
- border-left-style: hidden;\r
- border-right-style: hidden;\r
-}\r
-table.tableblock.frame-sides {\r
- border-top-style: hidden;\r
- border-bottom-style: hidden;\r
-}\r
-table.tableblock.frame-none {\r
- border-style: hidden;\r
-}\r
-\r
-th.tableblock.halign-left, td.tableblock.halign-left {\r
- text-align: left;\r
-}\r
-th.tableblock.halign-center, td.tableblock.halign-center {\r
- text-align: center;\r
-}\r
-th.tableblock.halign-right, td.tableblock.halign-right {\r
- text-align: right;\r
-}\r
-\r
-th.tableblock.valign-top, td.tableblock.valign-top {\r
- vertical-align: top;\r
-}\r
-th.tableblock.valign-middle, td.tableblock.valign-middle {\r
- vertical-align: middle;\r
-}\r
-th.tableblock.valign-bottom, td.tableblock.valign-bottom {\r
- vertical-align: bottom;\r
-}\r
-\r
-\r
-/*\r
- * manpage specific\r
- *\r
- * */\r
-\r
-body.manpage h1 {\r
- padding-top: 0.5em;\r
- padding-bottom: 0.5em;\r
- border-top: 2px solid silver;\r
- border-bottom: 2px solid silver;\r
-}\r
-body.manpage h2 {\r
- border-style: none;\r
-}\r
-body.manpage div.sectionbody {\r
- margin-left: 3em;\r
-}\r
-\r
-@media print {\r
- body.manpage div#toc { display: none; }\r
-}\r
-\r
-\r
-</style>\r
-<script type="text/javascript">\r
-/*<+'])');\r
- // Function that scans the DOM tree for header elements (the DOM2\r
- // nodeIterator API would be a better technique but not supported by all\r
- // browsers).\r
- var iterate = function (el) {\r
- for (var i = el.firstChild; i != null; i = i.nextSibling) {\r
- if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {\r
- var mo = re.exec(i.tagName);\r
- if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {\r
- result[result.length] = new TocEntry(i, getText(i), mo[1]-1);\r
- }\r
- iterate(i);\r
- }\r
- }\r
- }\r
- iterate(el);\r
- return result;\r
- }\r
-\r
- var toc = document.getElementById("toc");\r
- if (!toc) {\r
- return;\r
- }\r
-\r
- // Delete existing TOC entries in case we're reloading the TOC.\r
- var tocEntriesToRemove = [];\r
- var i;\r
- for (i = 0; i < toc.childNodes.length; i++) {\r
- var entry = toc.childNodes[i];\r
- if (entry.nodeName.toLowerCase() == 'div'\r
- && entry.getAttribute("class")\r
- && entry.getAttribute("class").match(/^toclevel/))\r
- tocEntriesToRemove.push(entry);\r
- }\r
- for (i = 0; i < tocEntriesToRemove.length; i++) {\r
- toc.removeChild(tocEntriesToRemove[i]);\r
- }\r
-\r
- // Rebuild TOC entries.\r
- var entries = tocEntries(document.getElementById("content"), toclevels);\r
- for (var i = 0; i < entries.length; ++i) {\r
- var entry = entries[i];\r
- if (entry.element.id == "")\r
- entry.element.id = "_toc_" + i;\r
- var a = document.createElement("a");\r
- a.href = "#" + entry.element.id;\r
- a.appendChild(document.createTextNode(entry.text));\r
- var div = document.createElement("div");\r
- div.appendChild(a);\r
- div.className = "toclevel" + entry.toclevel;\r
- toc.appendChild(div);\r
- }\r
- if (entries.length == 0)\r
- toc.parentNode.removeChild(toc);\r
-},\r
-\r
-\r
-/////////////////////////////////////////////////////////////////////\r
-// Footnotes generator\r
-/////////////////////////////////////////////////////////////////////\r
-\r
-/* Based on footnote generation code from:\r
- * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html\r
- */\r
-\r
-footnotes: function () {\r
- // Delete existing footnote entries in case we're reloading the footnodes.\r
- var i;\r
- var noteholder = document.getElementById("footnotes");\r
- if (!noteholder) {\r
- return;\r
- }\r
- var entriesToRemove = [];\r
- for (i = 0; i < noteholder.childNodes.length; i++) {\r
- var entry = noteholder.childNodes[i];\r
- if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")\r
- entriesToRemove.push(entry);\r
- }\r
- for (i = 0; i < entriesToRemove.length; i++) {\r
- noteholder.removeChild(entriesToRemove[i]);\r
- }\r
-\r
- // Rebuild footnote entries.\r
- var cont = document.getElementById("content");\r
- var spans = cont.getElementsByTagName("span");\r
- var refs = {};\r
- var n = 0;\r
- for (i=0; i<spans.length; i++) {\r
- if (spans[i].className == "footnote") {\r
- n++;\r
- var note = spans[i].getAttribute("data-note");\r
- if (!note) {\r
- // Use [\s\S] in place of . so multi-line matches work.\r
- // Because JavaScript has no s (dotall) regex flag.\r
- note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];\r
- spans[i].innerHTML =\r
- "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +\r
- "' title='View footnote' class='footnote'>" + n + "</a>]";\r
- spans[i].setAttribute("data-note", note);\r
- }\r
- noteholder.innerHTML +=\r
- "<div class='footnote' id='_footnote_" + n + "'>" +\r
- "<a href='#_footnoteref_" + n + "' title='Return to text'>" +\r
- n + "</a>. " + note + "</div>";\r
- var id =spans[i].getAttribute("id");\r
- if (id != null) refs["#"+id] = n;\r
- }\r
- }\r
- if (n == 0)\r
- noteholder.parentNode.removeChild(noteholder);\r
- else {\r
- // Process footnoterefs.\r
- for (i=0; i<spans.length; i++) {\r
- if (spans[i].className == "footnoteref") {\r
- var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");\r
- href = href.match(/#.*/)[0]; // Because IE return full URL.\r
- n = refs[href];\r
- spans[i].innerHTML =\r
- "[<a href='#_footnote_" + n +\r
- "' title='View footnote' class='footnote'>" + n + "</a>]";\r
- }\r
- }\r
- }\r
-},\r
-\r
-install: function(toclevels) {\r
- var timerId;\r
-\r
- function reinstall() {\r
- asciidoc.footnotes();\r
- if (toclevels) {\r
- asciidoc.toc(toclevels);\r
- }\r
- }\r
-\r
- function reinstallAndRemoveTimer() {\r
- clearInterval(timerId);\r
- reinstall();\r
- }\r
-\r
- timerId = setInterval(reinstall, 500);\r
- if (document.addEventListener)\r
- document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);\r
- else\r
- window.onload = reinstallAndRemoveTimer;\r
-}\r
-\r
-}\r
-asciidoc.install(2);\r
-/*]]>*/\r
-</script>\r
-</head>\r
-<body class="article">\r
-<div id="header">\r
-<h1>CCACHE(1)</h1>\r
-<span id="revnumber">version 3.7.12</span>\r
-<div id="toc">
- <div id="toctitle">Table of Contents</div>
- <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
-</div>\r
-</div>\r
-<div id="content">\r
-<div class="sect1">\r
-<h2 id="_name">Name</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>ccache - a fast C/C++ compiler cache</p></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_synopsis">Synopsis</h2>\r
-<div class="sectionbody">\r
-<div class="verseblock">\r
-<pre class="content"><strong>ccache</strong> [<em>options</em>]\r
-<strong>ccache</strong> <em>compiler</em> [<em>compiler options</em>]\r
-<em>compiler</em> [<em>compiler options</em>] (via symbolic link)</pre>\r
-<div class="attribution">\r
-</div></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_description">Description</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>ccache is a compiler cache. It speeds up recompilation by caching the result of\r
-previous compilations and detecting when the same compilation is being done\r
-again. Supported languages are C, C++, Objective-C and Objective-C++.</p></div>\r
-<div class="paragraph"><p>ccache has been carefully written to always produce exactly the same compiler\r
-output that you would get without the cache. The only way you should be able to\r
-tell that you are using ccache is the speed. Currently known exceptions to this\r
-goal are listed under <a href="#_caveats">CAVEATS</a>. If you ever discover an\r
-undocumented case where ccache changes the output of your compiler, please let\r
-us know.</p></div>\r
-<div class="sect2">\r
-<h3 id="_features">Features</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Keeps statistics on hits/misses.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Automatic cache size management.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Can cache compilations that generate warnings.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Easy installation.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Low overhead.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Optionally compresses files in the cache to reduce disk space.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_limitations">Limitations</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Only knows how to cache the compilation of a single\r
- C/C++/Objective-C/Objective-C++ file. Other types of compilations\r
- (multi-file compilation, linking, etc) will silently fall back to running the\r
- real compiler.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Only works with GCC and compilers that behave similar enough.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Some compiler flags are not supported. If such a flag is detected, ccache\r
- will silently fall back to running the real compiler.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_run_modes">Run modes</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>There are two ways to use ccache. You can either prefix your compilation\r
-commands with <strong>ccache</strong> or you can let ccache masquerade as the compiler by\r
-creating a symbolic link (named as the compiler) to ccache. The first method is\r
-most convenient if you just want to try out ccache or wish to use it for some\r
-specific projects. The second method is most useful for when you wish to use\r
-ccache for all your compilations.</p></div>\r
-<div class="paragraph"><p>To use the first method, just make sure that <strong>ccache</strong> is in your <strong>PATH</strong>.</p></div>\r
-<div class="paragraph"><p>To use the symlinks method, do something like this:</p></div>\r
-<div class="listingblock">\r
-<div class="content">\r
-<pre><code>cp ccache /usr/local/bin/\r
-ln -s ccache /usr/local/bin/gcc\r
-ln -s ccache /usr/local/bin/g++\r
-ln -s ccache /usr/local/bin/cc\r
-ln -s ccache /usr/local/bin/c++</code></pre>\r
-</div></div>\r
-<div class="paragraph"><p>And so forth. This will work as long as the directory with symlinks comes\r
-before the path to the compiler (which is usually in <code>/usr/bin</code>). After\r
-installing you may wish to run “which gcc” to make sure that the correct link\r
-is being used.</p></div>\r
-<div class="admonitionblock">\r
-<table><tr>\r
-<td class="icon">\r
-<div class="title">Warning</div>\r
-</td>\r
-<td class="content">The technique of letting ccache masquerade as the compiler works well,\r
-but currently doesn’t interact well with other tools that do the same thing.\r
-See <a href="#_using_ccache_with_other_compiler_wrappers">USING CCACHE WITH OTHER COMPILER WRAPPERS</a>.</td>\r
-</tr></table>\r
-</div>\r
-<div class="admonitionblock">\r
-<table><tr>\r
-<td class="icon">\r
-<div class="title">Warning</div>\r
-</td>\r
-<td class="content">Do not use a hard link, use a symbolic link. A hard link will cause\r
-“interesting” problems.</td>\r
-</tr></table>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_options">Options</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>These options only apply when you invoke ccache as “ccache”. When invoked as\r
-a compiler (via a symlink as described in the previous section), the normal\r
-compiler options apply and you should refer to the compiler’s documentation.</p></div>\r
-<div class="dlist"><dl>\r
-<dt class="hdlist1">\r
-<strong><code>-c, --cleanup</code></strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- Clean up the cache by removing old cached files until the specified file\r
- number and cache size limits are not exceeded. This also recalculates the\r
- cache file count and size totals. Normally, there is no need to initiate\r
- cleanup manually as ccache keeps the cache below the specified limits at\r
- runtime and keeps statistics up to date on each compilation. Forcing a\r
- cleanup is mostly useful if you manually modify the cache contents or\r
- believe that the cache size statistics may be inaccurate.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong><code>-C, --clear</code></strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- Clear the entire cache, removing all cached files, but keeping the\r
- configuration file.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong><code>--dump-manifest</code></strong>=<em>PATH</em>\r
-</dt>\r
-<dd>\r
-<p>\r
- Dump manifest file at PATH in text format. This is only useful when\r
- debugging ccache and its behavior.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong><code>-k, --get-config</code></strong>=<em>KEY</em>\r
-</dt>\r
-<dd>\r
-<p>\r
- Print the value of configuration option <em>KEY</em>. See\r
- <a href="#_configuration">CONFIGURATION</a> for more information.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong><code>--hash-file</code></strong>=<em>PATH</em>\r
-</dt>\r
-<dd>\r
-<p>\r
- Print the hash (in format <code><MD4>-<size></code>) of the file at PATH. This is only\r
- useful when debugging ccache and its behavior.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong><code>-h, --help</code></strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- Print an options summary page.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong><code>-F, --max-files</code></strong>=<em>N</em>\r
-</dt>\r
-<dd>\r
-<p>\r
- Set the maximum number of files allowed in the cache. Use 0 for no limit.\r
- The value is stored in a configuration file in the cache directory and\r
- applies to all future compilations.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong><code>-M, --max-size</code></strong>=<em>SIZE</em>\r
-</dt>\r
-<dd>\r
-<p>\r
- Set the maximum size of the files stored in the cache. <em>SIZE</em> should be a\r
- number followed by an optional suffix: k, M, G, T (decimal), Ki, Mi, Gi or\r
- Ti (binary). The default suffix is G. Use 0 for no limit. The value is\r
- stored in a configuration file in the cache directory and applies to all\r
- future compilations.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong><code>--print-stats</code></strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- Print statistics counter IDs and corresponding values machine-parsable\r
- (tab-separated) format.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong><code>-o, --set-config</code></strong>=<em>KEY=VALUE</em>\r
-</dt>\r
-<dd>\r
-<p>\r
- Set configuration option <em>KEY</em> to <em>VALUE</em>. See\r
- <a href="#_configuration">CONFIGURATION</a> for more information.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong><code>-p, --show-config</code></strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- Print current configuration options and from where they originate\r
- (environment variable, configuration file or compile-time default) in\r
- human-readable format.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong><code>-s, --show-stats</code></strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- Print a summary of configuration and statistics counters in human-readable\r
- format.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong><code>-V, --version</code></strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- Print version and copyright information.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong><code>-z, --zero-stats</code></strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- Zero the cache statistics (but not the configuration options).\r
-</p>\r
-</dd>\r
-</dl></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_extra_options">Extra options</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>When run as a compiler, ccache usually just takes the same command line options\r
-as the compiler you are using. The only exception to this is the option\r
-<strong>--ccache-skip</strong>. That option can be used to tell ccache to avoid interpreting\r
-the next option in any way and to pass it along to the compiler as-is.</p></div>\r
-<div class="admonitionblock">\r
-<table><tr>\r
-<td class="icon">\r
-<div class="title">Note</div>\r
-</td>\r
-<td class="content"><strong>--ccache-skip</strong> currently only tells ccache not to interpret the next\r
-option as a special compiler option — the option will still be included in the\r
-direct mode hash.</td>\r
-</tr></table>\r
-</div>\r
-<div class="paragraph"><p>The reason this can be important is that ccache does need to parse the command\r
-line and determine what is an input filename and what is a compiler option, as\r
-it needs the input filename to determine the name of the resulting object file\r
-(among other things). The heuristic ccache uses when parsing the command line\r
-is that any argument that exists as a file is treated as an input file name. By\r
-using <strong>--ccache-skip</strong> you can force an option to not be treated as an input\r
-file name and instead be passed along to the compiler as a command line option.</p></div>\r
-<div class="paragraph"><p>Another case where <strong>--ccache-skip</strong> can be useful is if ccache interprets an\r
-option specially but shouldn’t, since the option has another meaning for your\r
-compiler than what ccache thinks.</p></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_configuration">Configuration</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>ccache’s default behavior can be overridden by configuration file settings,\r
-which in turn can be overridden by environment variables with names starting\r
-with <strong>CCACHE_</strong>. ccache normally reads configuration from two files: first a\r
-system-level configuration file and secondly a cache-specific configuration\r
-file. The priority of configuration settings is as follows (where 1 is\r
-highest):</p></div>\r
-<div class="olist arabic"><ol class="arabic">\r
-<li>\r
-<p>\r
-Environment variables.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The cache-specific configuration file <strong><em><ccachedir></em>/ccache.conf</strong> (typically\r
- <strong>$HOME/.ccache/ccache.conf</strong>).\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The system-wide configuration file <strong><em><sysconfdir></em>/ccache.conf</strong> (typically\r
- <strong>/etc/ccache.conf</strong> or <strong>/usr/local/etc/ccache.conf</strong>).\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Compile-time defaults.\r
-</p>\r
-</li>\r
-</ol></div>\r
-<div class="paragraph"><p>As a special case, if the environment variable <strong>CCACHE_CONFIGPATH</strong> is set,\r
-ccache reads configuration from the specified path instead of the default\r
-paths.</p></div>\r
-<div class="sect2">\r
-<h3 id="_configuration_file_syntax">Configuration file syntax</h3>\r
-<div class="paragraph"><p>Configuration files are in a simple “key = value” format, one setting per\r
-line. Lines starting with a hash sign are comments. Blank lines are ignored, as\r
-is whitespace surrounding keys and values. Example:</p></div>\r
-<div class="listingblock">\r
-<div class="content">\r
-<pre><code># Set maximum cache size to 10 GB:\r
-max_size = 10G</code></pre>\r
-</div></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_boolean_values">Boolean values</h3>\r
-<div class="paragraph"><p>Some settings are boolean values (i.e. truth values). In a configuration file,\r
-such values must be set to the string <strong>true</strong> or <strong>false</strong>. For the corresponding\r
-environment variables, the semantics are a bit different: a set environment\r
-variable means “true” (even if set to the empty string), the following\r
-case-insensitive negative values are considered an error (rather than\r
-surprising the user): <strong>0</strong>, <strong>false</strong>, <strong>disable</strong> and <strong>no</strong>, and an unset\r
-environment variable means “false”. Each boolean environment variable also\r
-has a negated form starting with <strong>CCACHE_NO</strong>. For example, <strong>CCACHE_COMPRESS</strong>\r
-can be set to force compression and <strong>CCACHE_NOCOMPRESS</strong> can be set to force no\r
-compression.</p></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_configuration_settings">Configuration settings</h3>\r
-<div class="paragraph"><p>Below is a list of available configuration settings. The corresponding\r
-environment variable name is indicated in parentheses after each configuration\r
-setting key.</p></div>\r
-<div class="dlist"><dl>\r
-<dt class="hdlist1">\r
-<strong>base_dir</strong> (<strong>CCACHE_BASEDIR</strong>)\r
-</dt>\r
-<dd>\r
-<p>\r
- This setting should be an absolute path to a directory. ccache then\r
- rewrites absolute paths into relative paths before computing the hash that\r
- identifies the compilation, but only for paths under the specified\r
- directory. If set to the empty string (which is the default), no rewriting\r
- is done. A typical path to use as the base directory is your home directory\r
- or another directory that is a parent of your build directories. Don’t use\r
- <code>/</code> as the base directory since that will make ccache also rewrite paths to\r
- system header files, which doesn’t gain anything.\r
-</p>\r
-<div class="paragraph"><p>See also the discussion under <a href="#_compiling_in_different_directories">COMPILING IN DIFFERENT DIRECTORIES</a>.</p></div>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>cache_dir</strong> (<strong>CCACHE_DIR</strong>)\r
-</dt>\r
-<dd>\r
-<p>\r
- This setting specifies where ccache will keep its cached compiler outputs.\r
- It will only take effect if set in the system-wide configuration file or as\r
- an environment variable. The default is <strong>$HOME/.ccache</strong>.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>cache_dir_levels</strong> (<strong>CCACHE_NLEVELS</strong>)\r
-</dt>\r
-<dd>\r
-<p>\r
- This setting allows you to choose the number of directory levels in the\r
- cache directory. The default is 2. The minimum is 1 and the maximum is 8.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>compiler</strong> (<strong>CCACHE_COMPILER</strong> or (deprecated) <strong>CCACHE_CC</strong>)\r
-</dt>\r
-<dd>\r
-<p>\r
- This setting can be used to force the name of the compiler to use. If set\r
- to the empty string (which is the default), ccache works it out from the\r
- command line.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>compiler_check</strong> (<strong>CCACHE_COMPILERCHECK</strong>)\r
-</dt>\r
-<dd>\r
-<p>\r
- By default, ccache includes the modification time (“mtime”) and size of\r
- the compiler in the hash to ensure that results retrieved from the cache\r
- are accurate. This setting can be used to select another strategy. Possible\r
- values are:\r
-</p>\r
-<div class="openblock">\r
-<div class="content">\r
-<div class="dlist"><dl>\r
-<dt class="hdlist1">\r
-<strong>content</strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- Hash the content of the compiler binary. This makes ccache very slightly\r
- slower compared to the <strong>mtime</strong> setting, but makes it cope better with\r
- compiler upgrades during a build bootstrapping process.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>mtime</strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- Hash the compiler’s mtime and size, which is fast. This is the default.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>none</strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- Don’t hash anything. This may be good for situations where you can safely\r
- use the cached results even though the compiler’s mtime or size has changed\r
- (e.g. if the compiler is built as part of your build system and the\r
- compiler’s source has not changed, or if the compiler only has changes that\r
- don’t affect code generation). You should only use the <strong>none</strong> setting if\r
- you know what you are doing.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>string:value</strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- Use <strong>value</strong> as the string to calculate hash from. This can be the compiler\r
- revision number you retrieved earlier and set here via environment variable.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<em>a command string</em>\r
-</dt>\r
-<dd>\r
-<p>\r
- Hash the standard output and standard error output of the specified\r
- command. The string will be split on whitespace to find out the command and\r
- arguments to run. No other interpretation of the command string will be\r
- done, except that the special word <strong>%compiler%</strong> will be replaced with the\r
- path to the compiler. Several commands can be specified with semicolon as\r
- separator. Examples:\r
-</p>\r
-<div class="openblock">\r
-<div class="content">\r
-<div class="listingblock">\r
-<div class="content">\r
-<pre><code>%compiler% -v</code></pre>\r
-</div></div>\r
-<div class="listingblock">\r
-<div class="content">\r
-<pre><code>%compiler% -dumpmachine; %compiler% -dumpversion</code></pre>\r
-</div></div>\r
-<div class="paragraph"><p>You should make sure that the specified command is as fast as possible since it\r
-will be run once for each ccache invocation.</p></div>\r
-<div class="paragraph"><p>Identifying the compiler using a command is useful if you want to avoid cache\r
-misses when the compiler has been rebuilt but not changed.</p></div>\r
-<div class="paragraph"><p>Another case is when the compiler (as seen by ccache) actually isn’t the real\r
-compiler but another compiler wrapper — in that case, the default <strong>mtime</strong>\r
-method will hash the mtime and size of the other compiler wrapper, which means\r
-that ccache won’t be able to detect a compiler upgrade. Using a suitable\r
-command to identify the compiler is thus safer, but it’s also slower, so you\r
-should consider continue using the <strong>mtime</strong> method in combination with\r
-the <strong>prefix_command</strong> setting if possible. See\r
-<a href="#_using_ccache_with_other_compiler_wrappers">USING CCACHE WITH OTHER COMPILER WRAPPERS</a>.</p></div>\r
-</div></div>\r
-</dd>\r
-</dl></div>\r
-</div></div>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>compression</strong> (<strong>CCACHE_COMPRESS</strong> or <strong>CCACHE_NOCOMPRESS</strong>, see <a href="#_boolean_values">Boolean values</a> above)\r
-</dt>\r
-<dd>\r
-<p>\r
- If true, ccache will compress object files and other compiler output it\r
- puts in the cache. However, this setting has no effect on how files are\r
- retrieved from the cache; compressed and uncompressed results will still be\r
- usable regardless of this setting. The default is false.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>compression_level</strong> (<strong>CCACHE_COMPRESSLEVEL</strong>)\r
-</dt>\r
-<dd>\r
-<p>\r
- This setting determines the level at which ccache will compress object\r
- files. It only has effect if <strong>compression</strong> is enabled. The value defaults\r
- to 6, and must be no lower than 1 (fastest, worst compression) and no\r
- higher than 9 (slowest, best compression).\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>cpp_extension</strong> (<strong>CCACHE_EXTENSION</strong>)\r
-</dt>\r
-<dd>\r
-<p>\r
- This setting can be used to force a certain extension for the intermediate\r
- preprocessed file. The default is to automatically determine the extension\r
- to use for intermediate preprocessor files based on the type of file being\r
- compiled, but that sometimes doesn’t work. For example, when using the\r
- “aCC” compiler on HP-UX, set the cpp extension to <strong>i</strong>.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>debug</strong> (<strong>CCACHE_DEBUG</strong> or <strong>CCACHE_NODEBUG</strong>, see <a href="#_boolean_values">Boolean values</a> above)\r
-</dt>\r
-<dd>\r
-<p>\r
- If true, enable the debug mode. The debug mode creates per-object debug\r
- files that are helpful when debugging unexpected cache misses. Note however\r
- that ccache performance will be reduced slightly. See\r
- <a href="#_cache_debugging">debugging</a> for more information. The default is false.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>depend_mode</strong> (<strong>CCACHE_DEPEND</strong> or <strong>CCACHE_NODEPEND</strong>, see <a href="#_boolean_values">Boolean values</a> above)\r
-</dt>\r
-<dd>\r
-<p>\r
- If true, the depend mode will be used. The default is false. See\r
- <a href="#_the_depend_mode">THE DEPEND MODE</a>.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>direct_mode</strong> (<strong>CCACHE_DIRECT</strong> or <strong>CCACHE_NODIRECT</strong>, see <a href="#_boolean_values">Boolean values</a> above)\r
-</dt>\r
-<dd>\r
-<p>\r
- If true, the direct mode will be used. The default is true. See\r
- <a href="#_the_direct_mode">THE DIRECT MODE</a>.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>disable</strong> (<strong>CCACHE_DISABLE</strong> or <strong>CCACHE_NODISABLE</strong>, see <a href="#_boolean_values">Boolean values</a> above)\r
-</dt>\r
-<dd>\r
-<p>\r
- When true, ccache will just call the real compiler, bypassing the cache\r
- completely. The default is false.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>extra_files_to_hash</strong> (<strong>CCACHE_EXTRAFILES</strong>)\r
-</dt>\r
-<dd>\r
-<p>\r
- This setting is a list of paths to files that ccache will include in the\r
- the hash sum that identifies the build. The list separator is semicolon on\r
- Windows systems and colon on other systems.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>hard_link</strong> (<strong>CCACHE_HARDLINK</strong> or <strong>CCACHE_NOHARDLINK</strong>, see <a href="#_boolean_values">Boolean values</a> above)\r
-</dt>\r
-<dd>\r
-<p>\r
- If true, ccache will attempt to use hard links from the cache directory\r
- when creating the compiler output rather than using a file copy. Hard links\r
- are never made for compressed cache files. This means that you should not\r
- enable compression if you want to use hard links. The default is false.\r
-</p>\r
-<div class="admonitionblock">\r
-<table><tr>\r
-<td class="icon">\r
-<div class="title">Warning</div>\r
-</td>\r
-<td class="content">Do not enable this option unless you are aware of the consequences.\r
-Using hard links may be slightly faster in some situations, but there are\r
-several pitfalls since the resulting object file will share i-node with the\r
-cached object file:</td>\r
-</tr></table>\r
-</div>\r
-<div class="olist arabic"><ol class="arabic">\r
-<li>\r
-<p>\r
-If the resulting object file is modified in any way, the cached object file\r
- will be modified as well. For instance, if you run <code>strip object.o</code> or <code>echo\r
- >object.o</code>, you will corrupt the cache.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Programs that rely on modification times (like “make”) can be confused\r
- since ccache updates the cached files' modification times as part of the\r
- automatic cache size management. This will affect object files in the build\r
- tree as well, which can retrigger the linking step even though nothing\r
- really has changed.\r
-</p>\r
-</li>\r
-</ol></div>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>hash_dir</strong> (<strong>CCACHE_HASHDIR</strong> or <strong>CCACHE_NOHASHDIR</strong>, see <a href="#_boolean_values">Boolean values</a> above)\r
-</dt>\r
-<dd>\r
-<p>\r
- If true (which is the default), ccache will include the current working\r
- directory (CWD) in the hash that is used to distinguish two compilations\r
- when generating debug info (compiler option <strong>-g</strong> with variations).\r
- Exception: The CWD will not be included in the hash if <strong>base_dir</strong> is set\r
- (and matches the CWD) and the compiler option <strong>-fdebug-prefix-map</strong> is used.\r
- See also the discussion under\r
- <a href="#_compiling_in_different_directories">COMPILING IN DIFFERENT DIRECTORIES</a>.\r
-</p>\r
-<div class="paragraph"><p>The reason for including the CWD in the hash by default is to prevent a problem\r
-with the storage of the current working directory in the debug info of an\r
-object file, which can lead ccache to return a cached object file that has the\r
-working directory in the debug info set incorrectly.</p></div>\r
-<div class="paragraph"><p>You can disable this setting to get cache hits when compiling the same source\r
-code in different directories if you don’t mind that CWD in the debug info\r
-might be incorrect.</p></div>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>ignore_headers_in_manifest</strong> (<strong>CCACHE_IGNOREHEADERS</strong>)\r
-</dt>\r
-<dd>\r
-<p>\r
- This setting is a list of paths to files (or directories with headers) that\r
- ccache will <strong>not</strong> include in the manifest list that makes up the direct\r
- mode. Note that this can cause stale cache hits if those headers do indeed\r
- change. The list separator is semicolon on Windows systems and colon on\r
- other systems.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>keep_comments_cpp</strong> (<strong>CCACHE_COMMENTS</strong> or <strong>CCACHE_NOCOMMENTS</strong>, see <a href="#_boolean_values">Boolean values</a> above)\r
-</dt>\r
-<dd>\r
-<p>\r
- If true, ccache will not discard the comments before hashing preprocessor\r
- output. This can be used to check documentation with <strong>-Wdocumentation</strong>.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>limit_multiple</strong> (<strong>CCACHE_LIMIT_MULTIPLE</strong>)\r
-</dt>\r
-<dd>\r
-<p>\r
- Sets the limit when cleaning up. Files are deleted (in LRU order) until the\r
- levels are below the limit. The default is 0.8 (= 80%). See\r
- <a href="#_automatic_cleanup">AUTOMATIC CLEANUP</a> for more information.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>log_file</strong> (<strong>CCACHE_LOGFILE</strong>)\r
-</dt>\r
-<dd>\r
-<p>\r
- If set to a file path, ccache will write information on what it is doing to\r
- the specified file. This is useful for tracking down problems.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>max_files</strong> (<strong>CCACHE_MAXFILES</strong>)\r
-</dt>\r
-<dd>\r
-<p>\r
- This option specifies the maximum number of files to keep in the cache. Use\r
- 0 for no limit (which is the default). See also\r
- <a href="#_cache_size_management">CACHE SIZE MANAGEMENT</a>.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>max_size</strong> (<strong>CCACHE_MAXSIZE</strong>)\r
-</dt>\r
-<dd>\r
-<p>\r
- This option specifies the maximum size of the cache. Use 0 for no limit.\r
- The default value is 5G. Available suffixes: k, M, G, T (decimal) and Ki,\r
- Mi, Gi, Ti (binary). The default suffix is G. See also\r
- <a href="#_cache_size_management">CACHE SIZE MANAGEMENT</a>.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>path</strong> (<strong>CCACHE_PATH</strong>)\r
-</dt>\r
-<dd>\r
-<p>\r
- If set, ccache will search directories in this list when looking for the\r
- real compiler. The list separator is semicolon on Windows systems and colon\r
- on other systems. If not set, ccache will look for the first executable\r
- matching the compiler name in the normal <strong>PATH</strong> that isn’t a symbolic link\r
- to ccache itself.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>pch_external_checksum</strong> (<strong>CCACHE_PCH_EXTSUM</strong> or <strong>CCACHE_NOPCH_EXTSUM</strong>, see <a href="#_boolean_values">Boolean values</a> above)\r
-</dt>\r
-<dd>\r
-<p>\r
- When this option is set, and ccache finds a precompiled header file,\r
- ccache will look for a file with the extension “.sum” added\r
- (e.g. “pre.h.gch.sum”), and if found, it will hash this file instead\r
- of the precompiled header itself to work around the performance\r
- penalty of hashing very large files.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>prefix_command</strong> (<strong>CCACHE_PREFIX</strong>)\r
-</dt>\r
-<dd>\r
-<p>\r
- This option adds a list of prefixes (separated by space) to the command\r
- line that ccache uses when invoking the compiler. See also\r
- <a href="#_using_ccache_with_other_compiler_wrappers">USING CCACHE WITH OTHER COMPILER WRAPPERS</a>.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>prefix_command_cpp</strong> (<strong>CCACHE_PREFIX_CPP</strong>)\r
-</dt>\r
-<dd>\r
-<p>\r
- This option adds a list of prefixes (separated by space) to the command\r
- line that ccache uses when invoking the preprocessor.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>read_only</strong> (<strong>CCACHE_READONLY</strong> or <strong>CCACHE_NOREADONLY</strong>, see <a href="#_boolean_values">Boolean values</a> above)\r
-</dt>\r
-<dd>\r
-<p>\r
- If true, ccache will attempt to use existing cached object files, but it\r
- will not add new results to the cache. Statistics counters will still be\r
- updated, though, unless the <strong>stats</strong> option is set to <strong>false</strong>.\r
-</p>\r
-<div class="paragraph"><p>If you are using this because your ccache directory is read-only, you need to\r
-set <strong>temporary_dir</strong> since ccache will fail to create temporary files otherwise.\r
-You may also want to set <strong>stats = false</strong> to make ccache not even try to update\r
-stats files.</p></div>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>read_only_direct</strong> (<strong>CCACHE_READONLY_DIRECT</strong> or <strong>CCACHE_NOREADONLY_DIRECT</strong>, see <a href="#_boolean_values">Boolean values</a> above)\r
-</dt>\r
-<dd>\r
-<p>\r
- Just like <strong>read_only</strong> except that ccache will only try to retrieve results\r
- from the cache using the direct mode, not the preprocessor mode. See\r
- documentation for <strong>read_only</strong> regarding using a read-only ccache directory.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>recache</strong> (<strong>CCACHE_RECACHE</strong> or <strong>CCACHE_NORECACHE</strong>, see <a href="#_boolean_values">Boolean values</a> above)\r
-</dt>\r
-<dd>\r
-<p>\r
- If true, ccache will not use any previously stored result. New results will\r
- still be cached, possibly overwriting any pre-existing results.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>run_second_cpp</strong> (<strong>CCACHE_CPP2</strong> or <strong>CCACHE_NOCPP2</strong>, see <a href="#_boolean_values">Boolean values</a> above)\r
-</dt>\r
-<dd>\r
-<p>\r
- If true, ccache will first run the preprocessor to preprocess the source\r
- code (see <a href="#_the_preprocessor_mode">THE PREPROCESSOR MODE</a>) and then on a\r
- cache miss run the compiler on the source code to get hold of the object\r
- file. This is the default.\r
-</p>\r
-<div class="paragraph"><p>If false, ccache will first run preprocessor to preprocess the source code and\r
-then on a cache miss run the compiler on the <em>preprocessed source code</em> instead\r
-of the original source code. This makes cache misses slightly faster since the\r
-source code only has to be preprocessed once. The downside is that some\r
-compilers won’t produce the same result (for instance diagnostics warnings)\r
-when compiling preprocessed source code.</p></div>\r
-<div class="paragraph"><p>A solution to the above mentioned downside is to set <strong>run_second_cpp</strong> to false\r
-and pass <strong>-fdirectives-only</strong> (for GCC) or <strong>-frewrite-includes</strong> (for Clang) to\r
-the compiler. This will cause the compiler to leave the macros and other\r
-preprocessor information, and only process the <strong>#include</strong> directives. When run\r
-in this way, the preprocessor arguments will be passed to the compiler since it\r
-still has to do <em>some</em> preprocessing (like macros).</p></div>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>sloppiness</strong> (<strong>CCACHE_SLOPPINESS</strong>)\r
-</dt>\r
-<dd>\r
-<p>\r
- By default, ccache tries to give as few false cache hits as possible.\r
- However, in certain situations it’s possible that you know things that\r
- ccache can’t take for granted. This setting makes it possible to tell\r
- ccache to relax some checks in order to increase the hit rate. The value\r
- should be a comma-separated string with options. Available options are:\r
-</p>\r
-<div class="openblock">\r
-<div class="content">\r
-<div class="dlist"><dl>\r
-<dt class="hdlist1">\r
-<strong>clang_index_store</strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- Ignore the Clang compiler option <strong>-index-store-path</strong> and its argument when\r
- computing the manifest hash. This is useful if you use Xcode, which uses an\r
- index store path derived from the local project path. Note that the index\r
- store won’t be updated correctly on cache hits if you enable this option.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>file_stat_matches</strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- ccache normally examines a file’s contents to determine whether it matches\r
- the cached version. With this option set, ccache will consider a file as\r
- matching its cached version if the mtimes and ctimes match.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>file_stat_matches_ctime</strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- Ignore ctimes when <strong>file_stat_matches</strong> is enabled. This can be useful when\r
- backdating files' mtimes in a controlled way.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>include_file_ctime</strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- By default, ccache will not cache a file if it includes a header whose\r
- ctime is too new. This option disables that check.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>include_file_mtime</strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- By default, ccache will not cache a file if it includes a header whose\r
- mtime is too new. This option disables that check.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>locale</strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- ccache includes the environment variables <strong>LANG</strong>, <strong>LC_ALL</strong>, <strong>LC_CTYPE</strong> and\r
- <strong>LC_MESSAGES</strong> in the hash by default since they may affect localization of\r
- compiler warning messages. Set this option to tell ccache not to do that.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>pch_defines</strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- Be sloppy about <strong>#define</strong>s when precompiling a header file. See\r
- <a href="#_precompiled_headers">PRECOMPILED HEADERS</a> for more information.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>system_headers</strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- By default, ccache will also include all system headers in the manifest.\r
- With this option set, ccache will only include system headers in the hash\r
- but not add the system header files to the list of include files.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>time_macros</strong>\r
-</dt>\r
-<dd>\r
-<p>\r
- Ignore <code>__DATE__</code> and <code>__TIME__</code> being present in the source code.\r
-</p>\r
-</dd>\r
-</dl></div>\r
-</div></div>\r
-<div class="paragraph"><p>See the discussion under <a href="#_troubleshooting">TROUBLESHOOTING</a> for more\r
-information.</p></div>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>stats</strong> (<strong>CCACHE_STATS</strong> or <strong>CCACHE_NOSTATS</strong>, see <a href="#_boolean_values">Boolean values</a> above)\r
-</dt>\r
-<dd>\r
-<p>\r
- If true, ccache will update the statistics counters on each compilation.\r
- The default is true.\r
-</p>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>temporary_dir</strong> (<strong>CCACHE_TEMPDIR</strong>)\r
-</dt>\r
-<dd>\r
-<p>\r
- This setting specifies where ccache will put temporary files. The default\r
- is <strong><cache_dir>/tmp</strong>.\r
-</p>\r
-<div class="admonitionblock">\r
-<table><tr>\r
-<td class="icon">\r
-<div class="title">Note</div>\r
-</td>\r
-<td class="content">In previous versions of ccache, <strong>CCACHE_TEMPDIR</strong> had to be on the same\r
- filesystem as the <strong>CCACHE_DIR</strong> path, but this requirement has been\r
- relaxed.)</td>\r
-</tr></table>\r
-</div>\r
-</dd>\r
-<dt class="hdlist1">\r
-<strong>umask</strong> (<strong>CCACHE_UMASK</strong>)\r
-</dt>\r
-<dd>\r
-<p>\r
- This setting specifies the umask for ccache and all child processes (such\r
- as the compiler). This is mostly useful when you wish to share your cache\r
- with other users. Note that this also affects the file permissions set on\r
- the object files created from your compilations.\r
-</p>\r
-</dd>\r
-</dl></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_cache_size_management">Cache size management</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>By default, ccache has a 5 GB limit on the total size of files in the cache and\r
-no limit on the number of files. You can set different limits using the\r
-<strong>-M</strong>/<strong>--max-size</strong> and <strong>-F</strong>/<strong>--max-files</strong> options. Use <strong>ccache -s/--show-stats</strong>\r
-to see the cache size and the currently configured limits (in addition to other\r
-various statistics).</p></div>\r
-<div class="paragraph"><p>Cleanup can be triggered in two different ways: automatic and manual.</p></div>\r
-<div class="sect2">\r
-<h3 id="_automatic_cleanup">Automatic cleanup</h3>\r
-<div class="paragraph"><p>ccache maintains counters for various statistics about the cache, including the\r
-size and number of all cached files. In order to improve performance and reduce\r
-issues with concurrent ccache invocations, there is one statistics file for\r
-each of the sixteen subdirectories in the cache.</p></div>\r
-<div class="paragraph"><p>After a new compilation result has been written to the cache, ccache will\r
-update the size and file number statistics for the subdirectory (one of\r
-sixteen) to which the result was written. Then, if the size counter for said\r
-subdirectory is greater than <strong>max_size / 16</strong> or the file number counter is\r
-greater than <strong>max_files / 16</strong>, automatic cleanup is triggered.</p></div>\r
-<div class="paragraph"><p>When automatic cleanup is triggered for a subdirectory in the cache, ccache\r
-will:</p></div>\r
-<div class="olist arabic"><ol class="arabic">\r
-<li>\r
-<p>\r
-Count all files in the subdirectory and compute their aggregated size.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Remove files in LRU (least recently used) order until the size is at most\r
- <strong>limit_multiple * max_size / 16</strong> and the number of files is at most\r
- <strong>limit_multiple * max_files / 16</strong>, where <strong>limit_multiple</strong>, <strong>max_size</strong> and\r
- <strong>max_files</strong> are configuration settings.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Set the size and file number counters to match the files that were kept.\r
-</p>\r
-</li>\r
-</ol></div>\r
-<div class="paragraph"><p>The reason for removing more files than just those needed to not exceed the max\r
-limits is that a cleanup is a fairly slow operation, so it would not be a good\r
-idea to trigger it often, like after each cache miss.</p></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_manual_cleanup">Manual cleanup</h3>\r
-<div class="paragraph"><p>You can run <strong>ccache -c/--cleanup</strong> to force cleanup of the whole cache, i.e. all\r
-of the sixteen subdirectories. This will recalculate the statistics counters\r
-and make sure that the <strong>max_size</strong> and <strong>max_files</strong> settings are not exceeded.\r
-Note that <strong>limit_multiple</strong> is not taken into account for manual cleanup.</p></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_cache_compression">Cache compression</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>ccache can optionally compress all files it puts into the cache using the\r
-compression library zlib. While this may involve a tiny performance slowdown,\r
-it increases the number of files that fit in the cache. You can turn on\r
-compression with the <strong>compression</strong> configuration setting and you can also tweak\r
-the compression level with <strong>compression_level</strong>.</p></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_cache_statistics">Cache statistics</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p><strong>ccache -s/--show-stats</strong> can show the following statistics:</p></div>\r
-<div class="tableblock">\r
-<table rules="all"\r
-width="100%"\r
-frame="border"\r
-cellspacing="0" cellpadding="4">\r
-<col width="30%" />\r
-<col width="70%" />\r
-<thead>\r
-<tr>\r
-<th align="left" valign="top">Name </th>\r
-<th align="left" valign="top"> Description</th>\r
-</tr>\r
-</thead>\r
-<tbody>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">autoconf compile/link</p></td>\r
-<td align="left" valign="top"><p class="table">Uncachable compilation or linking by an autoconf test.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">bad compiler arguments</p></td>\r
-<td align="left" valign="top"><p class="table">Malformed compiler argument, e.g. missing a value for an option that requires\r
-an argument or failure to read a file specified by an option argument.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">cache file missing</p></td>\r
-<td align="left" valign="top"><p class="table">A file was unexpectedly missing from the cache. This only happens in rare\r
-situations, e.g. if one ccache instance is about to get a file from the cache\r
-while another instance removed the file as part of cache cleanup.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">cache hit (direct)</p></td>\r
-<td align="left" valign="top"><p class="table">A result was successfully found using <a href="#_the_direct_mode">the direct mode</a>.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">cache hit (preprocessed)</p></td>\r
-<td align="left" valign="top"><p class="table">A result was successfully found using <a href="#_the_preprocessor_mode">the preprocessor mode</a>.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">cache miss</p></td>\r
-<td align="left" valign="top"><p class="table">No result was found.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">cache size</p></td>\r
-<td align="left" valign="top"><p class="table">Current size of the cache.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">called for link</p></td>\r
-<td align="left" valign="top"><p class="table">The compiler was called for linking, not compiling.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">called for preprocessing</p></td>\r
-<td align="left" valign="top"><p class="table">The compiler was called for preprocessing, not compiling.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">can’t use precompiled header</p></td>\r
-<td align="left" valign="top"><p class="table">Preconditions for using <a href="#_precompiled_headers">precompiled headers</a> were not\r
-fulfilled.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">ccache internal error</p></td>\r
-<td align="left" valign="top"><p class="table">Unexpected failure, e.g. due to problems reading/writing the cache.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">cleanups performed</p></td>\r
-<td align="left" valign="top"><p class="table">Number of cleanups performed, either implicitly due to the cache size limit\r
-being reached or due to explicit <strong>ccache -c/--cleanup</strong> calls.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">compile failed</p></td>\r
-<td align="left" valign="top"><p class="table">The compilation failed. No result stored in the cache.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">compiler check failed</p></td>\r
-<td align="left" valign="top"><p class="table">A compiler check program specified by <strong>compiler_check</strong> (<strong>CCACHE_COMPILERCHECK</strong>)\r
-failed.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">compiler produced empty output</p></td>\r
-<td align="left" valign="top"><p class="table">The compiler’s output file (typically an object file) was empty after\r
-compilation.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">compiler produced no output</p></td>\r
-<td align="left" valign="top"><p class="table">The compiler’s output file (typically an object file) was missing after\r
-compilation.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">compiler produced stdout</p></td>\r
-<td align="left" valign="top"><p class="table">The compiler wrote data to standard output. This is something that compilers\r
-normally never do, so ccache is not designed to store such output in the cache.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">couldn’t find the compiler</p></td>\r
-<td align="left" valign="top"><p class="table">The compiler to execute could not be found.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">error hashing extra file</p></td>\r
-<td align="left" valign="top"><p class="table">Failure reading a file specified by <strong>extra_files_to_hash</strong>\r
-(<strong>CCACHE_EXTRAFILES</strong>).</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">files in cache</p></td>\r
-<td align="left" valign="top"><p class="table">Current number of files in the cache.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">multiple source files</p></td>\r
-<td align="left" valign="top"><p class="table">The compiler was called to compile multiple source files in one go. This is not\r
-supported by ccache.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">no input file</p></td>\r
-<td align="left" valign="top"><p class="table">No input file was specified to the compiler.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">output to a non-regular file</p></td>\r
-<td align="left" valign="top"><p class="table">The output path specified with <strong>-o</strong> is not a file (e.g. a directory or a device\r
-node).</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">output to stdout</p></td>\r
-<td align="left" valign="top"><p class="table">The compiler was instructed to write its output to standard output using <strong>-o\r
--</strong>. This is not supported by ccache.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">preprocessor error</p></td>\r
-<td align="left" valign="top"><p class="table">Preprocessing the source code using the compiler’s <strong>-E</strong> option failed.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">stats updated</p></td>\r
-<td align="left" valign="top"><p class="table">When statistics were updated the last time.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">stats zeroed</p></td>\r
-<td align="left" valign="top"><p class="table">When <strong>ccache -z</strong> was called the last time.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">unsupported code directive</p></td>\r
-<td align="left" valign="top"><p class="table">Code like the assembler <strong>.incbin</strong> directive was found. This is not supported\r
-by ccache.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">unsupported compiler option</p></td>\r
-<td align="left" valign="top"><p class="table">A compiler option not supported by ccache was found.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table">unsupported source language</p></td>\r
-<td align="left" valign="top"><p class="table">A source language e.g. specified with <strong>-x</strong> was unsupported by ccache.</p></td>\r
-</tr>\r
-</tbody>\r
-</table>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_how_ccache_works">How ccache works</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>The basic idea is to detect when you are compiling exactly the same code a\r
-second time and reuse the previously produced output. The detection is done by\r
-hashing different kinds of information that should be unique for the\r
-compilation and then using the hash sum to identify the cached output. ccache\r
-uses MD4, a very fast cryptographic hash algorithm, for the hashing. (MD4 is\r
-nowadays too weak to be useful in cryptographic contexts, but it should be safe\r
-enough to be used to identify recompilations.) On a cache hit, ccache is able\r
-to supply all of the correct compiler outputs (including all warnings,\r
-dependency file, etc) from the cache.</p></div>\r
-<div class="paragraph"><p>ccache has two ways of gathering information used to look up results in the\r
-cache:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-the <strong>direct mode</strong>, where ccache hashes the source code and include files\r
- directly\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-the <strong>preprocessor mode</strong>, where ccache runs the preprocessor on the source\r
- code and hashes the result\r
-</p>\r
-</li>\r
-</ul></div>\r
-<div class="paragraph"><p>The direct mode is generally faster since running the preprocessor has some\r
-overhead.</p></div>\r
-<div class="paragraph"><p>If no previous result is detected (i.e., there is a cache miss) using the\r
-direct mode, ccache will fall back to the preprocessor mode unless the <strong>depend\r
-mode</strong> is enabled. In the depend mode, ccache never runs the preprocessor, not\r
-even on cache misses. Read more in <a href="#_the_depend_mode">THE DEPEND MODE</a>\r
-below.</p></div>\r
-<div class="sect2">\r
-<h3 id="_common_hashed_information">Common hashed information</h3>\r
-<div class="paragraph"><p>The following information is always included in the hash:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-the extension used by the compiler for a file with preprocessor output\r
- (normally <strong>.i</strong> for C code and <strong>.ii</strong> for C++ code)\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-the compiler’s size and modification time (or other compiler-specific\r
- information specified by the <strong>compiler_check</strong> setting)\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-the name of the compiler\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-the current directory (if the <strong>hash_dir</strong> setting is enabled)\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-contents of files specified by the <strong>extra_files_to_hash</strong> setting (if any)\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_the_direct_mode">The direct mode</h3>\r
-<div class="paragraph"><p>In the direct mode, the hash is formed of the common information and:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-the input source file\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-the command line options\r
-</p>\r
-</li>\r
-</ul></div>\r
-<div class="paragraph"><p>Based on the hash, a data structure called “manifest” is looked up in the\r
-cache. The manifest contains:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-references to cached compilation results (object file, dependency file, etc)\r
- that were produced by previous compilations that matched the hash\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-paths to the include files that were read at the time the compilation results\r
- were stored in the cache\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-hash sums of the include files at the time the compilation results were\r
- stored in the cache\r
-</p>\r
-</li>\r
-</ul></div>\r
-<div class="paragraph"><p>The current contents of the include files are then hashed and compared to the\r
-information in the manifest. If there is a match, ccache knows the result of\r
-the compilation. If there is no match, ccache falls back to running the\r
-preprocessor. The output from the preprocessor is parsed to find the include\r
-files that were read. The paths and hash sums of those include files are then\r
-stored in the manifest along with information about the produced compilation\r
-result.</p></div>\r
-<div class="paragraph"><p>There is a catch with the direct mode: header files that were used by the\r
-compiler are recorded, but header files that were <strong>not</strong> used, but would have\r
-been used if they existed, are not. So, when ccache checks if a result can be\r
-taken from the cache, it currently can’t check if the existence of a new header\r
-file should invalidate the result. In practice, the direct mode is safe to use\r
-in the absolute majority of cases.</p></div>\r
-<div class="paragraph"><p>The direct mode will be disabled if any of the following holds:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-the configuration setting <strong>direct_mode</strong> is false\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-a modification time of one of the include files is too new (needed to avoid a\r
- race condition)\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-a compiler option not supported by the direct mode is used:\r
-</p>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-a <strong>-Wp,<em>X</em></strong> compiler option other than <strong>-Wp,-MD,<em>path</em></strong>,\r
- <strong>-Wp,-MMD,<em>path</em></strong> and <strong>-Wp,-D_define_</strong>\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-<strong>-Xpreprocessor</strong>\r
-</p>\r
-</li>\r
-</ul></div>\r
-</li>\r
-<li>\r
-<p>\r
-the string <code>__TIME__</code> is present in the source code\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_the_preprocessor_mode">The preprocessor mode</h3>\r
-<div class="paragraph"><p>In the preprocessor mode, the hash is formed of the common information and:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-the preprocessor output from running the compiler with <strong>-E</strong>\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-the command line options except options that affect include files (<strong>-I</strong>,\r
- <strong>-include</strong>, <strong>-D</strong>, etc; the theory is that these options will change the\r
- preprocessor output if they have any effect at all)\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-any standard error output generated by the preprocessor\r
-</p>\r
-</li>\r
-</ul></div>\r
-<div class="paragraph"><p>Based on the hash, the cached compilation result can be looked up directly in\r
-the cache.</p></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_the_depend_mode">The depend mode</h3>\r
-<div class="paragraph"><p>If the depend mode is enabled, ccache will not use the preprocessor at all. The\r
-hash used to identify results in the cache will be based on the direct mode\r
-hash described above plus information about include files read from the\r
-dependency file generated by the compiler with <strong>-MD</strong> or <strong>-MMD</strong>.</p></div>\r
-<div class="paragraph"><p>Advantages:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-The ccache overhead of a cache miss will be much smaller.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Not running the preprocessor at all can be good if compilation is performed\r
- remotely, for instance when using distcc or similar; ccache then won’t make\r
- potentially costly preprocessor calls on the local machine.\r
-</p>\r
-</li>\r
-</ul></div>\r
-<div class="paragraph"><p>Disadvantages:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-The cache hit rate will likely be lower since any change to compiler options\r
- or source code will make the hash different. Compare this with the default\r
- setup where ccache will fall back to the preprocessor mode, which is tolerant\r
- to some types of changes of compiler options and source code changes.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-If -MD is used, the manifest entries will include system header files as\r
- well, thus slowing down cache hits slightly, just as using -MD slows down\r
- make.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-If -MMD is used, the manifest entries will not include system header files,\r
- which means ccache will ignore changes in them.\r
-</p>\r
-</li>\r
-</ul></div>\r
-<div class="paragraph"><p>The depend mode will be disabled if any of the following holds:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-the configuration setting <strong>depend_mode</strong> is false\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-the configuration setting <strong>run_second_cpp</strong> is false\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-the compiler is not generating dependencies using <strong>-MD</strong> or <strong>-MMD</strong>\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_cache_debugging">Cache debugging</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>To find out what information ccache actually is hashing, you can enable the\r
-debug mode via the configuration setting <strong>debug</strong> or by setting <strong>CCACHE_DEBUG</strong>\r
-in the environment. This can be useful if you are investigating why you don’t\r
-get cache hits. Note that performance will be reduced slightly.</p></div>\r
-<div class="paragraph"><p>When the debug mode is enabled, ccache will create up to five additional files\r
-next to the object file:</p></div>\r
-<div class="tableblock">\r
-<table rules="all"\r
-width="100%"\r
-frame="border"\r
-cellspacing="0" cellpadding="4">\r
-<col width="30%" />\r
-<col width="70%" />\r
-<thead>\r
-<tr>\r
-<th align="left" valign="top">Filename </th>\r
-<th align="left" valign="top"> Description</th>\r
-</tr>\r
-</thead>\r
-<tbody>\r
-<tr>\r
-<td align="left" valign="top"><p class="table"><strong><objectfile>.ccache-input-c</strong></p></td>\r
-<td align="left" valign="top"><p class="table">Binary input hashed by both the direct mode and the preprocessor mode.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table"><strong><objectfile>.ccache-input-d</strong></p></td>\r
-<td align="left" valign="top"><p class="table">Binary input only hashed by the direct mode.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table"><strong><objectfile>.ccache-input-p</strong></p></td>\r
-<td align="left" valign="top"><p class="table">Binary input only hashed by the preprocessor mode.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table"><strong><objectfile>.ccache-input-text</strong></p></td>\r
-<td align="left" valign="top"><p class="table">Human-readable combined diffable text version of the three files above.</p></td>\r
-</tr>\r
-<tr>\r
-<td align="left" valign="top"><p class="table"><strong><objectfile>.ccache-log</strong></p></td>\r
-<td align="left" valign="top"><p class="table">Log for this object file.</p></td>\r
-</tr>\r
-</tbody>\r
-</table>\r
-</div>\r
-<div class="paragraph"><p>In the direct mode, ccache uses the MD4 hash of the <strong>ccache-input-c</strong>\r
-+ <strong>ccache-input-d</strong> data (where <strong>+</strong> means concatenation), while the\r
-<strong>ccache-input-c</strong> + <strong>ccache-input-p</strong> data is used in the preprocessor mode.</p></div>\r
-<div class="paragraph"><p>The <strong>ccache-input-text</strong> file is a combined text version of the three\r
-binary input files. It has three sections (“COMMON”, “DIRECT MODE” and\r
-“PREPROCESSOR MODE”), which is turn contain annotations that say what kind of\r
-data comes next.</p></div>\r
-<div class="paragraph"><p>To debug why you don’t get an expected cache hit for an object file, you can do\r
-something like this:</p></div>\r
-<div class="olist arabic"><ol class="arabic">\r
-<li>\r
-<p>\r
-Build with debug mode enabled.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Save the <strong><objectfile>.ccache-*</strong> files.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Build again with debug mode enabled.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Compare <strong><objectfile>.ccache-input-text</strong> for the two builds. This together\r
- with the <strong><objectfile>.ccache-log</strong> files should give you some clues about\r
- what is happening.\r
-</p>\r
-</li>\r
-</ol></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_compiling_in_different_directories">Compiling in different directories</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Some information included in the hash that identifies a unique compilation can\r
-contain absolute paths:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-The preprocessed source code may contain absolute paths to include files if\r
- the compiler option <strong>-g</strong> is used or if absolute paths are given to <strong>-I</strong> and\r
- similar compiler options.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Paths specified by compiler options (such as <strong>-I</strong>, <strong>-MF</strong>, etc) on the command\r
- line may be absolute.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The source code file path may be absolute, and that path may substituted for\r
- <code>__FILE__</code> macros in the source code or included in warnings emitted to\r
- standard error by the preprocessor.\r
-</p>\r
-</li>\r
-</ul></div>\r
-<div class="paragraph"><p>This means that if you compile the same code in different locations, you can’t\r
-share compilation results between the different build directories since you get\r
-cache misses because of the absolute build directory paths that are part of the\r
-hash.</p></div>\r
-<div class="paragraph"><p>Here’s what can be done to enable cache hits between different build\r
-directories:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-If you build with <strong>-g</strong> (or similar) to add debug information to the object\r
- file, you must either:\r
-</p>\r
-<div class="openblock">\r
-<div class="content">\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-use the <strong>-fdebug-prefix-map=<em>old</em>=<em>new</em></strong> option for relocating debug info to\r
- a common prefix (e.g. <strong>-fdebug-prefix-map=$PWD=.</strong>); or\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-set <strong>hash_dir = false</strong>.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div></div>\r
-</li>\r
-<li>\r
-<p>\r
-If you use absolute paths anywhere on the command line (e.g. the source code\r
- file path or an argument to compiler options like <strong>-I</strong> and <strong>-MF</strong>), you must\r
- to set <strong>base_dir</strong> to an absolute path to a “base directory”. ccache will\r
- then rewrite absolute paths under that directory to relative before computing\r
- the hash.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_precompiled_headers">Precompiled headers</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>ccache has support for GCC’s precompiled headers. However, you have to do some\r
-things to make it work properly:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-You must set <strong>sloppiness</strong> to <strong>pch_defines,time_macros</strong>. The reason is that\r
- ccache can’t tell whether <code>__TIME__</code> or <code>__DATE__</code> is used when using a\r
- precompiled header. Further, it can’t detect changes in <strong>#define</strong>s in the\r
- source code because of how preprocessing works in combination with\r
- precompiled headers.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-You must either:\r
-</p>\r
-<div class="openblock">\r
-<div class="content">\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-use the <strong>-include</strong> compiler option to include the precompiled header (i.e.,\r
- don’t use <strong>#include</strong> in the source code to include the header; the filename\r
- itself must be sufficient to find the header, i.e. <strong>-I</strong> paths are not\r
- searched); or\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-(for the Clang compiler) use the <strong>-include-pch</strong> compiler option to include\r
- the PCH file generated from the precompiled header; or\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-(for the GCC compiler) add the <strong>-fpch-preprocess</strong> compiler option when\r
- compiling.\r
-</p>\r
-</li>\r
-</ul></div>\r
-<div class="paragraph"><p>If you don’t do this, either the non-precompiled version of the header file\r
-will be used (if available) or ccache will fall back to running the real\r
-compiler and increase the statistics counter “preprocessor error” (if the\r
-non-precompiled header file is not available).</p></div>\r
-</div></div>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_sharing_a_cache">Sharing a cache</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>A group of developers can increase the cache hit rate by sharing a cache\r
-directory. To share a cache without unpleasant side effects, the following\r
-conditions should to be met:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Use the same cache directory.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Make sure that the configuration setting <strong>hard_link</strong> is false (which is the\r
- default).\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Make sure that all users are in the same group.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Set the configuration setting <strong>umask</strong> to 002. This ensures that cached files\r
- are accessible to everyone in the group.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Make sure that all users have write permission in the entire cache directory\r
- (and that you trust all users of the shared cache).\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Make sure that the setgid bit is set on all directories in the cache. This\r
- tells the filesystem to inherit group ownership for new directories. The\r
- following command might be useful for this:\r
-</p>\r
-<div class="openblock">\r
-<div class="content">\r
-<div class="listingblock">\r
-<div class="content">\r
-<pre><code>find $CCACHE_DIR -type d | xargs chmod g+s</code></pre>\r
-</div></div>\r
-</div></div>\r
-</li>\r
-</ul></div>\r
-<div class="paragraph"><p>The reason to avoid the hard link mode is that the hard links cause unwanted\r
-side effects, as all links to a cached file share the file’s modification\r
-timestamp. This results in false dependencies to be triggered by\r
-timestamp-based build systems whenever another user links to an existing file.\r
-Typically, users will see that their libraries and binaries are relinked\r
-without reason.</p></div>\r
-<div class="paragraph"><p>You may also want to make sure that a base directory is set appropriately, as\r
-discussed in a previous section.</p></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_sharing_a_cache_on_nfs">Sharing a cache on NFS</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>It is possible to put the cache directory on an NFS filesystem (or similar\r
-filesystems), but keep in mind that:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Having the cache on NFS may slow down compilation. Make sure to do some\r
- benchmarking to see if it’s worth it.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache hasn’t been tested very thoroughly on NFS.\r
-</p>\r
-</li>\r
-</ul></div>\r
-<div class="paragraph"><p>A tip is to set <strong>temporary_dir</strong> to a directory on the local host to avoid NFS\r
-traffic for temporary files.</p></div>\r
-<div class="paragraph"><p>It is recommended to use the same operating system version when using a shared\r
-cache. If operating system versions are different then system include files\r
-will likely be different and there will be few or no cache hits between the\r
-systems. One way of improving cache hit rate in that case is to set\r
-<a href="#config_sloppiness"><strong>sloppiness</strong></a> to <strong>system_headers</strong> to ignore system\r
-headers.</p></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_using_ccache_with_other_compiler_wrappers">Using ccache with other compiler wrappers</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>The recommended way of combining ccache with another compiler wrapper (such as\r
-“distcc”) is by letting ccache execute the compiler wrapper. This is\r
-accomplished by defining the configuration setting <strong>prefix_command</strong>, for\r
-example by setting the environment variable <strong>CCACHE_PREFIX</strong> to the name of the\r
-wrapper (e.g. <strong>distcc</strong>). ccache will then prefix the command line with the\r
-specified command when running the compiler. To specify several prefix\r
-commands, set <strong>prefix_command</strong> to a colon-separated list of commands.</p></div>\r
-<div class="paragraph"><p>Unless you set <strong>compiler_check</strong> to a suitable command (see the description of\r
-that configuration option), it is not recommended to use the form <strong>ccache\r
-anotherwrapper compiler args</strong> as the compilation command. It’s also not\r
-recommended to use the masquerading technique for the other compiler wrapper.\r
-The reason is that by default, ccache will in both cases hash the mtime and\r
-size of the other wrapper instead of the real compiler, which means that:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Compiler upgrades will not be detected properly.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The cached results will not be shared between compilations with and without\r
- the other wrapper.\r
-</p>\r
-</li>\r
-</ul></div>\r
-<div class="paragraph"><p>Another minor thing is that if <strong>prefix_command</strong> is used, ccache will not invoke\r
-the other wrapper when running the preprocessor, which increases performance.\r
-You can use the <strong>prefix_command_cpp</strong> configuration setting if you also want to\r
-invoke the other wrapper when doing preprocessing (normally by adding <strong>-E</strong>).</p></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_caveats">Caveats</h2>\r
-<div class="sectionbody">\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-The direct mode fails to pick up new header files in some rare scenarios. See\r
- <a href="#_the_direct_mode">THE DIRECT MODE</a> above.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-When run via ccache, warning messages produced by GCC 4.9 and newer will only\r
- be colored when the environment variable <strong>GCC_COLORS</strong> is set. An alternative\r
- to setting <strong>GCC_COLORS</strong> is to pass <code>-fdiagnostics-color</code> explicitly when\r
- compiling (but then color codes will also be present when redirecting stderr\r
- to a file).\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-If ccache guesses that the compiler may emit colored warnings, then a\r
- compilation with stderr referring to a TTY will be considered different from\r
- a compilation with a redirected stderr, thus not sharing cache entries. This\r
- happens for clang by default and for GCC when <strong>GCC_COLORS</strong> is set as\r
- mentioned above. If you want to share cache hits, you can pass\r
- <code>-f[no-]diagnostics-color</code> (GCC) or <code>-f[no-]color-diagnostics</code> (clang)\r
- explicitly when compiling (but then color codes will be either on or off for\r
- both the TTY and the redirected case).\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_troubleshooting">Troubleshooting</h2>\r
-<div class="sectionbody">\r
-<div class="sect2">\r
-<h3 id="_general">General</h3>\r
-<div class="paragraph"><p>A general tip for getting information about what ccache is doing is to enable\r
-debug logging by setting the configuration option <strong>debug</strong> (or the environment\r
-variable <strong>CCACHE_DEBUG</strong>); see <a href="#_cache_debugging">debugging</a> for more\r
-information. Another way of keeping track of what is happening is to check the\r
-output of <strong>ccache -s</strong>.</p></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_performance">Performance</h3>\r
-<div class="paragraph"><p>ccache has been written to perform well out of the box, but sometimes you may\r
-have to do some adjustments of how you use the compiler and ccache in order to\r
-improve performance.</p></div>\r
-<div class="paragraph"><p>Since ccache works best when I/O is fast, put the cache directory on a fast\r
-storage device if possible. Having lots of free memory so that files in the\r
-cache directory stay in the disk cache is also preferable.</p></div>\r
-<div class="paragraph"><p>A good way of monitoring how well ccache works is to run <strong>ccache -s</strong> before and\r
-after your build and then compare the statistics counters. Here are some common\r
-problems and what may be done to increase the hit rate:</p></div>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-If “cache hit (preprocessed)” has been incremented instead of “cache hit\r
- (direct)”, ccache has fallen back to preprocessor mode, which is generally\r
- slower. Some possible reasons are:\r
-</p>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-The source code has been modified in such a way that the preprocessor output\r
- is not affected.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Compiler arguments that are hashed in the direct mode but not in the\r
- preprocessor mode have changed (<strong>-I</strong>, <strong>-include</strong>, <strong>-D</strong>, etc) and they didn’t\r
- affect the preprocessor output.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The compiler option <strong>-Xpreprocessor</strong> or <strong>-Wp,<em>X</em></strong> (except <strong>-Wp,-MD,<em>path</em></strong>,\r
- <strong>-Wp,-MMD,<em>path</em></strong>, and <strong>-Wp,-D_define_</strong>) is used.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-This was the first compilation with a new value of the base directory\r
- setting.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-A modification time of one of the include files is too new (created the same\r
- second as the compilation is being done). This check is made to avoid a race\r
- condition. To fix this, create the include file earlier in the build process,\r
- if possible, or set <strong>sloppiness</strong> to <strong>include_file_ctime, include_file_mtime</strong>\r
- if you are willing to take the risk. (The race condition consists of these\r
- events: the preprocessor is run; an include file is modified by someone; the\r
- new include file is hashed by ccache; the real compiler is run on the\r
- preprocessor’s output, which contains data from the old header file; the\r
- wrong object file is stored in the cache.)\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The <code>__TIME__</code> preprocessor macro is (potentially) being used. ccache turns\r
- off direct mode if <code>__TIME__</code> is present in the source code. This is done as\r
- a safety measure since the string indicates that a <code>__TIME__</code> macro <em>may</em>\r
- affect the output. (To be sure, ccache would have to run the preprocessor,\r
- but the sole point of the direct mode is to avoid that.) If you know that\r
- <code>__TIME__</code> isn’t used in practise, or don’t care if ccache produces objects\r
- where <code>__TIME__</code> is expanded to something in the past, you can set\r
- <strong>sloppiness</strong> to <strong>time_macros</strong>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The <code>__DATE__</code> preprocessor macro is (potentially) being used and the date\r
- has changed. This is similar to how <code>__TIME__</code> is handled. If <code>__DATE__</code> is\r
- present in the source code, ccache hashes the current date in order to be\r
- able to produce the correct object file if the <code>__DATE__</code> macro affects the\r
- output. If you know that <code>__DATE__</code> isn’t used in practise, or don’t care if\r
- ccache produces objects where <code>__DATE__</code> is expanded to something in the\r
- past, you can set <strong>sloppiness</strong> to <strong>time_macros</strong>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The input file path has changed. ccache includes the input file path in the\r
- direct mode hash to be able to take relative include files into account and\r
- to produce a correct object file if the source code includes a <code>__FILE__</code>\r
- macro.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</li>\r
-<li>\r
-<p>\r
-If “cache miss” has been incremented even though the same code has been\r
- compiled and cached before, ccache has either detected that something has\r
- changed anyway or a cleanup has been performed (either explicitly or\r
- implicitly when a cache limit has been reached). Some perhaps unobvious\r
- things that may result in a cache miss are usage of <code>__TIME__</code> or\r
- <code>__DATE__</code> macros, or use of automatically generated code that contains a\r
- timestamp, build counter or other volatile information.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-If “multiple source files” has been incremented, it’s an indication that\r
- the compiler has been invoked on several source code files at once. ccache\r
- doesn’t support that. Compile the source code files separately if possible.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-If “unsupported compiler option” has been incremented, enable debug logging\r
- and check which option was rejected.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-If “preprocessor error” has been incremented, one possible reason is that\r
- precompiled headers are being used. See <a href="#_precompiled_headers">PRECOMPILED HEADERS</a> for how to remedy this.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-If “can’t use precompiled header” has been incremented, see\r
- <a href="#_precompiled_headers">PRECOMPILED HEADERS</a>.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_corrupt_object_files">Corrupt object files</h3>\r
-<div class="paragraph"><p>It should be noted that ccache is susceptible to general storage problems. If a\r
-bad object file sneaks into the cache for some reason, it will of course stay\r
-bad. Some possible reasons for erroneous object files are bad hardware (disk\r
-drive, disk controller, memory, etc), buggy drivers or file systems, a bad\r
-<strong>prefix_command</strong> or compiler wrapper. If this happens, the easiest way of\r
-fixing it is this:</p></div>\r
-<div class="olist arabic"><ol class="arabic">\r
-<li>\r
-<p>\r
-Build so that the bad object file ends up in the build tree.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Remove the bad object file from the build tree.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Rebuild with <strong>CCACHE_RECACHE</strong> set.\r
-</p>\r
-</li>\r
-</ol></div>\r
-<div class="paragraph"><p>An alternative is to clear the whole cache with <strong>ccache -C</strong> if you don’t mind\r
-losing other cached results.</p></div>\r
-<div class="paragraph"><p>There are no reported issues about ccache producing broken object files\r
-reproducibly. That doesn’t mean it can’t happen, so if you find a repeatable\r
-case, please report it.</p></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_more_information">More information</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Credits, mailing list information, bug reporting instructions, source code,\r
-etc, can be found on ccache’s web site: <a href="https://ccache.dev">https://ccache.dev</a>.</p></div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_author">Author</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>ccache was originally written by Andrew Tridgell and is currently developed and\r
-maintained by Joel Rosdahl. See AUTHORS.txt or AUTHORS.html and\r
-<a href="https://ccache.dev/credits.html">https://ccache.dev/credits.html</a> for a list of contributors.</p></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div id="footnotes"><hr /></div>\r
-<div id="footer">\r
-<div id="footer-text">\r
-Version 3.7.12<br />\r
-Last updated\r
- 2020-10-01 14:20:15 CEST\r
-</div>\r
-</div>\r
-</body>\r
-</html>\r
-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
-------------
manual.
-
ccache 3.7.7
------------
Release date: 2020-01-05
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.
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>\r
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"\r
- "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">\r
-<head>\r
-<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />\r
-<meta name="generator" content="AsciiDoc 9.0.0rc1" />\r
-<title>ccache news</title>\r
-<style type="text/css">\r
-/* Shared CSS for AsciiDoc xhtml11 and html5 backends */\r
-\r
-/* Default font. */\r
-body {\r
- font-family: Georgia,serif;\r
-}\r
-\r
-/* Title font. */\r
-h1, h2, h3, h4, h5, h6,\r
-div.title, caption.title,\r
-thead, p.table.header,\r
-#toctitle,\r
-#author, #revnumber, #revdate, #revremark,\r
-#footer {\r
- font-family: Arial,Helvetica,sans-serif;\r
-}\r
-\r
-body {\r
- margin: 1em 5% 1em 5%;\r
-}\r
-\r
-a {\r
- color: blue;\r
- text-decoration: underline;\r
-}\r
-a:visited {\r
- color: fuchsia;\r
-}\r
-\r
-em {\r
- font-style: italic;\r
- color: navy;\r
-}\r
-\r
-strong {\r
- font-weight: bold;\r
- color: #083194;\r
-}\r
-\r
-h1, h2, h3, h4, h5, h6 {\r
- color: #527bbd;\r
- margin-top: 1.2em;\r
- margin-bottom: 0.5em;\r
- line-height: 1.3;\r
-}\r
-\r
-h1, h2, h3 {\r
- border-bottom: 2px solid silver;\r
-}\r
-h2 {\r
- padding-top: 0.5em;\r
-}\r
-h3 {\r
- float: left;\r
-}\r
-h3 + * {\r
- clear: left;\r
-}\r
-h5 {\r
- font-size: 1.0em;\r
-}\r
-\r
-div.sectionbody {\r
- margin-left: 0;\r
-}\r
-\r
-hr {\r
- border: 1px solid silver;\r
-}\r
-\r
-p {\r
- margin-top: 0.5em;\r
- margin-bottom: 0.5em;\r
-}\r
-\r
-ul, ol, li > p {\r
- margin-top: 0;\r
-}\r
-ul > li { color: #aaa; }\r
-ul > li > * { color: black; }\r
-\r
-.monospaced, code, pre {\r
- font-family: "Courier New", Courier, monospace;\r
- font-size: inherit;\r
- color: navy;\r
- padding: 0;\r
- margin: 0;\r
-}\r
-pre {\r
- white-space: pre-wrap;\r
-}\r
-\r
-#author {\r
- color: #527bbd;\r
- font-weight: bold;\r
- font-size: 1.1em;\r
-}\r
-#email {\r
-}\r
-#revnumber, #revdate, #revremark {\r
-}\r
-\r
-#footer {\r
- font-size: small;\r
- border-top: 2px solid silver;\r
- padding-top: 0.5em;\r
- margin-top: 4.0em;\r
-}\r
-#footer-text {\r
- float: left;\r
- padding-bottom: 0.5em;\r
-}\r
-#footer-badges {\r
- float: right;\r
- padding-bottom: 0.5em;\r
-}\r
-\r
-#preamble {\r
- margin-top: 1.5em;\r
- margin-bottom: 1.5em;\r
-}\r
-div.imageblock, div.exampleblock, div.verseblock,\r
-div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,\r
-div.admonitionblock {\r
- margin-top: 1.0em;\r
- margin-bottom: 1.5em;\r
-}\r
-div.admonitionblock {\r
- margin-top: 2.0em;\r
- margin-bottom: 2.0em;\r
- margin-right: 10%;\r
- color: #606060;\r
-}\r
-\r
-div.content { /* Block element content. */\r
- padding: 0;\r
-}\r
-\r
-/* Block element titles. */\r
-div.title, caption.title {\r
- color: #527bbd;\r
- font-weight: bold;\r
- text-align: left;\r
- margin-top: 1.0em;\r
- margin-bottom: 0.5em;\r
-}\r
-div.title + * {\r
- margin-top: 0;\r
-}\r
-\r
-td div.title:first-child {\r
- margin-top: 0.0em;\r
-}\r
-div.content div.title:first-child {\r
- margin-top: 0.0em;\r
-}\r
-div.content + div.title {\r
- margin-top: 0.0em;\r
-}\r
-\r
-div.sidebarblock > div.content {\r
- background: #ffffee;\r
- border: 1px solid #dddddd;\r
- border-left: 4px solid #f0f0f0;\r
- padding: 0.5em;\r
-}\r
-\r
-div.listingblock > div.content {\r
- border: 1px solid #dddddd;\r
- border-left: 5px solid #f0f0f0;\r
- background: #f8f8f8;\r
- padding: 0.5em;\r
-}\r
-\r
-div.quoteblock, div.verseblock {\r
- padding-left: 1.0em;\r
- margin-left: 1.0em;\r
- margin-right: 10%;\r
- border-left: 5px solid #f0f0f0;\r
- color: #888;\r
-}\r
-\r
-div.quoteblock > div.attribution {\r
- padding-top: 0.5em;\r
- text-align: right;\r
-}\r
-\r
-div.verseblock > pre.content {\r
- font-family: inherit;\r
- font-size: inherit;\r
-}\r
-div.verseblock > div.attribution {\r
- padding-top: 0.75em;\r
- text-align: left;\r
-}\r
-/* DEPRECATED: Pre version 8.2.7 verse style literal block. */\r
-div.verseblock + div.attribution {\r
- text-align: left;\r
-}\r
-\r
-div.admonitionblock .icon {\r
- vertical-align: top;\r
- font-size: 1.1em;\r
- font-weight: bold;\r
- text-decoration: underline;\r
- color: #527bbd;\r
- padding-right: 0.5em;\r
-}\r
-div.admonitionblock td.content {\r
- padding-left: 0.5em;\r
- border-left: 3px solid #dddddd;\r
-}\r
-\r
-div.exampleblock > div.content {\r
- border-left: 3px solid #dddddd;\r
- padding-left: 0.5em;\r
-}\r
-\r
-div.imageblock div.content { padding-left: 0; }\r
-span.image img { border-style: none; vertical-align: text-bottom; }\r
-a.image:visited { color: white; }\r
-\r
-dl {\r
- margin-top: 0.8em;\r
- margin-bottom: 0.8em;\r
-}\r
-dt {\r
- margin-top: 0.5em;\r
- margin-bottom: 0;\r
- font-style: normal;\r
- color: navy;\r
-}\r
-dd > *:first-child {\r
- margin-top: 0.1em;\r
-}\r
-\r
-ul, ol {\r
- list-style-position: outside;\r
-}\r
-ol.arabic {\r
- list-style-type: decimal;\r
-}\r
-ol.loweralpha {\r
- list-style-type: lower-alpha;\r
-}\r
-ol.upperalpha {\r
- list-style-type: upper-alpha;\r
-}\r
-ol.lowerroman {\r
- list-style-type: lower-roman;\r
-}\r
-ol.upperroman {\r
- list-style-type: upper-roman;\r
-}\r
-\r
-div.compact ul, div.compact ol,\r
-div.compact p, div.compact p,\r
-div.compact div, div.compact div {\r
- margin-top: 0.1em;\r
- margin-bottom: 0.1em;\r
-}\r
-\r
-tfoot {\r
- font-weight: bold;\r
-}\r
-td > div.verse {\r
- white-space: pre;\r
-}\r
-\r
-div.hdlist {\r
- margin-top: 0.8em;\r
- margin-bottom: 0.8em;\r
-}\r
-div.hdlist tr {\r
- padding-bottom: 15px;\r
-}\r
-dt.hdlist1.strong, td.hdlist1.strong {\r
- font-weight: bold;\r
-}\r
-td.hdlist1 {\r
- vertical-align: top;\r
- font-style: normal;\r
- padding-right: 0.8em;\r
- color: navy;\r
-}\r
-td.hdlist2 {\r
- vertical-align: top;\r
-}\r
-div.hdlist.compact tr {\r
- margin: 0;\r
- padding-bottom: 0;\r
-}\r
-\r
-.comment {\r
- background: yellow;\r
-}\r
-\r
-.footnote, .footnoteref {\r
- font-size: 0.8em;\r
-}\r
-\r
-span.footnote, span.footnoteref {\r
- vertical-align: super;\r
-}\r
-\r
-#footnotes {\r
- margin: 20px 0 20px 0;\r
- padding: 7px 0 0 0;\r
-}\r
-\r
-#footnotes div.footnote {\r
- margin: 0 0 5px 0;\r
-}\r
-\r
-#footnotes hr {\r
- border: none;\r
- border-top: 1px solid silver;\r
- height: 1px;\r
- text-align: left;\r
- margin-left: 0;\r
- width: 20%;\r
- min-width: 100px;\r
-}\r
-\r
-div.colist td {\r
- padding-right: 0.5em;\r
- padding-bottom: 0.3em;\r
- vertical-align: top;\r
-}\r
-div.colist td img {\r
- margin-top: 0.3em;\r
-}\r
-\r
-@media print {\r
- #footer-badges { display: none; }\r
-}\r
-\r
-#toc {\r
- margin-bottom: 2.5em;\r
-}\r
-\r
-#toctitle {\r
- color: #527bbd;\r
- font-size: 1.1em;\r
- font-weight: bold;\r
- margin-top: 1.0em;\r
- margin-bottom: 0.1em;\r
-}\r
-\r
-div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {\r
- margin-top: 0;\r
- margin-bottom: 0;\r
-}\r
-div.toclevel2 {\r
- margin-left: 2em;\r
- font-size: 0.9em;\r
-}\r
-div.toclevel3 {\r
- margin-left: 4em;\r
- font-size: 0.9em;\r
-}\r
-div.toclevel4 {\r
- margin-left: 6em;\r
- font-size: 0.9em;\r
-}\r
-\r
-span.aqua { color: aqua; }\r
-span.black { color: black; }\r
-span.blue { color: blue; }\r
-span.fuchsia { color: fuchsia; }\r
-span.gray { color: gray; }\r
-span.green { color: green; }\r
-span.lime { color: lime; }\r
-span.maroon { color: maroon; }\r
-span.navy { color: navy; }\r
-span.olive { color: olive; }\r
-span.purple { color: purple; }\r
-span.red { color: red; }\r
-span.silver { color: silver; }\r
-span.teal { color: teal; }\r
-span.white { color: white; }\r
-span.yellow { color: yellow; }\r
-\r
-span.aqua-background { background: aqua; }\r
-span.black-background { background: black; }\r
-span.blue-background { background: blue; }\r
-span.fuchsia-background { background: fuchsia; }\r
-span.gray-background { background: gray; }\r
-span.green-background { background: green; }\r
-span.lime-background { background: lime; }\r
-span.maroon-background { background: maroon; }\r
-span.navy-background { background: navy; }\r
-span.olive-background { background: olive; }\r
-span.purple-background { background: purple; }\r
-span.red-background { background: red; }\r
-span.silver-background { background: silver; }\r
-span.teal-background { background: teal; }\r
-span.white-background { background: white; }\r
-span.yellow-background { background: yellow; }\r
-\r
-span.big { font-size: 2em; }\r
-span.small { font-size: 0.6em; }\r
-\r
-span.underline { text-decoration: underline; }\r
-span.overline { text-decoration: overline; }\r
-span.line-through { text-decoration: line-through; }\r
-\r
-div.unbreakable { page-break-inside: avoid; }\r
-\r
-\r
-/*\r
- * xhtml11 specific\r
- *\r
- * */\r
-\r
-div.tableblock {\r
- margin-top: 1.0em;\r
- margin-bottom: 1.5em;\r
-}\r
-div.tableblock > table {\r
- border: 3px solid #527bbd;\r
-}\r
-thead, p.table.header {\r
- font-weight: bold;\r
- color: #527bbd;\r
-}\r
-p.table {\r
- margin-top: 0;\r
-}\r
-/* Because the table frame attribute is overridden by CSS in most browsers. */\r
-div.tableblock > table[frame="void"] {\r
- border-style: none;\r
-}\r
-div.tableblock > table[frame="hsides"] {\r
- border-left-style: none;\r
- border-right-style: none;\r
-}\r
-div.tableblock > table[frame="vsides"] {\r
- border-top-style: none;\r
- border-bottom-style: none;\r
-}\r
-\r
-\r
-/*\r
- * html5 specific\r
- *\r
- * */\r
-\r
-table.tableblock {\r
- margin-top: 1.0em;\r
- margin-bottom: 1.5em;\r
-}\r
-thead, p.tableblock.header {\r
- font-weight: bold;\r
- color: #527bbd;\r
-}\r
-p.tableblock {\r
- margin-top: 0;\r
-}\r
-table.tableblock {\r
- border-width: 3px;\r
- border-spacing: 0px;\r
- border-style: solid;\r
- border-color: #527bbd;\r
- border-collapse: collapse;\r
-}\r
-th.tableblock, td.tableblock {\r
- border-width: 1px;\r
- padding: 4px;\r
- border-style: solid;\r
- border-color: #527bbd;\r
-}\r
-\r
-table.tableblock.frame-topbot {\r
- border-left-style: hidden;\r
- border-right-style: hidden;\r
-}\r
-table.tableblock.frame-sides {\r
- border-top-style: hidden;\r
- border-bottom-style: hidden;\r
-}\r
-table.tableblock.frame-none {\r
- border-style: hidden;\r
-}\r
-\r
-th.tableblock.halign-left, td.tableblock.halign-left {\r
- text-align: left;\r
-}\r
-th.tableblock.halign-center, td.tableblock.halign-center {\r
- text-align: center;\r
-}\r
-th.tableblock.halign-right, td.tableblock.halign-right {\r
- text-align: right;\r
-}\r
-\r
-th.tableblock.valign-top, td.tableblock.valign-top {\r
- vertical-align: top;\r
-}\r
-th.tableblock.valign-middle, td.tableblock.valign-middle {\r
- vertical-align: middle;\r
-}\r
-th.tableblock.valign-bottom, td.tableblock.valign-bottom {\r
- vertical-align: bottom;\r
-}\r
-\r
-\r
-/*\r
- * manpage specific\r
- *\r
- * */\r
-\r
-body.manpage h1 {\r
- padding-top: 0.5em;\r
- padding-bottom: 0.5em;\r
- border-top: 2px solid silver;\r
- border-bottom: 2px solid silver;\r
-}\r
-body.manpage h2 {\r
- border-style: none;\r
-}\r
-body.manpage div.sectionbody {\r
- margin-left: 3em;\r
-}\r
-\r
-@media print {\r
- body.manpage div#toc { display: none; }\r
-}\r
-\r
-\r
-</style>\r
-<script type="text/javascript">\r
-/*<+'])');\r
- // Function that scans the DOM tree for header elements (the DOM2\r
- // nodeIterator API would be a better technique but not supported by all\r
- // browsers).\r
- var iterate = function (el) {\r
- for (var i = el.firstChild; i != null; i = i.nextSibling) {\r
- if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {\r
- var mo = re.exec(i.tagName);\r
- if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {\r
- result[result.length] = new TocEntry(i, getText(i), mo[1]-1);\r
- }\r
- iterate(i);\r
- }\r
- }\r
- }\r
- iterate(el);\r
- return result;\r
- }\r
-\r
- var toc = document.getElementById("toc");\r
- if (!toc) {\r
- return;\r
- }\r
-\r
- // Delete existing TOC entries in case we're reloading the TOC.\r
- var tocEntriesToRemove = [];\r
- var i;\r
- for (i = 0; i < toc.childNodes.length; i++) {\r
- var entry = toc.childNodes[i];\r
- if (entry.nodeName.toLowerCase() == 'div'\r
- && entry.getAttribute("class")\r
- && entry.getAttribute("class").match(/^toclevel/))\r
- tocEntriesToRemove.push(entry);\r
- }\r
- for (i = 0; i < tocEntriesToRemove.length; i++) {\r
- toc.removeChild(tocEntriesToRemove[i]);\r
- }\r
-\r
- // Rebuild TOC entries.\r
- var entries = tocEntries(document.getElementById("content"), toclevels);\r
- for (var i = 0; i < entries.length; ++i) {\r
- var entry = entries[i];\r
- if (entry.element.id == "")\r
- entry.element.id = "_toc_" + i;\r
- var a = document.createElement("a");\r
- a.href = "#" + entry.element.id;\r
- a.appendChild(document.createTextNode(entry.text));\r
- var div = document.createElement("div");\r
- div.appendChild(a);\r
- div.className = "toclevel" + entry.toclevel;\r
- toc.appendChild(div);\r
- }\r
- if (entries.length == 0)\r
- toc.parentNode.removeChild(toc);\r
-},\r
-\r
-\r
-/////////////////////////////////////////////////////////////////////\r
-// Footnotes generator\r
-/////////////////////////////////////////////////////////////////////\r
-\r
-/* Based on footnote generation code from:\r
- * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html\r
- */\r
-\r
-footnotes: function () {\r
- // Delete existing footnote entries in case we're reloading the footnodes.\r
- var i;\r
- var noteholder = document.getElementById("footnotes");\r
- if (!noteholder) {\r
- return;\r
- }\r
- var entriesToRemove = [];\r
- for (i = 0; i < noteholder.childNodes.length; i++) {\r
- var entry = noteholder.childNodes[i];\r
- if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")\r
- entriesToRemove.push(entry);\r
- }\r
- for (i = 0; i < entriesToRemove.length; i++) {\r
- noteholder.removeChild(entriesToRemove[i]);\r
- }\r
-\r
- // Rebuild footnote entries.\r
- var cont = document.getElementById("content");\r
- var spans = cont.getElementsByTagName("span");\r
- var refs = {};\r
- var n = 0;\r
- for (i=0; i<spans.length; i++) {\r
- if (spans[i].className == "footnote") {\r
- n++;\r
- var note = spans[i].getAttribute("data-note");\r
- if (!note) {\r
- // Use [\s\S] in place of . so multi-line matches work.\r
- // Because JavaScript has no s (dotall) regex flag.\r
- note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];\r
- spans[i].innerHTML =\r
- "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +\r
- "' title='View footnote' class='footnote'>" + n + "</a>]";\r
- spans[i].setAttribute("data-note", note);\r
- }\r
- noteholder.innerHTML +=\r
- "<div class='footnote' id='_footnote_" + n + "'>" +\r
- "<a href='#_footnoteref_" + n + "' title='Return to text'>" +\r
- n + "</a>. " + note + "</div>";\r
- var id =spans[i].getAttribute("id");\r
- if (id != null) refs["#"+id] = n;\r
- }\r
- }\r
- if (n == 0)\r
- noteholder.parentNode.removeChild(noteholder);\r
- else {\r
- // Process footnoterefs.\r
- for (i=0; i<spans.length; i++) {\r
- if (spans[i].className == "footnoteref") {\r
- var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");\r
- href = href.match(/#.*/)[0]; // Because IE return full URL.\r
- n = refs[href];\r
- spans[i].innerHTML =\r
- "[<a href='#_footnote_" + n +\r
- "' title='View footnote' class='footnote'>" + n + "</a>]";\r
- }\r
- }\r
- }\r
-},\r
-\r
-install: function(toclevels) {\r
- var timerId;\r
-\r
- function reinstall() {\r
- asciidoc.footnotes();\r
- if (toclevels) {\r
- asciidoc.toc(toclevels);\r
- }\r
- }\r
-\r
- function reinstallAndRemoveTimer() {\r
- clearInterval(timerId);\r
- reinstall();\r
- }\r
-\r
- timerId = setInterval(reinstall, 500);\r
- if (document.addEventListener)\r
- document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);\r
- else\r
- window.onload = reinstallAndRemoveTimer;\r
-}\r
-\r
-}\r
-asciidoc.install(2);\r
-/*]]>*/\r
-</script>\r
-</head>\r
-<body class="article">\r
-<div id="header">\r
-<h1>ccache news</h1>\r
-<span id="revnumber">version 3.7.12</span>\r
-<div id="toc">
- <div id="toctitle">Table of Contents</div>
- <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
-</div>\r
-</div>\r
-<div id="content">\r
-<div class="sect1">\r
-<h2 id="_ccache_3_7_12">ccache 3.7.12</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2020-10-01</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Coverage files (<code>.gcno</code>) produced by GCC 9+ when using <code>-fprofile-dir=dir</code>\r
- are now handled gracefully by falling back to running the compiler.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed writing to log file larger than 2 GiB when running ccache compiled in\r
- 32-bit mode.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_other">Other</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Improved documentation about sharing a cache on NFS.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed test case failures with old objdump versions.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed test case failures with GCC 4.4.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_7_11">ccache 3.7.11</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2020-07-21</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_2">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Added knowledge about <code>-fprofile-{correction,reorder-functions,values}</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache now handles the Intel compiler option <code>-xCODE</code> (where <code>CODE</code> is a\r
- processor feature code) correctly.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for NVCC’s <code>-Werror</code> and <code>--Werror</code> options.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_other_2">Other</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-ccache’s “Directory is not hashed if using -gz[=zlib]” tests are now skipped\r
- for GCC 6.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_7_10">ccache 3.7.10</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2020-06-22</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_3">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Improved handling of profiling options. ccache should now work correctly for\r
- profiling options like <code>-fprofile-{generate,use}[=path]</code> for GCC ≥ 9 and\r
- Clang as well as <code>-fauto-profile[=path]</code> and the Clang-specific\r
- <code>-fprofile-instr-{generate,use}[=path]</code> and <code>-fprofile-sample-{use,accurate}</code>\r
- options.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache now copies files directly from the cache to the destination file\r
- instead of via a temporary file. This avoids problems when using filenames\r
- long enough to be near the file system’s filename max limit.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-When the hard-link mode is enabled, ccache now only uses hard links for\r
- object files, not other files like dependency files. This is because\r
- compilers unlink object files before writing to them but they don’t do that\r
- for dependency files, so the latter can become overwritten and therefore\r
- corrupted in the cache.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed a glitch related to hard-link mode and an empty cache.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache now supports the ccache.conf file to be a symlink.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Temporary files are now deleted immediately on signals like SIGTERM and\r
- SIGINT instead of some time later in a cleanup phase.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed a bug that affected ccache’s <code>-o/--set-config</code> option for the\r
- <code>base_dir</code> and <code>cache_dir_levels</code> keys.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_7_9">ccache 3.7.9</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2020-03-29</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_4">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed replacing of /dev/null when building as root with hard link mode\r
- enabled and using <code>-o /dev/null</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Removed incorrect assertion resulting in “ccache: error: Internal error in\r
- format” when using <code>-fdebug-prefix-map=X=</code> with X equal to <code>$PWD</code>.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_other_3">Other</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Improved CUDA/NVCC support: Recognize <code>-dc</code> and <code>-x cu</code> options.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved name of temporary file used in NFS-safe unlink.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_7_8">ccache 3.7.8</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2020-03-16</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_5">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Use <code>$PWD</code> instead of the real CWD (current working directory) when checking\r
- for CWD in preprocessed output. This fixes a problem when <code>$PWD</code> includes a\r
- symlink part and the user has set <code>hash_dir = false</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Rewrote the Windows version of the lockfile routines. This should mitigate\r
- several problems with the old implementation.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-If <code>localtime_r</code> fails the epoch time is now logged instead of garbage.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_other_4">Other</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Improved error message when a boolean environment variable has an invalid\r
- value.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved the regression fix in ccache 3.7.5 related to not passing\r
- compilation-only options to the preprocessor.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache’s PCH test suite now skips running the tests if it detects broken PCH\r
- compiler support.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed unit test failure on Windows.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed “stringop-truncation” build warning on Windows.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved “x_rename” implementation on Windows.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved removal of temporary file when rewriting absolute paths to relative\r
- in the dependency file.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Clarified “include_file_ctime sloppiness” in the Performance section in the\r
- manual.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_7_7">ccache 3.7.7</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2020-01-05</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_6">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed a bug related to object file location in the dependency file (if using\r
- <code>-MD</code> or <code>-MMD</code> but not <code>-MF</code> and the build directory is not the same as the\r
- source directory then the object file location in the <code>.d</code> file would become\r
- incorrect). This fixes regression in ccache 3.7.5 introduced by the bug fix\r
- related to EDG-based compilers. Note that this removes support for EDG-based\r
- compilers again. (A better fix for this is planned for ccache 4.0.)\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Removed the unify mode since it has bugs and shortcomings that are non-trivial\r
- or impossible to fix: it doesn’t work with the direct mode, it doesn’t handle\r
- C++ raw strings correctly, it can give false cache hits for <code>.incbin</code>\r
- directives, it’s turned off when using <code>-g</code> and it can make line numbers in\r
- warning messages and <code>__LINE__</code> macros incorrect.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-mtime and ctime values are now stored in the manifest files only when\r
- sloppy_file_stat is set. This avoids adding superfluous manifest file entries\r
- on direct mode cache misses.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-A “Result:” line is now always printed to the log.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The “cache miss” statistics counter will now be updated for read-only cache\r
- misses, making it consistent with the cache hit case.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_7_6">ccache 3.7.6</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2019-11-17</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_7">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-The opt-in “file_macro sloppiness” mode has been removed so that the input\r
- file path now is always included in the direct mode hash. This fixes a bug\r
- that could result in false cache hits in an edge case when “file_macro\r
- sloppiness” is enabled and several identical source files include a relative\r
- header file with the same name but in different directories.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Statistics files are no longer lost when the filesystem of the cache is full.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Bail out on too hard Clang option <code>-MJarg</code> (in addition to the previous\r
- bailout of <code>-MJ arg</code>).\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Properly handle color diagnostics in the depend mode as well.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_7_5">ccache 3.7.5</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2019-10-22</p></div>\r
-<div class="sect2">\r
-<h3 id="_new_features">New features</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Added support for <code>-MF=arg</code> (with an extra equal sign) as understood by\r
- EDG-based compilers.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_8">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed a regression in 3.7.2 that could result in a warning message instead of\r
- an error in an edge case related to usage of “-Werror”.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-An implicit <code>-MQ</code> is now passed to the preprocessor only if the object file\r
- extension is non-standard. This will make it easier to use EDG-based\r
- compilers (e.g. GHS) which don’t understand <code>-MQ</code>. (This is a bug fix of the\r
- corresponding improvement implemented in ccache 3.4.)\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache now falls back to running the real compiler instead of failing fataly\r
- if an internal temporary file is missing after compilation.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed a crash if localtime returns null pointer in localtime_r replacement.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed header file dependency tracking when building ccache itself.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed warning during configure in out-of-tree build in developer mode.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_7_4">ccache 3.7.4</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2019-09-12</p></div>\r
-<div class="sect2">\r
-<h3 id="_improvements">Improvements</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Added support for GCC 9’s <code>-gz[=type]</code> option (previously ccache would think\r
- that “-gz” alone would enable debug information, thus potentially including\r
- the current directory in the hash).\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for converting paths like “/c/users” into relative paths on\r
- Windows.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_7_3">ccache 3.7.3</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2019-08-17</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_9">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-The cache size (which is counted in “used disk blocks”) is now correct on\r
- filesystems that use more or less disk blocks than conventional filesystems,\r
- e.g. ecryptfs or btrfs/zfs with transparent compression. This also fixes a\r
- related problem with ccache’s own test suite when run on such file systems.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed a regression in 3.7.2 when using the compiler option “-Werror” and then\r
- “-Wno-error” later on the command line.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_7_2">ccache 3.7.2</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2019-07-19</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_10">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-The compiler option <code>-gdwarf*</code> no longer forces “run_second_cpp = true”.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added verification that the value passed to the <code>-o/--set-config</code> option is\r
- valid.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed detection of precompiled headers in the depend mode.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Bail out on too hard Clang option <code>-ftime-trace</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache now updates the correct stats file when adding/updating manifest\r
- files. This bug previously made the file and size statistics counters\r
- incorrect over time.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed warnings from Clang about unused arguments during preprocessing.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Unknown manifest versions are now handled gracefully in <code>--dump-manifest</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed <code>make check</code> with “funny” locales.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_documentation">Documentation</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Added a hint about not running <code>autogen.sh</code> when building from a release\r
- archive.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Mention that <code>xsltproc</code> is needed when building from the source repository.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_7_1">ccache 3.7.1</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2019-05-01</p></div>\r
-<div class="sect2">\r
-<h3 id="_changes">Changes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed a problem when using the compiler option <code>-MF /dev/null</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Long commandlines are now handled gracefully on Windows by using the <code>@file</code>\r
- syntax to avoid hitting the commandline size limit.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed complaint from GCC 9’s <code>-Werror=format-overflow</code> when compiling ccache\r
- itself.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_7">ccache 3.7</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2019-04-23</p></div>\r
-<div class="sect2">\r
-<h3 id="_changes_2">Changes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed crash when the debug mode is enabled and the output file is in a\r
- non-writable directory, e.g. when the output file is <code>/dev/null</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed an issue when printing very large log messages to the debug log.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed bugs related to support for <code>-gsplit-dwarf</code>. Previously ccache could\r
- produce an incorrect link to the <code>.dwo</code> file in the <code>.o</code> file.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Compilations with /dev/null as the input file are now cached.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache has learned how to construct the object filename if no <code>-o</code> option is\r
- given and the source filename does not include a <code>.</code> or ends with a <code>.</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed a temporary file leak when the depend mode is enabled and the compiler\r
- produces standard error output.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed a bug in the depend mode where a manifest hash only could be associated\r
- with one set of header dependencies.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Manifest files did not get marked as used on direct cache hits, so the LRU\r
- cache cleanup would incorrectly remove them eventually. This has been fixed.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The rewriting of absolute paths into relative paths in the dependency file\r
- has been enabled in the depend mode as well.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache now ignores unknown keys in configuration files for forward\r
- compatibility.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Rearranged command-line options into sections in the help text.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Documented the previously undocumented <code>--dump-manifest</code> and <code>--hash-file</code>\r
- options (only useful for debugging ccache itself).\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added missing documentation for the command-line option <code>-k/--get-config</code>\r
- added in ccache 3.5.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Renamed the <code>--print-config</code> command to <code>--show-config</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added a new <code>--print-stats</code> command that prints statistics counters in\r
- machine-parsable (tab-separated) format.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache no longer creates a missing output directory, thus mimicking the\r
- compiler behavior for <code>-o out/obj.o</code> when “out” doesn’t exist.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-<code>-fdebug-prefix-map=ARG</code>, <code>-ffile-prefix-map=ARG</code> and\r
- <code>-fmacro-prefix-map=ARG</code> are now included in the hash, but only the part\r
- before “ARG”. This fixes a bug where compiler feature detection of said flags\r
- would not work correctly with ccache.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Bail out on too hard compiler option <code>-gtoggle</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Bail out on too hard Clang options <code>--analyze</code> and <code>-analyze</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved debug logging of file hashes in depend mode.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved handling of various <code>-g*</code> options. In particular, ccache now\r
- understands that <code>-g0</code> cancels out previous <code>-g* options</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Worked around a problem with Automake related to <code>.d</code> files when using the\r
- hard link mode.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added opt-in (at configure time) support for enabling trace logs for\r
- profiling ccache itself. See <code>doc/DEVELOPER.md</code> in the code tree for more\r
- information\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Removed support for Fortran 77 again. Some Fortran support was added in\r
- ccache 3.3, but the implementation did not work when Fortran modules are\r
- involved.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_6">ccache 3.6</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2019-01-14</p></div>\r
-<div class="sect2">\r
-<h3 id="_changes_3">Changes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-ccache now has an opt-in “depend mode”. When enabled, ccache never executes\r
- the preprocessor, which results in much lower cache miss overhead at the\r
- expense of a lower potential cache hit rate. The depend mode is only possible\r
- to use when the compiler option <code>-MD</code> or <code>-MMD</code> is used.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for GCC’s <code>-ffile-prefix-map</code> option. The <code>-fmacro-prefix-map</code>\r
- option is now also skipped from the hash.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for multiple <code>-fsanitize-blacklist</code> arguments.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache now includes the environment variables <code>LANG</code>, <code>LC_ALL</code>, <code>LC_CTYPE</code>\r
- and <code>LC_MESSAGES</code> in the hash since they may affect localization of compiler\r
- warning messages. Set sloppiness to <code>locale</code> to opt out of this.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed a problem due to Clang overwriting the output file when compiling an\r
- assembler file.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Clarified the manual to explain the reasoning behind the “file_macro”\r
- sloppiness setting in a better way.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache now handles several levels of nonexistent directories when rewriting\r
- absolute paths to relative.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-A new sloppiness setting <code>clang_index_store</code> makes ccache skip the Clang\r
- compiler option <code>-index-store-path</code> and its argument when computing the\r
- manifest hash. This is useful if you use Xcode, which uses an index store\r
- path derived from the local project path. Note that the index store won’t be\r
- updated correctly on cache hits if you enable this option.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Rename sloppiness <code>no_system_headers</code> to <code>system_headers</code> for consistency\r
- with other options. <code>no_system_headers</code> can still be used as an\r
- (undocumented) alias.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The GCC variables “DEPENDENCIES_OUTPUT” and “SUNPRO_DEPENDENCIES” are now\r
- supported correctly.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The algorithm that scans for <code>__DATE_</code> and <code>__TIME__</code> tokens in the hashed\r
- source code now doesn’t produce false positives for tokens where <code>__DATE__</code>\r
- or <code>__TIME__</code> is a substring.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_5_1">ccache 3.5.1</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2019-01-02</p></div>\r
-<div class="sect2">\r
-<h3 id="_changes_4">Changes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Added missing getopt_long.c source file to release archive.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed (harmless) compiler warnings when building ccache object files.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-CFLAGS is no longer passed to the linker when linking ccache.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved development mode build flags.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_5">ccache 3.5</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2018-10-15</p></div>\r
-<div class="sect2">\r
-<h3 id="_changes_5">Changes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Added a boolean <code>debug</code> (<code>CCACHE_DEBUG</code>) configuration option. When enabled,\r
- ccache will create per-object debug files that are helpful e.g. when\r
- debugging unexpected cache misses. See also the new “Cache debugging” section\r
- in the manual.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Renamed <code>CCACHE_CC</code> to <code>CCACHE_COMPILER</code> (keeping the former as a deprecated\r
- alias).\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added a new command-line option <code>-k/--get-config</code> that prints the value of a\r
- config key.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-It is now possible to let ccache hash a precomputed checksum file instead of\r
- the full content of a precompiled header. This can save time for large\r
- precompiled headers. Note that the build system needs to keep the checksum\r
- file in sync with the precompiled header for this to work.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved performance substantially when using <code>hash_dir = false</code> on platforms\r
- like macOS where <code>getcwd()</code> is slow.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added “stats updated” timestamp in <code>ccache -s</code> output. This can be useful if\r
- you wonder whether ccache actually was used for your last build.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Renamed “stats zero time” to “stats zeroed” and documented it. The counter is\r
- also now only present in <code>ccache -s</code> output when <code>ccache -z</code> actually has\r
- been called.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The content of the <code>-fsanitize-blacklist</code> file is now included in the hash,\r
- so updates to the file will now correctly result in separate cache entries.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-It’s now possible to opt out of building and installing man pages when\r
- running <code>make install</code> in the source repository.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-If the compiler type can’t be detected (e.g. if it is named <code>cc</code>), use safer\r
- defaults that won’t trip up Clang.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Made the ccache test suite work on FreeBSD.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added <code>file_stat_matches_ctime</code> option to disable ctime check if\r
- <code>file_stat_matches</code> is enabled.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Made “./configure --without-bundled-zlib” do what’s intended.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_4_3">ccache 3.4.3</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2018-09-02</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_11">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed a race condition when creating the initial config file in the cache\r
- directory.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Bail out on too hard Clang option <code>-MJ</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Bail out on too hard option <code>-save-temps=obj</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Handle separate parameter to Clang option <code>-target</code> correctly.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Upgraded bundled zlib to version 1.2.11.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_4_2">ccache 3.4.2</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2018-03-25</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_12">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-The cleanup algorithm has been fixed to not misbehave when files are removed\r
- by another process while the cleanup process is running. Previously, too many\r
- files could be removed from the cache if multiple cleanup processes were\r
- triggered at the same time, in extreme cases trimming the cache to a much\r
- smaller size than the configured limits.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Correctly hash preprocessed headers located in a “.gch directory”.\r
- Previously, ccache would not pick up changes to such precompiled headers,\r
- risking false positive cache hits.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed build failure when using the bundled zlib sources.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache 3.3.5 added a workaround for not triggering Clang errors when a\r
- precompiled header’s dependency has an updated timestamp (but identical\r
- content). That workaround is now only applied when the compiler is Clang.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Made it possible to perform out-of-source builds in dev mode again.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_4_1">ccache 3.4.1</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2018-02-11</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_13">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed printing of version number in <code>ccache --version</code>.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_4">ccache 3.4</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2018-02-11</p></div>\r
-<div class="sect2">\r
-<h3 id="_new_features_and_enhancements">New features and enhancements</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-The compiler option form <code>--sysroot arg</code> is now handled like the documented\r
- <code>--sysroot=arg</code> form.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for caching <code>.su</code> files generated by GCC flag <code>-fstack-usage</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache should now work with distcc’s “pump” wrapper.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The optional unifier is no longer disabled when the direct mode is enabled.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for NVCC compiler options <code>--compiler-bindir/-ccbin</code>,\r
- <code>--output-directory/-odir</code> and <code>--libdevice-directory/-ldir</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Boolean environment variable settings no longer accept the following\r
- (case-insensitive) values: <code>0</code>, <code>false</code>, <code>disable</code> and <code>no</code>. All other values\r
- are accepted and taken to mean “true”. This is to stop users from setting\r
- e.g. <code>CCACHE_DISABLE=0</code> and then expect the cache to be used.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved support for <code>run_second_cpp = false</code>: If combined with passing\r
- <code>-fdirectives-only</code> (GCC) or <code>frewrite-includes</code> (Clang) to the compiler,\r
- diagnostics warnings and similar will be correct.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-An implicit <code>-MQ</code> is now passed to the preprocessor only if the object file\r
- extension is non-standard. This should make it easier to use EDG-based\r
- compilers (e.g. GHS) which don’t understand <code>-MQ</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache now treats an unreadable configuration file just like a missing\r
- configuration file.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Documented more pitfalls with enabling <code>hard_links</code> (<code>CCACHE_HARDLINK</code>).\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Documented caveats related to colored warnings from compilers.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_14">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-File size and number counters are now updated correctly when files are\r
- overwritten in the cache, e.g. when using <code>CCACHE_RECACHE</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-<code>run_second_cpp</code> is now forced for NVCC.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed how the NVCC options <code>-optf</code> and <code>-odir</code> are handled.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_3_6">ccache 3.3.6</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2018-01-28</p></div>\r
-<div class="sect2">\r
-<h3 id="_new_features_and_enhancements_2">New features and enhancements</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Improved instructions on how to get cache hits between different working\r
- directories.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_15">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed regression in ccache 3.3.5 related to the <code>UNCACHED_ERR_FD</code> feature.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_3_5">ccache 3.3.5</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2018-01-13</p></div>\r
-<div class="sect2">\r
-<h3 id="_new_features_and_enhancements_3">New features and enhancements</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Documented how automatic cache cleanup works.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_16">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed a regression where the original order of debug options could be lost.\r
- This reverts the “Improved parsing of <code>-g*</code> options” feature in ccache 3.3.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Multiple <code>-fdebug-prefix-map</code> options should now be handled correctly.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed matching of directories in the <code>ignore_headers_in_manifest</code>\r
- configuration option.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed detection of missing argument to <code>-opt</code>/<code>--options-file</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache now bails out when building a precompiled header if any of the\r
- corresponding header files has an updated timestamp. This fixes complaints\r
- from Clang.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed a bug related to erroneously storing a dependency file with absolute\r
- paths in the cache on a preprocessed hit.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-<code>ccache -c/--cleanup</code> now works like documented: it just recalculates size\r
- counters and trims the cache to not exceed the max size and file number\r
- limits. Previously, the forced cleanup took “limit_multiple” into account, so\r
- that <code>ccache -c/--cleanup</code> by default would trim the cache to 80% of the max\r
- limit.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache no longer ignores linker arguments for Clang since Clang warns about\r
- them.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Plugged a couple of file descriptor leaks.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed a bug where ccache would skip hashing the compiler argument following a\r
- <code>-fno-working-directory</code>, <code>-fworking-directory</code>, <code>-nostdinc</code>, <code>-nostdinc++</code>,\r
- <code>-remap</code> or <code>-trigraphs</code> option in preprocessor mode.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_3_4">ccache 3.3.4</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2017-02-17</p></div>\r
-<div class="sect2">\r
-<h3 id="_new_features_and_enhancements_4">New features and enhancements</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Documented the different cache statistics counters.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_17">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed a regression in ccache 3.3 related to potentially bad content of\r
- dependency files when compiling identical source code but with different\r
- source paths. This was only partially fixed in 3.3.2 and reverts the new\r
- “Names of included files are no longer included in the hash of the compiler’s\r
- preprocessed output” feature in 3.3.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Corrected statistics counter for <code>-optf</code>/<code>--options-file</code> failure.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed undefined behavior warnings in ccache found by <code>-fsanitize=undefined</code>.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_3_3">ccache 3.3.3</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2016-10-26</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_18">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-ccache now detects usage of <code>.incbin</code> assembler directives in the source code\r
- and avoids caching such compilations.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_3_2">ccache 3.3.2</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2016-09-28</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_19">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed a regression in ccache 3.3 related to potentially bad content of\r
- dependency files when compiling identical source code but with different\r
- source paths.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed a regression in ccache 3.3.1: ccache could get confused when using the\r
- compiler option <code>-Wp,</code> to pass multiple options to the preprocessor,\r
- resulting in missing dependency files from direct mode cache hits.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_3_1">ccache 3.3.1</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2016-09-07</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_20">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed a problem in the “multiple <code>-arch</code> options” support introduced in 3.3.\r
- When using the direct mode (the default), different combinations of <code>-arch</code>\r
- options were not detected properly.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed an issue when compiler option <code>-Wp,-MT,path</code> is used instead of <code>-MT\r
- path</code> (and similar for <code>-MF</code>, <code>-MP</code> and <code>-MQ</code>) and <code>run_second_cpp</code>\r
- (<code>CCACHE_CPP2</code>) is enabled.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_3">ccache 3.3</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2016-08-27</p></div>\r
-<div class="sect2">\r
-<h3 id="_notes">Notes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-A C99-compatible compiler is now required to build ccache.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_new_features_and_enhancements_5">New features and enhancements</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-The configuration option <code>run_second_cpp</code> (<code>CCACHE_CPP2</code>) now defaults to\r
- true. This improves ccache’s out-of-the-box experience for compilers that\r
- can’t compile their own preprocessed output with the same outcome as if they\r
- compiled the real source code directly, e.g. newer versions of GCC and Clang.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The configuration option <code>hash_dir</code> (<code>CCACHE_HASHDIR</code>) now defaults to true.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added a new <code>ignore_headers_in_manifest</code> configuration option, which\r
- specifies headers that should be ignored in the direct mode.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added a new <code>prefix_command_cpp</code> (<code>CCACHE_PREFIX_CPP</code>) configuration option,\r
- which specifies one or several prefixes to add to the command line ccache\r
- uses when invoking the preprocessor.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added a new <code>limit_multiple</code> (<code>CCACHE_LIMIT_MULTIPLE</code>) configuration option,\r
- which specifies how much of the cache to remove when cleaning.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added a new <code>keep_comments_cpp</code> (<code>CCACHE_COMMENTS</code>) configuration option,\r
- which tells ccache not to discard the comments before hashing preprocessor\r
- output. This can be used to check documentation with <code>-Wdocumentation</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added a new sloppiness option <code>no_system_headers</code>, which tells ccache not to\r
- include system headers in manifest files.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added a new statistics counter that tracks the number of performed cleanups\r
- due to the cache size being over the limit. The value is shown in the output\r
- of “ccache -s”.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for relocating debug info directory using <code>-fdebug-prefix-map</code>.\r
- This allows for cache hits even when <code>hash_dir</code> is used in combination with\r
- <code>base_dir</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added a new “cache hit rate” field to the output of “ccache -s”.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for caching compilation of assembler code produced by e.g. “gcc\r
- -S file.c”.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for cuda including the -optf/--options-file option.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for Fortran 77.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for multiple <code>-arch</code> options to produce “fat binaries”.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Multiple identical <code>-arch</code> arguments are now handled without bailing.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The concatenated form of some long compiler options is now recognized, for\r
- example when using <code>-isystemPATH</code> instead of <code>-isystem PATH</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-If hard-linking is enabled and but fails (e.g. due to cross-device linking),\r
- ccache now falls back to copying instead of running the compiler.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Made the <code>hash_dir</code> option only have effect when generating debug info.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache now knows how to convert absolute paths to relative paths inside\r
- dependency files when using <code>base_dir</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved parsing of <code>-g*</code> options.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Made ccache understand <code>-Wp,-D*</code> options.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache now understands the undocumented <code>-coverage</code> (only one dash) GCC\r
- option.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Names of included files are no longer included in the hash of the compiler’s\r
- preprocessed output. This leads to more potential cache hits when not using\r
- the direct mode.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Increased buffer size used when reading file data. This improves performance\r
- slightly.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_21">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Bail out on too hard compiler option <code>-P</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed Clang test suite when running on Linux.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed build and test for MinGW32 and Windows.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_2_9">ccache 3.2.9</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2016-09-28</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_22">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed a regression in ccache 3.2.8: ccache could get confused when using the\r
- compiler option <code>-Wp,</code> to pass multiple options to the preprocessor,\r
- resulting in missing dependency files from direct mode cache hits.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_2_8">ccache 3.2.8</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2016-09-07</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_23">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed an issue when compiler option <code>-Wp,-MT,path</code> is used instead of <code>-MT\r
- path</code> (and similar for <code>-MF</code>, <code>-MP</code> and <code>-MQ</code>) and <code>run_second_cpp</code>\r
- (<code>CCACHE_CPP2</code>) is enabled.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache now understands the undocumented <code>-coverage</code> (only one dash) GCC\r
- option.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_2_7">ccache 3.2.7</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2016-07-20</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_24">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed a bug which could lead to false cache hits for compiler command lines\r
- with a missing argument to an option that takes an argument.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache now knows how to work around a glitch in the output of GCC 6’s\r
- preprocessor.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_2_6">ccache 3.2.6</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2016-07-12</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_25">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed build problem on QNX, which lacks “SA_RESTART”.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Bail out on compiler option <code>-fstack-usage</code> since it creates a <code>.su</code> file\r
- which ccache currently doesn’t handle.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed a bug where (due to ccache rewriting paths) the compiler could choose\r
- incorrect include files if <code>CCACHE_BASEDIR</code> is used and the source file path\r
- is absolute and is a symlink.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_2_5">ccache 3.2.5</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2016-04-17</p></div>\r
-<div class="sect2">\r
-<h3 id="_new_features_and_enhancements_6">New features and enhancements</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Only pass Clang-specific <code>-stdlib=</code> to the preprocessor.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved handling of stale NFS handles.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Made it harder to misinterpret documentation of boolean environment settings’\r
- semantics.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_26">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Include m4 files used by configure.ac in the source dist archives.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Corrected “Performance” section in the manual regarding <code>__DATE_</code>, <code>__TIME__</code>\r
- and <code>__FILE__</code> macros.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed build on Solaris 10+ and AIX 7.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed failure to create directories on QNX.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Don’t (try to) update manifest file in “read-only” and “read-only direct”\r
- modes.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed a bug in caching of <code>stat</code> system calls in “file_stat_matches\r
- sloppiness mode”.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed bug in hashing of Clang plugins, leading to unnecessary cache misses.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed --print-config to show “pch_defines sloppiness”.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The man page is now built when running “make install” from Git repository\r
- sources.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_2_4">ccache 3.2.4</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2015-10-08</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_27">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed build error related to zlib on systems with older make versions\r
- (regression in ccache 3.2.3).\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Made conversion-to-bool explicit to avoid build warnings (and potential\r
- runtime errors) on legacy systems.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved signal handling: Kill compiler on SIGTERM; wait for compiler to exit\r
- before exiting; die appropriately.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Minor fixes related to Windows support.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The correct compression level is now used if compression is requested.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed a bug where cache cleanup could be run too early for caches larger than\r
- 64 GiB on 32-bit systems.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_2_3">ccache 3.2.3</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2015-08-16</p></div>\r
-<div class="sect2">\r
-<h3 id="_new_features_and_enhancements_7">New features and enhancements</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Added support for compiler option <code>-gsplit-dwarf</code>.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_28">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Support external zlib in nonstandard directory.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Avoid calling <code>exit()</code> inside an exit handler.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Let exit handler terminate properly.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Bail out on compiler option <code>--save-temps</code> in addition to <code>-save-temps</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Only log “Disabling direct mode” once when failing to read potential include\r
- files.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_2_2">ccache 3.2.2</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2015-05-10</p></div>\r
-<div class="sect2">\r
-<h3 id="_new_features_and_enhancements_8">New features and enhancements</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Added support for <code>CCACHE_COMPILERCHECK=string:<value></code>. This is a faster\r
- alternative to <code>CCACHE_COMPILERCHECK=<command></code> if the command’s output can\r
- be precalculated by the build system.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Add support for caching code coverage results (compiling for gcov).\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_29">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Made hash of cached result created with and without <code>CCACHE_CPP2</code> different.\r
- This makes it possible to rebuild with <code>CCACHE_CPP2</code> set without having to\r
- clear the cache to get new results.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Don’t try to reset a nonexistent stats file. This avoids “No such file or\r
- directory” messages in the ccache log when the cache directory doesn’t exist.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed a bug where ccache deleted Clang diagnostics after compiler failures.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Avoid performing an unnecessary copy of the object file on a cache miss.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Bail out on too hard compiler option <code>-fmodules</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Bail out on too hard compiler option <code>-fplugin=libcc1plugin</code> (interaction\r
- with GDB).\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed build error when compiling ccache with recent Clang versions.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Removed signal-unsafe code from signal handler.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Corrected logic for when to output cached stderr.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Wipe the whole cached result on failure retrieving a cached file.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed build error when compiling ccache with recent Clang versions.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_2_1">ccache 3.2.1</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2014-12-10</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_30">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed regression in temporary file handling, which lead to incorrect\r
- permissions for stats, manifest and ccache.conf files in the cache.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-<code>CACHEDIR.TAG</code> files are now created in the [0-9a-f] subdirectories so that\r
- ccache.conf is not lost in backups.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Made the default cache size suffix <code>G</code>, as previously documented.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-<code>-fdiagnostics-color=auto</code> is now passed to the compiler even if stderr is\r
- redirected. This fixes a problem when, for instance, a configure test probes\r
- if the compiler (wrapped via ccache) supports <code>-fdiagnostics-color=auto</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added missing documentation for <code>max_files</code> and <code>max_size</code> configuration\r
- options.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_2">ccache 3.2</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2014-11-17</p></div>\r
-<div class="sect2">\r
-<h3 id="_new_features_and_enhancements_9">New features and enhancements</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Added support for configuring ccache via one or several configuration files\r
- instead of via environment variables. Environment variables still have\r
- priority but are no longer the recommended way of customizing ccache\r
- behavior. See the manual for more information.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for compiler error/warning messages with color.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Made creation of temporary directories and cache directories smarter to avoid\r
- unnecessary <code>stat</code> calls.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved efficiency of the algorithm that scans for <code>__DATE_</code> and <code>__TIME__</code>\r
- tokens in the hashed source code.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for several binaries (separated by space) in <code>CCACHE_PREFIX</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The <code>-c</code> option is no longer passed to the preprocessor. This fixes problems\r
- with Clang and Solaris’s C++ compiler.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache no longer passes preprocessor options like <code>-D</code> and <code>-I</code> to the\r
- compiler when compiling preprocessed output. This fixes warnings emitted by\r
- Clang.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Compiler options <code>-fprofile-generate</code>, <code>-fprofile-arcs</code>, <code>-fprofile-use</code> and\r
- <code>-fbranch-probabilities</code> are now handled without bailing.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for Clang’s <code>--serialize-diagnostic</code> option, storing the\r
- diagnostic file (<code>.dia</code>) in the cache.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for precompiled headers when using Clang.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for Clang <code>.pth</code> (pretokenized header) files.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Changed the <code>-x</code> language option to use the new objective C standard for GCC\r
- and Clang.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-On a cache miss, ccache now instructs the compiler to create the object file\r
- at the real destination and then copies the file into the cache instead of\r
- the other way around. This is needed to support compiler options like\r
- <code>-fprofile-arcs</code> and <code>--serialize-diagnostics</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache now checks that included files’ ctimes aren’t too new. This check can\r
- be turned off by adding <code>include_file_ctime</code> to the “ccache sloppiness”\r
- setting.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added possibility to get cache hits based on filename, size, mtime and ctime\r
- only. On other words, source code files are not even read, only stat-ed. This\r
- operation mode is opt-in by adding <code>file_stat_matches</code> to the “ccache\r
- sloppiness” setting.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The filename part of options like <code>-Wp,-MDfilename</code> is no longer included in\r
- the hash since the filename doesn’t have any bearing on the result.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added a “read-only direct” configuration setting, which is like the ordinary\r
- read-only setting except that ccache will only try to retrieve results from\r
- the cache using the direct mode, not the preprocessor mode.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The display and interpretation of cache size has been changed to use SI\r
- units.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Default cache size is now 5 GB (was previously 1 GiB).\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added configuration option to set the compression level of compressed object\r
- files in the cache.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for <code>@file</code> and <code>-@file</code> arguments (reading options from a\r
- file).\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-<code>-Wl,</code> options are no longer included in the hash since they don’t affect\r
- compilation.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Bail out on too hard compiler option <code>-Wp,-P</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Optimized MD4 calculation code on little-endian systems.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Various improvements and fixes on win32.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved logging to the ccache log file.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added <code>--dump-manifest</code> command-line option for debugging purposes.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added <code>--with-bundled-zlib</code> configure option.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Upgraded bundled zlib to version 1.2.8.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved <code>dev.mk</code> to be more platform independent.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Made the test suite work with Clang and gcc-llvm on OS X.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Various other improvements of the test suite.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_31">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Any previous <code>.stderr</code> is now removed from the cache when recaching.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed an issue when handling the <code>-arch</code> compiler option with an argument.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed race condition when creating the initial cache directory.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed test suite failures when <code>CC</code> is a ccache-wrapped compiler.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_1_12">ccache 3.1.12</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2016-07-12</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_32">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed a bug where (due to ccache rewriting paths) the compiler could choose\r
- incorrect include files if <code>CCACHE_BASEDIR</code> is used and the source file path\r
- is absolute and is a symlink.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_1_11">ccache 3.1.11</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2015-03-07</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_33">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed bug which could result in false cache hits when source code contains\r
- <code>'"'</code> followed by <code>" /*"</code> or <code>" //"</code> (with variations).\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Made hash of cached result created with and without <code>CCACHE_CPP2</code> different.\r
- This makes it possible to rebuild with <code>CCACHE_CPP2</code> set without having to\r
- clear the cache to get new results.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Don’t try to reset a nonexistent stats file. This avoids “No such file or\r
- directory” messages in the ccache log when the cache directory doesn’t exist.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_1_10">ccache 3.1.10</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2014-10-19</p></div>\r
-<div class="sect2">\r
-<h3 id="_new_features_and_enhancements_10">New features and enhancements</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Added support for the <code>-Xclang</code> compiler option.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved handling of exit code of internally executed processes.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Zero length object files in the cache are now rejected as invalid.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Bail out on option <code>-gsplit-dwarf</code> (since it produces multiple output files).\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Compiler option <code>-fdebug-prefix-map</code> is now ignored (not part of the hash).\r
- (The <code>-fdebug-prefix-map</code> option may be used in combination with\r
- <code>CCACHE_BASEDIR</code> to reuse results across different directories.)\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added note in documentation that <code>--ccache-skip</code> currently does not mean\r
- “don’t hash the following option”.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-To enable support for precompiled headers (PCH), <code>CCACHE_SLOPPINESS</code> now also\r
- needs to include the new <code>pch_defines</code> sloppiness. This is because ccache\r
- can’t detect changes in the source code when only defined macros have been\r
- changed.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Stale files in the internal temporary directory (<code><ccache_dir>/tmp</code>) are now\r
- cleaned up if they are older than one hour.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_34">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed path canonicalization in <code>make_relative_path()</code> when path doesn’t\r
- exist.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed bug in <code>common_dir_prefix_length()</code>. This corrects the <code>CCACHE_BASEDIR</code>\r
- behavior.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache no longer tries to create the cache directory when <code>CCACHE_DISABLE</code> is\r
- set.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed bug when reading manifests with a very large number of file info\r
- entries.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed problem with logging of current working directory.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_1_9">ccache 3.1.9</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2013-01-06</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_35">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-The EAGAIN signal is now handled correctly when emitting cached stderr\r
- output. This fixes a problem triggered by large error outputs from the\r
- compiler.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Subdirectories in the cache are no longer created in read-only mode.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed so that ccache’s log file descriptor is not made available to the\r
- compiler.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved error reporting when failing to create temporary stdout/stderr files\r
- when executing the compiler.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Disappearing temporary stdout/stderr files are now handled gracefully.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_other_5">Other</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed test suite to work on ecryptfs.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_1_8">ccache 3.1.8</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2012-08-11</p></div>\r
-<div class="sect2">\r
-<h3 id="_new_features_and_enhancements_11">New features and enhancements</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Made paths to dependency files relative in order to increase cache hits.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added work-around to make ccache work with buggy GCC 4.1 when creating a\r
- pre-compiled header.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Clang plugins are now hashed to catch plugin upgrades.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_36">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Fixed crash when the current working directory has been removed.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed crash when stderr is closed.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Corrected a corner case when parsing backslash escapes in string\r
- literals.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Paths are now correctly canonicalized when computing paths relative to the\r
- base directory.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_other_6">Other</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Made git version macro work when compiling outside of the source directory.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed <code>static_assert</code> macro definition clash with GCC 4.7.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_1_7">ccache 3.1.7</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2012-01-08</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_37">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Non-writable <code>CCACHE_DIR</code> is now handled gracefully when <code>CCACHE_READONLY</code> is\r
- set.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Made failure to create files (typically due to bad directory permissions) in\r
- the cache directory fatal. Previously, such failures were silently and\r
- erroneously flagged as “compiler produced stdout”.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Both the <code>-specs=file</code> and <code>--specs=file</code> forms are now recognized.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added recognition and hashing of GCC plugins specified with <code>-fplugin=file</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-<code>CCACHE_COMPILERCHECK</code> now also determines how to hash explicit specs files\r
- (<code>-specs=file</code>).\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added <code>CPATH</code>, <code>C_INCLUDE_PATH</code> and similar environment variables to the hash\r
- to avoid false cache hits when such variables have changed.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Corrected log message when unify mode is enabled.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Reverted the GCC bug compatibility introduced in ccache 3.1.5 for <code>-MT</code>/<code>-MQ</code>\r
- options with concatenated arguments. (The bug is fixed in recent GCC\r
- versions.)\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_other_7">Other</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Corrected license header for <code>mdfour.c</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved documentation on how to fix bad object files in the cache.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_1_6">ccache 3.1.6</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2011-08-21</p></div>\r
-<div class="sect2">\r
-<h3 id="_new_features_and_enhancements_12">New features and enhancements</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Rewrite argument to <code>--sysroot</code> if <code>CCACHE_BASEDIR</code> is used.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_38">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Don’t crash if <code>getcwd()</code> fails.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed alignment of “called for preprocessing” counter.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_1_5">ccache 3.1.5</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2011-05-29</p></div>\r
-<div class="sect2">\r
-<h3 id="_new_features_and_enhancements_13">New features and enhancements</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Added a new statistics counter named “called for preprocessing”.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The original command line is now logged to the file specified with\r
- <code>CCACHE_LOGFILE</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved error logging when system calls fail.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for rewriting absolute paths in <code>-F</code>/<code>-iframework</code> GCC\r
- options.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved order of statistics counters in <code>ccache -s</code> output.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_39">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-The <code>-MF</code>/<code>-MT</code>/<code>-MQ</code> options with concatenated argument are now handled\r
- correctly when they are last on the command line.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache is now bug compatible with GCC for the <code>-MT</code>/<code>-MQ</code> options with\r
- concatenated arguments.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed a minor memory leak.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Systems that lack (and don’t need to be linked with) libm are now supported.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_1_4">ccache 3.1.4</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2011-01-09</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_40">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Made a work-around for a bug in <code>gzputc()</code> in zlib 1.2.5.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Corrupt manifest files are now removed so that they won’t block direct mode\r
- hits.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache now copes with file systems that don’t know about symbolic links.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The file handle in now correctly closed on write error when trying to create\r
- a cache dir tag.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_1_3">ccache 3.1.3</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2010-11-28</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_41">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-The -MFarg, -MTarg and -MQarg compiler options (i.e, without space between\r
- option and argument) are now handled correctly.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_other_8">Other</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Portability fixes for HP-UX 11.00 and other esoteric systems.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_1_2">ccache 3.1.2</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2010-11-21</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_42">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Bail out on too hard compiler options <code>-fdump-*</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-NULL return values from malloc/calloc of zero bytes are now handled\r
- correctly.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed issue when parsing precompiler output on AIX.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_other_9">Other</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Improved documentation on which information is included in the hash sum.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Made the “too new header file” test case work on file systems with\r
- unsynchronized clocks.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The test suite now also works on systems that lack a /dev/zero.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_1_1">ccache 3.1.1</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2010-11-07</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_43">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-ccache now falls back to preprocessor mode when a non-regular include file\r
- (device, socket, etc) has been detected so that potential hanging due to\r
- blocking reads is avoided.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-CRC errors are now detected when decompressing compressed files in the cache.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed potential object file corruption race on NFS.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Minor documentation corrections.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed configure detection of ar.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-ccache development version (set by dev.mk) now works with gits whose\r
- <code>describe</code> command doesn’t understand <code>--dirty</code>.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_other_10">Other</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Minor debug log message improvements.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_1">ccache 3.1</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2010-09-16</p></div>\r
-<div class="sect2">\r
-<h3 id="_new_features_and_enhancements_14">New features and enhancements</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Added support for hashing the output of a custom command (e.g. <code>%compiler%\r
- --version</code>) to identify the compiler instead of stat-ing or hashing the\r
- compiler binary. This can improve robustness when the compiler (as seen by\r
- ccache) actually isn’t the real compiler but another compiler wrapper.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for caching compilations that use precompiled headers. (See the\r
- manual for important instructions regarding this.)\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Locking of the files containing statistics counters is now done using\r
- symlinks instead of POSIX locks. This should make ccache behave a lot better\r
- on file systems where POSIX locks are slow or broken (e.g. NFS on some\r
- systems).\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Manifest files are now updated without the need of taking locks.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Updates of statistics counters are now always done in one of the sub-level\r
- statistics files. This reduces lock contention, which especially improves\r
- performance on slow NFS mounts.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Reading and writing of statistics counters has been made forward-compatible\r
- (unknown counters are retained).\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Files are now read without using <code>mmap()</code>. This has two benefits: it’s more\r
- robust against file changes during reading and it improves performance on\r
- poor systems where <code>mmap()</code> doesn’t use the disk cache.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added <code>.cp</code> and <code>.CP</code> as known C++ suffixes.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved logging.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added <code>-install_name</code> as an option known to take an argument. (This improves\r
- statistics when using the Darwin linker.)\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_44">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Non-fatal error messages are now never printed to stderr but logged instead.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed a bug affecting failing commands when <code>--ccache-skip</code> is used.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Made <code>--ccache-skip</code> work for all options.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-EINTR is now handled correctly.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_other_11">Other</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Work on porting ccache to win32 (native), mostly done by Ramiro Polla. The\r
- port is not yet finished, but will hopefully be complete in some subsequent\r
- release.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added a <code>--nostats</code> flag to the performance benchmark program.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Made the performance benchmark program more accurate when measuring cache\r
- hits.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added a new test framework for unit tests written in C.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Got rid of <code>configure-dev</code>; dev mode is now given by <code>dev.mk.in</code> presence.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved documentation on how to combine ccache with other compiler wrappers\r
- (like <code>distcc</code>).\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-New <code>LICENSE.txt</code> file with licensing and copyright details about bundled\r
- source code.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-New <code>AUTHORS.txt</code> file with a list of ccache contributors.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-New <code>HACKING.txt</code> file with some notes about ccache code conventions.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_0_1">ccache 3.0.1</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2010-07-15</p></div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_45">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-The statistics counter “called for link” is now correctly updated when\r
- linking with a single object file.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed a problem with out-of-source builds.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-<div class="sect1">\r
-<h2 id="_ccache_3_0">ccache 3.0</h2>\r
-<div class="sectionbody">\r
-<div class="paragraph"><p>Release date: 2010-06-20</p></div>\r
-<div class="sect2">\r
-<h3 id="_general">General</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-ccache is now licensed under the GNU General Public License (GPL) version 3\r
- or later.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_upgrade_notes">Upgrade notes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-The way the hashes are calculated has changed, so you won’t get cache hits\r
- for compilation results stored by older ccache versions. Because of this, you\r
- might as well clear the old cache directory with <code>ccache --clear</code> if you\r
- want, unless you plan to keep using an older ccache version.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_new_features_and_enhancements_15">New features and enhancements</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-ccache now has a “direct mode” where it computes a hash of the source code\r
- (including all included files) and compiler options without running the\r
- preprocessor. By not running the preprocessor, CPU usage is reduced; the\r
- speed is somewhere between 1 and 5 times that of ccache running in\r
- traditional mode, depending on the circumstances. The speedup will be higher\r
- when I/O is fast (e.g., when files are in the disk cache). The direct mode\r
- can be disabled by setting <code>CCACHE_NODIRECT</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Support has been added for rewriting absolute paths to relative paths when\r
- hashing, in order to increase cache hit rate when building the same source\r
- code in different directories even when compiling with <code>-g</code> and when using\r
- absolute include directory paths. This is done by setting the\r
- <code>CCACHE_BASEDIR</code> environment variable to an absolute path that specifies\r
- which paths to rewrite.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Object files are now optionally stored compressed in the cache. The runtime\r
- cost is negligible, and more files will fit in the ccache directory and in\r
- the disk cache. Set <code>CCACHE_COMPRESS</code> to enable object file compression. Note\r
- that you can’t use compression in combination with the hard link feature.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-A <code>CCACHE_COMPILERCHECK</code> option has been added. This option tells ccache what\r
- compiler-identifying information to hash to ensure that results retrieved\r
- from the cache are accurate. Possible values are: none (don’t hash anything),\r
- mtime (hash the compiler’s mtime and size) and content (hash the content of\r
- the compiler binary). The default is mtime.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-It is now possible to specify extra files whose contents should be included\r
- in the hash sum by setting the <code>CCACHE_EXTRAFILES</code> option.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for Objective-C and Objective-C++. The statistics counter\r
- “not a C/C++ file” has been renamed to “unsupported source language”.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for the <code>-x</code> compiler option.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for long command-line options.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-A <code>CACHEDIR.TAG</code> file is now created in the cache directory. See\r
- <a href="http://www.brynosaurus.com/cachedir/">http://www.brynosaurus.com/cachedir/</a>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Messages printed to the debug log (specified by <code>CCACHE_LOGFILE</code>) have been\r
- improved.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-You can relax some checks that ccache does in direct mode by setting\r
- <code>CCACHE_SLOPPINESS</code>. See the manual for more information.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-<code>CCACHE_TEMPDIR</code> no longer needs to be on the same filesystem as\r
- <code>CCACHE_DIR</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The default value of <code>CCACHE_TEMPDIR</code> has been changed to <code>$CCACHE_DIR/tmp</code>\r
- to avoid cluttering the top directory.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Temporary files that later will be moved into the cache are now created in\r
- the cache directory they will end up in. This makes ccache more friendly to\r
- Linux’s directory layout.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved the test suite and added tests for most of the new functionality.\r
- It’s now also possible to specify a subset of tests to run.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Standard error output from the compiler is now only stored in the cache if\r
- it’s non-empty.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-If the compiler produces no object file or an empty object file, but gives a\r
- zero exit status (could be due to a file system problem, a buggy program\r
- specified by <code>CCACHE_PREFIX</code>, etc.), ccache copes with it properly.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added <code>installcheck</code> and <code>distcheck</code> make targets.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Clarified cache size limit options’ and cleanup semantics.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved display of cache max size values.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The following options are no longer hashed in the preprocessor mode:\r
- <code>-imacros</code>, <code>-imultilib</code>, <code>-iprefix</code>, <code>-iquote</code>, <code>-isysroot</code>, <code>-iwithprefix</code>,\r
- <code>-iwithprefixbefore</code>, <code>-nostdinc</code>, <code>-nostdinc++</code> and <code>-U</code>.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-<div class="sect2">\r
-<h3 id="_bug_fixes_46">Bug fixes</h3>\r
-<div class="ulist"><ul>\r
-<li>\r
-<p>\r
-Various portability improvements.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved detection of home directory.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-User-defined <code>CPPFLAGS</code> and <code>LDFLAGS</code> are now respected in the Makefile.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed NFS issues.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Computation of the hash sum has been improved to decrease the risk of hash\r
- collisions. For instance, the compiler options <code>-X -Y</code> and <code>-X-Y</code> previously\r
- contributed equally to the hash sum.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Bail out on too hard compiler options <code>--coverage</code>, <code>-fprofile-arcs</code>,\r
- <code>-fprofile-generate</code>, <code>-fprofile-use</code>, <code>-frepo</code>, <code>-ftest-coverage</code> and\r
- <code>-save-temps</code>. Also bail out on <code>@file</code> style options.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Errors when using multiple <code>-arch</code> compiler options are now noted as\r
- “unsupported compiler option”.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-<code>-MD</code>/<code>-MMD</code> options without <code>-MT</code>/<code>-MF</code> are now handled correctly.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The <code>-finput-charset</code> option is now handled correctly.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Added support for <code>-Wp,-MD</code> and <code>-Wp,-MMD</code> options.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The compiler options <code>-Xassembler</code>, <code>-b</code>, <code>-G</code> and <code>-V</code> are now correctly\r
- recognized as taking an argument.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Debug information containing line numbers of predefined and command-line\r
- macros (enabled with the compiler option <code>-g3</code>) will now be correct.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Corrected LRU cleanup handling of object files.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-<code>utimes()</code> is now used instead of <code>utime()</code> when available.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Non-writable cache directories are now handled gracefully.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Corrected documentation about sharing the cache directory.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed compilation warnings from GCC 4.3.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The command specified by <code>CCACHE_PREFIX</code> is no longer part of the hash.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed bad memory access spotted by Valgrind.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Fixed a bug in <code>x_realloc</code>.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Freed memory is no longer referenced when compiling a <code>.i</code>/<code>.ii</code> file and\r
- falling back to running the real compiler.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The test suite is now immune to external values of the <code>CCACHE_*</code> environment\r
- variables.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Improved detection of recursive invocation.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-The ccache binary is now not unconditionally stripped when installing.\r
-</p>\r
-</li>\r
-<li>\r
-<p>\r
-Statistics counters are now correctly updated for -E option failures and\r
- internal errors.\r
-</p>\r
-</li>\r
-</ul></div>\r
-</div>\r
-</div>\r
-</div>\r
-</div>\r
-<div id="footnotes"><hr /></div>\r
-<div id="footer">\r
-<div id="footer-text">\r
-Version 3.7.12<br />\r
-Last updated\r
- 2020-10-01 14:20:15 CEST\r
-</div>\r
-</div>\r
-</body>\r
-</html>\r
+++ /dev/null
-'\" t
-.\" Title: ccache
-.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\" 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<MD4>\-<size>\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<ccachedir>\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<sysconfdir>\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<cache_dir>/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<objectfile>\&.ccache\-input\-c\fR
-T}:T{
-.sp
-Binary input hashed by both the direct mode and the preprocessor mode\&.
-T}
-T{
-.sp
-\fB<objectfile>\&.ccache\-input\-d\fR
-T}:T{
-.sp
-Binary input only hashed by the direct mode\&.
-T}
-T{
-.sp
-\fB<objectfile>\&.ccache\-input\-p\fR
-T}:T{
-.sp
-Binary input only hashed by the preprocessor mode\&.
-T}
-T{
-.sp
-\fB<objectfile>\&.ccache\-input\-text\fR
-T}:T{
-.sp
-Human\-readable combined diffable text version of the three files above\&.
-T}
-T{
-.sp
-\fB<objectfile>\&.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<objectfile>\&.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<objectfile>\&.ccache\-input\-text\fR
-for the two builds\&. This together with the
-\fB<objectfile>\&.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\&.
--- /dev/null
+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.
--- /dev/null
+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
--- /dev/null
+# 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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+FROM fedora:32
+
+RUN dnf install -y \
+ autoconf \
+ bash \
+ ccache \
+ clang \
+ cmake \
+ diffutils \
+ elfutils \
+ findutils \
+ gcc \
+ gcc-c++ \
+ libzstd-devel \
+ make \
+ && dnf clean all
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
+++ /dev/null
-#! /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
+++ /dev/null
-# _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
-
+++ /dev/null
-dnl ===========================================================================
-dnl Feature macro stuff borrowed from Python's configure.in
-dnl
-dnl For license information, see
-dnl <http://www.python.org/download/releases/2.6.2/license/>.
-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
+++ /dev/null
-# $Id: snprintf.m4,v 1.1.1.1 2008/01/06 03:24:00 holger Exp $
-
-# Copyright (c) 2008 Holger Weiss <holger@jhweiss.de>.
-#
-# 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 <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.
-
-# HW_HEADER_STDARG_H
-# ------------------
-# Define HAVE_STDARG_H to 1 if <stdarg.h> 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 <varargs.h> 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 <stdarg.h>
- #elif HAVE_VARARGS_H
- #include <varargs.h>
- #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 <stdarg.h>
- #elif HAVE_VARARGS_H
- #include <varargs.h>
- #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 <stdarg.h>
- #endif
- #include <stdio.h>
- 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 <locale.h>])
-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 <stdio.h>]],
- [[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:
--- /dev/null
+#!/bin/bash
+#
+# This script runs ci/build in a Docker container.
+
+if [ $# -eq 0 ] || [ ${1:-} = -h ] || [ ${1:-} = --help ]; then
+ cat <<EOF
+Usage: build-in-docker NAME [ARGUMENTS]
+
+NAME: Subdirectory name in the dockerfiles directory.
+ARGUMENTS: Arguments that will be passed to ci/build.
+EOF
+ exit 1
+fi
+
+set -eu
+
+if [ -n "${VERBOSE:-}" ]; then
+ set -x
+fi
+
+name=${1}
+shift
+
+tag="ccache-build:$name"
+command="${COMMAND:-/source/ci/build}"
+interactive="${INTERACTIVE:+--interactive --tty}"
+
+# Build (if not exists):
+docker build -t "$tag" "dockerfiles/$name"
+
+# Cache compilation across docker sessions
+mkdir -p build-in-docker
+mkdir -p build-in-docker/docker-ccache
+
+docker run \
+ --rm \
+ --volume "$PWD:/source" \
+ --volume "$PWD/build-in-docker/docker-ccache:/ccache" \
+ --tmpfs /builddir:rw,exec \
+ --workdir /builddir \
+ --env ASAN_OPTIONS="${ASAN_OPTIONS:-}" \
+ --env EXTRA_CMAKE_BUILD_FLAGS="${EXTRA_CMAKE_BUILD_FLAGS:-}" \
+ --env CC="${CC:-}" \
+ --env CCACHE_DIR=/ccache \
+ --env CCACHE_LOC="/source" \
+ --env CFLAGS="${CFLAGS:-}" \
+ --env CMAKE_PARAMS="${CMAKE_PARAMS:-}" \
+ --env CXX="${CXX:-}" \
+ --env CXXFLAGS="${CXXFLAGS:-}" \
+ --env LDFLAGS="${LDFLAGS:-}" \
+ --env NO_TEST="${NO_TEST:-}" \
+ --env SCAN_BUILD="${SCAN_BUILD:-}" \
+ --env SPECIAL="${SPECIAL:-}" \
+ --env VERBOSE="${VERBOSE:-}" \
+ $interactive \
+ "$tag" \
+ "$command" "$@"
--- /dev/null
+copyable
+creat
+files'
+pase
+seve
+stoll
+te
+thi
--- /dev/null
+#!/usr/bin/env python3
+
+import json
+import sys
+
+traces = {}
+for arg in sys.argv[1:]:
+ events = json.load(open(arg))["traceEvents"]
+ for event in events:
+ if event["name"] == "" and event["ph"] == "I":
+ time = float(event["args"]["time"])
+ # print "%.6f" % time
+ traces[time] = events
+ break
+
+times = sorted(traces)
+min_time = min(times)
+
+combined_events = []
+for time in times:
+ offset = (time - min_time) * 1000000.0
+ events = traces[time]
+ for event in events:
+ event["ts"] = int(event["ts"] + offset)
+ combined_events.append(event)
+
+print(json.dumps({"traceEvents": combined_events}))
--- /dev/null
+ConfigurationNotChecked
+missingIncludeSystem
+
+// Ignore third party code.
+*:src/third_party
--- /dev/null
+#!/bin/sh
+
+set -eu
+
+tmp_file=$(mktemp)
+trap "rm $tmp_file" EXIT
+
+all=
+check=
+
+for arg in "$@"; do
+ case $arg in
+ --all)
+ all=$arg
+ ;;
+ --check)
+ check=$arg
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+done
+
+if [ -n "$all" ]; then
+ exec sh "$0" $check $(git ls-files '*.[ch]' '*.[ch]pp' ':!:src/third_party')
+fi
+
+clang_format=${CLANG_FORMAT:-clang-format}
+[ -t 1 ] && cf_color="--color=always" || cf_color=""
+
+if [ -n "${VERBOSE:-}" ]; then
+ "$clang_format" --version
+fi
+
+status=0
+for file in "$@"; do
+ "$clang_format" "$file" >"$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
--- /dev/null
+#! /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> [compiler options] <source code file>"""
+
+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('<?xml version="1.0" encoding="UTF-8"?>')
+ print("<ccache-perf>")
+ for i, x in enumerate(PHASES):
+ print("<measurement>")
+ print("<name>%s</name>" % x.capitalize())
+ print("<seconds>%.4f</seconds>" % results[i])
+ print("<percent>%.4f</percent>" % (100 * (results[i] / results[0])))
+ print("<times>%.4f</times>" % (results[0] / results[i]))
+ print("</measurement>")
+ print("</ccache-perf>")
+
+
+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)
--- /dev/null
+:programname, isequal, "ccache" /var/log/ccache
+& ~
--- /dev/null
+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.
--- /dev/null
+#!/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)
--- /dev/null
+#!/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
--- /dev/null
+#!/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/^\*.*/<STDIN> . "\n"/es' doc/AUTHORS.adoc
+fi
--- /dev/null
+# 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
+...
--- /dev/null
+// 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>
+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<const char*>
+Args::to_argv() const
+{
+ std::vector<const char*> 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);
+ }
+}
--- /dev/null
+// 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 <deque>
+#include <string>
+
+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<Args> 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<char* const*>(args.to_argv().data())` to get an array
+ // suitable to pass to e.g. execv(2).
+ std::vector<const char*> 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<std::string> 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];
+}
--- /dev/null
+// 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 <string>
+#include <vector>
+
+// 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<std::string> sanitize_blacklists;
+
+ // Architectures from -arch options.
+ std::vector<std::string> arch_args;
+
+ // Relocating debuginfo in the format old=new.
+ std::vector<std::string> debug_prefix_maps;
+
+ // Argument list to add to compiler invocation in depend mode.
+ Args depend_extra_args;
+};
--- /dev/null
+// 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<uint8_t>& 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);
+}
--- /dev/null
+// 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 <string>
+#include <vector>
+
+// 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<uint8_t>& 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;
+}
--- /dev/null
+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)
--- /dev/null
+// 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();
+}
--- /dev/null
+// 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 <memory>
+
+// 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<typename T> 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<Decompressor> 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<typename T>
+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;
+}
--- /dev/null
+// 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<uint8_t>(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();
+}
--- /dev/null
+// 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 <memory>
+
+// 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<typename T> 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<Compressor> m_compressor;
+ Checksum m_checksum;
+};
+
+template<typename T>
+inline void
+CacheEntryWriter::write(T value)
+{
+ uint8_t buffer[sizeof(T)];
+ Util::int_to_big_endian(value, buffer);
+ write(buffer, sizeof(T));
+}
--- /dev/null
+// 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;
+ }
+}
--- /dev/null
+// 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 <string>
+
+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<Stat> m_stat;
+};
+
+inline CacheFile::CacheFile(const std::string& path) : m_path(path)
+{
+}
+
+inline const std::string&
+CacheFile::path() const
+{
+ return m_path;
+}
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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<uint8_t>(Type::none):
+ return Type::none;
+
+ case static_cast<uint8_t>(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
--- /dev/null
+// 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 <string>
+
+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
--- /dev/null
+// 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>
+Compressor::create_from_type(Compression::Type type,
+ FILE* stream,
+ int8_t compression_level)
+{
+ switch (type) {
+ case Compression::Type::none:
+ return std::make_unique<NullCompressor>(stream);
+
+ case Compression::Type::zstd:
+ return std::make_unique<ZstdCompressor>(stream, compression_level);
+ }
+
+ ASSERT(false);
+}
--- /dev/null
+// 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 <memory>
+
+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<Compressor> 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<typename T> 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;
+};
--- /dev/null
+// 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 <algorithm>
+#include <cassert>
+#include <fstream>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+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<std::string, ConfigItem> 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<std::string, std::string> 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<std::string> 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<uint32_t>::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<uint32_t>::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<void(
+ const std::string& line, const std::string& key, const std::string& value)>;
+
+// 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<std::string> 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<std::string>& 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";
+}
--- /dev/null
+// 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 <functional>
+#include <limits>
+#include <string>
+#include <unordered_map>
+
+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<void(const std::string& key,
+ const std::string& value,
+ const std::string& origin)>;
+
+ // 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<uint32_t>::max(); // Don't set umask
+
+ bool m_temporary_dir_configured_explicitly = false;
+
+ std::unordered_map<std::string /*key*/, std::string /*origin*/> m_origins;
+
+ void set_item(const std::string& key,
+ const std::string& value,
+ const nonstd::optional<std::string>& 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;
+}
--- /dev/null
+// 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 <algorithm>
+#include <string>
+#include <vector>
+
+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<std::string>& 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;
+ }
+ }
+}
--- /dev/null
+// 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 <string>
+#include <unordered_map>
+#include <vector>
+
+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<Digest>& manifest_name() const;
+
+ // Full path to the file containing the manifest (cachedir/a/b/cdef[...]M), if
+ // any.
+ const nonstd::optional<std::string>& manifest_path() const;
+
+ // Name (represented as a hash) of the file containing the cached result.
+ const nonstd::optional<Digest>& result_name() const;
+
+ // Full path to the file containing the result (cachedir/a/b/cdef[...]R).
+ const nonstd::optional<std::string>& 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<std::string, Digest> 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<std::string> 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<File> hash_debug_files;
+
+ // Options to ignore for the hash.
+ const std::vector<std::string>& ignore_options() const;
+ void set_ignore_options(const std::vector<std::string>& options);
+
+ // Original umask before applying the `umask`/`CCACHE_UMASK` configuration, or
+ // `nullopt` if there is no such configuration.
+ nonstd::optional<mode_t> original_umask;
+
+#ifdef MTR_ENABLED
+ // Internal tracing.
+ std::unique_ptr<MiniTrace> 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<Digest> m_manifest_name;
+ nonstd::optional<std::string> m_manifest_path;
+
+ nonstd::optional<Digest> m_result_name;
+ nonstd::optional<std::string> m_result_path;
+
+ // Options to ignore for the hash.
+ std::vector<std::string> m_ignore_options;
+
+ // [Start of variables touched by the signal handler]
+
+ // Temporary files to remove at program exit.
+ std::vector<std::string> 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<Digest>&
+Context::manifest_name() const
+{
+ return m_manifest_name;
+}
+
+inline const nonstd::optional<std::string>&
+Context::manifest_path() const
+{
+ return m_manifest_path;
+}
+
+inline const nonstd::optional<Digest>&
+Context::result_name() const
+{
+ return m_result_name;
+}
+
+inline const nonstd::optional<std::string>&
+Context::result_path() const
+{
+ return m_result_path;
+}
+
+inline const std::vector<std::string>&
+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;
+}
--- /dev/null
+// 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 <algorithm>
+
+Counters::Counters() : m_counters(static_cast<size_t>(Statistic::END))
+{
+}
+
+uint64_t
+Counters::get(Statistic statistic) const
+{
+ const auto index = static_cast<size_t>(statistic);
+ ASSERT(index < static_cast<size_t>(Statistic::END));
+ return index < m_counters.size() ? m_counters[index] : 0;
+}
+
+void
+Counters::set(Statistic statistic, uint64_t value)
+{
+ const auto index = static_cast<size_t>(statistic);
+ ASSERT(index < static_cast<size_t>(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<size_t>(statistic);
+ if (i >= m_counters.size()) {
+ m_counters.resize(i + 1);
+ }
+ auto& counter = m_counters[i];
+ counter =
+ std::max(static_cast<int64_t>(0), static_cast<int64_t>(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<int64_t>(0),
+ static_cast<int64_t>(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; });
+}
--- /dev/null
+// 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 <vector>
+
+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<uint64_t> m_counters;
+};
--- /dev/null
+// 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>
+Decompressor::create_from_type(Compression::Type type, FILE* stream)
+{
+ switch (type) {
+ case Compression::Type::none:
+ return std::make_unique<NullDecompressor>(stream);
+
+ case Compression::Type::zstd:
+ return std::make_unique<ZstdDecompressor>(stream);
+ }
+
+ ASSERT(false);
+}
--- /dev/null
+// 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 <memory>
+
+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<Decompressor> 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;
+};
--- /dev/null
+// 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 <string>
+
+// 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);
+}
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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 <string>
+
+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;
+}
--- /dev/null
+// 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 <functional>
+
+class Finalizer
+{
+public:
+ Finalizer(std::function<void()> finalizer);
+ ~Finalizer();
+
+private:
+ std::function<void()> m_finalizer;
+};
+
+inline Finalizer::Finalizer(std::function<void()> finalizer)
+ : m_finalizer(finalizer)
+{
+}
+
+inline Finalizer::~Finalizer()
+{
+ m_finalizer();
+}
--- /dev/null
+// 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<nonstd::string_view>
+{
+ template<typename ParseContext>
+ constexpr auto
+ parse(ParseContext& ctx) const -> decltype(ctx.begin())
+ {
+ return ctx.begin();
+ }
+
+ template<typename FormatContext>
+ 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
--- /dev/null
+// 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<const char*>(data), size);
+ hash_buffer(buffer);
+
+ switch (hash_type) {
+ case HashType::binary:
+ add_debug_text(
+ Util::format_base16(static_cast<const uint8_t*>(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<const char*>(&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);
+ }
+}
--- /dev/null
+// 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);
+};
--- /dev/null
+// 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 <atomic>
+#include <libgen.h>
+#include <sys/mman.h>
+#include <type_traits>
+
+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<int>(InodeCache::ContentType::binary) == 0,
+ "Numeric value is part of key, increment version number if changed.");
+static_assert(
+ static_cast<int>(InodeCache::ContentType::code) == 1,
+ "Numeric value is part of key, increment version number if changed.");
+static_assert(
+ static_cast<int>(InodeCache::ContentType::code_with_sloppy_time_macros) == 2,
+ "Numeric value is part of key, increment version number if changed.");
+static_assert(
+ static_cast<int>(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<int64_t> hits;
+ std::atomic<int64_t> misses;
+ std::atomic<int64_t> 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<SharedRegion*>(mmap(
+ nullptr, sizeof(SharedRegion), PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0));
+ fd.close();
+ if (sr == reinterpret_cast<void*>(-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<SharedRegion*>(mmap(nullptr,
+ sizeof(SharedRegion),
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ *tmp_file.fd,
+ 0));
+ if (sr == reinterpret_cast<void*>(-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;
+}
--- /dev/null
+// 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 <string>
+
+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;
+};
--- /dev/null
+// 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 <algorithm>
+#include <sstream>
+#include <thread>
+
+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
+ }
+}
--- /dev/null
+// 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 <string>
+
+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
+}
--- /dev/null
+// 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 <syslog.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#ifdef __linux__
+# ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+# endif
+#endif
+
+#ifdef _WIN32
+# include <psapi.h>
+# include <sys/locking.h>
+# include <tchar.h>
+#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<int>(tv.tv_usec),
+ static_cast<int>(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<int>(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
--- /dev/null
+// 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 <string>
+#include <utility>
+
+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<typename... T>
+inline void
+log(T&&... args)
+{
+ if (!enabled()) {
+ return;
+ }
+ log(nonstd::string_view(fmt::format(std::forward<T>(args)...)));
+}
+
+// Log a message (plus a newline character) without flushing and with a reused
+// timestamp. `args` are forwarded to `fmt::format`.
+template<typename... T>
+inline void
+bulk_log(T&&... args)
+{
+ if (!enabled()) {
+ return;
+ }
+ bulk_log(nonstd::string_view(fmt::format(std::forward<T>(args)...)));
+}
+
+} // namespace Logging
--- /dev/null
+// 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.
+//
+// <manifest> ::= <header> <body> <epilogue
+// <header> ::= <magic> <version> <compr_type> <compr_level>
+// <content_len>
+// <magic> ::= 4 bytes ("cCrS")
+// <version> ::= uint8_t
+// <compr_type> ::= <compr_none> | <compr_zstd>
+// <compr_none> ::= 0 (uint8_t)
+// <compr_zstd> ::= 1 (uint8_t)
+// <compr_level> ::= int8_t
+// <content_len> ::= uint64_t ; size of file if stored uncompressed
+// <body> ::= <paths> <includes> <results> ; body is potentially
+// ; compressed
+// <paths> ::= <n_paths> <path_entry>*
+// <n_paths> ::= uint32_t
+// <path_entry> ::= <path_len> <path>
+// <path_len> ::= uint16_t
+// <path> ::= path_len bytes
+// <includes> ::= <n_includes> <include_entry>*
+// <n_includes> ::= uint32_t
+// <include_entry> ::= <path_index> <digest> <fsize> <mtime> <ctime>
+// <path_index> ::= uint32_t
+// <digest> ::= Digest::size() bytes
+// <fsize> ::= uint64_t ; file size
+// <mtime> ::= int64_t ; modification time
+// <ctime> ::= int64_t ; status change time
+// <results> ::= <n_results> <result>*
+// <n_results> ::= uint32_t
+// <result> ::= <n_indexes> <include_index>* <name>
+// <n_indexes> ::= uint32_t
+// <include_index> ::= uint32_t
+// <name> ::= Digest::size() bytes
+// <epilogue> ::= <checksum>
+// <checksum> ::= uint64_t ; XXH3 of content bytes
+//
+// Sketch of concrete layout:
+
+// <magic> 4 bytes
+// <version> 1 byte
+// <compr_type> 1 byte
+// <compr_level> 1 byte
+// <content_len> 8 bytes
+// --- [potentially compressed from here] -------------------------------------
+// <n_paths> 4 bytes
+// <path_len> 2 bytes
+// <path> path_len bytes
+// ...
+// ----------------------------------------------------------------------------
+// <n_includes> 4 bytes
+// <path_index> 4 bytes
+// <digest> Digest::size() bytes
+// <fsize> 8 bytes
+// <mtime> 8 bytes
+// <ctime> 8 bytes
+// ...
+// ----------------------------------------------------------------------------
+// <n_results> 4 bytes
+// <n_indexes> 4 bytes
+// <include_index> 4 bytes
+// ...
+// <name> 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<FileInfo>
+{
+ 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<uint32_t> file_info_indexes;
+
+ // Name of the result.
+ Digest name;
+};
+
+struct ManifestData
+{
+ // Referenced include files.
+ std::vector<std::string> files;
+
+ // Information about referenced include files.
+ std::vector<FileInfo> file_infos;
+
+ // Result names plus references to include file infos.
+ std::vector<ResultEntry> results;
+
+ void
+ add_result_entry(
+ const Digest& result_digest,
+ const std::unordered_map<std::string, Digest>& included_files,
+ time_t time_of_compilation,
+ bool save_timestamp)
+ {
+ std::unordered_map<std::string, uint32_t /*index*/> mf_files;
+ for (uint32_t i = 0; i < files.size(); ++i) {
+ mf_files.emplace(files[i], i);
+ }
+
+ std::unordered_map<FileInfo, uint32_t /*index*/> mf_file_infos;
+ for (uint32_t i = 0; i < file_infos.size(); ++i) {
+ mf_file_infos.emplace(file_infos[i], i);
+ }
+
+ std::vector<uint32_t> 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<std::string, uint32_t>& mf_files,
+ const std::unordered_map<FileInfo, uint32_t>& 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<ManifestData>
+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<ManifestData>();
+
+ 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<uint32_t>(mf.files.size());
+ for (const auto& file : mf.files) {
+ writer.write<uint16_t>(file.length());
+ writer.write(file.data(), file.length());
+ }
+
+ writer.write<uint32_t>(mf.file_infos.size());
+ for (const auto& file_info : mf.file_infos) {
+ writer.write<uint32_t>(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<uint32_t>(mf.results.size());
+ for (const auto& result : mf.results) {
+ writer.write<uint32_t>(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<std::string, FileStats>& stated_files,
+ std::unordered_map<std::string, Digest>& 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<Digest>
+get(const Context& ctx, const std::string& path)
+{
+ std::unique_ptr<ManifestData> 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<std::string, FileStats> stated_files;
+ std::unordered_map<std::string, Digest> 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<std::string, Digest>& 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<ManifestData> mf;
+ try {
+ mf = read_manifest(path);
+ if (!mf) {
+ // Manifest file didn't exist.
+ mf = std::make_unique<ManifestData>();
+ }
+ } catch (const Error& e) {
+ log("Error: {}", e.what());
+ // Manifest file was corrupt, ignore.
+ mf = std::make_unique<ManifestData>();
+ }
+
+ 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<ManifestData>();
+ } 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<ManifestData>();
+ }
+
+ 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<ManifestData> 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
--- /dev/null
+// 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 <string>
+#include <unordered_map>
+
+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<Digest> 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<std::string, Digest>& included_files,
+ time_t time_of_compilation,
+ bool save_timestamp);
+bool dump(const std::string& path, FILE* stream);
+
+} // namespace Manifest
--- /dev/null
+// 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 <sys/time.h>
+# 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<void*>(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
--- /dev/null
+// 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/minitrace.h"
+
+#ifdef MTR_ENABLED
+
+# include <string>
+
+struct ArgsInfo;
+
+class MiniTrace
+{
+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
--- /dev/null
+// 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;
+};
--- /dev/null
+// 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");
+ }
+}
--- /dev/null
+// 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;
+};
--- /dev/null
+// 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");
+ }
+}
--- /dev/null
+// 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;
+};
--- /dev/null
+// 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 <sys/ioctl.h>
+#endif
+
+#ifdef __sun
+# include <termios.h>
+#endif
+
+#include <algorithm>
+
+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<int16_t>(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);
+}
--- /dev/null
+// 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 <string>
+
+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)
+};
--- /dev/null
+// 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 <algorithm>
+
+// Result data format
+// ==================
+//
+// Integers are big-endian.
+//
+// <result> ::= <header> <body> <epilogue>
+// <header> ::= <magic> <version> <compr_type> <compr_level>
+// <content_len>
+// <magic> ::= 4 bytes ("cCrS")
+// <version> ::= uint8_t
+// <compr_type> ::= <compr_none> | <compr_zstd>
+// <compr_none> ::= 0 (uint8_t)
+// <compr_zstd> ::= 1 (uint8_t)
+// <compr_level> ::= int8_t
+// <content_len> ::= uint64_t ; size of file if stored uncompressed
+// <body> ::= <n_entries> <entry>* ; potentially compressed
+// <n_entries> ::= uint8_t
+// <entry> ::= <embedded_file_entry> | <raw_file_entry>
+// <embedded_file_entry> ::= <embedded_file_marker> <suffix_len> <suffix>
+// <data_len> <data>
+// <embedded_file_marker> ::= 0 (uint8_t)
+// <embedded_file_type> ::= uint8_t
+// <data_len> ::= uint64_t
+// <data> ::= data_len bytes
+// <raw_file_entry> ::= <raw_file_marker> <suffix_len> <suffix> <file_len>
+// <raw_file_marker> ::= 1 (uint8_t)
+// <file_len> ::= uint64_t
+// <epilogue> ::= <checksum>
+// <checksum> ::= uint64_t ; XXH3 of content bytes
+//
+// Sketch of concrete layout:
+//
+// <magic> 4 bytes
+// <version> 1 byte
+// <compr_type> 1 byte
+// <compr_level> 1 byte
+// <content_len> 8 bytes
+// --- [potentially compressed from here] -------------------------------------
+// <n_entries> 1 byte
+// <embedded_file_marker> 1 byte
+// <embedded_file_type> 1 byte
+// <data_len> 8 bytes
+// <data> data_len bytes
+// ...
+// <ref_marker> 1 byte
+// <key_len> 1 byte
+// <key> 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 = "<unknown 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 "<stderr>";
+
+ 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<std::string>
+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<std::string>
+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<uint8_t>(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<uint8_t>(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<uint64_t>(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
--- /dev/null
+// 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 <map>
+#include <string>
+#include <vector>
+
+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<std::string> 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<std::string> 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<std::string> finalize();
+
+private:
+ Context& m_ctx;
+ const std::string m_result_path;
+ std::vector<std::pair<FileType, std::string>> 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
--- /dev/null
+// 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<std::string> 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()
+{
+}
--- /dev/null
+// 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<std::string> raw_file) override;
+ void on_entry_data(const uint8_t* data, size_t size) override;
+ void on_entry_end() override;
+
+private:
+ FILE* m_stream;
+};
--- /dev/null
+// 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<std::string> 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();
+ }
+}
--- /dev/null
+// 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<std::string> 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;
+};
--- /dev/null
+// 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<std::string> 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<const char*>(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());
+ }
+}
--- /dev/null
+// 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<std::string> 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();
+};
--- /dev/null
+// 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();
+}
--- /dev/null
+// 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();
+};
--- /dev/null
+// 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));
+ }
+}
--- /dev/null
+// 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 <string>
+
+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
--- /dev/null
+// 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<void(const std::string& path)> 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<Counters, time_t>
+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<Counters>
+update(const std::string& path,
+ std::function<void(Counters& counters)> 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<std::string>
+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
--- /dev/null
+// 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 <functional>
+#include <string>
+
+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<Counters> update(const std::string& path,
+ std::function<void(Counters& counters)>);
+
+// Return a human-readable string representing the final ccache result, or
+// nullopt if there was no result.
+nonstd::optional<std::string> 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
--- /dev/null
+// 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
+
+namespace std {
+
+#if __cplusplus < 201402L
+template<typename T, typename... TArgs>
+inline unique_ptr<T>
+make_unique(TArgs&&... args)
+{
+ return unique_ptr<T>(new T(std::forward<TArgs>(args)...));
+}
+#endif
+
+} // namespace std
--- /dev/null
+// 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
+}
--- /dev/null
+// 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 <string>
+
+// 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;
+};
--- /dev/null
+// 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<void()> function)
+{
+ {
+ std::unique_lock<std::mutex> 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<std::mutex> 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<void()> task;
+
+ {
+ std::unique_lock<std::mutex> 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();
+ }
+}
--- /dev/null
+// 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 <condition_variable>
+#include <functional>
+#include <limits>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+class ThreadPool
+{
+public:
+ explicit ThreadPool(
+ size_t number_of_threads,
+ size_t task_queue_max_size = std::numeric_limits<size_t>::max());
+ ~ThreadPool();
+
+ void enqueue(std::function<void()> function);
+ void shut_down();
+
+private:
+ std::vector<std::thread> m_worker_threads;
+ std::queue<std::function<void()>> 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();
+};
--- /dev/null
+// 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<mode_t> new_umask);
+ ~UmaskScope();
+
+private:
+ nonstd::optional<mode_t> m_saved_umask;
+};
+
+UmaskScope::UmaskScope(nonstd::optional<mode_t> 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
+}
--- /dev/null
+// 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 <algorithm>
+#include <fstream>
+
+#ifndef HAVE_DIRENT_H
+# include <filesystem>
+#endif
+
+#ifdef HAVE_PWD_H
+# include <pwd.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#ifdef HAVE_LINUX_FS_H
+# include <linux/magic.h>
+# include <sys/statfs.h>
+#elif defined(HAVE_STRUCT_STATFS_F_FSTYPENAME)
+# include <sys/mount.h>
+# include <sys/param.h>
+#endif
+
+#ifdef _WIN32
+# include "Win32Util.hpp"
+#endif
+
+#ifdef __linux__
+# ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+# endif
+# ifdef HAVE_LINUX_FS_H
+# include <linux/fs.h>
+# ifndef FICLONE
+# define FICLONE _IOW(0x94, 9, int)
+# endif
+# define FILE_CLONING_SUPPORTED 1
+# endif
+#endif
+
+#ifdef __APPLE__
+# ifdef HAVE_SYS_CLONEFILE_H
+# include <sys/clonefile.h>
+# 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<typename T>
+std::vector<T>
+split_at(string_view input, const char* separators)
+{
+ ASSERT(separators != nullptr && separators[0] != '\0');
+
+ std::vector<T> 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 <path> to <absolute path> in the following two cases, where X may
+ // be optional ANSI CSI sequences:
+ //
+ // In file included from X<path>X:1:
+ // X<path>X: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<std::shared_ptr<CacheFile>>& 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<CacheFile>(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<tm>
+localtime(optional<time_t> 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<std::string> 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<int64_t> min_value,
+ optional<int64_t> 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<uint64_t>(result);
+}
+
+uint64_t
+parse_unsigned(const std::string& value,
+ optional<uint64_t> min_value,
+ optional<uint64_t> 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<size_t>(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<char[]> 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<char[]> 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<string_view>
+split_into_views(string_view input, const char* separators)
+{
+ return split_at<string_view>(input, separators);
+}
+
+std::vector<std::string>
+split_into_strings(string_view input, const char* separators)
+{
+ return split_at<std::string>(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<const uint8_t*>(data) + written, size - written);
+ if (count == -1) {
+ if (errno != EAGAIN && errno != EINTR) {
+ throw Error(strerror(errno));
+ }
+ } else {
+ written += count;
+ }
+ } while (static_cast<size_t>(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
--- /dev/null
+// 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 <algorithm>
+#include <functional>
+#include <ios>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+class Context;
+
+namespace Util {
+
+using DataReceiver = std::function<void(const void* data, size_t size)>;
+using ProgressReceiver = std::function<void(double progress)>;
+using SubdirVisitor = std::function<void(
+ const std::string& dir_path, const ProgressReceiver& progress_receiver)>;
+using TraverseVisitor =
+ std::function<void(const std::string& path, bool is_dir)>;
+
+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<typename T>
+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<typename T>
+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<std::shared_ptr<CacheFile>>& 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<typename T>
+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<tm> localtime(nonstd::optional<time_t> 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<int64_t> min_value = nonstd::nullopt,
+ nonstd::optional<int64_t> 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<uint64_t> min_value = nonstd::nullopt,
+ nonstd::optional<uint64_t> 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<int64_t>(new_stat.size_on_disk())
+ - static_cast<int64_t>(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<nonstd::string_view> split_into_views(nonstd::string_view input,
+ const char* separators);
+
+// Same as `split_into_views` but the words are copied from `input`.
+std::vector<std::string> 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
--- /dev/null
+// 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 <chrono>
+#include <thread>
+
+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<LPSTR>(&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<sc::seconds>(d);
+ tp->tv_sec = static_cast<long>(s.count());
+ tp->tv_usec =
+ static_cast<long>(sc::duration_cast<sc::microseconds>(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<char*>(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
--- /dev/null
+// 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 <string>
+
+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
--- /dev/null
+// 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 <algorithm>
+
+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<int>(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);
+}
--- /dev/null
+// 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 <zstd.h>
+
+// 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;
+};
--- /dev/null
+// 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<uint8_t*>(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");
+ }
+}
--- /dev/null
+// 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 <fstream>
+#include <zstd.h>
+
+// 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;
+};
--- /dev/null
+// 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 <cassert>
+
+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<std::string> 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<Statistic>
+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-<version>) 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 <path> 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 <http://gcc.gnu.org/onlinedocs/cpp/Environment-Variables.html>.
+ // 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};
+}
--- /dev/null
+// 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<Statistic> 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);
+++ /dev/null
-// 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;
-}
--- /dev/null
+// 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();
+}
--- /dev/null
+// 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);
+++ /dev/null
-// 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 <getopt.h>
-#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 (<MD4>-<size>) 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 <https://ccache.dev>.\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 (<cache_dir>/<x>/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 <built-in> or <command-line>.
- 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 \"<command-line>\"\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 \"<command-line>\" 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 <http://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html>:
- //
- // "[...] 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-<version>) 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 <http://gcc.gnu.org/onlinedocs/cpp/Environment-Variables.html>.
- // 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);
-}
--- /dev/null
+// 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 <getopt.h>
+#elif defined(_WIN32)
+# include "third_party/win32/getopt.h"
+#else
+# include "third_party/getopt_long.h"
+#endif
+
+#ifdef _WIN32
+# include "Win32Util.hpp"
+#endif
+
+#include <algorithm>
+#include <cmath>
+#include <limits>
+
+#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 <https://ccache.dev/credits.html> 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 <https://ccache.dev>.
+)";
+
+// 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 <built-in> or <command-line>.
+ 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 \"<command-line>\"\n";
+ static const string_view hash_32_command_line_2_newline =
+ "# 32 \"<command-line>\" 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<std::string>
+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<Digest>
+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
+ // <https://bugs.llvm.org/show_bug.cgi?id=39782>.
+ 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 <http://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html>:
+ //
+ // "[...] 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 <https://gcc.gnu.org/onlinedocs/gcc/Environment-Variables.html>:
+ "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<std::string> 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<std::string>& 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<Digest>
+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<Digest> 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<Statistic>
+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<uint32_t>::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<MiniTrace>(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<Counters>
+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<char* const*>(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<Digest> result_name;
+ optional<Digest> 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<char* const*>(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<int8_t> 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;
+ }
+}
+++ /dev/null
-// 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 <windows.h>
-# 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
--- /dev/null
+// 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 <functional>
+#include <string>
+
+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<std::string(const Context& ctx,
+ const std::string& name,
+ const std::string& exclude_name)>;
+
+// Tested by unit tests.
+void find_compiler(Context& ctx,
+ const FindExecutableFunction& find_executable_function);
+nonstd::optional<std::string>
+rewrite_dep_file_paths(const Context& ctx, const std::string& file_content);
+++ /dev/null
-// 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 <math.h>
-
-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);
-}
--- /dev/null
+// 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 <algorithm>
+
+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<std::shared_ptr<CacheFile>> 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<CacheFile>& f1,
+ const std::shared_ptr<CacheFile>& f2) {
+ return f1->lstat().mtime() < f2->lstat().mtime();
+ });
+
+ log("Before cleanup: {:.0f} KiB, {:.0f} files",
+ static_cast<double>(cache_size) / 1024,
+ static_cast<double>(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<int64_t>(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<double>(cache_size) / 1024,
+ static_cast<double>(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<std::shared_ptr<CacheFile>> 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
+}
--- /dev/null
+// 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 <string>
+
+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);
+++ /dev/null
-// 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);
-}
--- /dev/null
+// 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<const CompOpt*>(key1);
+ const CompOpt* opt2 = static_cast<const CompOpt*>(key2);
+ return strcmp(opt1->name, opt2->name);
+}
+
+static int
+compare_prefix_compopts(const void* key1, const void* key2)
+{
+ const CompOpt* opt1 = static_cast<const CompOpt*>(key1);
+ const CompOpt* opt2 = static_cast<const CompOpt*>(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<CompOpt*>(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<CompOpt*>(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);
+}
+++ /dev/null
-#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
--- /dev/null
+// 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 <string>
+
+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);
--- /dev/null
+// 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 <string>
+#include <thread>
+
+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<std::mutex> 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<std::mutex> lock(m_mutex);
+ return m_content_size;
+}
+
+uint64_t
+RecompressionStatistics::old_size() const
+{
+ std::unique_lock<std::mutex> lock(m_mutex);
+ return m_old_size;
+}
+
+uint64_t
+RecompressionStatistics::new_size() const
+{
+ std::unique_lock<std::mutex> lock(m_mutex);
+ return m_new_size;
+}
+
+uint64_t
+RecompressionStatistics::incompressible_size() const
+{
+ std::unique_lock<std::mutex> 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<CacheEntryReader>
+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<CacheEntryReader>(
+ stream, Result::k_magic, Result::k_version);
+
+ case CacheFile::Type::manifest:
+ return std::make_unique<CacheEntryReader>(
+ stream, Manifest::k_magic, Manifest::k_version);
+
+ case CacheFile::Type::unknown:
+ ASSERT(false); // Handled at function entry.
+ }
+
+ ASSERT(false);
+}
+
+std::unique_ptr<CacheEntryWriter>
+create_writer(FILE* stream,
+ const CacheEntryReader& reader,
+ Compression::Type compression_type,
+ int8_t compression_level)
+{
+ return std::make_unique<CacheEntryWriter>(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<int8_t> 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<std::shared_ptr<CacheFile>> 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<double>(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<int8_t> 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<std::shared_ptr<CacheFile>> 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<double>(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<double>(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<int64_t>(statistics.new_size())
+ - static_cast<int64_t>(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);
+}
--- /dev/null
+// 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<int8_t> level,
+ const Util::ProgressReceiver& progress_receiver);
+++ /dev/null
-// 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;
-}
+++ /dev/null
-#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
+++ /dev/null
-// 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;
- }
-}
+++ /dev/null
-%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)
+++ /dev/null
-#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
+++ /dev/null
-/* 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 <bug-gperf@gnu.org>."
-#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; }
+++ /dev/null
-// 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;
-}
+++ /dev/null
-// Copyright (C) 2010-2016 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 COUNTERS_H
-#define COUNTERS_H
-
-#include <stddef.h>
-
-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);
-
-#endif
+++ /dev/null
-%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"
+++ /dev/null
-#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
+++ /dev/null
-/* 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 <bug-gperf@gnu.org>."
-#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; }
--- /dev/null
+// 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 <stdexcept>
+#include <string>
+#include <utility>
+
+// 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<typename... T> inline Error(T&&... args);
+};
+
+inline Error::Error(const std::string& message) : ErrorBase(message)
+{
+}
+
+template<typename... T>
+inline Error::Error(T&&... args)
+ : ErrorBase(fmt::format(std::forward<T>(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<typename... T> inline Fatal(T&&... args);
+};
+
+inline Fatal::Fatal(const std::string& message) : ErrorBase(message)
+{
+}
+
+template<typename... T>
+inline Fatal::Fatal(T&&... args)
+ : ErrorBase(fmt::format(std::forward<T>(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<int> exit_code = nonstd::nullopt);
+
+ nonstd::optional<int> exit_code() const;
+ Statistic statistic() const;
+
+private:
+ Statistic m_statistic;
+ nonstd::optional<int> m_exit_code;
+};
+
+inline Failure::Failure(Statistic statistic, nonstd::optional<int> exit_code)
+ : m_statistic(statistic), m_exit_code(exit_code)
+{
+}
+
+inline nonstd::optional<int>
+Failure::exit_code() const
+{
+ return m_exit_code;
+}
+
+inline Statistic
+Failure::statistic() const
+{
+ return m_statistic;
+}
+++ /dev/null
-// 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;
-}
--- /dev/null
+// 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<char*>(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<char* const*>(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 {};
+}
--- /dev/null
+// 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 <string>
+
+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
+++ /dev/null
-// 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);
- }
-}
+++ /dev/null
-/*
- * getopt_long() -- long options parser
- *
- * 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.
- */
-
-#include "config.h"
-
-#ifdef HAVE_GETOPT_LONG
-
-typedef int do_not_warn_about_empty_compilation_unit;
-
-#else
-
-#include "getopt_long.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#define BADCH '?'
-#define BADARG ':'
-#define EMSG ""
-
-int
-getopt_long(int argc, char *const argv[],
- const char *optstring,
- const struct option * longopts, int *longindex)
-{
- static char *place = EMSG; /* option letter processing */
- char *oli; /* option letter list index */
-
- if (!*place)
- { /* update scanning pointer */
- if (optind >= argc)
- {
- place = EMSG;
- return -1;
- }
-
- place = argv[optind];
-
- if (place[0] != '-')
- {
- place = EMSG;
- return -1;
- }
-
- place++;
-
- if (place[0] == '-' && place[1] == '\0')
- { /* found "--" */
- ++optind;
- place = EMSG;
- return -1;
- }
-
- if (place[0] == '-' && place[1])
- {
- /* long option */
- size_t namelen;
- int i;
-
- place++;
-
- namelen = strcspn(place, "=");
- for (i = 0; longopts[i].name != NULL; i++)
- {
- if (strlen(longopts[i].name) == namelen
- && strncmp(place, longopts[i].name, namelen) == 0)
- {
- if (longopts[i].has_arg)
- {
- if (place[namelen] == '=')
- optarg = place + namelen + 1;
- else if (optind < argc - 1)
- {
- optind++;
- optarg = argv[optind];
- }
- else
- {
- if (optstring[0] == ':')
- return BADARG;
- if (opterr)
- fprintf(stderr,
- "%s: option requires an argument -- %s\n",
- argv[0], place);
- place = EMSG;
- optind++;
- return BADCH;
- }
- }
- else
- {
- optarg = NULL;
- if (place[namelen] != 0)
- {
- /* XXX error? */
- }
- }
-
- optind++;
-
- if (longindex)
- *longindex = i;
-
- place = EMSG;
-
- if (longopts[i].flag == NULL)
- return longopts[i].val;
- else
- {
- *longopts[i].flag = longopts[i].val;
- return 0;
- }
- }
- }
-
- if (opterr && optstring[0] != ':')
- fprintf(stderr,
- "%s: illegal option -- %s\n", argv[0], place);
- place = EMSG;
- optind++;
- return BADCH;
- }
- }
-
- /* short option */
- optopt = (int) *place++;
-
- oli = strchr(optstring, optopt);
- if (!oli)
- {
- if (!*place)
- ++optind;
- if (opterr && *optstring != ':')
- fprintf(stderr,
- "%s: illegal option -- %c\n", argv[0], optopt);
- return BADCH;
- }
-
- if (oli[1] != ':')
- { /* don't need argument */
- optarg = NULL;
- if (!*place)
- ++optind;
- }
- else
- { /* need an argument */
- if (*place) /* no white space */
- optarg = place;
- else if (argc <= ++optind)
- { /* no arg */
- place = EMSG;
- if (*optstring == ':')
- return BADARG;
- if (opterr)
- fprintf(stderr,
- "%s: option requires an argument -- %c\n",
- argv[0], optopt);
- return BADCH;
- }
- else
- /* white space */
- optarg = argv[optind];
- place = EMSG;
- ++optind;
- }
- return optopt;
-}
-
-#endif /* HAVE_GETOPT_LONG */
+++ /dev/null
-/*
- * Portions Copyright (c) 1987, 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * Portions Copyright (c) 2003-2010, PostgreSQL Global Development Group
- */
-#ifndef GETOPT_LONG_H
-#define GETOPT_LONG_H
-
-extern int opterr;
-extern int optind;
-extern int optopt;
-extern char *optarg;
-
-struct option
-{
- const char *name;
- int has_arg;
- int *flag;
- int val;
-};
-
-#define no_argument 0
-#define required_argument 1
-
-extern int getopt_long(int argc, char *const argv[],
- const char *optstring,
- const struct option * longopts, int *longindex);
-
-#endif /* GETOPT_LONG_H */
+++ /dev/null
-// 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;
-}
+++ /dev/null
-// 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
+++ /dev/null
-/*
- 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 <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <math.h>
-
-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.
-*/
+++ /dev/null
-/*
- 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.
-*/
+++ /dev/null
-/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
-
-#include "hashtable.h"
-#define HASHTABLE_INDEXFOR
-#include "hashtable_private.h"
-#include "hashtable_itr.h"
-#include <stdlib.h> /* 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.
-*/
+++ /dev/null
-/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
-
-#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.
-*/
+++ /dev/null
-/* Copyright (C) 2002, 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
-
-#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.
-*/
+++ /dev/null
-// 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;
-}
--- /dev/null
+// 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 <immintrin.h>
+#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
+// <http://0x80.pl/articles/simd-strfind.html>.
+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<const __m256i*>(&str[pos]));
+ const __m256i block_last =
+ _mm256_loadu_si256(reinterpret_cast<const __m256i*>(&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<char*>(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<char* const*>(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;
+}
+++ /dev/null
-// 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 <inttypes.h>
-
-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
--- /dev/null
+// 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 <string>
+
+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);
+++ /dev/null
-// 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);
-}
--- /dev/null
+// 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);
+}
+++ /dev/null
-#ifndef CCACHE_LANGUAGE_H
-#define CCACHE_LANGUAGE_H
-
-#include <stdbool.h>
-
-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
--- /dev/null
+// 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 <string>
+
+// 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);
+++ /dev/null
-// 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 <staleness_limit> <path>\n");
- }
- return 1;
-}
-#endif
+++ /dev/null
-#ifndef CCACHE_MACROSKIP_H
-#define CCACHE_MACROSKIP_H
-
-#include <stdint.h>
-
-// 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
--- /dev/null
+// 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,
+};
+++ /dev/null
-// ccache -- a fast C/C++ compiler cache
-//
-// Copyright (C) 2010-2016 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
-
-int
-ccache_main(int argc, char *argv[]);
-
-int
-main(int argc, char *argv[])
-{
- return ccache_main(argc, argv);
-}
--- /dev/null
+// 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
+
+int ccache_main(int argc, const char* const* argv);
+
+int
+main(int argc, char* const* argv)
+{
+ return ccache_main(argc, argv);
+}
+++ /dev/null
-// 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 <zlib.h>
-
-// Sketchy specification of the manifest disk format:
-//
-// <magic> magic number (4 bytes)
-// <version> file format version (1 byte unsigned int)
-// <hash_size> size of the hash fields (in bytes) (1 byte unsigned int)
-// <reserved> reserved for future use (2 bytes)
-// ----------------------------------------------------------------------------
-// <n> number of include file paths (4 bytes unsigned int)
-// <path_0> path to include file (NUL-terminated string,
-// ... at most 1024 bytes)
-// <path_n-1>
-// ----------------------------------------------------------------------------
-// <n> number of include file hash entries (4 bytes unsigned int)
-// <index[0]> index of include file path (4 bytes unsigned int)
-// <hash[0]> hash of include file (<hash_size> bytes)
-// <size[0]> size of include file (4 bytes unsigned int)
-// <mtime[0]> mtime of include file (8 bytes signed int)
-// <ctime[0]> ctime of include file (8 bytes signed int)
-// ...
-// <index[n-1]>
-// <hash[n-1]>
-// <size[n-1]>
-// <mtime[n-1]>
-// <ctime[n-1]>
-// ----------------------------------------------------------------------------
-// <n> number of object name entries (4 bytes unsigned int)
-// <m[0]> number of include file hash indexes (4 bytes unsigned int)
-// <index[0][0]> include file hash index (4 bytes unsigned int)
-// ...
-// <index[0][m[0]-1]>
-// <hash[0]> hash part of object name (<hash_size> bytes)
-// <size[0]> size part of object name (4 bytes unsigned int)
-// ...
-// <m[n-1]> number of include file hash indexes
-// <index[n-1][0]> include file hash index
-// ...
-// <index[n-1][m[n-1]]>
-// <hash[n-1]>
-// <size[n-1]>
-
-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;
-}
+++ /dev/null
-#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
+++ /dev/null
-// 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);
-}
+++ /dev/null
-#ifndef MDFOUR_H
-#define MDFOUR_H
-
-#include <stddef.h>
-#include <inttypes.h>
-
-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
+++ /dev/null
-// minitrace
-// Copyright 2014 by Henrik Rydgård
-// http://www.github.com/hrydgard/minitrace
-// Released under the MIT license.
-
-// See minitrace.h for basic documentation.
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#ifdef _WIN32
-#pragma warning (disable:4996)
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#define __thread __declspec(thread)
-#define pthread_mutex_t CRITICAL_SECTION
-#define pthread_mutex_init(a, b) InitializeCriticalSection(a)
-#define pthread_mutex_lock(a) EnterCriticalSection(a)
-#define pthread_mutex_unlock(a) LeaveCriticalSection(a)
-#define pthread_mutex_destroy(a) DeleteCriticalSection(a)
-#else
-#include <signal.h>
-#include <pthread.h>
-#include <sys/time.h>
-#include <unistd.h>
-#endif
-
-#include "minitrace.h"
-
-#ifdef __GNUC__
-#define ATTR_NORETURN __attribute__((noreturn))
-#else
-#define ATTR_NORETURN
-#endif
-
-#define ARRAY_SIZE(x) sizeof(x)/sizeof(x[0])
-
-// Ugh, this struct is already pretty heavy.
-// Will probably need to move arguments to a second buffer to support more than one.
-typedef struct raw_event {
- const char *name;
- const char *cat;
- void *id;
- int64_t ts;
- uint32_t pid;
- uint32_t tid;
- char ph;
- mtr_arg_type arg_type;
- const char *arg_name;
- union {
- const char *a_str;
- int a_int;
- double a_double;
- };
-} raw_event_t;
-
-static raw_event_t *buffer;
-static volatile int count;
-static int is_tracing = 0;
-static int64_t time_offset;
-static int first_line = 1;
-static FILE *f;
-static __thread int cur_thread_id; // Thread local storage
-static int cur_process_id;
-static pthread_mutex_t mutex;
-
-#define STRING_POOL_SIZE 100
-static char *str_pool[100];
-
-// Tiny portability layer.
-// Exposes:
-// get_cur_thread_id()
-// get_cur_process_id()
-// mtr_time_s()
-// pthread basics
-#ifdef _WIN32
-static int get_cur_thread_id() {
- return (int)GetCurrentThreadId();
-}
-static int get_cur_process_id() {
- return (int)GetCurrentProcessId();
-}
-
-static uint64_t _frequency = 0;
-static uint64_t _starttime = 0;
-double mtr_time_s() {
- if (_frequency == 0) {
- QueryPerformanceFrequency((LARGE_INTEGER*)&_frequency);
- QueryPerformanceCounter((LARGE_INTEGER*)&_starttime);
- }
- __int64 time;
- QueryPerformanceCounter((LARGE_INTEGER*)&time);
- return ((double) (time - _starttime) / (double) _frequency);
-}
-
-// Ctrl+C handling for Windows console apps
-static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) {
- if (is_tracing && fdwCtrlType == CTRL_C_EVENT) {
- printf("Ctrl-C detected! Flushing trace and shutting down.\n\n");
- mtr_flush();
- mtr_shutdown();
- }
- ExitProcess(1);
-}
-
-void mtr_register_sigint_handler() {
- // For console apps:
- SetConsoleCtrlHandler(&CtrlHandler, TRUE);
-}
-
-#else
-
-static inline int get_cur_thread_id() {
- return (int)(intptr_t)pthread_self();
-}
-static inline int get_cur_process_id() {
- return (int)getpid();
-}
-
-#if defined(BLACKBERRY)
-double mtr_time_s() {
- struct timespec time;
- clock_gettime(CLOCK_MONOTONIC, &time); // Linux must use CLOCK_MONOTONIC_RAW due to time warps
- return time.tv_sec + time.tv_nsec / 1.0e9;
-}
-#else
-double mtr_time_s() {
- static time_t start;
- struct timeval tv;
- gettimeofday(&tv, NULL);
- if (start == 0) {
- start = tv.tv_sec;
- }
- tv.tv_sec -= start;
- return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
-}
-#endif // !BLACKBERRY
-
-static void termination_handler(int signum) ATTR_NORETURN;
-static void termination_handler(int signum) {
- (void) signum;
- if (is_tracing) {
- printf("Ctrl-C detected! Flushing trace and shutting down.\n\n");
- mtr_flush();
- fwrite("\n]}\n", 1, 4, f);
- fclose(f);
- }
- exit(1);
-}
-
-void mtr_register_sigint_handler() {
-#ifndef MTR_ENABLED
- return;
-#endif
- // Avoid altering set-to-be-ignored handlers while registering.
- if (signal(SIGINT, &termination_handler) == SIG_IGN)
- signal(SIGINT, SIG_IGN);
-}
-
-#endif
-
-void mtr_init_from_stream(void *stream) {
-#ifndef MTR_ENABLED
- return;
-#endif
- buffer = (raw_event_t *)malloc(INTERNAL_MINITRACE_BUFFER_SIZE * sizeof(raw_event_t));
- is_tracing = 1;
- count = 0;
- f = (FILE *)stream;
- const char *header = "{\"traceEvents\":[\n";
- fwrite(header, 1, strlen(header), f);
- time_offset = (uint64_t)(mtr_time_s() * 1000000);
- first_line = 1;
- pthread_mutex_init(&mutex, 0);
-}
-
-void mtr_init(const char *json_file) {
-#ifndef MTR_ENABLED
- return;
-#endif
- mtr_init_from_stream(fopen(json_file, "wb"));
-}
-
-void mtr_shutdown() {
- int i;
-#ifndef MTR_ENABLED
- return;
-#endif
- is_tracing = 0;
- mtr_flush();
- fwrite("\n]}\n", 1, 4, f);
- fclose(f);
- pthread_mutex_destroy(&mutex);
- f = 0;
- free(buffer);
- buffer = 0;
- for (i = 0; i < STRING_POOL_SIZE; i++) {
- if (str_pool[i]) {
- free(str_pool[i]);
- str_pool[i] = 0;
- }
- }
-}
-
-const char *mtr_pool_string(const char *str) {
- int i;
- for (i = 0; i < STRING_POOL_SIZE; i++) {
- if (!str_pool[i]) {
- str_pool[i] = (char*)malloc(strlen(str) + 1);
- strcpy(str_pool[i], str);
- return str_pool[i];
- } else {
- if (!strcmp(str, str_pool[i]))
- return str_pool[i];
- }
- }
- return "string pool full";
-}
-
-void mtr_start() {
-#ifndef MTR_ENABLED
- return;
-#endif
- is_tracing = 1;
-}
-
-void mtr_stop() {
-#ifndef MTR_ENABLED
- return;
-#endif
- is_tracing = 0;
-}
-
-// TODO: fwrite more than one line at a time.
-void mtr_flush() {
-#ifndef MTR_ENABLED
- return;
-#endif
- int i = 0;
- char linebuf[1024];
- char arg_buf[1024];
- char id_buf[256];
- // We have to lock while flushing. So we really should avoid flushing as much as possible.
-
-
- pthread_mutex_lock(&mutex);
- int old_tracing = is_tracing;
- is_tracing = 0; // Stop logging even if using interlocked increments instead of the mutex. Can cause data loss.
-
- for (i = 0; i < count; i++) {
- raw_event_t *raw = &buffer[i];
- int len;
- switch (raw->arg_type) {
- case MTR_ARG_TYPE_INT:
- snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":%i", raw->arg_name, raw->a_int);
- break;
- case MTR_ARG_TYPE_STRING_CONST:
- snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str);
- break;
- case MTR_ARG_TYPE_STRING_COPY:
- if (strlen(raw->a_str) > 700) {
- snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%.*s\"", raw->arg_name, 700, raw->a_str);
- } else {
- snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str);
- }
- break;
- case MTR_ARG_TYPE_NONE:
- arg_buf[0] = '\0';
- break;
- }
- if (raw->id) {
- switch (raw->ph) {
- case 'S':
- case 'T':
- case 'F':
- // TODO: Support full 64-bit pointers
- snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"id\":\"0x%08x\"", (uint32_t)(uintptr_t)raw->id);
- break;
- case 'X':
- snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"dur\":%i", (int)raw->a_double);
- break;
- }
- } else {
- id_buf[0] = 0;
- }
- const char *cat = raw->cat;
-#ifdef _WIN32
- // On Windows, we often end up with backslashes in category.
- char temp[256];
- {
- int len = (int)strlen(cat);
- int i;
- if (len > 255) len = 255;
- for (i = 0; i < len; i++) {
- temp[i] = cat[i] == '\\' ? '/' : cat[i];
- }
- temp[len] = 0;
- cat = temp;
- }
-#endif
-
- len = snprintf(linebuf, ARRAY_SIZE(linebuf), "%s{\"cat\":\"%s\",\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64 ",\"ph\":\"%c\",\"name\":\"%s\",\"args\":{%s}%s}",
- first_line ? "" : ",\n",
- cat, raw->pid, raw->tid, raw->ts - time_offset, raw->ph, raw->name, arg_buf, id_buf);
- fwrite(linebuf, 1, len, f);
- first_line = 0;
- }
- count = 0;
- is_tracing = old_tracing;
- pthread_mutex_unlock(&mutex);
-}
-
-void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id) {
-#ifndef MTR_ENABLED
- return;
-#endif
- if (!is_tracing || count >= INTERNAL_MINITRACE_BUFFER_SIZE)
- return;
- double ts = mtr_time_s();
- if (!cur_thread_id) {
- cur_thread_id = get_cur_thread_id();
- }
- if (!cur_process_id) {
- cur_process_id = get_cur_process_id();
- }
-
-#if 0 && _WIN32 // This should work, feel free to enable if you're adventurous and need performance.
- int bufPos = InterlockedExchangeAdd((LONG volatile *)&count, 1);
- raw_event_t *ev = &buffer[bufPos];
-#else
- pthread_mutex_lock(&mutex);
- raw_event_t *ev = &buffer[count];
- count++;
- pthread_mutex_unlock(&mutex);
-#endif
-
- ev->cat = category;
- ev->name = name;
- ev->id = id;
- ev->ph = ph;
- if (ev->ph == 'X') {
- double x;
- memcpy(&x, id, sizeof(double));
- ev->ts = (int64_t)(x * 1000000);
- ev->a_double = (ts - x) * 1000000;
- } else {
- ev->ts = (int64_t)(ts * 1000000);
- }
- ev->tid = cur_thread_id;
- ev->pid = cur_process_id;
- ev->arg_type = MTR_ARG_TYPE_NONE;
-}
-
-void internal_mtr_raw_event_arg(const char *category, const char *name, char ph, void *id, mtr_arg_type arg_type, const char *arg_name, void *arg_value) {
-#ifndef MTR_ENABLED
- return;
-#endif
- if (!is_tracing || count >= INTERNAL_MINITRACE_BUFFER_SIZE)
- return;
- if (!cur_thread_id) {
- cur_thread_id = get_cur_thread_id();
- }
- if (!cur_process_id) {
- cur_process_id = get_cur_process_id();
- }
- double ts = mtr_time_s();
-
-#if 0 && _WIN32 // This should work, feel free to enable if you're adventurous and need performance.
- int bufPos = InterlockedExchangeAdd((LONG volatile *)&count, 1);
- raw_event_t *ev = &buffer[bufPos];
-#else
- pthread_mutex_lock(&mutex);
- raw_event_t *ev = &buffer[count];
- count++;
- pthread_mutex_unlock(&mutex);
-#endif
-
- ev->cat = category;
- ev->name = name;
- ev->id = id;
- ev->ts = (int64_t)(ts * 1000000);
- ev->ph = ph;
- ev->tid = cur_thread_id;
- ev->pid = cur_process_id;
- ev->arg_type = arg_type;
- ev->arg_name = arg_name;
- switch (arg_type) {
- case MTR_ARG_TYPE_INT: ev->a_int = (int)(uintptr_t)arg_value; break;
- case MTR_ARG_TYPE_STRING_CONST: ev->a_str = (const char*)arg_value; break;
- case MTR_ARG_TYPE_STRING_COPY: ev->a_str = strdup((const char*)arg_value); break;
- case MTR_ARG_TYPE_NONE: break;
- }
-}
-
+++ /dev/null
-// Minitrace
-//
-// Copyright 2014 by Henrik Rydgård
-// http://www.github.com/hrydgard/minitrace
-// Released under the MIT license.
-//
-// Ultra-light dependency free library for performance tracing C/C++ applications.
-// Produces traces compatible with Google Chrome's trace viewer.
-// Simply open "about:tracing" in Chrome and load the produced JSON.
-//
-// This contains far less template magic than the original libraries from Chrome
-// because this is meant to be usable from C.
-//
-// See README.md for a tutorial.
-//
-// The trace format is documented here:
-// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit
-// More:
-// http://www.altdevblogaday.com/2012/08/21/using-chrometracing-to-view-your-inline-profiling-data/
-
-#ifndef MINITRACE_H
-#define MINITRACE_H
-
-#include <inttypes.h>
-
-// If MTR_ENABLED is not defined, Minitrace does nothing and has near zero overhead.
-// Preferably, set this flag in your build system. If you can't just uncomment this line.
-// #define MTR_ENABLED
-
-// By default, will collect up to 1000000 events, then you must flush.
-// It's recommended that you simply call mtr_flush on a background thread
-// occasionally. It's safe...ish.
-#define INTERNAL_MINITRACE_BUFFER_SIZE 1000000
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Initializes Minitrace. Must be called very early during startup of your executable,
-// before any MTR_ statements.
-void mtr_init(const char *json_file);
-// Same as above, but allows passing in a custom stream (FILE *), as returned by
-// fopen(). It should be opened for writing, preferably in binary mode to avoid
-// processing of line endings (i.e. the "wb" mode).
-void mtr_init_from_stream(void *stream);
-
-// Shuts down minitrace cleanly, flushing the trace buffer.
-void mtr_shutdown(void);
-
-// Lets you enable and disable Minitrace at runtime.
-// May cause strange discontinuities in the output.
-// Minitrace is enabled on startup by default.
-void mtr_start(void);
-void mtr_stop(void);
-
-// Flushes the collected data to disk, clearing the buffer for new data.
-void mtr_flush(void);
-
-// Returns the current time in seconds. Used internally by Minitrace. No caching.
-double mtr_time_s(void);
-
-// Registers a handler that will flush the trace on Ctrl+C.
-// Works on Linux and MacOSX, and in Win32 console applications.
-void mtr_register_sigint_handler(void);
-
-// Utility function that should rarely be used.
-// If str is semi dynamic, store it permanently in a small pool so we don't need to malloc it.
-// The pool fills up fast though and performance isn't great.
-// Returns a fixed string if the pool is full.
-const char *mtr_pool_string(const char *str);
-
-// Commented-out types will be supported in the future.
-typedef enum {
- MTR_ARG_TYPE_NONE = 0,
- MTR_ARG_TYPE_INT = 1, // I
- // MTR_ARG_TYPE_FLOAT = 2, // TODO
- // MTR_ARG_TYPE_DOUBLE = 3, // TODO
- MTR_ARG_TYPE_STRING_CONST = 8, // C
- MTR_ARG_TYPE_STRING_COPY = 9,
- // MTR_ARG_TYPE_JSON_COPY = 10,
-} mtr_arg_type;
-
-// TODO: Add support for more than one argument (metadata) per event
-// Having more costs speed and memory.
-#define MTR_MAX_ARGS 1
-
-// Only use the macros to call these.
-void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id);
-void internal_mtr_raw_event_arg(const char *category, const char *name, char ph, void *id, mtr_arg_type arg_type, const char *arg_name, void *arg_value);
-
-#ifdef MTR_ENABLED
-
-// c - category. Can be filtered by in trace viewer (or at least that's the intention).
-// A good use is to pass __FILE__, there are macros further below that will do it for you.
-// n - name. Pass __FUNCTION__ in most cases, unless you are marking up parts of one.
-
-// Scopes. In C++, use MTR_SCOPE. In C, always match them within the same scope.
-#define MTR_BEGIN(c, n) internal_mtr_raw_event(c, n, 'B', 0)
-#define MTR_END(c, n) internal_mtr_raw_event(c, n, 'E', 0)
-#define MTR_SCOPE(c, n) MTRScopedTrace ____mtr_scope(c, n)
-#define MTR_SCOPE_LIMIT(c, n, l) MTRScopedTraceLimit ____mtr_scope(c, n, l)
-
-// Async events. Can span threads. ID identifies which events to connect in the view.
-#define MTR_START(c, n, id) internal_mtr_raw_event(c, n, 'S', (void *)(id))
-#define MTR_STEP(c, n, id, step) internal_mtr_raw_event_arg(c, n, 'T', (void *)(id), MTR_ARG_TYPE_STRING_CONST, "step", (void *)(step))
-#define MTR_FINISH(c, n, id) internal_mtr_raw_event(c, n, 'F', (void *)(id))
-
-// Flow events. Like async events, but displayed in a more fancy way in the viewer.
-#define MTR_FLOW_START(c, n, id) internal_mtr_raw_event(c, n, 's', (void *)(id))
-#define MTR_FLOW_STEP(c, n, id, step) internal_mtr_raw_event_arg(c, n, 't', (void *)(id), MTR_ARG_TYPE_STRING_CONST, "step", (void *)(step))
-#define MTR_FLOW_FINISH(c, n, id) internal_mtr_raw_event(c, n, 'f', (void *)(id))
-
-// The same macros, but with a single named argument which shows up as metadata in the viewer.
-// _I for int.
-// _C is for a const string arg.
-// _S will copy the string, freeing on flush (expensive but sometimes necessary).
-// but required if the string was generated dynamically.
-
-// Note that it's fine to match BEGIN_S with END and BEGIN with END_S, etc.
-#define MTR_BEGIN_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
-#define MTR_END_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
-#define MTR_SCOPE_C(c, n, aname, astrval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
-
-#define MTR_BEGIN_S(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval))
-#define MTR_END_S(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval))
-#define MTR_SCOPE_S(c, n, aname, astrval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval))
-
-#define MTR_BEGIN_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval))
-#define MTR_END_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval))
-#define MTR_SCOPE_I(c, n, aname, aintval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval))
-
-// Instant events. For things with no duration.
-#define MTR_INSTANT(c, n) internal_mtr_raw_event(c, n, 'I', 0)
-#define MTR_INSTANT_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'I', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
-#define MTR_INSTANT_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'I', 0, MTR_ARG_TYPE_INT, aname, (void *)(aintval))
-
-// Counters (can't do multi-value counters yet)
-#define MTR_COUNTER(c, n, val) internal_mtr_raw_event_arg(c, n, 'C', 0, MTR_ARG_TYPE_INT, n, (void *)(intptr_t)(val))
-
-// Metadata. Call at the start preferably. Must be const strings.
-
-#define MTR_META_PROCESS_NAME(n) internal_mtr_raw_event_arg("", "process_name", 'M', 0, MTR_ARG_TYPE_STRING_COPY, "name", (void *)(n))
-#define MTR_META_THREAD_NAME(n) internal_mtr_raw_event_arg("", "thread_name", 'M', 0, MTR_ARG_TYPE_STRING_COPY, "name", (void *)(n))
-#define MTR_META_THREAD_SORT_INDEX(i) internal_mtr_raw_event_arg("", "thread_sort_index", 'M', 0, MTR_ARG_TYPE_INT, "sort_index", (void *)(i))
-
-#else
-
-#define MTR_BEGIN(c, n)
-#define MTR_END(c, n)
-#define MTR_SCOPE(c, n)
-#define MTR_START(c, n, id)
-#define MTR_STEP(c, n, id, step)
-#define MTR_FINISH(c, n, id)
-#define MTR_FLOW_START(c, n, id)
-#define MTR_FLOW_STEP(c, n, id, step)
-#define MTR_FLOW_FINISH(c, n, id)
-#define MTR_INSTANT(c, n)
-
-#define MTR_BEGIN_C(c, n, aname, astrval)
-#define MTR_END_C(c, n, aname, astrval)
-#define MTR_SCOPE_C(c, n, aname, astrval)
-
-#define MTR_BEGIN_S(c, n, aname, astrval)
-#define MTR_END_S(c, n, aname, astrval)
-#define MTR_SCOPE_S(c, n, aname, astrval)
-
-#define MTR_BEGIN_I(c, n, aname, aintval)
-#define MTR_END_I(c, n, aname, aintval)
-#define MTR_SCOPE_I(c, n, aname, aintval)
-
-#define MTR_INSTANT(c, n)
-#define MTR_INSTANT_C(c, n, aname, astrval)
-#define MTR_INSTANT_I(c, n, aname, aintval)
-
-// Counters (can't do multi-value counters yet)
-#define MTR_COUNTER(c, n, val)
-
-// Metadata. Call at the start preferably. Must be const strings.
-
-#define MTR_META_PROCESS_NAME(n)
-
-#define MTR_META_THREAD_NAME(n)
-#define MTR_META_THREAD_SORT_INDEX(i)
-
-#endif
-
-// Shortcuts for simple function timing with automatic categories and names.
-
-#define MTR_BEGIN_FUNC() MTR_BEGIN(__FILE__, __FUNCTION__)
-#define MTR_END_FUNC() MTR_END(__FILE__, __FUNCTION__)
-#define MTR_SCOPE_FUNC() MTR_SCOPE(__FILE__, __FUNCTION__)
-#define MTR_INSTANT_FUNC() MTR_INSTANT(__FILE__, __FUNCTION__)
-#define MTR_SCOPE_FUNC_LIMIT_S(l) MTRScopedTraceLimit ____mtr_scope(__FILE__, __FUNCTION__, l)
-#define MTR_SCOPE_FUNC_LIMIT_MS(l) MTRScopedTraceLimit ____mtr_scope(__FILE__, __FUNCTION__, (double)l * 0.000001)
-
-// Same, but with a single argument of the usual types.
-#define MTR_BEGIN_FUNC_S(aname, arg) MTR_BEGIN_S(__FILE__, __FUNCTION__, aname, arg)
-#define MTR_END_FUNC_S(aname, arg) MTR_END_S(__FILE__, __FUNCTION__, aname, arg)
-#define MTR_SCOPE_FUNC_S(aname, arg) MTR_SCOPE_S(__FILE__, __FUNCTION__, aname, arg)
-
-#define MTR_BEGIN_FUNC_C(aname, arg) MTR_BEGIN_C(__FILE__, __FUNCTION__, aname, arg)
-#define MTR_END_FUNC_C(aname, arg) MTR_END_C(__FILE__, __FUNCTION__, aname, arg)
-#define MTR_SCOPE_FUNC_C(aname, arg) MTR_SCOPE_C(__FILE__, __FUNCTION__, aname, arg)
-
-#define MTR_BEGIN_FUNC_I(aname, arg) MTR_BEGIN_I(__FILE__, __FUNCTION__, aname, arg)
-#define MTR_END_FUNC_I(aname, arg) MTR_END_I(__FILE__, __FUNCTION__, aname, arg)
-#define MTR_SCOPE_FUNC_I(aname, arg) MTR_SCOPE_I(__FILE__, __FUNCTION__, aname, arg)
-
-#ifdef __cplusplus
-}
-
-#ifdef MTR_ENABLED
-// These are optimized to use X events (combined B and E). Much easier to do in C++ than in C.
-class MTRScopedTrace {
-public:
- MTRScopedTrace(const char *category, const char *name)
- : category_(category), name_(name) {
- start_time_ = mtr_time_s();
- }
- ~MTRScopedTrace() {
- internal_mtr_raw_event(category_, name_, 'X', &start_time_);
- }
-
-private:
- const char *category_;
- const char *name_;
- double start_time_;
-};
-
-// Only outputs a block if execution time exceeded the limit.
-// TODO: This will effectively call mtr_time_s twice at the end, which is bad.
-class MTRScopedTraceLimit {
-public:
- MTRScopedTraceLimit(const char *category, const char *name, double limit_s)
- : category_(category), name_(name), limit_(limit_s) {
- start_time_ = mtr_time_s();
- }
- ~MTRScopedTraceLimit() {
- double end_time = mtr_time_s();
- if (end_time - start_time_ >= limit_) {
- internal_mtr_raw_event(category_, name_, 'X', &start_time_);
- }
- }
-
-private:
- const char *category_;
- const char *name_;
- double start_time_;
- double limit_;
-};
-
-class MTRScopedTraceArg {
-public:
- MTRScopedTraceArg(const char *category, const char *name, mtr_arg_type arg_type, const char *arg_name, void *arg_value)
- : category_(category), name_(name) {
- internal_mtr_raw_event_arg(category, name, 'B', 0, arg_type, arg_name, arg_value);
- }
- ~MTRScopedTraceArg() {
- internal_mtr_raw_event(category_, name_, 'E', 0);
- }
-
-private:
- const char *category_;
- const char *name_;
-};
-#endif
-
-#endif
-
-#endif
+++ /dev/null
-// MurmurHashNeutral2, by Austin Appleby. Released to the public domain. See
-// <http://murmurhash.googlepages.com>.
-
-#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;
-}
+++ /dev/null
-#ifndef MURMURHASHNEUTRAL2_H
-#define MURMURHASHNEUTRAL2_H
-
-unsigned int murmurhashneutral2(const void *key, int len, unsigned int seed);
-
-#endif
+++ /dev/null
-/* $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 <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.
- */
-
-/*
- * History
- *
- * 2008-01-20 Holger Weiss <holger@jhweiss.de> 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 <holger@jhweiss.de> for C99-snprintf 1.0:
- *
- * Added a lot of new features, fixed many bugs, and incorporated various
- * improvements done by Andrew Tridgell <tridge@samba.org>, Russ Allbery
- * <rra@stanford.edu>, Hrvoje Niksic <hniksic@xemacs.org>, Damien Miller
- * <djm@mindrot.org>, 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 <holger@jhweiss.de> 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 <me@mutt.org> 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 <roessler@does-not-exist.org> for Mutt 0.89i:
- *
- * The PGP code was using unsigned hexadecimal formats. Unfortunately,
- * unsigned formats simply didn't work.
- *
- * 1997-10-22 Brandon Long <blong@fiction.net> 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 <blong@fiction.net> 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 <config.h>
- * #endif
- * #if HAVE_STDARG_H
- * #include <stdarg.h>
- * #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
- * <http://www.jhweiss.de/software/snprintf.html>.
- */
-
-#if HAVE_CONFIG_H
-#include <config.h>
-#endif /* HAVE_CONFIG_H */
-
-#ifdef TEST_SNPRINTF
-#include <math.h> /* For pow(3), NAN, and INFINITY. */
-#include <string.h> /* 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 <stdint.h> 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 <stdio.h> /* 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 <stdarg.h>
-#define VA_START(ap, last) va_start(ap, last)
-#define VA_SHIFT(ap, value, type) /* No-op for ANSI C. */
-#else /* Assume <varargs.h> is available. */
-#include <varargs.h>
-#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 <stdlib.h> /* 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 <errno.h> /* For ERANGE and errno. */
-#include <limits.h> /* For *_MAX. */
-#if HAVE_INTTYPES_H
-#include <inttypes.h> /* For intmax_t (if not defined in <stdint.h>). */
-#endif /* HAVE_INTTYPES_H */
-#if HAVE_LOCALE_H
-#include <locale.h> /* For localeconv(3). */
-#endif /* HAVE_LOCALE_H */
-#if HAVE_STDDEF_H
-#include <stddef.h> /* For ptrdiff_t. */
-#endif /* HAVE_STDDEF_H */
-#if HAVE_STDINT_H
-#include <stdint.h> /* 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: */
+++ /dev/null
-// 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 <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-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);
-}
+++ /dev/null
-// 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 <sys/file.h>
-#ifdef HAVE_SYS_MMAN_H
-#include <sys/mman.h>
-#endif
-#include <sys/stat.h>
-#include <sys/types.h>
-#ifdef HAVE_SYS_WAIT_H
-#include <sys/wait.h>
-#endif
-
-#include <assert.h>
-#include <ctype.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include <time.h>
-#include <unistd.h>
-#include <utime.h>
-
-// 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
--- /dev/null
+// 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 <sys/file.h>
+#endif
+
+#ifdef HAVE_SYS_MMAN_H
+# include <sys/mman.h>
+#endif
+#include <sys/stat.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#include <cassert>
+#include <cctype>
+#include <cerrno>
+#include <cinttypes>
+#include <climits>
+#include <csignal>
+#include <cstdarg>
+#include <cstddef>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+
+#ifdef HAVE_DIRENT_H
+# include <dirent.h>
+#endif
+
+#include <fcntl.h>
+
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_UTIME_H
+# include <utime.h>
+#elif defined(HAVE_SYS_UTIME_H)
+# include <sys/utime.h>
+#endif
+
+#ifdef HAVE_VARARGS_H
+# include <varargs.h>
+#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 <sys/stat.h>
+# 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 <direct.h>
+# include <io.h>
+# include <process.h>
+# define NOMINMAX 1
+# include <windows.h>
+# 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<T>::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
--- /dev/null
+# 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
+...
+
--- /dev/null
+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
+ $<$<COMPILE_LANGUAGE:C>:-Wno-implicit-function-declaration
+ -Wno-int-conversion>)
+endif()
+
+if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
+ target_compile_options(
+ third_party_lib
+ PRIVATE $<$<COMPILE_LANGUAGE:C>:-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)
--- /dev/null
+/* (C) 2012 Peter Conrad <conrad@quisquis.de>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+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
--- /dev/null
+/* (C) 2012 Peter Conrad <conrad@quisquis.de>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef BASE32_HEX_H
+#define BASE32_HEX_H
+
+#include <stdint.h>
+
+extern unsigned int base32hex(char *out, const uint8_t *in, unsigned int len);
+
+#endif
--- /dev/null
+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
--- /dev/null
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+
+#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<uint8_t> 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<uint8_t> 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);
+}
--- /dev/null
+#ifndef BLAKE3_H
+#define BLAKE3_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#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 */
--- /dev/null
+#include "blake3_impl.h"
+
+#include <immintrin.h>
+
+#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
+}
--- /dev/null
+#if defined(__ELF__) && defined(__linux__)
+.section .note.GNU-stack,"",%progbits
+#endif
+
+#if defined(__ELF__) && defined(__CET__) && defined(__has_include)
+#if __has_include(<cet.h>)
+#include <cet.h>
+#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
+
--- /dev/null
+.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
+
--- /dev/null
+#include "blake3_impl.h"
+
+#include <immintrin.h>
+
+#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];
+ }
+}
--- /dev/null
+#if defined(__ELF__) && defined(__linux__)
+.section .note.GNU-stack,"",%progbits
+#endif
+
+#if defined(__ELF__) && defined(__CET__) && defined(__has_include)
+#if __has_include(<cet.h>)
+#include <cet.h>
+#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
--- /dev/null
+.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
--- /dev/null
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "blake3_impl.h"
+
+#if defined(IS_X86)
+#if defined(_MSC_VER)
+#include <intrin.h>
+#elif defined(__GNUC__)
+#include <immintrin.h>
+#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;
+}
--- /dev/null
+#ifndef BLAKE3_IMPL_H
+#define BLAKE3_IMPL_H
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#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 <intrin.h>
+#endif
+#include <immintrin.h>
+#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 */
--- /dev/null
+#include "blake3_impl.h"
+
+#include <arm_neon.h>
+
+// 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];
+ }
+}
--- /dev/null
+#include "blake3_impl.h"
+#include <string.h>
+
+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];
+ }
+}
--- /dev/null
+#include "blake3_impl.h"
+
+#include <immintrin.h>
+
+#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];
+ }
+}
--- /dev/null
+#if defined(__ELF__) && defined(__linux__)
+.section .note.GNU-stack,"",%progbits
+#endif
+
+#if defined(__ELF__) && defined(__CET__) && defined(__has_include)
+#if __has_include(<cet.h>)
+#include <cet.h>
+#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
--- /dev/null
+.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
--- /dev/null
+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
--- /dev/null
+#include "blake3_impl.h"
+
+#include <immintrin.h>
+
+#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];
+ }
+}
--- /dev/null
+#if defined(__ELF__) && defined(__linux__)
+.section .note.GNU-stack,"",%progbits
+#endif
+
+#if defined(__ELF__) && defined(__CET__) && defined(__has_include)
+#if __has_include(<cet.h>)
+#include <cet.h>
+#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
--- /dev/null
+.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
--- /dev/null
+// ====================================================================== 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 <iosfwd>
+#include <cstddef>
+#include <ostream>
+#else // DOCTEST_CONFIG_USE_STD_HEADERS
+
+#if DOCTEST_CLANG
+// to detect if libc++ is being used with clang (the _LIBCPP_VERSION identifier)
+#include <ciso646>
+#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 <class charT>
+struct char_traits;
+template <>
+struct char_traits<char>;
+template <class charT, class traits>
+class basic_ostream;
+typedef basic_ostream<char, char_traits<char>> ostream;
+template <class... Types>
+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 _Ty>
+class allocator;
+template <class _Elem, class _Traits, class _Alloc>
+class basic_string;
+using string = basic_string<char, char_traits<char>, allocator<char>>;
+#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 <type_traits>
+#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<String*>(this)->c_str(); } // NOLINT
+ char* c_str() {
+ if(isOnStack())
+ return reinterpret_cast<char*>(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 <bool CONDITION, typename TYPE = void>
+ struct enable_if
+ {};
+
+ template <typename TYPE>
+ struct enable_if<true, TYPE>
+ { typedef TYPE type; };
+#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING) || DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+
+ // clang-format off
+ template<class T> struct remove_reference { typedef T type; };
+ template<class T> struct remove_reference<T&> { typedef T type; };
+ template<class T> struct remove_reference<T&&> { typedef T type; };
+
+ template<class T> struct remove_const { typedef T type; };
+ template<class T> struct remove_const<const T> { typedef T type; };
+ // clang-format on
+
+ template <typename T>
+ struct deferred_false
+ // cppcheck-suppress unusedStructMember
+ { static const bool value = false; };
+
+ namespace has_insertion_operator_impl {
+ std::ostream &os();
+ template<class T>
+ DOCTEST_REF_WRAP(T) val();
+
+ template<class, class = void>
+ struct check {
+ static constexpr auto value = false;
+ };
+
+ template<class T>
+ struct check<T, decltype(os() << val<T>(), void())> {
+ static constexpr auto value = true;
+ };
+ } // namespace has_insertion_operator_impl
+
+ template<class T>
+ using has_insertion_operator = has_insertion_operator_impl::check<T>;
+
+ 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 <bool C>
+ struct StringMakerBase
+ {
+ template <typename T>
+ static String convert(const DOCTEST_REF_WRAP(T)) {
+ return "{?}";
+ }
+ };
+
+ template <>
+ struct StringMakerBase<true>
+ {
+ template <typename T>
+ static String convert(const DOCTEST_REF_WRAP(T) in) {
+ *getTlsOss() << in;
+ return getTlsOssResult();
+ }
+ };
+
+ DOCTEST_INTERFACE String rawMemoryToString(const void* object, unsigned size);
+
+ template <typename T>
+ String rawMemoryToString(const DOCTEST_REF_WRAP(T) object) {
+ return rawMemoryToString(&object, sizeof(object));
+ }
+
+ template <typename T>
+ const char* type_to_string() {
+ return "<>";
+ }
+} // namespace detail
+
+template <typename T>
+struct StringMaker : public detail::StringMakerBase<detail::has_insertion_operator<T>::value>
+{};
+
+template <typename T>
+struct StringMaker<T*>
+{
+ template <typename U>
+ static String convert(U* p) {
+ if(p)
+ return detail::rawMemoryToString(p);
+ return "NULL";
+ }
+};
+
+template <typename R, typename C>
+struct StringMaker<R C::*>
+{
+ static String convert(R C::*p) {
+ if(p)
+ return detail::rawMemoryToString(p);
+ return "NULL";
+ }
+};
+
+template <typename T>
+String toString(const DOCTEST_REF_WRAP(T) value) {
+ return StringMaker<T>::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 <typename T>
+ explicit Approx(const T& value,
+ typename detail::enable_if<std::is_constructible<double, T>::value>::type* =
+ static_cast<T*>(nullptr)) {
+ *this = Approx(static_cast<double>(value));
+ }
+#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+
+ Approx& epsilon(double newEpsilon);
+
+#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+ template <typename T>
+ typename detail::enable_if<std::is_constructible<double, T>::value, Approx&>::type epsilon(
+ const T& newEpsilon) {
+ m_epsilon = static_cast<double>(newEpsilon);
+ return *this;
+ }
+#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+
+ Approx& scale(double newScale);
+
+#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+ template <typename T>
+ typename detail::enable_if<std::is_constructible<double, T>::value, Approx&>::type scale(
+ const T& newScale) {
+ m_scale = static_cast<double>(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 <typename T> friend typename detail::enable_if<std::is_constructible<double, T>::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<class T> struct decay_array { typedef T type; };
+ template<class T, unsigned N> struct decay_array<T[N]> { typedef T* type; };
+ template<class T> struct decay_array<T[]> { typedef T* type; };
+
+ template<class T> struct not_char_pointer { enum { value = 1 }; };
+ template<> struct not_char_pointer<char*> { enum { value = 0 }; };
+ template<> struct not_char_pointer<const char*> { enum { value = 0 }; };
+
+ template<class T> struct can_use_op : public not_char_pointer<typename decay_array<T>::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 <typename L, typename R>
+ 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 <typename R> \
+ 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 <typename R> \
+ rt& operator op(const R&) { \
+ static_assert(deferred_false<R>::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<can_use_op<L>::value || can_use_op<R>::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 <typename L, typename R> \
+ 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 <typename L>
+ // 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 <typename L>
+ Expression_lhs<const DOCTEST_REF_WRAP(L)> operator<<(const DOCTEST_REF_WRAP(L) operand) {
+ return Expression_lhs<const DOCTEST_REF_WRAP(L)>(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 <typename T>
+ 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 <typename T>
+ 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<typename T>
+ int instantiationHelper(const T&) { return 0; }
+
+ namespace binaryAssertComparison {
+ enum Enum
+ {
+ eq = 0,
+ ne,
+ gt,
+ lt,
+ ge,
+ le
+ };
+ } // namespace binaryAssertComparison
+
+ // clang-format off
+ template <int, class L, class R> 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 <class L, class R> struct RelationalComparator<n, L, R> { 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 <int comparison, typename L, typename R>
+ DOCTEST_NOINLINE void binary_assert(const DOCTEST_REF_WRAP(L) lhs,
+ const DOCTEST_REF_WRAP(R) rhs) {
+ m_failed = !RelationalComparator<comparison, L, R>()(lhs, rhs);
+ if(m_failed || getContextOptions()->success)
+ m_decomp = stringifyBinaryExpr(lhs, ", ", rhs);
+ }
+
+ template <typename L>
+ 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 <int comparison, typename L, typename R>
+ 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<comparison, L, R>()(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 <typename L>
+ 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 <typename T>
+ 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 <bool C>
+ struct StringStreamBase
+ {
+ template <typename T>
+ 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<true>
+ {
+ template <typename T>
+ static void convert(std::ostream* s, const T& in) {
+ *s << in;
+ }
+ };
+
+ template <typename T>
+ struct StringStream : public StringStreamBase<has_insertion_operator<T>::value>
+ {};
+
+ template <typename T>
+ void toStream(std::ostream* s, const T& value) {
+ StringStream<T>::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 <typename L> 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 <typename T>
+ MessageBuilder& operator<<(const T& in) {
+ toStream(m_stream, in);
+ return *this;
+ }
+
+ bool log();
+ void react();
+ };
+
+ template <typename L>
+ ContextScope<L> MakeContextScope(const L &lambda) {
+ return ContextScope<L>(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 <typename T>
+int registerExceptionTranslator(String (*translateFunction)(T)) {
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors")
+ static detail::ExceptionTranslator<T> 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 <typename T>
+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 <typename Reporter>
+ IReporter* reporterCreator(const ContextOptions& o) {
+ return new Reporter(o);
+ }
+} // namespace detail
+
+template <typename Reporter>
+int registerReporter(const char* name, int priority, bool isReporter) {
+ detail::registerReporterImpl(name, priority, detail::reporterCreator<Reporter>, 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<void>(__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 <typeinfo> 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 <typename T> \
+ static void func(); \
+ namespace { \
+ template <typename Tuple> \
+ struct iter; \
+ template <typename Type, typename... Rest> \
+ struct iter<std::tuple<Type, Rest...>> \
+ { \
+ iter(const char* file, unsigned line, int index) { \
+ doctest::detail::regTest(doctest::detail::TestCase(func<Type>, file, line, \
+ doctest_detail_test_suite_ns::getCurrentTestSuite(), \
+ doctest::detail::type_to_string<Type>(), \
+ int(line) * 1000 + index) \
+ * dec); \
+ iter<std::tuple<Rest...>>(file, line, index + 1); \
+ } \
+ }; \
+ template <> \
+ struct iter<std::tuple<>> \
+ { \
+ iter(const char*, unsigned, int) {} \
+ }; \
+ } \
+ template <typename T> \
+ 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 <typename T> \
+ 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<reporter>(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<reporter>(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 <ASSERT>_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<doctest::detail::binaryAssertComparison::comp>( \
+ __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::detail::binaryAssertComparison::comparison>( \
+ 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 <typename DOCTEST_UNUSED_TEMPLATE_TYPE> \
+ struct der : public base \
+ { void f(); }; \
+ } \
+ template <typename DOCTEST_UNUSED_TEMPLATE_TYPE> \
+ inline void der<DOCTEST_UNUSED_TEMPLATE_TYPE>::f()
+
+#define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, name) \
+ template <typename DOCTEST_UNUSED_TEMPLATE_TYPE> \
+ 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 <typeinfo> 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 <typename type> \
+ inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)()
+
+#define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, type, id) \
+ template <typename type> \
+ 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 <typename DOCTEST_UNUSED_TEMPLATE_TYPE> \
+ 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 <ctime>
+#include <cmath>
+#include <climits>
+// borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/onqtam/doctest/pull/37
+#ifdef __BORLANDC__
+#include <math.h>
+#endif // __BORLANDC__
+#include <new>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+#include <utility>
+#include <fstream>
+#include <sstream>
+#include <iostream>
+#include <algorithm>
+#include <iomanip>
+#include <vector>
+#include <atomic>
+#include <mutex>
+#include <set>
+#include <map>
+#include <exception>
+#include <stdexcept>
+#ifdef DOCTEST_CONFIG_POSIX_SIGNALS
+#include <csignal>
+#endif // DOCTEST_CONFIG_POSIX_SIGNALS
+#include <cfloat>
+#include <cctype>
+#include <cstdint>
+
+#ifdef DOCTEST_PLATFORM_MAC
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/sysctl.h>
+#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 <AfxWin.h>
+#else
+#if defined(__MINGW32__) || defined(__MINGW64__)
+#include <windows.h>
+#else // MINGW
+#include <Windows.h>
+#endif // MINGW
+#endif
+#include <io.h>
+
+#else // DOCTEST_PLATFORM_WINDOWS
+
+#include <sys/time.h>
+#include <unistd.h>
+
+#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 <typename T>
+ 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<char*>(&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<int>(size), inc = 1;
+ if(Endianness::which() == Endianness::Little) {
+ i = end - 1;
+ end = inc = -1;
+ }
+
+ unsigned const char* bytes = static_cast<unsigned const char*>(object);
+ std::ostringstream oss;
+ oss << "0x" << std::setfill('0') << std::hex;
+ for(; i != end; i += inc)
+ oss << std::setw(2) << static_cast<unsigned>(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<ticks_t>(t.tv_sec) * 1000000 + static_cast<ticks_t>(t.tv_usec);
+ }
+#endif // DOCTEST_PLATFORM_WINDOWS
+
+ struct Timer
+ {
+ void start() { m_ticks = getCurrentTicks(); }
+ unsigned int getElapsedMicroseconds() const {
+ return static_cast<unsigned int>(getCurrentTicks() - m_ticks);
+ }
+ //unsigned int getElapsedMilliseconds() const {
+ // return static_cast<unsigned int>(getElapsedMicroseconds() / 1000);
+ //}
+ double getElapsedSeconds() const { return static_cast<double>(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<int> numAssertsCurrentTest_atomic;
+ std::atomic<int> numAssertsFailedCurrentTest_atomic;
+
+ std::vector<std::vector<String>> filters = decltype(filters)(9); // 9 different filters
+
+ std::vector<IReporter*> reporters_currently_used;
+
+ const TestCase* currentTest = nullptr;
+
+ assert_handler ah = nullptr;
+
+ Timer timer;
+
+ std::vector<String> stringifiedContexts; // logging from INFO() due to an exception
+
+ // stuff for subcases
+ std::vector<SubcaseSignature> subcasesStack;
+ std::set<decltype(subcasesStack)> subcasesPassed;
+ int subcasesCurrentMaxLevel;
+ bool should_reenter;
+ std::atomic<bool> 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<unsigned char*>(&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<String*>(this)->operator[](i); // NOLINT
+}
+
+char& String::operator[](unsigned i) {
+ if(isOnStack())
+ return reinterpret_cast<char*>(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<const char*>(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<double>(std::numeric_limits<float>::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<double>(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<std::pair<int, String>, 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<String>& 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<TestCaseData&>(*this) = static_cast<const TestCaseData&>(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<TestCase>& getRegisteredTests() {
+ static std::set<TestCase> 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<const IExceptionTranslator*>& getExceptionTranslators() {
+ static std::vector<const IExceptionTranslator*> 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<IContextScope*> 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 = "<unknown signal>";
+ 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 <typename Ex>
+ 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<typename T>
+ 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<typename T>
+ 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<std::string> 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<int>(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_tags.back() << ">";
+ }
+ 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 << "<!--" << text << "-->";
+ // m_needsNewline = true;
+ // return *this;
+ //}
+
+ //void XmlWriter::writeStylesheetRef( std::string const& url ) {
+ // m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\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 << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<String> 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<JUnitTestMessage> failures, errors;
+ };
+
+ void add(const std::string& classname, const std::string& name) {
+ testcases.emplace_back(classname, name);
+ }
+
+ void appendSubcaseNamesToLastTestcase(std::vector<String> 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<JUnitTestCase> 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<std::mutex> lock(mutex);
+ testCaseData.addError("exception", e.error_string.c_str());
+ }
+
+ void subcase_start(const SubcaseSignature& in) override {
+ std::lock_guard<std::mutex> 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<std::mutex> 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<SubcaseSignature> 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<int>(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 <int>/<string> options/filters are:\n\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-case=<filters> "
+ << Whitespace(sizePrefixDisplay*1) << "filters tests by their name\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tce, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-case-exclude=<filters> "
+ << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their name\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sf, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "source-file=<filters> "
+ << Whitespace(sizePrefixDisplay*1) << "filters tests by their file\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sfe, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "source-file-exclude=<filters> "
+ << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their file\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ts, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-suite=<filters> "
+ << Whitespace(sizePrefixDisplay*1) << "filters tests by their test suite\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "tse, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "test-suite-exclude=<filters> "
+ << Whitespace(sizePrefixDisplay*1) << "filters OUT tests by their test suite\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase=<filters> "
+ << Whitespace(sizePrefixDisplay*1) << "filters subcases by their name\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "sce, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase-exclude=<filters> "
+ << Whitespace(sizePrefixDisplay*1) << "filters OUT subcases by their name\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "r, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "reporters=<filters> "
+ << Whitespace(sizePrefixDisplay*1) << "reporters to use (console is default)\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "o, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "out=<string> "
+ << Whitespace(sizePrefixDisplay*1) << "output filename\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ob, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "order-by=<string> "
+ << Whitespace(sizePrefixDisplay*1) << "how the tests should be ordered\n";
+ s << Whitespace(sizePrefixDisplay*3) << " <string> - by [file/suite/name/rand]\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "rs, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "rand-seed=<int> "
+ << Whitespace(sizePrefixDisplay*1) << "seed for random ordering\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "f, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "first=<int> "
+ << 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=<int> "
+ << 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=<int> "
+ << Whitespace(sizePrefixDisplay*1) << "stop after <int> failed assertions\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "scfl,--" DOCTEST_OPTIONS_PREFIX_DISPLAY "subcase-filter-levels=<int> "
+ << Whitespace(sizePrefixDisplay*1) << "apply filters for the first <int> 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=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "include successful assertions in output\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "cs, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "case-sensitive=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "filters being treated as case sensitive\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "e, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "exit=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "exits after the tests finish\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "d, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "duration=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "prints the time duration of each test\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nt, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-throw=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "skips exceptions-related assert checks\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ne, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-exitcode=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "returns (or exits) always with success\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nr, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-run=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "skips all runtime doctest operations\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nv, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-version=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "omit the framework version in the output\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-colors=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "disables colors in output\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "fc, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "force-colors=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "use colors even when not in a tty\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nb, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-breaks=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "disables breakpoints in debuggers\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "ns, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-skip=<bool> "
+ << 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=<bool> "
+ << 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=<bool> "
+ << Whitespace(sizePrefixDisplay*1) << "only filenames and no paths in output\n";
+ s << " -" DOCTEST_OPTIONS_PREFIX_DISPLAY "nln, --" DOCTEST_OPTIONS_PREFIX_DISPLAY "no-line-numbers=<bool> "
+ << 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<std::mutex> lock(mutex);
+ subcasesStack.push_back(subc);
+ ++currentSubcaseLevel;
+ hasLoggedCurrentTestStart = false;
+ }
+
+ void subcase_end() override {
+ std::lock_guard<std::mutex> lock(mutex);
+ --currentSubcaseLevel;
+ hasLoggedCurrentTestStart = false;
+ }
+
+ void log_assert(const AssertData& rb) override {
+ if(!rb.m_failed && !opt.success)
+ return;
+
+ std::lock_guard<std::mutex> 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<std::mutex> 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<String>& 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<const TestCase*> 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<String> testSuitesPassingFilt;
+
+ bool query_mode = p->count || p->list_test_cases || p->list_test_suites;
+ std::vector<const TestCaseData*> 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
--- /dev/null
+// 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 <cstdio> // std::FILE
+#include <cstring>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+// 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(<string_view>) && \
+ (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \
+ (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
+# include <string_view>
+# define FMT_USE_STRING_VIEW
+#elif FMT_HAS_INCLUDE("experimental/string_view") && __cplusplus >= 201402L
+# include <experimental/string_view>
+# 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 <bool B, class T = void>
+using enable_if_t = typename std::enable_if<B, T>::type;
+template <bool B, class T, class F>
+using conditional_t = typename std::conditional<B, T, F>::type;
+template <bool B> using bool_constant = std::integral_constant<bool, B>;
+template <typename T>
+using remove_reference_t = typename std::remove_reference<T>::type;
+template <typename T>
+using remove_const_t = typename std::remove_const<T>::type;
+template <typename T>
+using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
+template <typename T> struct type_identity { using type = T; };
+template <typename T> using type_identity_t = typename type_identity<T>::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 <typename T> 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 <typename Char> using std_string_view = std::basic_string_view<Char>;
+#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
+template <typename Char>
+using std_string_view = std::experimental::basic_string_view<Char>;
+#else
+template <typename T> 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 <typename Int>
+FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
+ FMT_ASSERT(value >= 0, "negative value");
+ return static_cast<typename std::make_unsigned<Int>::type>(value);
+}
+
+FMT_SUPPRESS_MSC_WARNING(4566) constexpr unsigned char micro[] = "\u00B5";
+
+template <typename Char> 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 <typename Char> 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<Char>::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<Char>::length(s)) {}
+
+ /** Constructs a string reference from a ``std::basic_string`` object. */
+ template <typename Traits, typename Alloc>
+ FMT_CONSTEXPR basic_string_view(
+ const std::basic_string<Char, Traits, Alloc>& s) FMT_NOEXCEPT
+ : data_(s.data()),
+ size_(s.size()) {}
+
+ template <typename S, FMT_ENABLE_IF(std::is_same<
+ S, detail::std_string_view<Char>>::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<Char>::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<char>;
+using wstring_view = basic_string_view<wchar_t>;
+
+/** Specifies if ``T`` is a character type. Can be specialized by users. */
+template <typename T> struct is_char : std::false_type {};
+template <> struct is_char<char> : std::true_type {};
+template <> struct is_char<wchar_t> : std::true_type {};
+template <> struct is_char<detail::char8_type> : std::true_type {};
+template <> struct is_char<char16_t> : std::true_type {};
+template <> struct is_char<char32_t> : 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 <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
+inline basic_string_view<Char> to_string_view(const Char* s) {
+ return s;
+}
+
+template <typename Char, typename Traits, typename Alloc>
+inline basic_string_view<Char> to_string_view(
+ const std::basic_string<Char, Traits, Alloc>& s) {
+ return s;
+}
+
+template <typename Char>
+inline basic_string_view<Char> to_string_view(basic_string_view<Char> s) {
+ return s;
+}
+
+template <typename Char,
+ FMT_ENABLE_IF(!std::is_empty<detail::std_string_view<Char>>::value)>
+inline basic_string_view<Char> to_string_view(detail::std_string_view<Char> 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 <typename S>
+struct is_compile_string : std::is_base_of<compile_string, S> {};
+
+template <typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
+constexpr basic_string_view<typename S::char_type> 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 <typename S>
+struct is_string : std::is_class<decltype(to_string_view(std::declval<S>()))> {
+};
+
+template <typename S, typename = void> struct char_t_impl {};
+template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
+ using result = decltype(to_string_view(std::declval<S>()));
+ 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 <typename S> using char_t = typename detail::char_t_impl<S>::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<char> |
+ +-----------------------+-------------------------------------+
+ | wformat_parse_context | basic_format_parse_context<wchar_t> |
+ +-----------------------+-------------------------------------+
+ \endrst
+ */
+template <typename Char, typename ErrorHandler = detail::error_handler>
+class basic_format_parse_context : private ErrorHandler {
+ private:
+ basic_string_view<Char> format_str_;
+ int next_arg_id_;
+
+ public:
+ using char_type = Char;
+ using iterator = typename basic_string_view<Char>::iterator;
+
+ explicit constexpr basic_format_parse_context(
+ basic_string_view<Char> 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<Char>) {}
+
+ 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<char>;
+using wformat_parse_context = basic_format_parse_context<wchar_t>;
+
+template <typename Context> class basic_format_arg;
+template <typename Context> class basic_format_args;
+template <typename Context> class dynamic_format_arg_store;
+
+// A formatter for objects of type T.
+template <typename T, typename Char = char, typename Enable = void>
+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 <typename T, typename Context>
+using has_formatter =
+ std::is_constructible<typename Context::template formatter_type<T>>;
+
+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 <typename T> 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 <typename U> void append(const U* begin, const U* end);
+
+ template <typename I> T& operator[](I index) { return ptr_[index]; }
+ template <typename I> const T& operator[](I index) const {
+ return ptr_[index];
+ }
+};
+
+// A container-backed buffer.
+template <typename Container>
+class container_buffer : public buffer<typename Container::value_type> {
+ 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<typename Container::value_type>(c.size()), container_(c) {}
+};
+
+// Extracts a reference to the container from back_insert_iterator.
+template <typename Container>
+inline Container& get_container(std::back_insert_iterator<Container> it) {
+ using bi_iterator = std::back_insert_iterator<Container>;
+ struct accessor : bi_iterator {
+ accessor(bi_iterator iter) : bi_iterator(iter) {}
+ using bi_iterator::container;
+ };
+ return *accessor(it).container;
+}
+
+template <typename T, typename Char = char, typename Enable = void>
+struct fallback_formatter {
+ fallback_formatter() = delete;
+};
+
+// Specifies if T has an enabled fallback_formatter specialization.
+template <typename T, typename Context>
+using has_fallback_formatter =
+ std::is_constructible<fallback_formatter<T, typename Context::char_type>>;
+
+struct view {};
+
+template <typename Char, typename T> struct named_arg : view {
+ const Char* name;
+ const T& value;
+ named_arg(const Char* n, const T& v) : name(n), value(v) {}
+};
+
+template <typename Char> struct named_arg_info {
+ const Char* name;
+ int id;
+};
+
+template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
+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<Char> named_args_[NUM_NAMED_ARGS];
+
+ template <typename... U>
+ 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<Char>* named_args() { return named_args_; }
+};
+
+template <typename T, typename Char, size_t NUM_ARGS>
+struct arg_data<T, Char, NUM_ARGS, 0> {
+ T args_[NUM_ARGS != 0 ? NUM_ARGS : 1];
+
+ template <typename... U>
+ 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 <typename Char>
+inline void init_named_args(named_arg_info<Char>*, int, int) {}
+
+template <typename Char, typename T, typename... Tail>
+void init_named_args(named_arg_info<Char>* 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 <typename Char, typename T, typename... Tail>
+void init_named_args(named_arg_info<Char>* named_args, int arg_count,
+ int named_arg_count, const named_arg<Char, T>& 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 <typename... Args>
+FMT_INLINE void init_named_args(std::nullptr_t, int, int, const Args&...) {}
+
+template <typename T> struct is_named_arg : std::false_type {};
+
+template <typename T, typename Char>
+struct is_named_arg<named_arg<Char, T>> : std::true_type {};
+
+template <bool B = false> constexpr size_t count() { return B ? 1 : 0; }
+template <bool B1, bool B2, bool... Tail> constexpr size_t count() {
+ return (B1 ? 1 : 0) + count<B2, Tail...>();
+}
+
+template <typename... Args> constexpr size_t count_named_args() {
+ return count<is_named_arg<Args>::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 <typename T, typename Char>
+struct type_constant : std::integral_constant<type, type::custom_type> {};
+
+#define FMT_TYPE_CONSTANT(Type, constant) \
+ template <typename Char> \
+ struct type_constant<Type, Char> \
+ : std::integral_constant<type, type::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<Char>, 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 <typename Char> struct string_value {
+ const Char* data;
+ size_t size;
+};
+
+template <typename Char> struct named_arg_value {
+ const named_arg_info<Char>* data;
+ size_t size;
+};
+
+template <typename Context> 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 <typename Context> 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<char_type> string;
+ custom_value<Context> custom;
+ named_arg_value<char_type> 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<char_type> val) {
+ string.data = val.data();
+ string.size = val.size();
+ }
+ FMT_INLINE value(const void* val) : pointer(val) {}
+ FMT_INLINE value(const named_arg_info<char_type>* args, size_t size)
+ : named_args{args, size} {}
+
+ template <typename T> 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<T>` for `format` and
+ // `printf_formatter<T>` for `printf`.
+ custom.format = format_custom_arg<
+ T, conditional_t<has_formatter<T, Context>::value,
+ typename Context::template formatter_type<T>,
+ fallback_formatter<T, char_type>>>;
+ }
+
+ private:
+ // Formats an argument of a custom type, such as a user-defined class.
+ template <typename T, typename Formatter>
+ 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<const T*>(arg), ctx));
+ }
+};
+
+template <typename Context, typename T>
+FMT_CONSTEXPR basic_format_arg<Context> 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<long_short, int, long long>;
+using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
+
+// Maps formatting arguments to core types.
+template <typename Context> 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 <typename T, FMT_ENABLE_IF(is_char<T>::value)>
+ FMT_CONSTEXPR char_type map(T val) {
+ static_assert(
+ std::is_same<T, char>::value || std::is_same<T, char_type>::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 <typename T, FMT_ENABLE_IF(is_string<T>::value)>
+ FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
+ static_assert(std::is_same<char_type, char_t<T>>::value,
+ "mixing character types is disallowed");
+ return to_string_view(val);
+ }
+ template <typename T,
+ FMT_ENABLE_IF(
+ std::is_constructible<basic_string_view<char_type>, T>::value &&
+ !is_string<T>::value && !has_formatter<T, Context>::value &&
+ !has_fallback_formatter<T, Context>::value)>
+ FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
+ return basic_string_view<char_type>(val);
+ }
+ template <
+ typename T,
+ FMT_ENABLE_IF(
+ std::is_constructible<std_string_view<char_type>, T>::value &&
+ !std::is_constructible<basic_string_view<char_type>, T>::value &&
+ !is_string<T>::value && !has_formatter<T, Context>::value &&
+ !has_fallback_formatter<T, Context>::value)>
+ FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
+ return std_string_view<char_type>(val);
+ }
+ FMT_CONSTEXPR const char* map(const signed char* val) {
+ static_assert(std::is_same<char_type, char>::value, "invalid string type");
+ return reinterpret_cast<const char*>(val);
+ }
+ FMT_CONSTEXPR const char* map(const unsigned char* val) {
+ static_assert(std::is_same<char_type, char>::value, "invalid string type");
+ return reinterpret_cast<const char*>(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 <typename T> 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 <typename T,
+ FMT_ENABLE_IF(std::is_enum<T>::value &&
+ !has_formatter<T, Context>::value &&
+ !has_fallback_formatter<T, Context>::value)>
+ FMT_CONSTEXPR auto map(const T& val)
+ -> decltype(std::declval<arg_mapper>().map(
+ static_cast<typename std::underlying_type<T>::type>(val))) {
+ return map(static_cast<typename std::underlying_type<T>::type>(val));
+ }
+ template <typename T,
+ FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
+ (has_formatter<T, Context>::value ||
+ has_fallback_formatter<T, Context>::value))>
+ FMT_CONSTEXPR const T& map(const T& val) {
+ return val;
+ }
+
+ template <typename T>
+ FMT_CONSTEXPR auto map(const named_arg<char_type, T>& val)
+ -> decltype(std::declval<arg_mapper>().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<T> specialization: "
+ "https://fmt.dev/latest/api.html#formatting-user-defined-types");
+ return 0;
+ }
+};
+
+// A type constant after applying arg_mapper<Context>.
+template <typename T, typename Context>
+using mapped_type_constant =
+ type_constant<decltype(arg_mapper<Context>().map(std::declval<const T&>())),
+ 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 <typename Context> class basic_format_arg {
+ private:
+ detail::value<Context> value_;
+ detail::type type_;
+
+ template <typename ContextType, typename T>
+ friend FMT_CONSTEXPR basic_format_arg<ContextType> detail::make_arg(
+ const T& value);
+
+ template <typename Visitor, typename Ctx>
+ friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
+ const basic_format_arg<Ctx>& arg)
+ -> decltype(vis(0));
+
+ friend class basic_format_args<Context>;
+ friend class dynamic_format_arg_store<Context>;
+
+ using char_type = typename Context::char_type;
+
+ template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
+ friend struct detail::arg_data;
+
+ basic_format_arg(const detail::named_arg_info<char_type>* args, size_t size)
+ : value_(args, size) {}
+
+ public:
+ class handle {
+ public:
+ explicit handle(detail::custom_value<Context> 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<Context> 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 <typename Visitor, typename Context>
+FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg(
+ Visitor&& vis, const basic_format_arg<Context>& 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<char_type>(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<Context>::handle(arg.value_.custom));
+ }
+ return vis(monostate());
+}
+
+// Checks whether T is a container with contiguous storage.
+template <typename T> struct is_contiguous : std::false_type {};
+template <typename Char>
+struct is_contiguous<std::basic_string<Char>> : std::true_type {};
+template <typename Char>
+struct is_contiguous<detail::buffer<Char>> : std::true_type {};
+
+namespace detail {
+
+template <typename OutputIt>
+struct is_back_insert_iterator : std::false_type {};
+template <typename Container>
+struct is_back_insert_iterator<std::back_insert_iterator<Container>>
+ : std::true_type {};
+
+template <typename OutputIt>
+struct is_contiguous_back_insert_iterator : std::false_type {};
+template <typename Container>
+struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
+ : is_contiguous<Container> {};
+
+// A type-erased reference to an std::locale to avoid heavy <locale> include.
+class locale_ref {
+ private:
+ const void* locale_; // A type-erased pointer to std::locale.
+
+ public:
+ locale_ref() : locale_(nullptr) {}
+ template <typename Locale> explicit locale_ref(const Locale& loc);
+
+ explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; }
+
+ template <typename Locale> Locale get() const;
+};
+
+template <typename> constexpr unsigned long long encode_types() { return 0; }
+
+template <typename Context, typename Arg, typename... Args>
+constexpr unsigned long long encode_types() {
+ return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
+ (encode_types<Context, Args...>() << packed_arg_bits);
+}
+
+template <typename Context, typename T>
+FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T& value) {
+ basic_format_arg<Context> arg;
+ arg.type_ = mapped_type_constant<T, Context>::value;
+ arg.value_ = arg_mapper<Context>().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 <bool IS_PACKED, typename Context, type, typename T,
+ FMT_ENABLE_IF(IS_PACKED)>
+inline value<Context> make_arg(const T& val) {
+ return arg_mapper<Context>().map(val);
+}
+
+template <bool IS_PACKED, typename Context, type, typename T,
+ FMT_ENABLE_IF(!IS_PACKED)>
+inline basic_format_arg<Context> make_arg(const T& value) {
+ return make_arg<Context>(value);
+}
+
+template <typename T> struct is_reference_wrapper : std::false_type {};
+template <typename T>
+struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
+
+template <typename T> const T& unwrap(const T& v) { return v; }
+template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
+ return static_cast<const T&>(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 <typename = void> struct node {
+ virtual ~node() = default;
+ std::unique_ptr<node<>> next;
+ };
+
+ template <typename T> struct typed_node : node<> {
+ T value;
+
+ template <typename Arg>
+ FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
+
+ template <typename Char>
+ FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
+ : value(arg.data(), arg.size()) {}
+ };
+
+ std::unique_ptr<node<>> head_;
+
+ public:
+ template <typename T, typename Arg> const T& push(const Arg& arg) {
+ auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
+ auto& value = new_node->value;
+ new_node->next = std::move(head_);
+ head_ = std::move(new_node);
+ return value;
+ }
+};
+} // namespace detail
+
+// Formatting context.
+template <typename OutputIt, typename Char> class basic_format_context {
+ public:
+ /** The character type for the output. */
+ using char_type = Char;
+
+ private:
+ OutputIt out_;
+ basic_format_args<basic_format_context> args_;
+ detail::locale_ref loc_;
+
+ public:
+ using iterator = OutputIt;
+ using format_arg = basic_format_arg<basic_format_context>;
+ using parse_context_type = basic_format_parse_context<Char>;
+ template <typename T> using formatter_type = formatter<T, char_type>;
+
+ 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<basic_format_context> 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<char_type> name) { return args_.get(name); }
+ int arg_id(basic_string_view<char_type> name) { return args_.get_id(name); }
+ const basic_format_args<basic_format_context>& 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<iterator>()) out_ = it;
+ }
+
+ detail::locale_ref locale() { return loc_; }
+};
+
+template <typename Char>
+using buffer_context =
+ basic_format_context<std::back_insert_iterator<detail::buffer<Char>>, Char>;
+using format_context = buffer_context<char>;
+using wformat_context = buffer_context<wchar_t>;
+
+// Workaround a bug in gcc: https://stackoverflow.com/q/62767544/471164.
+#define FMT_BUFFER_CONTEXT(Char) \
+ basic_format_context<std::back_insert_iterator<detail::buffer<Char>>, 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 <typename Context, typename... Args>
+class format_arg_store
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+ // Workaround a GCC template argument substitution bug.
+ : public basic_format_args<Context>
+#endif
+{
+ private:
+ static const size_t num_args = sizeof...(Args);
+ static const size_t num_named_args = detail::count_named_args<Args...>();
+ static const bool is_packed = num_args <= detail::max_packed_args;
+
+ using value_type = conditional_t<is_packed, detail::value<Context>,
+ basic_format_arg<Context>>;
+
+ detail::arg_data<value_type, typename Context::char_type, num_args,
+ num_named_args>
+ data_;
+
+ friend class basic_format_args<Context>;
+
+ static constexpr unsigned long long desc =
+ (is_packed ? detail::encode_types<Context, Args...>()
+ : detail::is_unpacked_bit | num_args) |
+ (num_named_args != 0
+ ? static_cast<unsigned long long>(detail::has_named_args_bit)
+ : 0);
+
+ public:
+ format_arg_store(const Args&... args)
+ :
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+ basic_format_args<Context>(*this),
+#endif
+ data_{detail::make_arg<
+ is_packed, Context,
+ detail::mapped_type_constant<Args, Context>::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 <typename Context = format_context, typename... Args>
+inline format_arg_store<Context, Args...> 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 <typename Char, typename T>
+inline detail::named_arg<Char, T> arg(const Char* name, const T& arg) {
+ static_assert(!detail::is_named_arg<T>(), "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 <typename Context>
+class dynamic_format_arg_store
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+ // Workaround a GCC template argument substitution bug.
+ : public basic_format_args<Context>
+#endif
+{
+ private:
+ using char_type = typename Context::char_type;
+
+ template <typename T> struct need_copy {
+ static constexpr detail::type mapped_type =
+ detail::mapped_type_constant<T, Context>::value;
+
+ enum {
+ value = !(detail::is_reference_wrapper<T>::value ||
+ std::is_same<T, basic_string_view<char_type>>::value ||
+ std::is_same<T, detail::std_string_view<char_type>>::value ||
+ (mapped_type != detail::type::cstring_type &&
+ mapped_type != detail::type::string_type &&
+ mapped_type != detail::type::custom_type))
+ };
+ };
+
+ template <typename T>
+ using stored_type = conditional_t<detail::is_string<T>::value,
+ std::basic_string<char_type>, T>;
+
+ // Storage of basic_format_arg must be contiguous.
+ std::vector<basic_format_arg<Context>> data_;
+ std::vector<detail::named_arg_info<char_type>> 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<Context>;
+
+ unsigned long long get_types() const {
+ return detail::is_unpacked_bit | data_.size() |
+ (named_info_.empty()
+ ? 0ULL
+ : static_cast<unsigned long long>(detail::has_named_args_bit));
+ }
+
+ const basic_format_arg<Context>* data() const {
+ return named_info_.empty() ? data_.data() : data_.data() + 1;
+ }
+
+ template <typename T> void emplace_arg(const T& arg) {
+ data_.emplace_back(detail::make_arg<Context>(arg));
+ }
+
+ template <typename T>
+ void emplace_arg(const detail::named_arg<char_type, T>& arg) {
+ if (named_info_.empty()) {
+ constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
+ data_.insert(data_.begin(), {zero_ptr, 0});
+ }
+ data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
+ auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
+ data->pop_back();
+ };
+ std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
+ guard{&data_, pop_one};
+ named_info_.push_back({arg.name, static_cast<int>(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<fmt::format_context> store;
+ store.push_back(42);
+ store.push_back("abc");
+ store.push_back(1.5f);
+ std::string result = fmt::vformat("{} and {} and {}", store);
+ \endrst
+ */
+ template <typename T> void push_back(const T& arg) {
+ if (detail::const_check(need_copy<T>::value))
+ emplace_arg(dynamic_args_.push<stored_type<T>>(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<fmt::format_context> 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 <typename T> void push_back(std::reference_wrapper<T> arg) {
+ static_assert(
+ detail::is_named_arg<typename std::remove_cv<T>::type>::value ||
+ need_copy<T>::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 <typename T>
+ void push_back(const detail::named_arg<char_type, T>& arg) {
+ const char_type* arg_name =
+ dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
+ if (detail::const_check(need_copy<T>::value)) {
+ emplace_arg(
+ fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(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 <typename Context> class basic_format_args {
+ public:
+ using size_type = int;
+ using format_arg = basic_format_arg<Context>;
+
+ 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<Context>* 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<detail::type>((desc_ >> shift) & mask);
+ }
+
+ basic_format_args(unsigned long long desc,
+ const detail::value<Context>* 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 <typename... Args>
+ FMT_INLINE basic_format_args(const format_arg_store<Context, Args...>& 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<Context>& 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 <typename Char> format_arg get(basic_string_view<Char> name) const {
+ int id = get_id(name);
+ return id >= 0 ? get(id) : format_arg();
+ }
+
+ template <typename Char> int get_id(basic_string_view<Char> 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<int>(is_packed() ? max_packed
+ : desc_ & ~detail::is_unpacked_bit);
+ }
+};
+
+/** An alias to ``basic_format_args<context>``. */
+// It is a separate type rather than an alias to make symbols readable.
+struct format_args : basic_format_args<format_context> {
+ template <typename... Args>
+ FMT_INLINE format_args(const Args&... args) : basic_format_args(args...) {}
+};
+struct wformat_args : basic_format_args<wformat_context> {
+ using basic_format_args::basic_format_args;
+};
+
+namespace detail {
+
+// Reports a compile-time error if S is not a valid format string.
+template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
+FMT_INLINE void check_format_string(const S&) {
+#ifdef FMT_ENFORCE_COMPILE_STRING
+ static_assert(is_compile_string<S>::value,
+ "FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
+ "FMT_STRING.");
+#endif
+}
+template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
+void check_format_string(S);
+
+template <typename... Args, typename S, typename Char = char_t<S>>
+inline format_arg_store<buffer_context<Char>, remove_reference_t<Args>...>
+make_args_checked(const S& format_str,
+ const remove_reference_t<Args>&... args) {
+ static_assert(count<(std::is_base_of<view, remove_reference_t<Args>>::value &&
+ std::is_reference<Args>::value)...>() == 0,
+ "passing views as lvalues is disallowed");
+ check_format_string<Args...>(format_str);
+ return {args...};
+}
+
+template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
+std::basic_string<Char> vformat(
+ basic_string_view<Char> format_str,
+ basic_format_args<buffer_context<type_identity_t<Char>>> args);
+
+FMT_API std::string vformat(string_view format_str, format_args args);
+
+template <typename Char>
+typename FMT_BUFFER_CONTEXT(Char)::iterator vformat_to(
+ buffer<Char>& buf, basic_string_view<Char> format_str,
+ basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args);
+
+template <typename Char, typename Args,
+ FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
+inline void vprint_mojibake(std::FILE*, basic_string_view<Char>, 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<Container> with
+// vformat_to<ArgFormatter>(...) overload, so SFINAE on iterator type instead.
+template <
+ typename OutputIt, typename S, typename Char = char_t<S>,
+ FMT_ENABLE_IF(detail::is_contiguous_back_insert_iterator<OutputIt>::value)>
+OutputIt vformat_to(
+ OutputIt out, const S& format_str,
+ basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+ auto& c = detail::get_container(out);
+ detail::container_buffer<remove_reference_t<decltype(c)>> buf(c);
+ detail::vformat_to(buf, to_string_view(format_str), args);
+ return out;
+}
+
+template <typename Container, typename S, typename... Args,
+ FMT_ENABLE_IF(
+ is_contiguous<Container>::value&& detail::is_string<S>::value)>
+inline std::back_insert_iterator<Container> format_to(
+ std::back_insert_iterator<Container> out, const S& format_str,
+ Args&&... args) {
+ return vformat_to(out, to_string_view(format_str),
+ detail::make_args_checked<Args...>(format_str, args...));
+}
+
+template <typename S, typename Char = char_t<S>>
+FMT_INLINE std::basic_string<Char> vformat(
+ const S& format_str,
+ basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+ return detail::vformat(to_string_view(format_str), args);
+}
+
+/**
+ \rst
+ Formats arguments and returns the result as a string.
+
+ **Example**::
+
+ #include <fmt/core.h>
+ std::string message = fmt::format("The answer is {}", 42);
+ \endrst
+*/
+// Pass char_t as a default template parameter instead of using
+// std::basic_string<char_t<S>> to reduce the symbol size.
+template <typename S, typename... Args, typename Char = char_t<S>>
+FMT_INLINE std::basic_string<Char> format(const S& format_str, Args&&... args) {
+ const auto& vargs = detail::make_args_checked<Args...>(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 <typename S, typename... Args, typename Char = char_t<S>>
+inline void print(std::FILE* f, const S& format_str, Args&&... args) {
+ const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
+ return detail::is_unicode<Char>()
+ ? 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 <typename S, typename... Args, typename Char = char_t<S>>
+inline void print(const S& format_str, Args&&... args) {
+ const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
+ return detail::is_unicode<Char>()
+ ? vprint(to_string_view(format_str), vargs)
+ : detail::vprint_mojibake(stdout, to_string_view(format_str),
+ vargs);
+}
+FMT_END_NAMESPACE
+
+#endif // FMT_CORE_H_
--- /dev/null
+// 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 <cassert>
+#include <cctype>
+#include <climits>
+#include <cmath>
+#include <cstdarg>
+#include <cstring> // for std::memmove
+#include <cwchar>
+#include <exception>
+
+#include "format.h"
+#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
+# include <locale>
+#endif
+
+#ifdef _WIN32
+# if !defined(NOMINMAX) && !defined(WIN32_LEAN_AND_MEAN)
+# define NOMINMAX
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# undef WIN32_LEAN_AND_MEAN
+# undef NOMINMAX
+# else
+# include <windows.h>
+# endif
+# include <io.h>
+#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<char>& 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<uint32_or_64_or_128_t<int>>(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 <typename Locale>
+locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
+ static_assert(std::is_same<Locale, std::locale>::value, "");
+}
+
+template <typename Locale> Locale locale_ref::get() const {
+ static_assert(std::is_same<Locale, std::locale>::value, "");
+ return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
+}
+
+template <typename Char> FMT_FUNC std::string grouping_impl(locale_ref loc) {
+ return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()).grouping();
+}
+template <typename Char> FMT_FUNC Char thousands_sep_impl(locale_ref loc) {
+ return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
+ .thousands_sep();
+}
+template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
+ return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
+ .decimal_point();
+}
+} // namespace detail
+#else
+template <typename Char>
+FMT_FUNC std::string detail::grouping_impl(locale_ref) {
+ return "\03";
+}
+template <typename Char> FMT_FUNC Char detail::thousands_sep_impl(locale_ref) {
+ return FMT_STATIC_THOUSANDS_SEPARATOR;
+}
+template <typename Char> 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<int>(sizeof(void*)) - 1;
+ while (i > 0 && n.value[i] == 0) --i;
+ auto char_digits = std::numeric_limits<unsigned char>::digits / 4;
+ return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1;
+}
+
+template <typename T>
+const typename basic_data<T>::digit_pair basic_data<T>::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 <typename T>
+const char basic_data<T>::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 <typename T>
+const uint64_t basic_data<T>::powers_of_10_64[] = {
+ 1, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL),
+ 10000000000000000000ULL};
+
+template <typename T>
+const uint32_t basic_data<T>::zero_or_powers_of_10_32[] = {0,
+ FMT_POWERS_OF_10(1)};
+
+template <typename T>
+const uint64_t basic_data<T>::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 <typename T>
+const uint64_t basic_data<T>::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 <typename T>
+const int16_t basic_data<T>::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 <typename T>
+const char basic_data<T>::foreground_color[] = "\x1b[38;2;";
+template <typename T>
+const char basic_data<T>::background_color[] = "\x1b[48;2;";
+template <typename T> const char basic_data<T>::reset_color[] = "\x1b[0m";
+template <typename T> const wchar_t basic_data<T>::wreset_color[] = L"\x1b[0m";
+template <typename T> const char basic_data<T>::signs[] = {0, '-', '+', ' '};
+template <typename T>
+const char basic_data<T>::left_padding_shifts[] = {31, 31, 0, 1, 0};
+template <typename T>
+const char basic_data<T>::right_padding_shifts[] = {0, 31, 0, 1, 0};
+
+template <typename T> struct bits {
+ static FMT_CONSTEXPR_DECL const int value =
+ static_cast<int>(sizeof(T) * std::numeric_limits<unsigned char>::digits);
+};
+
+class fp;
+template <int SHIFT = 0> 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<double>::digits - 1;
+ static FMT_CONSTEXPR_DECL const uint64_t implicit_bit =
+ 1ULL << double_significand_size;
+ static FMT_CONSTEXPR_DECL const int significand_size =
+ bits<significand_type>::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 <typename Double> explicit fp(Double d) { assign(d); }
+
+ // Assigns d to this and return true iff predecessor is closer than successor.
+ template <typename Double, FMT_ENABLE_IF(sizeof(Double) == sizeof(uint64_t))>
+ bool assign(Double d) {
+ // Assume double is in the format [sign][exponent][significand].
+ using limits = std::numeric_limits<Double>;
+ const int exponent_size =
+ bits<Double>::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<uint64_t>(d);
+ f = u & significand_mask;
+ int biased_e =
+ static_cast<int>((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 <typename Double, FMT_ENABLE_IF(sizeof(Double) != sizeof(uint64_t))>
+ 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 <typename Double> 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 <typename Double> boundaries assign_float_with_boundaries(Double d) {
+ assign(d);
+ constexpr int min_normal_e = std::numeric_limits<float>::min_exponent -
+ std::numeric_limits<double>::digits;
+ significand_type half_ulp = 1 << (std::numeric_limits<double>::digits -
+ std::numeric_limits<float>::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 <int SHIFT> 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<uint64_t>(product >> 64);
+ return (static_cast<uint64_t>(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<int>(
+ ((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<uint32_t>(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<bigit, bigits_capacity> 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<bigit>::value;
+
+ friend struct formatter<bigint>;
+
+ void subtract_bigits(int index, bigit other, bigit& borrow) {
+ auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
+ (*this)[index] = static_cast<bigit>(result);
+ borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
+ }
+
+ void remove_leading_zeros() {
+ int num_bigits = static_cast<int>(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<bigit>(result);
+ carry = static_cast<bigit>(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<bigit>(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<int>(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 <typename Int> bigint& operator*=(Int value) {
+ FMT_ASSERT(value > 0, "");
+ multiply(uint32_or_64_or_128_t<Int>(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<int>(lhs.bigits_.size()) - 1;
+ int j = static_cast<int>(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<double_bigit>(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<bigit, bigits_capacity> n(std::move(bigits_));
+ int num_bigits = static_cast<int>(bigits_.size());
+ int num_result_bigits = 2 * num_bigits;
+ bigits_.resize(to_unsigned(num_result_bigits));
+ using accumulator_t = conditional_t<FMT_USE_INT128, uint128_t, accumulator>;
+ 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<double_bigit>(n[i]) * n[j];
+ }
+ (*this)[bigit_index] = static_cast<bigit>(sum);
+ sum >>= bits<bigit>::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<double_bigit>(n[i++]) * n[j--];
+ (*this)[bigit_index] = static_cast<bigit>(sum);
+ sum >>= bits<bigit>::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<int>(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 <typename Handler>
+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<uint32_t>(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<uint64_t>(integral) << -one.e) + fractional;
+ result = handler.on_digit(static_cast<char>('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<char>('0' + static_cast<char>(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 <typename Double>
+void fallback_format(Double d, buffer<char>& 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<char>('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 <typename T>
+int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
+ static_assert(!std::is_same<T, float>::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 <typename T>
+int snprintf_float(T value, int precision, float_specs specs,
+ buffer<char>& 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<T, float>::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<T, long double>()) *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<int>(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<int>(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<const unsigned char*>(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<detail::bigint> {
+ 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<wchar_t>(cp));
+ } else {
+ cp -= 0x10000;
+ buffer_.push_back(static_cast<wchar_t>(0xD800 + (cp >> 10)));
+ buffer_.push_back(static_cast<wchar_t>(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<char>& 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 <typename T> FMT_INLINE std::string operator()(T value) const {
+ return to_string(value);
+ }
+ std::string operator()(basic_format_arg<format_context>::handle h) const {
+ memory_buffer buf;
+ detail::buffer<char>& 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<buffer_context<char>>(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<HANDLE>(_get_osfhandle(fd)),
+ u16.c_str(), static_cast<DWORD>(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<buffer_context<char>>(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_
--- /dev/null
+/*
+ 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 <algorithm>
+#include <cerrno>
+#include <cmath>
+#include <cstdint>
+#include <limits>
+#include <memory>
+#include <stdexcept>
+
+#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 <typename Exception> 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<void>(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 <intrin.h> // _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<uint32_t>(x >> 32))) return 63 - (r + 32);
+
+ // Scan the low 32 bits.
+ _BitScanReverse(&r, static_cast<uint32_t>(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<Dest*>(&source)` that doesn't have
+// undefined behavior (e.g. due to type aliasing).
+// Example: uint64_t d = bit_cast<uint64_t>(2.718);
+template <typename Dest, typename Source>
+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<bytes>(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<fallback_uintptr>(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<uintptr_t>(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<T>::max() but shorter and not affected by the max macro.
+template <typename T> constexpr T max_value() {
+ return (std::numeric_limits<T>::max)();
+}
+template <typename T> constexpr int num_bits() {
+ return std::numeric_limits<T>::digits;
+}
+// std::numeric_limits<T>::digits may return 0 for 128-bit ints.
+template <> constexpr int num_bits<int128_t>() { return 128; }
+template <> constexpr int num_bits<uint128_t>() { return 128; }
+template <> constexpr int num_bits<fallback_uintptr>() {
+ return static_cast<int>(sizeof(void*) *
+ std::numeric_limits<unsigned char>::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 <typename... Ts> struct void_t_impl { using type = void; };
+
+template <typename... Ts>
+using void_t = typename detail::void_t_impl<Ts...>::type;
+
+// An approximation of iterator_t for pre-C++20 systems.
+template <typename T>
+using iterator_t = decltype(std::begin(std::declval<T&>()));
+template <typename T> using sentinel_t = decltype(std::end(std::declval<T&>()));
+
+// 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 <typename It, typename Enable = void>
+struct iterator_category : std::false_type {};
+
+template <typename T> struct iterator_category<T*> {
+ using type = std::random_access_iterator_tag;
+};
+
+template <typename It>
+struct iterator_category<It, void_t<typename It::iterator_category>> {
+ using type = typename It::iterator_category;
+};
+
+// Detect if *any* given type models the OutputIterator concept.
+template <typename It> 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 <typename U>
+ static decltype(*(std::declval<U>())) test(std::input_iterator_tag);
+ template <typename U> static char& test(std::output_iterator_tag);
+ template <typename U> static const char& test(...);
+
+ using type = decltype(test<It>(typename iterator_category<It>::type{}));
+
+ public:
+ enum { value = !std::is_const<remove_reference_t<type>>::value };
+};
+
+// A workaround for std::string not having mutable data() until C++17.
+template <typename Char> inline Char* get_data(std::basic_string<Char>& s) {
+ return &s[0];
+}
+template <typename Container>
+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 <typename T> using checked_ptr = stdext::checked_array_iterator<T*>;
+template <typename T> checked_ptr<T> make_checked(T* p, size_t size) {
+ return {p, size};
+}
+#else
+template <typename T> using checked_ptr = T*;
+template <typename T> inline T* make_checked(T* p, size_t) { return p; }
+#endif
+
+template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
+#if FMT_CLANG_VERSION
+__attribute__((no_sanitize("undefined")))
+#endif
+inline checked_ptr<typename Container::value_type>
+reserve(std::back_insert_iterator<Container> 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 <typename Iterator> inline Iterator& reserve(Iterator& it, size_t) {
+ return it;
+}
+
+template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
+inline std::back_insert_iterator<Container> base_iterator(
+ std::back_insert_iterator<Container>& it,
+ checked_ptr<typename Container::value_type>) {
+ return it;
+}
+
+template <typename Iterator>
+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 <typename T> 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 <typename OutputIt> 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<OutputIt>::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 <typename OutputIt,
+ typename Enable = typename std::is_void<
+ typename std::iterator_traits<OutputIt>::value_type>::type>
+class truncating_iterator;
+
+template <typename OutputIt>
+class truncating_iterator<OutputIt, std::false_type>
+ : public truncating_iterator_base<OutputIt> {
+ mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
+
+ public:
+ using value_type = typename truncating_iterator_base<OutputIt>::value_type;
+
+ truncating_iterator(OutputIt out, size_t limit)
+ : truncating_iterator_base<OutputIt>(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 <typename OutputIt>
+class truncating_iterator<OutputIt, std::true_type>
+ : public truncating_iterator_base<OutputIt> {
+ public:
+ truncating_iterator(OutputIt out, size_t limit)
+ : truncating_iterator_base<OutputIt>(out, limit) {}
+
+ template <typename T> 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 <typename Char>
+inline size_t count_code_points(basic_string_view<Char> s) {
+ return s.size();
+}
+
+// Counts the number of code points in a UTF-8 string.
+inline size_t count_code_points(basic_string_view<char> 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<char8_type> s) {
+ return count_code_points(basic_string_view<char>(
+ reinterpret_cast<const char*>(s.data()), s.size()));
+}
+
+template <typename Char>
+inline size_t code_point_index(basic_string_view<Char> 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<char8_type> 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 <typename InputIt, typename OutChar>
+using needs_conversion = bool_constant<
+ std::is_same<typename std::iterator_traits<InputIt>::value_type,
+ char>::value &&
+ std::is_same<OutChar, char8_type>::value>;
+
+template <typename OutChar, typename InputIt, typename OutputIt,
+ FMT_ENABLE_IF(!needs_conversion<InputIt, OutChar>::value)>
+OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
+ return std::copy(begin, end, it);
+}
+
+template <typename OutChar, typename InputIt, typename OutputIt,
+ FMT_ENABLE_IF(needs_conversion<InputIt, OutChar>::value)>
+OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
+ return std::transform(begin, end, it,
+ [](char c) { return static_cast<char8_type>(c); });
+}
+
+#ifndef FMT_USE_GRISU
+# define FMT_USE_GRISU 1
+#endif
+
+template <typename T> constexpr bool use_grisu() {
+ return FMT_USE_GRISU && std::numeric_limits<double>::is_iec559 &&
+ sizeof(T) <= sizeof(double);
+}
+
+template <typename T>
+template <typename U>
+void buffer<T>::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<char> |
+ +----------------+------------------------------+
+ | wmemory_buffer | basic_memory_buffer<wchar_t> |
+ +----------------+------------------------------+
+
+ **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 <typename T, size_t SIZE = inline_buffer_size,
+ typename Allocator = std::allocator<T>>
+class basic_memory_buffer : public detail::buffer<T> {
+ 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 <typename T, size_t SIZE, typename Allocator>
+void basic_memory_buffer<T, SIZE, Allocator>::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<Allocator>::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<char>;
+using wmemory_buffer = basic_memory_buffer<wchar_t>;
+
+template <typename T, size_t SIZE, typename Allocator>
+struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : 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 <typename T>
+using is_signed =
+ std::integral_constant<bool, std::numeric_limits<T>::is_signed ||
+ std::is_same<T, int128_t>::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 <typename T, FMT_ENABLE_IF(is_signed<T>::value)>
+FMT_CONSTEXPR bool is_negative(T value) {
+ return value < 0;
+}
+template <typename T, FMT_ENABLE_IF(!is_signed<T>::value)>
+FMT_CONSTEXPR bool is_negative(T) {
+ return false;
+}
+
+template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+FMT_CONSTEXPR bool is_supported_floating_point(T) {
+ return (std::is_same<T, float>::value && FMT_USE_FLOAT) ||
+ (std::is_same<T, double>::value && FMT_USE_DOUBLE) ||
+ (std::is_same<T, long double>::value && FMT_USE_LONG_DOUBLE);
+}
+
+// Smallest of uint32_t, uint64_t, uint128_t that is large enough to
+// represent all values of T.
+template <typename T>
+using uint32_or_64_or_128_t =
+ conditional_t<num_bits<T>() <= 32, uint32_t,
+ conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>>;
+
+// Static data is placed in this class template for the header-only config.
+template <typename T = void> 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<void>;
+#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 <unsigned BITS, typename UInt> 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 <typename Int> constexpr int digits10() FMT_NOEXCEPT {
+ return std::numeric_limits<Int>::digits10;
+}
+template <> constexpr int digits10<int128_t>() FMT_NOEXCEPT { return 38; }
+template <> constexpr int digits10<uint128_t>() FMT_NOEXCEPT { return 38; }
+
+template <typename Char> FMT_API std::string grouping_impl(locale_ref loc);
+template <typename Char> inline std::string grouping(locale_ref loc) {
+ return grouping_impl<char>(loc);
+}
+template <> inline std::string grouping<wchar_t>(locale_ref loc) {
+ return grouping_impl<wchar_t>(loc);
+}
+
+template <typename Char> FMT_API Char thousands_sep_impl(locale_ref loc);
+template <typename Char> inline Char thousands_sep(locale_ref loc) {
+ return Char(thousands_sep_impl<char>(loc));
+}
+template <> inline wchar_t thousands_sep(locale_ref loc) {
+ return thousands_sep_impl<wchar_t>(loc);
+}
+
+template <typename Char> FMT_API Char decimal_point_impl(locale_ref loc);
+template <typename Char> inline Char decimal_point(locale_ref loc) {
+ return Char(decimal_point_impl<char>(loc));
+}
+template <> inline wchar_t decimal_point(locale_ref loc) {
+ return decimal_point_impl<wchar_t>(loc);
+}
+
+// Compares two characters for equality.
+template <typename Char> 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 <typename Char> void copy2(Char* dst, const char* src) {
+ *dst++ = static_cast<Char>(*src++);
+ *dst = static_cast<Char>(*src);
+}
+inline void copy2(char* dst, const char* src) { memcpy(dst, src, 2); }
+
+template <typename Iterator> 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 <typename Char, typename UInt>
+inline format_decimal_result<Char*> 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<Char>('0' + value);
+ return {out, end};
+ }
+ out -= 2;
+ copy2(out, data::digits[value]);
+ return {out, end};
+}
+
+template <typename Char, typename UInt, typename Iterator,
+ FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<Iterator>>::value)>
+inline format_decimal_result<Iterator> format_decimal(Iterator out, UInt value,
+ int num_digits) {
+ // Buffer should be large enough to hold all digits (<= digits10 + 1).
+ enum { max_size = digits10<UInt>() + 1 };
+ Char buffer[2 * max_size];
+ auto end = format_decimal(buffer, value, num_digits).end;
+ return {out, detail::copy_str<Char>(buffer, end, out)};
+}
+
+template <unsigned BASE_BITS, typename Char, typename UInt>
+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<Char>(BASE_BITS < 4 ? static_cast<char>('0' + digit)
+ : digits[digit]);
+ } while ((value >>= BASE_BITS) != 0);
+ return end;
+}
+
+template <unsigned BASE_BITS, typename Char>
+Char* format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits,
+ bool = false) {
+ auto char_digits = std::numeric_limits<unsigned char>::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<BASE_BITS>(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<Char>(data::hex_digits[digit]);
+ value >>= BASE_BITS;
+ }
+ }
+ return buffer;
+}
+
+template <unsigned BASE_BITS, typename Char, typename It, typename UInt>
+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<UInt>() / BASE_BITS + 1];
+ format_uint<BASE_BITS>(buffer, value, num_digits, upper);
+ return detail::copy_str<Char>(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 <typename T = void> struct null {};
+
+// Workaround an array initialization issue in gcc 4.8.
+template <typename Char> struct fill_t {
+ private:
+ enum { max_size = 4 };
+ Char data_[max_size];
+ unsigned char size_;
+
+ public:
+ FMT_CONSTEXPR void operator=(basic_string_view<Char> 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<unsigned char>(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<Char> make() {
+ auto fill = fill_t<Char>();
+ 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 <typename Char> 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<Char> fill;
+
+ constexpr basic_format_specs()
+ : width(0),
+ precision(-1),
+ type(0),
+ align(align::none),
+ sign(sign::none),
+ alt(false),
+ fill(detail::fill_t<Char>::make()) {}
+};
+
+using format_specs = basic_format_specs<char>;
+
+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 <typename Char, typename It> It write_exponent(int exp, It it) {
+ FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
+ if (exp < 0) {
+ *it++ = static_cast<Char>('-');
+ exp = -exp;
+ } else {
+ *it++ = static_cast<Char>('+');
+ }
+ if (exp >= 100) {
+ const char* top = data::digits[exp / 100];
+ if (exp >= 1000) *it++ = static_cast<Char>(top[0]);
+ *it++ = static_cast<Char>(top[1]);
+ exp %= 100;
+ }
+ const char* d = data::digits[exp];
+ *it++ = static_cast<Char>(d[0]);
+ *it++ = static_cast<Char>(d[1]);
+ return it;
+}
+
+template <typename Char> 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 <typename It> 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<Char>(*digits_);
+ int num_zeros = specs_.precision - num_digits_;
+ if (num_digits_ > 1 || specs_.showpoint) *it++ = decimal_point_;
+ it = copy_str<Char>(digits_ + 1, digits_ + num_digits_, it);
+ if (num_zeros > 0 && specs_.showpoint)
+ it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
+ *it++ = static_cast<Char>(specs_.upper ? 'E' : 'e');
+ return write_exponent<Char>(full_exp - 1, it);
+ }
+ if (num_digits_ <= full_exp) {
+ // 1234e7 -> 12340000000[.0+]
+ it = copy_str<Char>(digits_, digits_ + num_digits_, it);
+ it = std::fill_n(it, full_exp - num_digits_, static_cast<Char>('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<Char>('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<Char>('0'));
+ }
+ } else if (full_exp > 0) {
+ // 1234e-2 -> 12.34[0+]
+ it = copy_str<Char>(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<Char>(digits_ + full_exp, digits_ + num_digits, it);
+ }
+ *it++ = decimal_point_;
+ it = copy_str<Char>(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<Char>('0'));
+ }
+ } else {
+ // 1234e-6 -> 0.001234
+ *it++ = static_cast<Char>('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<Char>('0'));
+ it = copy_str<Char>(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 <typename It> It operator()(It it) const {
+ if (specs_.sign) *it++ = static_cast<Char>(data::signs[specs_.sign]);
+ return prettify(it);
+ }
+};
+
+template <typename T>
+int format_float(T value, int precision, float_specs specs, buffer<char>& buf);
+
+// Formats a floating-point number with snprintf.
+template <typename T>
+int snprintf_float(T value, int precision, float_specs specs,
+ buffer<char>& buf);
+
+template <typename T> T promote_float(T value) { return value; }
+inline double promote_float(float value) { return static_cast<double>(value); }
+
+template <typename Handler>
+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 <typename ErrorHandler = error_handler, typename Char>
+FMT_CONSTEXPR float_specs parse_float_type_spec(
+ const basic_format_specs<Char>& 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 <typename Char, typename Handler>
+FMT_CONSTEXPR void handle_char_specs(const basic_format_specs<Char>* 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 <typename Char, typename Handler>
+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 <typename Char, typename ErrorHandler>
+FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler&& eh) {
+ if (spec != 0 && spec != 's') eh.on_error("invalid type specifier");
+}
+
+template <typename Char, typename ErrorHandler>
+FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) {
+ if (spec != 0 && spec != 'p') eh.on_error("invalid type specifier");
+}
+
+template <typename ErrorHandler> 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 <typename ErrorHandler>
+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<ErrorHandler>(*this));
+ }
+ FMT_CONSTEXPR void on_char() {}
+};
+
+template <typename ErrorHandler>
+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 <typename OutputIt, typename Char>
+FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t<Char>& 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 <align::type align = align::left, typename OutputIt, typename Char,
+ typename F>
+inline OutputIt write_padded(OutputIt out,
+ const basic_format_specs<Char>& 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 <align::type align = align::left, typename OutputIt, typename Char,
+ typename F>
+inline OutputIt write_padded(OutputIt out,
+ const basic_format_specs<Char>& specs, size_t size,
+ const F& f) {
+ return write_padded<align>(out, specs, size, size, f);
+}
+
+template <typename Char, typename OutputIt>
+OutputIt write_bytes(OutputIt out, string_view bytes,
+ const basic_format_specs<Char>& specs) {
+ using iterator = remove_reference_t<decltype(reserve(out, 0))>;
+ return write_padded(out, specs, bytes.size(), [bytes](iterator it) {
+ const char* data = bytes.data();
+ return copy_str<Char>(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 <typename Char> struct write_int_data {
+ size_t size;
+ size_t padding;
+
+ write_int_data(int num_digits, string_view prefix,
+ const basic_format_specs<Char>& 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
+// <left-padding><prefix><numeric-padding><digits><right-padding>
+// where <digits> are written by f(it).
+template <typename OutputIt, typename Char, typename F>
+OutputIt write_int(OutputIt out, int num_digits, string_view prefix,
+ const basic_format_specs<Char>& specs, F f) {
+ auto data = write_int_data<Char>(num_digits, prefix, specs);
+ using iterator = remove_reference_t<decltype(reserve(out, 0))>;
+ return write_padded<align::right>(out, specs, data.size, [=](iterator it) {
+ if (prefix.size() != 0)
+ it = copy_str<Char>(prefix.begin(), prefix.end(), it);
+ it = std::fill_n(it, data.padding, static_cast<Char>('0'));
+ return f(it);
+ });
+}
+
+template <typename StrChar, typename Char, typename OutputIt>
+OutputIt write(OutputIt out, basic_string_view<StrChar> s,
+ const basic_format_specs<Char>& 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<StrChar>(data, size))
+ : 0;
+ using iterator = remove_reference_t<decltype(reserve(out, 0))>;
+ return write_padded(out, specs, size, width, [=](iterator it) {
+ return copy_str<Char>(data, data + size, it);
+ });
+}
+
+// The handle_int_type_spec handler that writes an integer.
+template <typename OutputIt, typename Char, typename UInt> struct int_writer {
+ OutputIt out;
+ locale_ref locale;
+ const basic_format_specs<Char>& specs;
+ UInt abs_value;
+ char prefix[4];
+ unsigned prefix_size;
+
+ using iterator =
+ remove_reference_t<decltype(reserve(std::declval<OutputIt&>(), 0))>;
+
+ string_view get_prefix() const { return string_view(prefix, prefix_size); }
+
+ template <typename Int>
+ int_writer(OutputIt output, locale_ref loc, Int value,
+ const basic_format_specs<Char>& s)
+ : out(output),
+ locale(loc),
+ specs(s),
+ abs_value(static_cast<UInt>(value)),
+ prefix_size(0) {
+ static_assert(std::is_same<uint32_or_64_or_128_t<Int>, 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<Char>(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<char>(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<Char>(locale);
+ if (groups.empty()) return on_dec();
+ auto sep = thousands_sep<Char>(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<char>()) {
+ 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<Char> buffer;
+ size += prefix_size;
+ buffer.resize(size);
+ basic_string_view<Char> 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<Char>(digits[i]);
+ if (*group <= 0 || ++digit_index % *group != 0 ||
+ *group == max_value<char>())
+ 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<Char>('-');
+ using iterator = remove_reference_t<decltype(reserve(out, 0))>;
+ auto data = buffer.data();
+ out = write_padded<align::right>(out, specs, size, size, [=](iterator it) {
+ return copy_str<Char>(data, data + size, it);
+ });
+ }
+
+ void on_chr() { *out++ = static_cast<Char>(abs_value); }
+
+ FMT_NORETURN void on_error() {
+ FMT_THROW(format_error("invalid type specifier"));
+ }
+};
+
+template <typename Char, typename OutputIt>
+OutputIt write_nonfinite(OutputIt out, bool isinf,
+ const basic_format_specs<Char>& 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<decltype(reserve(out, 0))>;
+ return write_padded(out, specs, size, [=](iterator it) {
+ if (sign) *it++ = static_cast<Char>(data::signs[sign]);
+ return copy_str<Char>(str, str + str_size, it);
+ });
+}
+
+template <typename Char, typename OutputIt, typename T,
+ FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+OutputIt write(OutputIt out, T value, basic_format_specs<Char> 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<Char>(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<int>())
+ FMT_THROW(format_error("number is too big"));
+ else
+ ++precision;
+ }
+ if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
+ fspecs.use_grisu = use_grisu<T>();
+ int exp = format_float(promote_float(value), precision, fspecs, buffer);
+ fspecs.precision = precision;
+ Char point =
+ fspecs.locale ? decimal_point<Char>(loc) : static_cast<Char>('.');
+ float_writer<Char> w(buffer.data(), static_cast<int>(buffer.size()), exp,
+ fspecs, point);
+ return write_padded<align::right>(out, specs, w.size(), w);
+}
+
+template <typename Char, typename OutputIt, typename T,
+ FMT_ENABLE_IF(std::is_floating_point<T>::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<Char>();
+ 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<T, float>())) fspecs.binary32 = true;
+ fspecs.use_grisu = use_grisu<T>();
+ int exp = format_float(promote_float(value), precision, fspecs, buffer);
+ fspecs.precision = precision;
+ float_writer<Char> w(buffer.data(), static_cast<int>(buffer.size()), exp,
+ fspecs, static_cast<Char>('.'));
+ return base_iterator(out, w(reserve(out, w.size())));
+}
+
+template <typename Char, typename OutputIt>
+OutputIt write_char(OutputIt out, Char value,
+ const basic_format_specs<Char>& specs) {
+ using iterator = remove_reference_t<decltype(reserve(out, 0))>;
+ return write_padded(out, specs, 1, [=](iterator it) {
+ *it++ = value;
+ return it;
+ });
+}
+
+template <typename Char, typename OutputIt, typename UIntPtr>
+OutputIt write_ptr(OutputIt out, UIntPtr value,
+ const basic_format_specs<Char>* specs) {
+ int num_digits = count_digits<4>(value);
+ auto size = to_unsigned(num_digits) + size_t(2);
+ using iterator = remove_reference_t<decltype(reserve(out, 0))>;
+ auto write = [=](iterator it) {
+ *it++ = static_cast<Char>('0');
+ *it++ = static_cast<Char>('x');
+ return format_uint<4, Char>(it, value, num_digits);
+ };
+ return specs ? write_padded<align::right>(out, *specs, size, write)
+ : base_iterator(out, write(reserve(out, size)));
+}
+
+template <typename T> struct is_integral : std::is_integral<T> {};
+template <> struct is_integral<int128_t> : std::true_type {};
+template <> struct is_integral<uint128_t> : std::true_type {};
+
+template <typename Char, typename OutputIt>
+OutputIt write(OutputIt out, monostate) {
+ FMT_ASSERT(false, "");
+ return out;
+}
+
+template <typename Char, typename OutputIt,
+ FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
+OutputIt write(OutputIt out, string_view value) {
+ auto it = reserve(out, value.size());
+ it = copy_str<Char>(value.begin(), value.end(), it);
+ return base_iterator(out, it);
+}
+
+template <typename Char, typename OutputIt>
+OutputIt write(OutputIt out, basic_string_view<Char> value) {
+ auto it = reserve(out, value.size());
+ it = std::copy(value.begin(), value.end(), it);
+ return base_iterator(out, it);
+}
+
+template <typename Char, typename OutputIt, typename T,
+ FMT_ENABLE_IF(is_integral<T>::value &&
+ !std::is_same<T, bool>::value &&
+ !std::is_same<T, Char>::value)>
+OutputIt write(OutputIt out, T value) {
+ auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(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<size_t>(num_digits));
+ if (negative) *it++ = static_cast<Char>('-');
+ it = format_decimal<Char>(it, abs_value, num_digits).end;
+ return base_iterator(out, it);
+}
+
+template <typename Char, typename OutputIt>
+OutputIt write(OutputIt out, bool value) {
+ return write<Char>(out, string_view(value ? "true" : "false"));
+}
+
+template <typename Char, typename OutputIt>
+OutputIt write(OutputIt out, Char value) {
+ auto it = reserve(out, 1);
+ *it++ = value;
+ return base_iterator(out, it);
+}
+
+template <typename Char, typename OutputIt>
+OutputIt write(OutputIt out, const Char* value) {
+ if (!value) {
+ FMT_THROW(format_error("string pointer is null"));
+ } else {
+ auto length = std::char_traits<Char>::length(value);
+ out = write(out, basic_string_view<Char>(value, length));
+ }
+ return out;
+}
+
+template <typename Char, typename OutputIt>
+OutputIt write(OutputIt out, const void* value) {
+ return write_ptr<Char>(out, to_uintptr(value), nullptr);
+}
+
+template <typename Char, typename OutputIt, typename T>
+auto write(OutputIt out, const T& value) -> typename std::enable_if<
+ mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value ==
+ type::custom_type,
+ OutputIt>::type {
+ basic_format_context<OutputIt, Char> ctx(out, {}, {});
+ return formatter<T>().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 <typename OutputIt, typename Char> struct default_arg_formatter {
+ using context = basic_format_context<OutputIt, Char>;
+
+ OutputIt out;
+ basic_format_args<context> args;
+ locale_ref loc;
+
+ template <typename T> OutputIt operator()(T value) {
+ return write<Char>(out, value);
+ }
+
+ OutputIt operator()(typename basic_format_arg<context>::handle handle) {
+ basic_format_parse_context<Char> parse_ctx({});
+ basic_format_context<OutputIt, Char> format_ctx(out, args, loc);
+ handle.format(parse_ctx, format_ctx);
+ return format_ctx.out();
+ }
+};
+
+template <typename OutputIt, typename Char,
+ typename ErrorHandler = error_handler>
+class arg_formatter_base {
+ public:
+ using iterator = OutputIt;
+ using char_type = Char;
+ using format_specs = basic_format_specs<Char>;
+
+ 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<decltype(
+ detail::reserve(std::declval<iterator&>(), 0))>;
+
+ template <typename T> void write_int(T value, const format_specs& spec) {
+ using uint_type = uint32_or_64_or_128_t<T>;
+ int_writer<iterator, Char, uint_type> 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 <typename Ch, FMT_ENABLE_IF(std::is_same<Ch, Char>::value)>
+ void write(Ch value) {
+ out_ = detail::write<Char>(out_, value);
+ }
+
+ void write(string_view value) {
+ auto&& it = reserve(value.size());
+ it = copy_str<Char>(value.begin(), value.end(), it);
+ }
+ void write(wstring_view value) {
+ static_assert(std::is_same<Char, wchar_t>::value, "");
+ auto&& it = reserve(value.size());
+ it = std::copy(value.begin(), value.end(), it);
+ }
+
+ template <typename Ch>
+ void write(const Ch* s, size_t size, const format_specs& specs) {
+ auto width = specs.width != 0
+ ? count_code_points(basic_string_view<Ch>(s, size))
+ : 0;
+ out_ = write_padded(out_, specs, size, width, [=](reserve_iterator it) {
+ return copy_str<Char>(s, s + size, it);
+ });
+ }
+
+ template <typename Ch>
+ void write(basic_string_view<Ch> s, const format_specs& specs = {}) {
+ out_ = detail::write(out_, s, specs);
+ }
+
+ void write_pointer(const void* p) {
+ out_ = write_ptr<char_type>(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<int>(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<Char>(out_, value);
+ }
+
+ void write(const Char* value) {
+ if (!value) {
+ FMT_THROW(format_error("string pointer is null"));
+ } else {
+ auto length = std::char_traits<char_type>::length(value);
+ basic_string_view<char_type> 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 <typename T, FMT_ENABLE_IF(is_integral<T>::value)>
+ FMT_INLINE iterator operator()(T value) {
+ if (specs_)
+ write_int(value, *specs_);
+ else
+ out_ = detail::write<Char>(out_, value);
+ return out_;
+ }
+
+ iterator operator()(Char value) {
+ handle_char_specs(specs_,
+ char_spec_handler(*this, static_cast<Char>(value)));
+ return out_;
+ }
+
+ iterator operator()(bool value) {
+ if (specs_ && specs_->type) return (*this)(value ? 1 : 0);
+ write(value != 0);
+ return out_;
+ }
+
+ template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::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<Char> 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 <typename Char> 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 <typename Char, typename ErrorHandler>
+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<int>();
+ 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<int>(value);
+}
+
+template <typename Context> class custom_formatter {
+ private:
+ using char_type = typename Context::char_type;
+
+ basic_format_parse_context<char_type>& parse_ctx_;
+ Context& ctx_;
+
+ public:
+ explicit custom_formatter(basic_format_parse_context<char_type>& parse_ctx,
+ Context& ctx)
+ : parse_ctx_(parse_ctx), ctx_(ctx) {}
+
+ bool operator()(typename basic_format_arg<Context>::handle h) const {
+ h.format(parse_ctx_, ctx_);
+ return true;
+ }
+
+ template <typename T> bool operator()(T) const { return false; }
+};
+
+template <typename T>
+using is_integer =
+ bool_constant<is_integral<T>::value && !std::is_same<T, bool>::value &&
+ !std::is_same<T, char>::value &&
+ !std::is_same<T, wchar_t>::value>;
+
+template <typename ErrorHandler> class width_checker {
+ public:
+ explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {}
+
+ template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
+ FMT_CONSTEXPR unsigned long long operator()(T value) {
+ if (is_negative(value)) handler_.on_error("negative width");
+ return static_cast<unsigned long long>(value);
+ }
+
+ template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
+ FMT_CONSTEXPR unsigned long long operator()(T) {
+ handler_.on_error("width is not integer");
+ return 0;
+ }
+
+ private:
+ ErrorHandler& handler_;
+};
+
+template <typename ErrorHandler> class precision_checker {
+ public:
+ explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {}
+
+ template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
+ FMT_CONSTEXPR unsigned long long operator()(T value) {
+ if (is_negative(value)) handler_.on_error("negative precision");
+ return static_cast<unsigned long long>(value);
+ }
+
+ template <typename T, FMT_ENABLE_IF(!is_integer<T>::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 <typename Char> class specs_setter {
+ public:
+ explicit FMT_CONSTEXPR specs_setter(basic_format_specs<Char>& 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<Char> 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<char>(type);
+ }
+
+ protected:
+ basic_format_specs<Char>& specs_;
+};
+
+template <typename ErrorHandler> 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 <typename Handler> class specs_checker : public Handler {
+ private:
+ numeric_specs_checker<Handler> 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 <template <typename> class Handler, typename FormatArg,
+ typename ErrorHandler>
+FMT_CONSTEXPR int get_dynamic_spec(FormatArg arg, ErrorHandler eh) {
+ unsigned long long value = visit_format_arg(Handler<ErrorHandler>(eh), arg);
+ if (value > to_unsigned(max_value<int>())) eh.on_error("number is too big");
+ return static_cast<int>(value);
+}
+
+struct auto_id {};
+
+template <typename Context, typename ID>
+FMT_CONSTEXPR typename Context::format_arg get_arg(Context& ctx, ID id) {
+ auto arg = ctx.arg(id);
+ if (!arg) ctx.on_error("argument not found");
+ return arg;
+}
+
+// The standard format specifier handler with checking.
+template <typename ParseContext, typename Context>
+class specs_handler : public specs_setter<typename Context::char_type> {
+ public:
+ using char_type = typename Context::char_type;
+
+ FMT_CONSTEXPR specs_handler(basic_format_specs<char_type>& specs,
+ ParseContext& parse_ctx, Context& ctx)
+ : specs_setter<char_type>(specs),
+ parse_context_(parse_ctx),
+ context_(ctx) {}
+
+ template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
+ this->specs_.width = get_dynamic_spec<width_checker>(
+ get_arg(arg_id), context_.error_handler());
+ }
+
+ template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
+ this->specs_.precision = get_dynamic_spec<precision_checker>(
+ get_arg(arg_id), context_.error_handler());
+ }
+
+ void on_error(const char* message) { context_.on_error(message); }
+
+ private:
+ // This is only needed for compatibility with gcc 4.4.
+ using format_arg = typename Context::format_arg;
+
+ FMT_CONSTEXPR format_arg get_arg(auto_id) {
+ return detail::get_arg(context_, parse_context_.next_arg_id());
+ }
+
+ FMT_CONSTEXPR format_arg get_arg(int arg_id) {
+ parse_context_.check_arg_id(arg_id);
+ return detail::get_arg(context_, arg_id);
+ }
+
+ FMT_CONSTEXPR format_arg get_arg(basic_string_view<char_type> arg_id) {
+ parse_context_.check_arg_id(arg_id);
+ return detail::get_arg(context_, arg_id);
+ }
+
+ ParseContext& parse_context_;
+ Context& context_;
+};
+
+enum class arg_id_kind { none, index, name };
+
+// An argument reference.
+template <typename Char> struct arg_ref {
+ FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {}
+
+ FMT_CONSTEXPR explicit arg_ref(int index)
+ : kind(arg_id_kind::index), val(index) {}
+ FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name)
+ : kind(arg_id_kind::name), val(name) {}
+
+ FMT_CONSTEXPR arg_ref& operator=(int idx) {
+ kind = arg_id_kind::index;
+ val.index = idx;
+ return *this;
+ }
+
+ arg_id_kind kind;
+ union value {
+ FMT_CONSTEXPR value(int id = 0) : index{id} {}
+ FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
+
+ int index;
+ basic_string_view<Char> name;
+ } val;
+};
+
+// Format specifiers with width and precision resolved at formatting rather
+// than parsing time to allow re-using the same parsed specifiers with
+// different sets of arguments (precompilation of format strings).
+template <typename Char>
+struct dynamic_format_specs : basic_format_specs<Char> {
+ arg_ref<Char> width_ref;
+ arg_ref<Char> precision_ref;
+};
+
+// Format spec handler that saves references to arguments representing dynamic
+// width and precision to be resolved at formatting time.
+template <typename ParseContext>
+class dynamic_specs_handler
+ : public specs_setter<typename ParseContext::char_type> {
+ public:
+ using char_type = typename ParseContext::char_type;
+
+ FMT_CONSTEXPR dynamic_specs_handler(dynamic_format_specs<char_type>& specs,
+ ParseContext& ctx)
+ : specs_setter<char_type>(specs), specs_(specs), context_(ctx) {}
+
+ FMT_CONSTEXPR dynamic_specs_handler(const dynamic_specs_handler& other)
+ : specs_setter<char_type>(other),
+ specs_(other.specs_),
+ context_(other.context_) {}
+
+ template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
+ specs_.width_ref = make_arg_ref(arg_id);
+ }
+
+ template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
+ specs_.precision_ref = make_arg_ref(arg_id);
+ }
+
+ FMT_CONSTEXPR void on_error(const char* message) {
+ context_.on_error(message);
+ }
+
+ private:
+ using arg_ref_type = arg_ref<char_type>;
+
+ FMT_CONSTEXPR arg_ref_type make_arg_ref(int arg_id) {
+ context_.check_arg_id(arg_id);
+ return arg_ref_type(arg_id);
+ }
+
+ FMT_CONSTEXPR arg_ref_type make_arg_ref(auto_id) {
+ return arg_ref_type(context_.next_arg_id());
+ }
+
+ FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<char_type> arg_id) {
+ context_.check_arg_id(arg_id);
+ basic_string_view<char_type> format_str(
+ context_.begin(), to_unsigned(context_.end() - context_.begin()));
+ return arg_ref_type(arg_id);
+ }
+
+ dynamic_format_specs<char_type>& specs_;
+ ParseContext& context_;
+};
+
+template <typename Char, typename IDHandler>
+FMT_CONSTEXPR const Char* parse_arg_id(const Char* begin, const Char* end,
+ IDHandler&& handler) {
+ FMT_ASSERT(begin != end, "");
+ Char c = *begin;
+ if (c == '}' || c == ':') {
+ handler();
+ return begin;
+ }
+ if (c >= '0' && c <= '9') {
+ int index = 0;
+ if (c != '0')
+ index = parse_nonnegative_int(begin, end, handler);
+ else
+ ++begin;
+ if (begin == end || (*begin != '}' && *begin != ':'))
+ handler.on_error("invalid format string");
+ else
+ handler(index);
+ return begin;
+ }
+ if (!is_name_start(c)) {
+ handler.on_error("invalid format string");
+ return begin;
+ }
+ auto it = begin;
+ do {
+ ++it;
+ } while (it != end && (is_name_start(c = *it) || ('0' <= c && c <= '9')));
+ handler(basic_string_view<Char>(begin, to_unsigned(it - begin)));
+ return it;
+}
+
+// Adapts SpecHandler to IDHandler API for dynamic width.
+template <typename SpecHandler, typename Char> struct width_adapter {
+ explicit FMT_CONSTEXPR width_adapter(SpecHandler& h) : handler(h) {}
+
+ FMT_CONSTEXPR void operator()() { handler.on_dynamic_width(auto_id()); }
+ FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_width(id); }
+ FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
+ handler.on_dynamic_width(id);
+ }
+
+ FMT_CONSTEXPR void on_error(const char* message) {
+ handler.on_error(message);
+ }
+
+ SpecHandler& handler;
+};
+
+// Adapts SpecHandler to IDHandler API for dynamic precision.
+template <typename SpecHandler, typename Char> struct precision_adapter {
+ explicit FMT_CONSTEXPR precision_adapter(SpecHandler& h) : handler(h) {}
+
+ FMT_CONSTEXPR void operator()() { handler.on_dynamic_precision(auto_id()); }
+ FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_precision(id); }
+ FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
+ handler.on_dynamic_precision(id);
+ }
+
+ FMT_CONSTEXPR void on_error(const char* message) {
+ handler.on_error(message);
+ }
+
+ SpecHandler& handler;
+};
+
+template <typename Char>
+FMT_CONSTEXPR const Char* next_code_point(const Char* begin, const Char* end) {
+ if (const_check(sizeof(Char) != 1) || (*begin & 0x80) == 0) return begin + 1;
+ do {
+ ++begin;
+ } while (begin != end && (*begin & 0xc0) == 0x80);
+ return begin;
+}
+
+// Parses fill and alignment.
+template <typename Char, typename Handler>
+FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end,
+ Handler&& handler) {
+ FMT_ASSERT(begin != end, "");
+ auto align = align::none;
+ auto p = next_code_point(begin, end);
+ if (p == end) p = begin;
+ for (;;) {
+ switch (static_cast<char>(*p)) {
+ case '<':
+ align = align::left;
+ break;
+ case '>':
+ align = align::right;
+ break;
+#if FMT_DEPRECATED_NUMERIC_ALIGN
+ case '=':
+ align = align::numeric;
+ break;
+#endif
+ case '^':
+ align = align::center;
+ break;
+ }
+ if (align != align::none) {
+ if (p != begin) {
+ auto c = *begin;
+ if (c == '{')
+ return handler.on_error("invalid fill character '{'"), begin;
+ handler.on_fill(basic_string_view<Char>(begin, to_unsigned(p - begin)));
+ begin = p + 1;
+ } else
+ ++begin;
+ handler.on_align(align);
+ break;
+ } else if (p == begin) {
+ break;
+ }
+ p = begin;
+ }
+ return begin;
+}
+
+template <typename Char, typename Handler>
+FMT_CONSTEXPR const Char* parse_width(const Char* begin, const Char* end,
+ Handler&& handler) {
+ FMT_ASSERT(begin != end, "");
+ if ('0' <= *begin && *begin <= '9') {
+ handler.on_width(parse_nonnegative_int(begin, end, handler));
+ } else if (*begin == '{') {
+ ++begin;
+ if (begin != end)
+ begin = parse_arg_id(begin, end, width_adapter<Handler, Char>(handler));
+ if (begin == end || *begin != '}')
+ return handler.on_error("invalid format string"), begin;
+ ++begin;
+ }
+ return begin;
+}
+
+template <typename Char, typename Handler>
+FMT_CONSTEXPR const Char* parse_precision(const Char* begin, const Char* end,
+ Handler&& handler) {
+ ++begin;
+ auto c = begin != end ? *begin : Char();
+ if ('0' <= c && c <= '9') {
+ handler.on_precision(parse_nonnegative_int(begin, end, handler));
+ } else if (c == '{') {
+ ++begin;
+ if (begin != end) {
+ begin =
+ parse_arg_id(begin, end, precision_adapter<Handler, Char>(handler));
+ }
+ if (begin == end || *begin++ != '}')
+ return handler.on_error("invalid format string"), begin;
+ } else {
+ return handler.on_error("missing precision specifier"), begin;
+ }
+ handler.end_precision();
+ return begin;
+}
+
+// Parses standard format specifiers and sends notifications about parsed
+// components to handler.
+template <typename Char, typename SpecHandler>
+FMT_CONSTEXPR const Char* parse_format_specs(const Char* begin, const Char* end,
+ SpecHandler&& handler) {
+ if (begin == end || *begin == '}') return begin;
+
+ begin = parse_align(begin, end, handler);
+ if (begin == end) return begin;
+
+ // Parse sign.
+ switch (static_cast<char>(*begin)) {
+ case '+':
+ handler.on_plus();
+ ++begin;
+ break;
+ case '-':
+ handler.on_minus();
+ ++begin;
+ break;
+ case ' ':
+ handler.on_space();
+ ++begin;
+ break;
+ }
+ if (begin == end) return begin;
+
+ if (*begin == '#') {
+ handler.on_hash();
+ if (++begin == end) return begin;
+ }
+
+ // Parse zero flag.
+ if (*begin == '0') {
+ handler.on_zero();
+ if (++begin == end) return begin;
+ }
+
+ begin = parse_width(begin, end, handler);
+ if (begin == end) return begin;
+
+ // Parse precision.
+ if (*begin == '.') {
+ begin = parse_precision(begin, end, handler);
+ }
+
+ // Parse type.
+ if (begin != end && *begin != '}') handler.on_type(*begin++);
+ return begin;
+}
+
+// Return the result via the out param to workaround gcc bug 77539.
+template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
+FMT_CONSTEXPR bool find(Ptr first, Ptr last, T value, Ptr& out) {
+ for (out = first; out != last; ++out) {
+ if (*out == value) return true;
+ }
+ return false;
+}
+
+template <>
+inline bool find<false, char>(const char* first, const char* last, char value,
+ const char*& out) {
+ out = static_cast<const char*>(
+ std::memchr(first, value, detail::to_unsigned(last - first)));
+ return out != nullptr;
+}
+
+template <typename Handler, typename Char> struct id_adapter {
+ Handler& handler;
+ int arg_id;
+
+ FMT_CONSTEXPR void operator()() { arg_id = handler.on_arg_id(); }
+ FMT_CONSTEXPR void operator()(int id) { arg_id = handler.on_arg_id(id); }
+ FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
+ arg_id = handler.on_arg_id(id);
+ }
+ FMT_CONSTEXPR void on_error(const char* message) {
+ handler.on_error(message);
+ }
+};
+
+template <typename Char, typename Handler>
+FMT_CONSTEXPR const Char* parse_replacement_field(const Char* begin,
+ const Char* end,
+ Handler&& handler) {
+ ++begin;
+ if (begin == end) return handler.on_error("invalid format string"), end;
+ if (static_cast<char>(*begin) == '}') {
+ handler.on_replacement_field(handler.on_arg_id(), begin);
+ } else if (*begin == '{') {
+ handler.on_text(begin, begin + 1);
+ } else {
+ auto adapter = id_adapter<Handler, Char>{handler, 0};
+ begin = parse_arg_id(begin, end, adapter);
+ Char c = begin != end ? *begin : Char();
+ if (c == '}') {
+ handler.on_replacement_field(adapter.arg_id, begin);
+ } else if (c == ':') {
+ begin = handler.on_format_specs(adapter.arg_id, begin + 1, end);
+ if (begin == end || *begin != '}')
+ return handler.on_error("unknown format specifier"), end;
+ } else {
+ return handler.on_error("missing '}' in format string"), end;
+ }
+ }
+ return begin + 1;
+}
+
+template <bool IS_CONSTEXPR, typename Char, typename Handler>
+FMT_CONSTEXPR_DECL FMT_INLINE void parse_format_string(
+ basic_string_view<Char> format_str, Handler&& handler) {
+ auto begin = format_str.data();
+ auto end = begin + format_str.size();
+ if (end - begin < 32) {
+ // Use a simple loop instead of memchr for small strings.
+ const Char* p = begin;
+ while (p != end) {
+ auto c = *p++;
+ if (c == '{') {
+ handler.on_text(begin, p - 1);
+ begin = p = parse_replacement_field(p - 1, end, handler);
+ } else if (c == '}') {
+ if (p == end || *p != '}')
+ return handler.on_error("unmatched '}' in format string");
+ handler.on_text(begin, p);
+ begin = ++p;
+ }
+ }
+ handler.on_text(begin, end);
+ return;
+ }
+ struct writer {
+ FMT_CONSTEXPR void operator()(const Char* begin, const Char* end) {
+ if (begin == end) return;
+ for (;;) {
+ const Char* p = nullptr;
+ if (!find<IS_CONSTEXPR>(begin, end, '}', p))
+ return handler_.on_text(begin, end);
+ ++p;
+ if (p == end || *p != '}')
+ return handler_.on_error("unmatched '}' in format string");
+ handler_.on_text(begin, p);
+ begin = p + 1;
+ }
+ }
+ Handler& handler_;
+ } write{handler};
+ while (begin != end) {
+ // Doing two passes with memchr (one for '{' and another for '}') is up to
+ // 2.5x faster than the naive one-pass implementation on big format strings.
+ const Char* p = begin;
+ if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, '{', p))
+ return write(begin, end);
+ write(begin, p);
+ begin = parse_replacement_field(p, end, handler);
+ }
+}
+
+template <typename T, typename ParseContext>
+FMT_CONSTEXPR const typename ParseContext::char_type* parse_format_specs(
+ ParseContext& ctx) {
+ using char_type = typename ParseContext::char_type;
+ using context = buffer_context<char_type>;
+ using mapped_type =
+ conditional_t<detail::mapped_type_constant<T, context>::value !=
+ type::custom_type,
+ decltype(arg_mapper<context>().map(std::declval<T>())), T>;
+ auto f = conditional_t<has_formatter<mapped_type, context>::value,
+ formatter<mapped_type, char_type>,
+ detail::fallback_formatter<T, char_type>>();
+ return f.parse(ctx);
+}
+
+template <typename ArgFormatter, typename Char, typename Context>
+struct format_handler : detail::error_handler {
+ basic_format_parse_context<Char> parse_context;
+ Context context;
+
+ format_handler(typename ArgFormatter::iterator out,
+ basic_string_view<Char> str,
+ basic_format_args<Context> format_args, detail::locale_ref loc)
+ : parse_context(str), context(out, format_args, loc) {}
+
+ void on_text(const Char* begin, const Char* end) {
+ auto size = to_unsigned(end - begin);
+ auto out = context.out();
+ auto&& it = reserve(out, size);
+ it = std::copy_n(begin, size, it);
+ context.advance_to(out);
+ }
+
+ int on_arg_id() { return parse_context.next_arg_id(); }
+ int on_arg_id(int id) { return parse_context.check_arg_id(id), id; }
+ int on_arg_id(basic_string_view<Char> id) {
+ int arg_id = context.arg_id(id);
+ if (arg_id < 0) on_error("argument not found");
+ return arg_id;
+ }
+
+ FMT_INLINE void on_replacement_field(int id, const Char*) {
+ auto arg = get_arg(context, id);
+ context.advance_to(visit_format_arg(
+ default_arg_formatter<typename ArgFormatter::iterator, Char>{
+ context.out(), context.args(), context.locale()},
+ arg));
+ }
+
+ const Char* on_format_specs(int id, const Char* begin, const Char* end) {
+ advance_to(parse_context, begin);
+ auto arg = get_arg(context, id);
+ custom_formatter<Context> f(parse_context, context);
+ if (visit_format_arg(f, arg)) return parse_context.begin();
+ basic_format_specs<Char> specs;
+ using parse_context_t = basic_format_parse_context<Char>;
+ specs_checker<specs_handler<parse_context_t, Context>> handler(
+ specs_handler<parse_context_t, Context>(specs, parse_context, context),
+ arg.type());
+ begin = parse_format_specs(begin, end, handler);
+ if (begin == end || *begin != '}') on_error("missing '}' in format string");
+ advance_to(parse_context, begin);
+ context.advance_to(
+ visit_format_arg(ArgFormatter(context, &parse_context, &specs), arg));
+ return begin;
+ }
+};
+
+// A parse context with extra argument id checks. It is only used at compile
+// time because adding checks at runtime would introduce substantial overhead
+// and would be redundant since argument ids are checked when arguments are
+// retrieved anyway.
+template <typename Char, typename ErrorHandler = error_handler>
+class compile_parse_context
+ : public basic_format_parse_context<Char, ErrorHandler> {
+ private:
+ int num_args_;
+ using base = basic_format_parse_context<Char, ErrorHandler>;
+
+ public:
+ explicit FMT_CONSTEXPR compile_parse_context(
+ basic_string_view<Char> format_str, int num_args = max_value<int>(),
+ ErrorHandler eh = {})
+ : base(format_str, eh), num_args_(num_args) {}
+
+ FMT_CONSTEXPR int next_arg_id() {
+ int id = base::next_arg_id();
+ if (id >= num_args_) this->on_error("argument not found");
+ return id;
+ }
+
+ FMT_CONSTEXPR void check_arg_id(int id) {
+ base::check_arg_id(id);
+ if (id >= num_args_) this->on_error("argument not found");
+ }
+ using base::check_arg_id;
+};
+
+template <typename Char, typename ErrorHandler, typename... Args>
+class format_string_checker {
+ public:
+ explicit FMT_CONSTEXPR format_string_checker(
+ basic_string_view<Char> format_str, ErrorHandler eh)
+ : context_(format_str, num_args, eh),
+ parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
+
+ FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
+
+ FMT_CONSTEXPR int on_arg_id() { return context_.next_arg_id(); }
+ FMT_CONSTEXPR int on_arg_id(int id) { return context_.check_arg_id(id), id; }
+ FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
+ on_error("compile-time checks don't support named arguments");
+ return 0;
+ }
+
+ FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
+
+ FMT_CONSTEXPR const Char* on_format_specs(int id, const Char* begin,
+ const Char*) {
+ advance_to(context_, begin);
+ return id < num_args ? parse_funcs_[id](context_) : begin;
+ }
+
+ FMT_CONSTEXPR void on_error(const char* message) {
+ context_.on_error(message);
+ }
+
+ private:
+ using parse_context_type = compile_parse_context<Char, ErrorHandler>;
+ enum { num_args = sizeof...(Args) };
+
+ // Format specifier parsing function.
+ using parse_func = const Char* (*)(parse_context_type&);
+
+ parse_context_type context_;
+ parse_func parse_funcs_[num_args > 0 ? num_args : 1];
+};
+
+// Converts string literals to basic_string_view.
+template <typename Char, size_t N>
+FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
+ const Char (&s)[N]) {
+ // Remove trailing null character if needed. Won't be present if this is used
+ // with raw character array (i.e. not defined as a string).
+ return {s,
+ N - ((std::char_traits<Char>::to_int_type(s[N - 1]) == 0) ? 1 : 0)};
+}
+
+// Converts string_view to basic_string_view.
+template <typename Char>
+FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
+ const std_string_view<Char>& s) {
+ return {s.data(), s.size()};
+}
+
+#define FMT_STRING_IMPL(s, base) \
+ [] { \
+ /* Use a macro-like name to avoid shadowing warnings. */ \
+ struct FMT_COMPILE_STRING : base { \
+ using char_type = fmt::remove_cvref_t<decltype(s[0])>; \
+ FMT_MAYBE_UNUSED FMT_CONSTEXPR \
+ operator fmt::basic_string_view<char_type>() const { \
+ return fmt::detail::compile_string_to_view<char_type>(s); \
+ } \
+ }; \
+ return FMT_COMPILE_STRING(); \
+ }()
+
+/**
+ \rst
+ Constructs a compile-time format string from a string literal *s*.
+
+ **Example**::
+
+ // A compile-time error because 'd' is an invalid specifier for strings.
+ std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
+ \endrst
+ */
+#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::compile_string)
+
+template <typename... Args, typename S,
+ enable_if_t<(is_compile_string<S>::value), int>>
+void check_format_string(S format_str) {
+ FMT_CONSTEXPR_DECL auto s = to_string_view(format_str);
+ using checker = format_string_checker<typename S::char_type, error_handler,
+ remove_cvref_t<Args>...>;
+ FMT_CONSTEXPR_DECL bool invalid_format =
+ (parse_format_string<true>(s, checker(s, {})), true);
+ (void)invalid_format;
+}
+
+template <template <typename> class Handler, typename Context>
+void handle_dynamic_spec(int& value, arg_ref<typename Context::char_type> ref,
+ Context& ctx) {
+ switch (ref.kind) {
+ case arg_id_kind::none:
+ break;
+ case arg_id_kind::index:
+ value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.index),
+ ctx.error_handler());
+ break;
+ case arg_id_kind::name:
+ value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.name),
+ ctx.error_handler());
+ break;
+ }
+}
+
+using format_func = void (*)(detail::buffer<char>&, int, string_view);
+
+FMT_API void format_error_code(buffer<char>& out, int error_code,
+ string_view message) FMT_NOEXCEPT;
+
+FMT_API void report_error(format_func func, int error_code,
+ string_view message) FMT_NOEXCEPT;
+
+/** The default argument formatter. */
+template <typename OutputIt, typename Char>
+class arg_formatter : public arg_formatter_base<OutputIt, Char> {
+ private:
+ using char_type = Char;
+ using base = arg_formatter_base<OutputIt, Char>;
+ using context_type = basic_format_context<OutputIt, Char>;
+
+ context_type& ctx_;
+ basic_format_parse_context<char_type>* parse_ctx_;
+ const Char* ptr_;
+
+ public:
+ using iterator = typename base::iterator;
+ using format_specs = typename base::format_specs;
+
+ /**
+ \rst
+ Constructs an argument formatter object.
+ *ctx* is a reference to the formatting context,
+ *specs* contains format specifier information for standard argument types.
+ \endrst
+ */
+ explicit arg_formatter(
+ context_type& ctx,
+ basic_format_parse_context<char_type>* parse_ctx = nullptr,
+ format_specs* specs = nullptr, const Char* ptr = nullptr)
+ : base(ctx.out(), specs, ctx.locale()),
+ ctx_(ctx),
+ parse_ctx_(parse_ctx),
+ ptr_(ptr) {}
+
+ using base::operator();
+
+ /** Formats an argument of a user-defined type. */
+ iterator operator()(typename basic_format_arg<context_type>::handle handle) {
+ if (ptr_) advance_to(*parse_ctx_, ptr_);
+ handle.format(*parse_ctx_, ctx_);
+ return ctx_.out();
+ }
+};
+} // namespace detail
+
+template <typename OutputIt, typename Char>
+using arg_formatter FMT_DEPRECATED_ALIAS =
+ detail::arg_formatter<OutputIt, Char>;
+
+/**
+ An error returned by an operating system or a language runtime,
+ for example a file opening error.
+*/
+FMT_CLASS_API
+class FMT_API system_error : public std::runtime_error {
+ private:
+ void init(int err_code, string_view format_str, format_args args);
+
+ protected:
+ int error_code_;
+
+ system_error() : std::runtime_error(""), error_code_(0) {}
+
+ public:
+ /**
+ \rst
+ Constructs a :class:`fmt::system_error` object with a description
+ formatted with `fmt::format_system_error`. *message* and additional
+ arguments passed into the constructor are formatted similarly to
+ `fmt::format`.
+
+ **Example**::
+
+ // This throws a system_error with the description
+ // cannot open file 'madeup': No such file or directory
+ // or similar (system message may vary).
+ const char *filename = "madeup";
+ std::FILE *file = std::fopen(filename, "r");
+ if (!file)
+ throw fmt::system_error(errno, "cannot open file '{}'", filename);
+ \endrst
+ */
+ template <typename... Args>
+ system_error(int error_code, string_view message, const Args&... args)
+ : std::runtime_error("") {
+ init(error_code, message, make_format_args(args...));
+ }
+ system_error(const system_error&) = default;
+ system_error& operator=(const system_error&) = default;
+ system_error(system_error&&) = default;
+ system_error& operator=(system_error&&) = default;
+ ~system_error() FMT_NOEXCEPT FMT_OVERRIDE;
+
+ int error_code() const { return error_code_; }
+};
+
+/**
+ \rst
+ Formats an error returned by an operating system or a language runtime,
+ for example a file opening error, and writes it to *out* in the following
+ form:
+
+ .. parsed-literal::
+ *<message>*: *<system-message>*
+
+ where *<message>* is the passed message and *<system-message>* is
+ the system message corresponding to the error code.
+ *error_code* is a system error code as given by ``errno``.
+ If *error_code* is not a valid error code such as -1, the system message
+ may look like "Unknown error -1" and is platform-dependent.
+ \endrst
+ */
+FMT_API void format_system_error(detail::buffer<char>& out, int error_code,
+ string_view message) FMT_NOEXCEPT;
+
+// Reports a system error without throwing an exception.
+// Can be used to report errors from destructors.
+FMT_API void report_system_error(int error_code,
+ string_view message) FMT_NOEXCEPT;
+
+/** Fast integer formatter. */
+class format_int {
+ private:
+ // Buffer should be large enough to hold all digits (digits10 + 1),
+ // a sign and a null character.
+ enum { buffer_size = std::numeric_limits<unsigned long long>::digits10 + 3 };
+ mutable char buffer_[buffer_size];
+ char* str_;
+
+ template <typename UInt> char* format_unsigned(UInt value) {
+ auto n = static_cast<detail::uint32_or_64_or_128_t<UInt>>(value);
+ return detail::format_decimal(buffer_, n, buffer_size - 1).begin;
+ }
+
+ template <typename Int> char* format_signed(Int value) {
+ auto abs_value = static_cast<detail::uint32_or_64_or_128_t<Int>>(value);
+ bool negative = value < 0;
+ if (negative) abs_value = 0 - abs_value;
+ auto begin = format_unsigned(abs_value);
+ if (negative) *--begin = '-';
+ return begin;
+ }
+
+ public:
+ explicit format_int(int value) : str_(format_signed(value)) {}
+ explicit format_int(long value) : str_(format_signed(value)) {}
+ explicit format_int(long long value) : str_(format_signed(value)) {}
+ explicit format_int(unsigned value) : str_(format_unsigned(value)) {}
+ explicit format_int(unsigned long value) : str_(format_unsigned(value)) {}
+ explicit format_int(unsigned long long value)
+ : str_(format_unsigned(value)) {}
+
+ /** Returns the number of characters written to the output buffer. */
+ size_t size() const {
+ return detail::to_unsigned(buffer_ - str_ + buffer_size - 1);
+ }
+
+ /**
+ Returns a pointer to the output buffer content. No terminating null
+ character is appended.
+ */
+ const char* data() const { return str_; }
+
+ /**
+ Returns a pointer to the output buffer content with terminating null
+ character appended.
+ */
+ const char* c_str() const {
+ buffer_[buffer_size - 1] = '\0';
+ return str_;
+ }
+
+ /**
+ \rst
+ Returns the content of the output buffer as an ``std::string``.
+ \endrst
+ */
+ std::string str() const { return std::string(str_, size()); }
+};
+
+// A formatter specialization for the core types corresponding to detail::type
+// constants.
+template <typename T, typename Char>
+struct formatter<T, Char,
+ enable_if_t<detail::type_constant<T, Char>::value !=
+ detail::type::custom_type>> {
+ FMT_CONSTEXPR formatter() = default;
+
+ // Parses format specifiers stopping either at the end of the range or at the
+ // terminating '}'.
+ template <typename ParseContext>
+ FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+ using handler_type = detail::dynamic_specs_handler<ParseContext>;
+ auto type = detail::type_constant<T, Char>::value;
+ detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
+ type);
+ auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
+ auto eh = ctx.error_handler();
+ switch (type) {
+ case detail::type::none_type:
+ FMT_ASSERT(false, "invalid argument type");
+ break;
+ case detail::type::int_type:
+ case detail::type::uint_type:
+ case detail::type::long_long_type:
+ case detail::type::ulong_long_type:
+ case detail::type::int128_type:
+ case detail::type::uint128_type:
+ case detail::type::bool_type:
+ handle_int_type_spec(specs_.type,
+ detail::int_type_checker<decltype(eh)>(eh));
+ break;
+ case detail::type::char_type:
+ handle_char_specs(
+ &specs_, detail::char_specs_checker<decltype(eh)>(specs_.type, eh));
+ break;
+ case detail::type::float_type:
+ if (detail::const_check(FMT_USE_FLOAT))
+ detail::parse_float_type_spec(specs_, eh);
+ else
+ FMT_ASSERT(false, "float support disabled");
+ break;
+ case detail::type::double_type:
+ if (detail::const_check(FMT_USE_DOUBLE))
+ detail::parse_float_type_spec(specs_, eh);
+ else
+ FMT_ASSERT(false, "double support disabled");
+ break;
+ case detail::type::long_double_type:
+ if (detail::const_check(FMT_USE_LONG_DOUBLE))
+ detail::parse_float_type_spec(specs_, eh);
+ else
+ FMT_ASSERT(false, "long double support disabled");
+ break;
+ case detail::type::cstring_type:
+ detail::handle_cstring_type_spec(
+ specs_.type, detail::cstring_type_checker<decltype(eh)>(eh));
+ break;
+ case detail::type::string_type:
+ detail::check_string_type_spec(specs_.type, eh);
+ break;
+ case detail::type::pointer_type:
+ detail::check_pointer_type_spec(specs_.type, eh);
+ break;
+ case detail::type::custom_type:
+ // Custom format specifiers should be checked in parse functions of
+ // formatter specializations.
+ break;
+ }
+ return it;
+ }
+
+ template <typename FormatContext>
+ auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
+ detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
+ specs_.width_ref, ctx);
+ detail::handle_dynamic_spec<detail::precision_checker>(
+ specs_.precision, specs_.precision_ref, ctx);
+ using af = detail::arg_formatter<typename FormatContext::iterator,
+ typename FormatContext::char_type>;
+ return visit_format_arg(af(ctx, nullptr, &specs_),
+ detail::make_arg<FormatContext>(val));
+ }
+
+ private:
+ detail::dynamic_format_specs<Char> specs_;
+};
+
+#define FMT_FORMAT_AS(Type, Base) \
+ template <typename Char> \
+ struct formatter<Type, Char> : formatter<Base, Char> { \
+ template <typename FormatContext> \
+ auto format(Type const& val, FormatContext& ctx) -> decltype(ctx.out()) { \
+ return formatter<Base, Char>::format(val, ctx); \
+ } \
+ }
+
+FMT_FORMAT_AS(signed char, int);
+FMT_FORMAT_AS(unsigned char, unsigned);
+FMT_FORMAT_AS(short, int);
+FMT_FORMAT_AS(unsigned short, unsigned);
+FMT_FORMAT_AS(long, long long);
+FMT_FORMAT_AS(unsigned long, unsigned long long);
+FMT_FORMAT_AS(Char*, const Char*);
+FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
+FMT_FORMAT_AS(std::nullptr_t, const void*);
+FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
+
+template <typename Char>
+struct formatter<void*, Char> : formatter<const void*, Char> {
+ template <typename FormatContext>
+ auto format(void* val, FormatContext& ctx) -> decltype(ctx.out()) {
+ return formatter<const void*, Char>::format(val, ctx);
+ }
+};
+
+template <typename Char, size_t N>
+struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {
+ template <typename FormatContext>
+ auto format(const Char* val, FormatContext& ctx) -> decltype(ctx.out()) {
+ return formatter<basic_string_view<Char>, Char>::format(val, ctx);
+ }
+};
+
+// A formatter for types known only at run time such as variant alternatives.
+//
+// Usage:
+// using variant = std::variant<int, std::string>;
+// template <>
+// struct formatter<variant>: dynamic_formatter<> {
+// void format(buffer &buf, const variant &v, context &ctx) {
+// visit([&](const auto &val) { format(buf, val, ctx); }, v);
+// }
+// };
+template <typename Char = char> class dynamic_formatter {
+ private:
+ struct null_handler : detail::error_handler {
+ void on_align(align_t) {}
+ void on_plus() {}
+ void on_minus() {}
+ void on_space() {}
+ void on_hash() {}
+ };
+
+ public:
+ template <typename ParseContext>
+ auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+ format_str_ = ctx.begin();
+ // Checks are deferred to formatting time when the argument type is known.
+ detail::dynamic_specs_handler<ParseContext> handler(specs_, ctx);
+ return parse_format_specs(ctx.begin(), ctx.end(), handler);
+ }
+
+ template <typename T, typename FormatContext>
+ auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
+ handle_specs(ctx);
+ detail::specs_checker<null_handler> checker(
+ null_handler(), detail::mapped_type_constant<T, FormatContext>::value);
+ checker.on_align(specs_.align);
+ switch (specs_.sign) {
+ case sign::none:
+ break;
+ case sign::plus:
+ checker.on_plus();
+ break;
+ case sign::minus:
+ checker.on_minus();
+ break;
+ case sign::space:
+ checker.on_space();
+ break;
+ }
+ if (specs_.alt) checker.on_hash();
+ if (specs_.precision >= 0) checker.end_precision();
+ using af = detail::arg_formatter<typename FormatContext::iterator,
+ typename FormatContext::char_type>;
+ visit_format_arg(af(ctx, nullptr, &specs_),
+ detail::make_arg<FormatContext>(val));
+ return ctx.out();
+ }
+
+ private:
+ template <typename Context> void handle_specs(Context& ctx) {
+ detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
+ specs_.width_ref, ctx);
+ detail::handle_dynamic_spec<detail::precision_checker>(
+ specs_.precision, specs_.precision_ref, ctx);
+ }
+
+ detail::dynamic_format_specs<Char> specs_;
+ const Char* format_str_;
+};
+
+template <typename Char, typename ErrorHandler>
+FMT_CONSTEXPR void advance_to(
+ basic_format_parse_context<Char, ErrorHandler>& ctx, const Char* p) {
+ ctx.advance_to(ctx.begin() + (p - &*ctx.begin()));
+}
+
+/** Formats arguments and writes the output to the range. */
+template <typename ArgFormatter, typename Char, typename Context>
+typename Context::iterator vformat_to(
+ typename ArgFormatter::iterator out, basic_string_view<Char> format_str,
+ basic_format_args<Context> args,
+ detail::locale_ref loc = detail::locale_ref()) {
+ if (format_str.size() == 2 && detail::equal2(format_str.data(), "{}")) {
+ auto arg = args.get(0);
+ if (!arg) detail::error_handler().on_error("argument not found");
+ using iterator = typename ArgFormatter::iterator;
+ return visit_format_arg(
+ detail::default_arg_formatter<iterator, Char>{out, args, loc}, arg);
+ }
+ detail::format_handler<ArgFormatter, Char, Context> h(out, format_str, args,
+ loc);
+ detail::parse_format_string<false>(format_str, h);
+ return h.context.out();
+}
+
+// Casts ``p`` to ``const void*`` for pointer formatting.
+// Example:
+// auto s = format("{}", ptr(p));
+template <typename T> inline const void* ptr(const T* p) { return p; }
+template <typename T> inline const void* ptr(const std::unique_ptr<T>& p) {
+ return p.get();
+}
+template <typename T> inline const void* ptr(const std::shared_ptr<T>& p) {
+ return p.get();
+}
+
+class bytes {
+ private:
+ string_view data_;
+ friend struct formatter<bytes>;
+
+ public:
+ explicit bytes(string_view data) : data_(data) {}
+};
+
+template <> struct formatter<bytes> {
+ template <typename ParseContext>
+ FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+ using handler_type = detail::dynamic_specs_handler<ParseContext>;
+ detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
+ detail::type::string_type);
+ auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
+ detail::check_string_type_spec(specs_.type, ctx.error_handler());
+ return it;
+ }
+
+ template <typename FormatContext>
+ auto format(bytes b, FormatContext& ctx) -> decltype(ctx.out()) {
+ detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
+ specs_.width_ref, ctx);
+ detail::handle_dynamic_spec<detail::precision_checker>(
+ specs_.precision, specs_.precision_ref, ctx);
+ return detail::write_bytes(ctx.out(), b.data_, specs_);
+ }
+
+ private:
+ detail::dynamic_format_specs<char> specs_;
+};
+
+template <typename It, typename Sentinel, typename Char>
+struct arg_join : detail::view {
+ It begin;
+ Sentinel end;
+ basic_string_view<Char> sep;
+
+ arg_join(It b, Sentinel e, basic_string_view<Char> s)
+ : begin(b), end(e), sep(s) {}
+};
+
+template <typename It, typename Sentinel, typename Char>
+struct formatter<arg_join<It, Sentinel, Char>, Char>
+ : formatter<typename std::iterator_traits<It>::value_type, Char> {
+ template <typename FormatContext>
+ auto format(const arg_join<It, Sentinel, Char>& value, FormatContext& ctx)
+ -> decltype(ctx.out()) {
+ using base = formatter<typename std::iterator_traits<It>::value_type, Char>;
+ auto it = value.begin;
+ auto out = ctx.out();
+ if (it != value.end) {
+ out = base::format(*it++, ctx);
+ while (it != value.end) {
+ out = std::copy(value.sep.begin(), value.sep.end(), out);
+ ctx.advance_to(out);
+ out = base::format(*it++, ctx);
+ }
+ }
+ return out;
+ }
+};
+
+/**
+ Returns an object that formats the iterator range `[begin, end)` with elements
+ separated by `sep`.
+ */
+template <typename It, typename Sentinel>
+arg_join<It, Sentinel, char> join(It begin, Sentinel end, string_view sep) {
+ return {begin, end, sep};
+}
+
+template <typename It, typename Sentinel>
+arg_join<It, Sentinel, wchar_t> join(It begin, Sentinel end, wstring_view sep) {
+ return {begin, end, sep};
+}
+
+/**
+ \rst
+ Returns an object that formats `range` with elements separated by `sep`.
+
+ **Example**::
+
+ std::vector<int> v = {1, 2, 3};
+ fmt::print("{}", fmt::join(v, ", "));
+ // Output: "1, 2, 3"
+
+ ``fmt::join`` applies passed format specifiers to the range elements::
+
+ fmt::print("{:02}", fmt::join(v, ", "));
+ // Output: "01, 02, 03"
+ \endrst
+ */
+template <typename Range>
+arg_join<detail::iterator_t<const Range>, detail::sentinel_t<const Range>, char>
+join(const Range& range, string_view sep) {
+ return join(std::begin(range), std::end(range), sep);
+}
+
+template <typename Range>
+arg_join<detail::iterator_t<const Range>, detail::sentinel_t<const Range>,
+ wchar_t>
+join(const Range& range, wstring_view sep) {
+ return join(std::begin(range), std::end(range), sep);
+}
+
+/**
+ \rst
+ Converts *value* to ``std::string`` using the default format for type *T*.
+
+ **Example**::
+
+ #include <fmt/format.h>
+
+ std::string answer = fmt::to_string(42);
+ \endrst
+ */
+template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+inline std::string to_string(const T& value) {
+ std::string result;
+ detail::write<char>(std::back_inserter(result), value);
+ return result;
+}
+
+template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+inline std::string to_string(T value) {
+ // The buffer should be large enough to store the number including the sign or
+ // "false" for bool.
+ constexpr int max_size = detail::digits10<T>() + 2;
+ char buffer[max_size > 5 ? max_size : 5];
+ char* begin = buffer;
+ return std::string(begin, detail::write<char>(begin, value));
+}
+
+/**
+ Converts *value* to ``std::wstring`` using the default format for type *T*.
+ */
+template <typename T> inline std::wstring to_wstring(const T& value) {
+ return format(L"{}", value);
+}
+
+template <typename Char, size_t SIZE>
+std::basic_string<Char> to_string(const basic_memory_buffer<Char, SIZE>& buf) {
+ auto size = buf.size();
+ detail::assume(size < std::basic_string<Char>().max_size());
+ return std::basic_string<Char>(buf.data(), size);
+}
+
+template <typename Char>
+typename buffer_context<Char>::iterator detail::vformat_to(
+ detail::buffer<Char>& buf, basic_string_view<Char> format_str,
+ basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+ using af = arg_formatter<typename buffer_context<Char>::iterator, Char>;
+ return vformat_to<af>(std::back_inserter(buf), to_string_view(format_str),
+ args);
+}
+
+#ifndef FMT_HEADER_ONLY
+extern template format_context::iterator detail::vformat_to(
+ detail::buffer<char>&, string_view, basic_format_args<format_context>);
+namespace detail {
+extern template FMT_API std::string grouping_impl<char>(locale_ref loc);
+extern template FMT_API std::string grouping_impl<wchar_t>(locale_ref loc);
+extern template FMT_API char thousands_sep_impl<char>(locale_ref loc);
+extern template FMT_API wchar_t thousands_sep_impl<wchar_t>(locale_ref loc);
+extern template FMT_API char decimal_point_impl(locale_ref loc);
+extern template FMT_API wchar_t decimal_point_impl(locale_ref loc);
+extern template int format_float<double>(double value, int precision,
+ float_specs specs, buffer<char>& buf);
+extern template int format_float<long double>(long double value, int precision,
+ float_specs specs,
+ buffer<char>& buf);
+int snprintf_float(float value, int precision, float_specs specs,
+ buffer<char>& buf) = delete;
+extern template int snprintf_float<double>(double value, int precision,
+ float_specs specs,
+ buffer<char>& buf);
+extern template int snprintf_float<long double>(long double value,
+ int precision,
+ float_specs specs,
+ buffer<char>& buf);
+} // namespace detail
+#endif
+
+template <typename S, typename Char = char_t<S>,
+ FMT_ENABLE_IF(detail::is_string<S>::value)>
+inline typename FMT_BUFFER_CONTEXT(Char)::iterator vformat_to(
+ detail::buffer<Char>& buf, const S& format_str,
+ basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args) {
+ return detail::vformat_to(buf, to_string_view(format_str), args);
+}
+
+template <typename S, typename... Args, size_t SIZE = inline_buffer_size,
+ typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
+inline typename buffer_context<Char>::iterator format_to(
+ basic_memory_buffer<Char, SIZE>& buf, const S& format_str, Args&&... args) {
+ detail::check_format_string<Args...>(format_str);
+ using context = buffer_context<Char>;
+ return detail::vformat_to(buf, to_string_view(format_str),
+ make_format_args<context>(args...));
+}
+
+template <typename OutputIt, typename Char = char>
+using format_context_t = basic_format_context<OutputIt, Char>;
+
+template <typename OutputIt, typename Char = char>
+using format_args_t = basic_format_args<format_context_t<OutputIt, Char>>;
+
+template <
+ typename S, typename OutputIt, typename... Args,
+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value &&
+ !detail::is_contiguous_back_insert_iterator<OutputIt>::value)>
+inline OutputIt vformat_to(
+ OutputIt out, const S& format_str,
+ format_args_t<type_identity_t<OutputIt>, char_t<S>> args) {
+ using af = detail::arg_formatter<OutputIt, char_t<S>>;
+ return vformat_to<af>(out, to_string_view(format_str), args);
+}
+
+/**
+ \rst
+ Formats arguments, writes the result to the output iterator ``out`` and returns
+ the iterator past the end of the output range.
+
+ **Example**::
+
+ std::vector<char> out;
+ fmt::format_to(std::back_inserter(out), "{}", 42);
+ \endrst
+ */
+template <typename OutputIt, typename S, typename... Args,
+ FMT_ENABLE_IF(
+ detail::is_output_iterator<OutputIt>::value &&
+ !detail::is_contiguous_back_insert_iterator<OutputIt>::value &&
+ detail::is_string<S>::value)>
+inline OutputIt format_to(OutputIt out, const S& format_str, Args&&... args) {
+ detail::check_format_string<Args...>(format_str);
+ using context = format_context_t<OutputIt, char_t<S>>;
+ return vformat_to(out, to_string_view(format_str),
+ make_format_args<context>(args...));
+}
+
+template <typename OutputIt> struct format_to_n_result {
+ /** Iterator past the end of the output range. */
+ OutputIt out;
+ /** Total (not truncated) output size. */
+ size_t size;
+};
+
+template <typename OutputIt, typename Char = typename OutputIt::value_type>
+using format_to_n_context =
+ format_context_t<detail::truncating_iterator<OutputIt>, Char>;
+
+template <typename OutputIt, typename Char = typename OutputIt::value_type>
+using format_to_n_args = basic_format_args<format_to_n_context<OutputIt, Char>>;
+
+template <typename OutputIt, typename Char, typename... Args>
+inline format_arg_store<format_to_n_context<OutputIt, Char>, Args...>
+make_format_to_n_args(const Args&... args) {
+ return format_arg_store<format_to_n_context<OutputIt, Char>, Args...>(
+ args...);
+}
+
+template <typename OutputIt, typename Char, typename... Args,
+ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value)>
+inline format_to_n_result<OutputIt> vformat_to_n(
+ OutputIt out, size_t n, basic_string_view<Char> format_str,
+ format_to_n_args<type_identity_t<OutputIt>, type_identity_t<Char>> args) {
+ auto it = vformat_to(detail::truncating_iterator<OutputIt>(out, n),
+ format_str, args);
+ return {it.base(), it.count()};
+}
+
+/**
+ \rst
+ Formats arguments, writes up to ``n`` characters of the result to the output
+ iterator ``out`` and returns the total output size and the iterator past the
+ end of the output range.
+ \endrst
+ */
+template <typename OutputIt, typename S, typename... Args,
+ FMT_ENABLE_IF(detail::is_string<S>::value&&
+ detail::is_output_iterator<OutputIt>::value)>
+inline format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
+ const S& format_str,
+ const Args&... args) {
+ detail::check_format_string<Args...>(format_str);
+ using context = format_to_n_context<OutputIt, char_t<S>>;
+ return vformat_to_n(out, n, to_string_view(format_str),
+ make_format_args<context>(args...));
+}
+
+template <typename Char, enable_if_t<(!std::is_same<Char, char>::value), int>>
+std::basic_string<Char> detail::vformat(
+ basic_string_view<Char> format_str,
+ basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+ basic_memory_buffer<Char> buffer;
+ detail::vformat_to(buffer, format_str, args);
+ return to_string(buffer);
+}
+
+/**
+ Returns the number of characters in the output of
+ ``format(format_str, args...)``.
+ */
+template <typename... Args>
+inline size_t formatted_size(string_view format_str, const Args&... args) {
+ return format_to(detail::counting_iterator(), format_str, args...).count();
+}
+
+template <typename Char, FMT_ENABLE_IF(std::is_same<Char, wchar_t>::value)>
+void vprint(std::FILE* f, basic_string_view<Char> format_str,
+ wformat_args args) {
+ wmemory_buffer buffer;
+ detail::vformat_to(buffer, format_str, args);
+ buffer.push_back(L'\0');
+ if (std::fputws(buffer.data(), f) == -1)
+ FMT_THROW(system_error(errno, "cannot write to file"));
+}
+
+template <typename Char, FMT_ENABLE_IF(std::is_same<Char, wchar_t>::value)>
+void vprint(basic_string_view<Char> format_str, wformat_args args) {
+ vprint(stdout, format_str, args);
+}
+
+#if FMT_USE_USER_DEFINED_LITERALS
+namespace detail {
+
+# if FMT_USE_UDL_TEMPLATE
+template <typename Char, Char... CHARS> class udl_formatter {
+ public:
+ template <typename... Args>
+ std::basic_string<Char> operator()(Args&&... args) const {
+ static FMT_CONSTEXPR_DECL Char s[] = {CHARS..., '\0'};
+ check_format_string<remove_cvref_t<Args>...>(FMT_STRING(s));
+ return format(s, std::forward<Args>(args)...);
+ }
+};
+# else
+template <typename Char> struct udl_formatter {
+ basic_string_view<Char> str;
+
+ template <typename... Args>
+ std::basic_string<Char> operator()(Args&&... args) const {
+ return format(str, std::forward<Args>(args)...);
+ }
+};
+# endif // FMT_USE_UDL_TEMPLATE
+
+template <typename Char> struct udl_arg {
+ const Char* str;
+
+ template <typename T> named_arg<Char, T> operator=(T&& value) const {
+ return {str, std::forward<T>(value)};
+ }
+};
+} // namespace detail
+
+inline namespace literals {
+# if FMT_USE_UDL_TEMPLATE
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wpedantic"
+# if FMT_CLANG_VERSION
+# pragma GCC diagnostic ignored "-Wgnu-string-literal-operator-template"
+# endif
+template <typename Char, Char... CHARS>
+FMT_CONSTEXPR detail::udl_formatter<Char, CHARS...> operator""_format() {
+ return {};
+}
+# pragma GCC diagnostic pop
+# else
+/**
+ \rst
+ User-defined literal equivalent of :func:`fmt::format`.
+
+ **Example**::
+
+ using namespace fmt::literals;
+ std::string message = "The answer is {}"_format(42);
+ \endrst
+ */
+FMT_CONSTEXPR detail::udl_formatter<char> operator"" _format(const char* s,
+ size_t n) {
+ return {{s, n}};
+}
+FMT_CONSTEXPR detail::udl_formatter<wchar_t> operator"" _format(
+ const wchar_t* s, size_t n) {
+ return {{s, n}};
+}
+# endif // FMT_USE_UDL_TEMPLATE
+
+/**
+ \rst
+ User-defined literal equivalent of :func:`fmt::arg`.
+
+ **Example**::
+
+ using namespace fmt::literals;
+ fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23);
+ \endrst
+ */
+FMT_CONSTEXPR detail::udl_arg<char> operator"" _a(const char* s, size_t) {
+ return {s};
+}
+FMT_CONSTEXPR detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
+ return {s};
+}
+} // namespace literals
+#endif // FMT_USE_USER_DEFINED_LITERALS
+FMT_END_NAMESPACE
+
+#ifdef FMT_HEADER_ONLY
+# define FMT_FUNC inline
+# include "format-inl.h"
+#else
+# define FMT_FUNC
+#endif
+
+#endif // FMT_FORMAT_H_
--- /dev/null
+// Formatting library for C++
+//
+// Copyright (c) 2012 - 2016, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#include "fmt/format-inl.h"
+
+FMT_BEGIN_NAMESPACE
+namespace detail {
+
+template <typename T>
+int format_float(char* buf, std::size_t size, const char* format, int precision,
+ T value) {
+#ifdef FMT_FUZZ
+ if (precision > 100000)
+ throw std::runtime_error(
+ "fuzz mode - avoid large allocation inside snprintf");
+#endif
+ // Suppress the warning about nonliteral format string.
+ int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
+ return precision < 0 ? snprintf_ptr(buf, size, format, value)
+ : snprintf_ptr(buf, size, format, precision, value);
+}
+} // namespace detail
+
+template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
+
+// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
+int (*instantiate_format_float)(double, int, detail::float_specs,
+ detail::buffer<char>&) = detail::format_float;
+
+#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
+template FMT_API detail::locale_ref::locale_ref(const std::locale& loc);
+template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
+#endif
+
+// Explicit instantiations for char.
+
+template FMT_API std::string detail::grouping_impl<char>(locale_ref);
+template FMT_API char detail::thousands_sep_impl(locale_ref);
+template FMT_API char detail::decimal_point_impl(locale_ref);
+
+template FMT_API void detail::buffer<char>::append(const char*, const char*);
+
+template FMT_API FMT_BUFFER_CONTEXT(char)::iterator detail::vformat_to(
+ detail::buffer<char>&, string_view,
+ basic_format_args<FMT_BUFFER_CONTEXT(char)>);
+
+template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
+ detail::buffer<char>&);
+template FMT_API int detail::snprintf_float(long double, int,
+ detail::float_specs,
+ detail::buffer<char>&);
+template FMT_API int detail::format_float(double, int, detail::float_specs,
+ detail::buffer<char>&);
+template FMT_API int detail::format_float(long double, int, detail::float_specs,
+ detail::buffer<char>&);
+
+// Explicit instantiations for wchar_t.
+
+template FMT_API std::string detail::grouping_impl<wchar_t>(locale_ref);
+template FMT_API wchar_t detail::thousands_sep_impl(locale_ref);
+template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
+
+template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
+ const wchar_t*);
+FMT_END_NAMESPACE
--- /dev/null
+/*
+ * getopt_long() -- long options parser
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#ifdef HAVE_GETOPT_LONG
+
+typedef int do_not_warn_about_empty_compilation_unit;
+
+#else
+
+#include "getopt_long.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#define BADCH '?'
+#define BADARG ':'
+#define EMSG ""
+
+int
+getopt_long(int argc, char *const argv[],
+ const char *optstring,
+ const struct option * longopts, int *longindex)
+{
+ static char *place = EMSG; /* option letter processing */
+ char *oli; /* option letter list index */
+
+ if (!*place)
+ { /* update scanning pointer */
+ if (optind >= argc)
+ {
+ place = EMSG;
+ return -1;
+ }
+
+ place = argv[optind];
+
+ if (place[0] != '-')
+ {
+ place = EMSG;
+ return -1;
+ }
+
+ place++;
+
+ if (place[0] == '-' && place[1] == '\0')
+ { /* found "--" */
+ ++optind;
+ place = EMSG;
+ return -1;
+ }
+
+ if (place[0] == '-' && place[1])
+ {
+ /* long option */
+ size_t namelen;
+ int i;
+
+ place++;
+
+ namelen = strcspn(place, "=");
+ for (i = 0; longopts[i].name != NULL; i++)
+ {
+ if (strlen(longopts[i].name) == namelen
+ && strncmp(place, longopts[i].name, namelen) == 0)
+ {
+ if (longopts[i].has_arg)
+ {
+ if (place[namelen] == '=')
+ optarg = place + namelen + 1;
+ else if (optind < argc - 1)
+ {
+ optind++;
+ optarg = argv[optind];
+ }
+ else
+ {
+ if (optstring[0] == ':')
+ return BADARG;
+ if (opterr)
+ fprintf(stderr,
+ "%s: option requires an argument -- %s\n",
+ argv[0], place);
+ place = EMSG;
+ optind++;
+ return BADCH;
+ }
+ }
+ else
+ {
+ optarg = NULL;
+ if (place[namelen] != 0)
+ {
+ /* XXX error? */
+ }
+ }
+
+ optind++;
+
+ if (longindex)
+ *longindex = i;
+
+ place = EMSG;
+
+ if (longopts[i].flag == NULL)
+ return longopts[i].val;
+ else
+ {
+ *longopts[i].flag = longopts[i].val;
+ return 0;
+ }
+ }
+ }
+
+ if (opterr && optstring[0] != ':')
+ fprintf(stderr,
+ "%s: illegal option -- %s\n", argv[0], place);
+ place = EMSG;
+ optind++;
+ return BADCH;
+ }
+ }
+
+ /* short option */
+ optopt = (int) *place++;
+
+ oli = strchr(optstring, optopt);
+ if (!oli)
+ {
+ if (!*place)
+ ++optind;
+ if (opterr && *optstring != ':')
+ fprintf(stderr,
+ "%s: illegal option -- %c\n", argv[0], optopt);
+ return BADCH;
+ }
+
+ if (oli[1] != ':')
+ { /* don't need argument */
+ optarg = NULL;
+ if (!*place)
+ ++optind;
+ }
+ else
+ { /* need an argument */
+ if (*place) /* no white space */
+ optarg = place;
+ else if (argc <= ++optind)
+ { /* no arg */
+ place = EMSG;
+ if (*optstring == ':')
+ return BADARG;
+ if (opterr)
+ fprintf(stderr,
+ "%s: option requires an argument -- %c\n",
+ argv[0], optopt);
+ return BADCH;
+ }
+ else
+ /* white space */
+ optarg = argv[optind];
+ place = EMSG;
+ ++optind;
+ }
+ return optopt;
+}
+
+#endif /* HAVE_GETOPT_LONG */
--- /dev/null
+/*
+ * Portions Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Portions Copyright (c) 2003-2010, PostgreSQL Global Development Group
+ */
+#ifndef GETOPT_LONG_H
+#define GETOPT_LONG_H
+
+extern int opterr;
+extern int optind;
+extern int optopt;
+extern char *optarg;
+
+struct option
+{
+ const char *name;
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+#define no_argument 0
+#define required_argument 1
+
+extern int getopt_long(int argc, char *const argv[],
+ const char *optstring,
+ const struct option * longopts, int *longindex);
+
+#endif /* GETOPT_LONG_H */
--- /dev/null
+// minitrace
+// Copyright 2014 by Henrik Rydgård
+// http://www.github.com/hrydgard/minitrace
+// Released under the MIT license.
+
+// See minitrace.h for basic documentation.
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef _WIN32
+#pragma warning (disable:4996)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#define __thread __declspec(thread)
+#define pthread_mutex_t CRITICAL_SECTION
+#define pthread_mutex_init(a, b) InitializeCriticalSection(a)
+#define pthread_mutex_lock(a) EnterCriticalSection(a)
+#define pthread_mutex_unlock(a) LeaveCriticalSection(a)
+#define pthread_mutex_destroy(a) DeleteCriticalSection(a)
+#else
+#include <signal.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+
+#include "minitrace.h"
+
+#ifdef __GNUC__
+#define ATTR_NORETURN __attribute__((noreturn))
+#else
+#define ATTR_NORETURN
+#endif
+
+#define ARRAY_SIZE(x) sizeof(x)/sizeof(x[0])
+
+// Ugh, this struct is already pretty heavy.
+// Will probably need to move arguments to a second buffer to support more than one.
+typedef struct raw_event {
+ const char *name;
+ const char *cat;
+ void *id;
+ int64_t ts;
+ uint32_t pid;
+ uint32_t tid;
+ char ph;
+ mtr_arg_type arg_type;
+ const char *arg_name;
+ union {
+ const char *a_str;
+ int a_int;
+ double a_double;
+ };
+} raw_event_t;
+
+static raw_event_t *buffer;
+static volatile int count;
+static int is_tracing = 0;
+static int64_t time_offset;
+static int first_line = 1;
+static FILE *f;
+static __thread int cur_thread_id; // Thread local storage
+static int cur_process_id;
+static pthread_mutex_t mutex;
+
+#define STRING_POOL_SIZE 100
+static char *str_pool[100];
+
+// Tiny portability layer.
+// Exposes:
+// get_cur_thread_id()
+// get_cur_process_id()
+// mtr_time_s()
+// pthread basics
+#ifdef _WIN32
+static int get_cur_thread_id() {
+ return (int)GetCurrentThreadId();
+}
+static int get_cur_process_id() {
+ return (int)GetCurrentProcessId();
+}
+
+static uint64_t _frequency = 0;
+static uint64_t _starttime = 0;
+double mtr_time_s() {
+ if (_frequency == 0) {
+ QueryPerformanceFrequency((LARGE_INTEGER*)&_frequency);
+ QueryPerformanceCounter((LARGE_INTEGER*)&_starttime);
+ }
+ __int64 time;
+ QueryPerformanceCounter((LARGE_INTEGER*)&time);
+ return ((double) (time - _starttime) / (double) _frequency);
+}
+
+// Ctrl+C handling for Windows console apps
+static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) {
+ if (is_tracing && fdwCtrlType == CTRL_C_EVENT) {
+ printf("Ctrl-C detected! Flushing trace and shutting down.\n\n");
+ mtr_flush();
+ mtr_shutdown();
+ }
+ ExitProcess(1);
+}
+
+void mtr_register_sigint_handler() {
+ // For console apps:
+ SetConsoleCtrlHandler(&CtrlHandler, TRUE);
+}
+
+#else
+
+static inline int get_cur_thread_id() {
+ return (int)(intptr_t)pthread_self();
+}
+static inline int get_cur_process_id() {
+ return (int)getpid();
+}
+
+#if defined(BLACKBERRY)
+double mtr_time_s() {
+ struct timespec time;
+ clock_gettime(CLOCK_MONOTONIC, &time); // Linux must use CLOCK_MONOTONIC_RAW due to time warps
+ return time.tv_sec + time.tv_nsec / 1.0e9;
+}
+#else
+double mtr_time_s() {
+ static time_t start;
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ if (start == 0) {
+ start = tv.tv_sec;
+ }
+ tv.tv_sec -= start;
+ return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
+}
+#endif // !BLACKBERRY
+
+static void termination_handler(int signum) ATTR_NORETURN;
+static void termination_handler(int signum) {
+ (void) signum;
+ if (is_tracing) {
+ printf("Ctrl-C detected! Flushing trace and shutting down.\n\n");
+ mtr_flush();
+ fwrite("\n]}\n", 1, 4, f);
+ fclose(f);
+ }
+ exit(1);
+}
+
+void mtr_register_sigint_handler() {
+#ifndef MTR_ENABLED
+ return;
+#endif
+ // Avoid altering set-to-be-ignored handlers while registering.
+ if (signal(SIGINT, &termination_handler) == SIG_IGN)
+ signal(SIGINT, SIG_IGN);
+}
+
+#endif
+
+void mtr_init_from_stream(void *stream) {
+#ifndef MTR_ENABLED
+ return;
+#endif
+ buffer = (raw_event_t *)malloc(INTERNAL_MINITRACE_BUFFER_SIZE * sizeof(raw_event_t));
+ is_tracing = 1;
+ count = 0;
+ f = (FILE *)stream;
+ const char *header = "{\"traceEvents\":[\n";
+ fwrite(header, 1, strlen(header), f);
+ time_offset = (uint64_t)(mtr_time_s() * 1000000);
+ first_line = 1;
+ pthread_mutex_init(&mutex, 0);
+}
+
+void mtr_init(const char *json_file) {
+#ifndef MTR_ENABLED
+ return;
+#endif
+ mtr_init_from_stream(fopen(json_file, "wb"));
+}
+
+void mtr_shutdown() {
+ int i;
+#ifndef MTR_ENABLED
+ return;
+#endif
+ is_tracing = 0;
+ mtr_flush();
+ fwrite("\n]}\n", 1, 4, f);
+ fclose(f);
+ pthread_mutex_destroy(&mutex);
+ f = 0;
+ free(buffer);
+ buffer = 0;
+ for (i = 0; i < STRING_POOL_SIZE; i++) {
+ if (str_pool[i]) {
+ free(str_pool[i]);
+ str_pool[i] = 0;
+ }
+ }
+}
+
+const char *mtr_pool_string(const char *str) {
+ int i;
+ for (i = 0; i < STRING_POOL_SIZE; i++) {
+ if (!str_pool[i]) {
+ str_pool[i] = (char*)malloc(strlen(str) + 1);
+ strcpy(str_pool[i], str);
+ return str_pool[i];
+ } else {
+ if (!strcmp(str, str_pool[i]))
+ return str_pool[i];
+ }
+ }
+ return "string pool full";
+}
+
+void mtr_start() {
+#ifndef MTR_ENABLED
+ return;
+#endif
+ is_tracing = 1;
+}
+
+void mtr_stop() {
+#ifndef MTR_ENABLED
+ return;
+#endif
+ is_tracing = 0;
+}
+
+// TODO: fwrite more than one line at a time.
+void mtr_flush() {
+#ifndef MTR_ENABLED
+ return;
+#endif
+ int i = 0;
+ char linebuf[1024];
+ char arg_buf[1024];
+ char id_buf[256];
+ // We have to lock while flushing. So we really should avoid flushing as much as possible.
+
+
+ pthread_mutex_lock(&mutex);
+ int old_tracing = is_tracing;
+ is_tracing = 0; // Stop logging even if using interlocked increments instead of the mutex. Can cause data loss.
+
+ for (i = 0; i < count; i++) {
+ raw_event_t *raw = &buffer[i];
+ int len;
+ switch (raw->arg_type) {
+ case MTR_ARG_TYPE_INT:
+ snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":%i", raw->arg_name, raw->a_int);
+ break;
+ case MTR_ARG_TYPE_STRING_CONST:
+ snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str);
+ break;
+ case MTR_ARG_TYPE_STRING_COPY:
+ if (strlen(raw->a_str) > 700) {
+ snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%.*s\"", raw->arg_name, 700, raw->a_str);
+ } else {
+ snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str);
+ }
+ break;
+ case MTR_ARG_TYPE_NONE:
+ arg_buf[0] = '\0';
+ break;
+ }
+ if (raw->id) {
+ switch (raw->ph) {
+ case 'S':
+ case 'T':
+ case 'F':
+ // TODO: Support full 64-bit pointers
+ snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"id\":\"0x%08x\"", (uint32_t)(uintptr_t)raw->id);
+ break;
+ case 'X':
+ snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"dur\":%i", (int)raw->a_double);
+ break;
+ }
+ } else {
+ id_buf[0] = 0;
+ }
+ const char *cat = raw->cat;
+#ifdef _WIN32
+ // On Windows, we often end up with backslashes in category.
+ char temp[256];
+ {
+ int len = (int)strlen(cat);
+ int i;
+ if (len > 255) len = 255;
+ for (i = 0; i < len; i++) {
+ temp[i] = cat[i] == '\\' ? '/' : cat[i];
+ }
+ temp[len] = 0;
+ cat = temp;
+ }
+#endif
+
+ len = snprintf(linebuf, ARRAY_SIZE(linebuf), "%s{\"cat\":\"%s\",\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64 ",\"ph\":\"%c\",\"name\":\"%s\",\"args\":{%s}%s}",
+ first_line ? "" : ",\n",
+ cat, raw->pid, raw->tid, raw->ts - time_offset, raw->ph, raw->name, arg_buf, id_buf);
+ fwrite(linebuf, 1, len, f);
+ first_line = 0;
+ }
+ count = 0;
+ is_tracing = old_tracing;
+ pthread_mutex_unlock(&mutex);
+}
+
+void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id) {
+#ifndef MTR_ENABLED
+ return;
+#endif
+ if (!is_tracing || count >= INTERNAL_MINITRACE_BUFFER_SIZE)
+ return;
+ double ts = mtr_time_s();
+ if (!cur_thread_id) {
+ cur_thread_id = get_cur_thread_id();
+ }
+ if (!cur_process_id) {
+ cur_process_id = get_cur_process_id();
+ }
+
+#if 0 && _WIN32 // This should work, feel free to enable if you're adventurous and need performance.
+ int bufPos = InterlockedExchangeAdd((LONG volatile *)&count, 1);
+ raw_event_t *ev = &buffer[bufPos];
+#else
+ pthread_mutex_lock(&mutex);
+ raw_event_t *ev = &buffer[count];
+ count++;
+ pthread_mutex_unlock(&mutex);
+#endif
+
+ ev->cat = category;
+ ev->name = name;
+ ev->id = id;
+ ev->ph = ph;
+ if (ev->ph == 'X') {
+ double x;
+ memcpy(&x, id, sizeof(double));
+ ev->ts = (int64_t)(x * 1000000);
+ ev->a_double = (ts - x) * 1000000;
+ } else {
+ ev->ts = (int64_t)(ts * 1000000);
+ }
+ ev->tid = cur_thread_id;
+ ev->pid = cur_process_id;
+ ev->arg_type = MTR_ARG_TYPE_NONE;
+}
+
+void internal_mtr_raw_event_arg(const char *category, const char *name, char ph, void *id, mtr_arg_type arg_type, const char *arg_name, void *arg_value) {
+#ifndef MTR_ENABLED
+ return;
+#endif
+ if (!is_tracing || count >= INTERNAL_MINITRACE_BUFFER_SIZE)
+ return;
+ if (!cur_thread_id) {
+ cur_thread_id = get_cur_thread_id();
+ }
+ if (!cur_process_id) {
+ cur_process_id = get_cur_process_id();
+ }
+ double ts = mtr_time_s();
+
+#if 0 && _WIN32 // This should work, feel free to enable if you're adventurous and need performance.
+ int bufPos = InterlockedExchangeAdd((LONG volatile *)&count, 1);
+ raw_event_t *ev = &buffer[bufPos];
+#else
+ pthread_mutex_lock(&mutex);
+ raw_event_t *ev = &buffer[count];
+ count++;
+ pthread_mutex_unlock(&mutex);
+#endif
+
+ ev->cat = category;
+ ev->name = name;
+ ev->id = id;
+ ev->ts = (int64_t)(ts * 1000000);
+ ev->ph = ph;
+ ev->tid = cur_thread_id;
+ ev->pid = cur_process_id;
+ ev->arg_type = arg_type;
+ ev->arg_name = arg_name;
+ switch (arg_type) {
+ case MTR_ARG_TYPE_INT: ev->a_int = (int)(uintptr_t)arg_value; break;
+ case MTR_ARG_TYPE_STRING_CONST: ev->a_str = (const char*)arg_value; break;
+ case MTR_ARG_TYPE_STRING_COPY: ev->a_str = strdup((const char*)arg_value); break;
+ case MTR_ARG_TYPE_NONE: break;
+ }
+}
+
--- /dev/null
+// Minitrace
+//
+// Copyright 2014 by Henrik Rydgård
+// http://www.github.com/hrydgard/minitrace
+// Released under the MIT license.
+//
+// Ultra-light dependency free library for performance tracing C/C++ applications.
+// Produces traces compatible with Google Chrome's trace viewer.
+// Simply open "about:tracing" in Chrome and load the produced JSON.
+//
+// This contains far less template magic than the original libraries from Chrome
+// because this is meant to be usable from C.
+//
+// See README.md for a tutorial.
+//
+// The trace format is documented here:
+// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit
+// More:
+// http://www.altdevblogaday.com/2012/08/21/using-chrometracing-to-view-your-inline-profiling-data/
+
+#ifndef MINITRACE_H
+#define MINITRACE_H
+
+#include <inttypes.h>
+
+// If MTR_ENABLED is not defined, Minitrace does nothing and has near zero overhead.
+// Preferably, set this flag in your build system. If you can't just uncomment this line.
+// #define MTR_ENABLED
+
+// By default, will collect up to 1000000 events, then you must flush.
+// It's recommended that you simply call mtr_flush on a background thread
+// occasionally. It's safe...ish.
+#define INTERNAL_MINITRACE_BUFFER_SIZE 1000000
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Initializes Minitrace. Must be called very early during startup of your executable,
+// before any MTR_ statements.
+void mtr_init(const char *json_file);
+// Same as above, but allows passing in a custom stream (FILE *), as returned by
+// fopen(). It should be opened for writing, preferably in binary mode to avoid
+// processing of line endings (i.e. the "wb" mode).
+void mtr_init_from_stream(void *stream);
+
+// Shuts down minitrace cleanly, flushing the trace buffer.
+void mtr_shutdown(void);
+
+// Lets you enable and disable Minitrace at runtime.
+// May cause strange discontinuities in the output.
+// Minitrace is enabled on startup by default.
+void mtr_start(void);
+void mtr_stop(void);
+
+// Flushes the collected data to disk, clearing the buffer for new data.
+void mtr_flush(void);
+
+// Returns the current time in seconds. Used internally by Minitrace. No caching.
+double mtr_time_s(void);
+
+// Registers a handler that will flush the trace on Ctrl+C.
+// Works on Linux and MacOSX, and in Win32 console applications.
+void mtr_register_sigint_handler(void);
+
+// Utility function that should rarely be used.
+// If str is semi dynamic, store it permanently in a small pool so we don't need to malloc it.
+// The pool fills up fast though and performance isn't great.
+// Returns a fixed string if the pool is full.
+const char *mtr_pool_string(const char *str);
+
+// Commented-out types will be supported in the future.
+typedef enum {
+ MTR_ARG_TYPE_NONE = 0,
+ MTR_ARG_TYPE_INT = 1, // I
+ // MTR_ARG_TYPE_FLOAT = 2, // TODO
+ // MTR_ARG_TYPE_DOUBLE = 3, // TODO
+ MTR_ARG_TYPE_STRING_CONST = 8, // C
+ MTR_ARG_TYPE_STRING_COPY = 9,
+ // MTR_ARG_TYPE_JSON_COPY = 10,
+} mtr_arg_type;
+
+// TODO: Add support for more than one argument (metadata) per event
+// Having more costs speed and memory.
+#define MTR_MAX_ARGS 1
+
+// Only use the macros to call these.
+void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id);
+void internal_mtr_raw_event_arg(const char *category, const char *name, char ph, void *id, mtr_arg_type arg_type, const char *arg_name, void *arg_value);
+
+#ifdef MTR_ENABLED
+
+// c - category. Can be filtered by in trace viewer (or at least that's the intention).
+// A good use is to pass __FILE__, there are macros further below that will do it for you.
+// n - name. Pass __FUNCTION__ in most cases, unless you are marking up parts of one.
+
+// Scopes. In C++, use MTR_SCOPE. In C, always match them within the same scope.
+#define MTR_BEGIN(c, n) internal_mtr_raw_event(c, n, 'B', 0)
+#define MTR_END(c, n) internal_mtr_raw_event(c, n, 'E', 0)
+#define MTR_SCOPE(c, n) MTRScopedTrace ____mtr_scope(c, n)
+#define MTR_SCOPE_LIMIT(c, n, l) MTRScopedTraceLimit ____mtr_scope(c, n, l)
+
+// Async events. Can span threads. ID identifies which events to connect in the view.
+#define MTR_START(c, n, id) internal_mtr_raw_event(c, n, 'S', (void *)(id))
+#define MTR_STEP(c, n, id, step) internal_mtr_raw_event_arg(c, n, 'T', (void *)(id), MTR_ARG_TYPE_STRING_CONST, "step", (void *)(step))
+#define MTR_FINISH(c, n, id) internal_mtr_raw_event(c, n, 'F', (void *)(id))
+
+// Flow events. Like async events, but displayed in a more fancy way in the viewer.
+#define MTR_FLOW_START(c, n, id) internal_mtr_raw_event(c, n, 's', (void *)(id))
+#define MTR_FLOW_STEP(c, n, id, step) internal_mtr_raw_event_arg(c, n, 't', (void *)(id), MTR_ARG_TYPE_STRING_CONST, "step", (void *)(step))
+#define MTR_FLOW_FINISH(c, n, id) internal_mtr_raw_event(c, n, 'f', (void *)(id))
+
+// The same macros, but with a single named argument which shows up as metadata in the viewer.
+// _I for int.
+// _C is for a const string arg.
+// _S will copy the string, freeing on flush (expensive but sometimes necessary).
+// but required if the string was generated dynamically.
+
+// Note that it's fine to match BEGIN_S with END and BEGIN with END_S, etc.
+#define MTR_BEGIN_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
+#define MTR_END_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
+#define MTR_SCOPE_C(c, n, aname, astrval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
+
+#define MTR_BEGIN_S(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval))
+#define MTR_END_S(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval))
+#define MTR_SCOPE_S(c, n, aname, astrval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval))
+
+#define MTR_BEGIN_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval))
+#define MTR_END_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval))
+#define MTR_SCOPE_I(c, n, aname, aintval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval))
+
+// Instant events. For things with no duration.
+#define MTR_INSTANT(c, n) internal_mtr_raw_event(c, n, 'I', 0)
+#define MTR_INSTANT_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'I', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
+#define MTR_INSTANT_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'I', 0, MTR_ARG_TYPE_INT, aname, (void *)(aintval))
+
+// Counters (can't do multi-value counters yet)
+#define MTR_COUNTER(c, n, val) internal_mtr_raw_event_arg(c, n, 'C', 0, MTR_ARG_TYPE_INT, n, (void *)(intptr_t)(val))
+
+// Metadata. Call at the start preferably. Must be const strings.
+
+#define MTR_META_PROCESS_NAME(n) internal_mtr_raw_event_arg("", "process_name", 'M', 0, MTR_ARG_TYPE_STRING_COPY, "name", (void *)(n))
+#define MTR_META_THREAD_NAME(n) internal_mtr_raw_event_arg("", "thread_name", 'M', 0, MTR_ARG_TYPE_STRING_COPY, "name", (void *)(n))
+#define MTR_META_THREAD_SORT_INDEX(i) internal_mtr_raw_event_arg("", "thread_sort_index", 'M', 0, MTR_ARG_TYPE_INT, "sort_index", (void *)(i))
+
+#else
+
+#define MTR_BEGIN(c, n)
+#define MTR_END(c, n)
+#define MTR_SCOPE(c, n)
+#define MTR_START(c, n, id)
+#define MTR_STEP(c, n, id, step)
+#define MTR_FINISH(c, n, id)
+#define MTR_FLOW_START(c, n, id)
+#define MTR_FLOW_STEP(c, n, id, step)
+#define MTR_FLOW_FINISH(c, n, id)
+#define MTR_INSTANT(c, n)
+
+#define MTR_BEGIN_C(c, n, aname, astrval)
+#define MTR_END_C(c, n, aname, astrval)
+#define MTR_SCOPE_C(c, n, aname, astrval)
+
+#define MTR_BEGIN_S(c, n, aname, astrval)
+#define MTR_END_S(c, n, aname, astrval)
+#define MTR_SCOPE_S(c, n, aname, astrval)
+
+#define MTR_BEGIN_I(c, n, aname, aintval)
+#define MTR_END_I(c, n, aname, aintval)
+#define MTR_SCOPE_I(c, n, aname, aintval)
+
+#define MTR_INSTANT(c, n)
+#define MTR_INSTANT_C(c, n, aname, astrval)
+#define MTR_INSTANT_I(c, n, aname, aintval)
+
+// Counters (can't do multi-value counters yet)
+#define MTR_COUNTER(c, n, val)
+
+// Metadata. Call at the start preferably. Must be const strings.
+
+#define MTR_META_PROCESS_NAME(n)
+
+#define MTR_META_THREAD_NAME(n)
+#define MTR_META_THREAD_SORT_INDEX(i)
+
+#endif
+
+// Shortcuts for simple function timing with automatic categories and names.
+
+#define MTR_BEGIN_FUNC() MTR_BEGIN(__FILE__, __FUNCTION__)
+#define MTR_END_FUNC() MTR_END(__FILE__, __FUNCTION__)
+#define MTR_SCOPE_FUNC() MTR_SCOPE(__FILE__, __FUNCTION__)
+#define MTR_INSTANT_FUNC() MTR_INSTANT(__FILE__, __FUNCTION__)
+#define MTR_SCOPE_FUNC_LIMIT_S(l) MTRScopedTraceLimit ____mtr_scope(__FILE__, __FUNCTION__, l)
+#define MTR_SCOPE_FUNC_LIMIT_MS(l) MTRScopedTraceLimit ____mtr_scope(__FILE__, __FUNCTION__, (double)l * 0.000001)
+
+// Same, but with a single argument of the usual types.
+#define MTR_BEGIN_FUNC_S(aname, arg) MTR_BEGIN_S(__FILE__, __FUNCTION__, aname, arg)
+#define MTR_END_FUNC_S(aname, arg) MTR_END_S(__FILE__, __FUNCTION__, aname, arg)
+#define MTR_SCOPE_FUNC_S(aname, arg) MTR_SCOPE_S(__FILE__, __FUNCTION__, aname, arg)
+
+#define MTR_BEGIN_FUNC_C(aname, arg) MTR_BEGIN_C(__FILE__, __FUNCTION__, aname, arg)
+#define MTR_END_FUNC_C(aname, arg) MTR_END_C(__FILE__, __FUNCTION__, aname, arg)
+#define MTR_SCOPE_FUNC_C(aname, arg) MTR_SCOPE_C(__FILE__, __FUNCTION__, aname, arg)
+
+#define MTR_BEGIN_FUNC_I(aname, arg) MTR_BEGIN_I(__FILE__, __FUNCTION__, aname, arg)
+#define MTR_END_FUNC_I(aname, arg) MTR_END_I(__FILE__, __FUNCTION__, aname, arg)
+#define MTR_SCOPE_FUNC_I(aname, arg) MTR_SCOPE_I(__FILE__, __FUNCTION__, aname, arg)
+
+#ifdef __cplusplus
+}
+
+#ifdef MTR_ENABLED
+// These are optimized to use X events (combined B and E). Much easier to do in C++ than in C.
+class MTRScopedTrace {
+public:
+ MTRScopedTrace(const char *category, const char *name)
+ : category_(category), name_(name) {
+ start_time_ = mtr_time_s();
+ }
+ ~MTRScopedTrace() {
+ internal_mtr_raw_event(category_, name_, 'X', &start_time_);
+ }
+
+private:
+ const char *category_;
+ const char *name_;
+ double start_time_;
+};
+
+// Only outputs a block if execution time exceeded the limit.
+// TODO: This will effectively call mtr_time_s twice at the end, which is bad.
+class MTRScopedTraceLimit {
+public:
+ MTRScopedTraceLimit(const char *category, const char *name, double limit_s)
+ : category_(category), name_(name), limit_(limit_s) {
+ start_time_ = mtr_time_s();
+ }
+ ~MTRScopedTraceLimit() {
+ double end_time = mtr_time_s();
+ if (end_time - start_time_ >= limit_) {
+ internal_mtr_raw_event(category_, name_, 'X', &start_time_);
+ }
+ }
+
+private:
+ const char *category_;
+ const char *name_;
+ double start_time_;
+ double limit_;
+};
+
+class MTRScopedTraceArg {
+public:
+ MTRScopedTraceArg(const char *category, const char *name, mtr_arg_type arg_type, const char *arg_name, void *arg_value)
+ : category_(category), name_(name) {
+ internal_mtr_raw_event_arg(category, name, 'B', 0, arg_type, arg_name, arg_value);
+ }
+ ~MTRScopedTraceArg() {
+ internal_mtr_raw_event(category_, name_, 'E', 0);
+ }
+
+private:
+ const char *category_;
+ const char *name_;
+};
+#endif
+
+#endif
+
+#endif
--- /dev/null
+//
+// Copyright (c) 2014-2018 Martin Moene
+//
+// https://github.com/martinmoene/optional-lite
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#pragma once
+
+#ifndef NONSTD_OPTIONAL_LITE_HPP
+#define NONSTD_OPTIONAL_LITE_HPP
+
+#define optional_lite_MAJOR 3
+#define optional_lite_MINOR 2
+#define optional_lite_PATCH 0
+
+#define optional_lite_VERSION optional_STRINGIFY(optional_lite_MAJOR) "." optional_STRINGIFY(optional_lite_MINOR) "." optional_STRINGIFY(optional_lite_PATCH)
+
+#define optional_STRINGIFY( x ) optional_STRINGIFY_( x )
+#define optional_STRINGIFY_( x ) #x
+
+// optional-lite configuration:
+
+#define optional_OPTIONAL_DEFAULT 0
+#define optional_OPTIONAL_NONSTD 1
+#define optional_OPTIONAL_STD 2
+
+#if !defined( optional_CONFIG_SELECT_OPTIONAL )
+# define optional_CONFIG_SELECT_OPTIONAL ( optional_HAVE_STD_OPTIONAL ? optional_OPTIONAL_STD : optional_OPTIONAL_NONSTD )
+#endif
+
+// Control presence of exception handling (try and auto discover):
+
+#ifndef optional_CONFIG_NO_EXCEPTIONS
+# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
+# define optional_CONFIG_NO_EXCEPTIONS 0
+# else
+# define optional_CONFIG_NO_EXCEPTIONS 1
+# endif
+#endif
+
+// C++ language version detection (C++20 is speculative):
+// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
+
+#ifndef optional_CPLUSPLUS
+# if defined(_MSVC_LANG ) && !defined(__clang__)
+# define optional_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
+# else
+# define optional_CPLUSPLUS __cplusplus
+# endif
+#endif
+
+#define optional_CPP98_OR_GREATER ( optional_CPLUSPLUS >= 199711L )
+#define optional_CPP11_OR_GREATER ( optional_CPLUSPLUS >= 201103L )
+#define optional_CPP11_OR_GREATER_ ( optional_CPLUSPLUS >= 201103L )
+#define optional_CPP14_OR_GREATER ( optional_CPLUSPLUS >= 201402L )
+#define optional_CPP17_OR_GREATER ( optional_CPLUSPLUS >= 201703L )
+#define optional_CPP20_OR_GREATER ( optional_CPLUSPLUS >= 202000L )
+
+// C++ language version (represent 98 as 3):
+
+#define optional_CPLUSPLUS_V ( optional_CPLUSPLUS / 100 - (optional_CPLUSPLUS > 200000 ? 2000 : 1994) )
+
+// Use C++17 std::optional if available and requested:
+
+#if optional_CPP17_OR_GREATER && defined(__has_include )
+# if __has_include( <optional> )
+# define optional_HAVE_STD_OPTIONAL 1
+# else
+# define optional_HAVE_STD_OPTIONAL 0
+# endif
+#else
+# define optional_HAVE_STD_OPTIONAL 0
+#endif
+
+#define optional_USES_STD_OPTIONAL ( (optional_CONFIG_SELECT_OPTIONAL == optional_OPTIONAL_STD) || ((optional_CONFIG_SELECT_OPTIONAL == optional_OPTIONAL_DEFAULT) && optional_HAVE_STD_OPTIONAL) )
+
+//
+// in_place: code duplicated in any-lite, expected-lite, optional-lite, value-ptr-lite, variant-lite:
+//
+
+#ifndef nonstd_lite_HAVE_IN_PLACE_TYPES
+#define nonstd_lite_HAVE_IN_PLACE_TYPES 1
+
+// C++17 std::in_place in <utility>:
+
+#if optional_CPP17_OR_GREATER
+
+#include <utility>
+
+namespace nonstd {
+
+using std::in_place;
+using std::in_place_type;
+using std::in_place_index;
+using std::in_place_t;
+using std::in_place_type_t;
+using std::in_place_index_t;
+
+#define nonstd_lite_in_place_t( T) std::in_place_t
+#define nonstd_lite_in_place_type_t( T) std::in_place_type_t<T>
+#define nonstd_lite_in_place_index_t(K) std::in_place_index_t<K>
+
+#define nonstd_lite_in_place( T) std::in_place_t{}
+#define nonstd_lite_in_place_type( T) std::in_place_type_t<T>{}
+#define nonstd_lite_in_place_index(K) std::in_place_index_t<K>{}
+
+} // namespace nonstd
+
+#else // optional_CPP17_OR_GREATER
+
+#include <cstddef>
+
+namespace nonstd {
+namespace detail {
+
+template< class T >
+struct in_place_type_tag {};
+
+template< std::size_t K >
+struct in_place_index_tag {};
+
+} // namespace detail
+
+struct in_place_t {};
+
+template< class T >
+inline in_place_t in_place( detail::in_place_type_tag<T> /*unused*/ = detail::in_place_type_tag<T>() )
+{
+ return in_place_t();
+}
+
+template< std::size_t K >
+inline in_place_t in_place( detail::in_place_index_tag<K> /*unused*/ = detail::in_place_index_tag<K>() )
+{
+ return in_place_t();
+}
+
+template< class T >
+inline in_place_t in_place_type( detail::in_place_type_tag<T> /*unused*/ = detail::in_place_type_tag<T>() )
+{
+ return in_place_t();
+}
+
+template< std::size_t K >
+inline in_place_t in_place_index( detail::in_place_index_tag<K> /*unused*/ = detail::in_place_index_tag<K>() )
+{
+ return in_place_t();
+}
+
+// mimic templated typedef:
+
+#define nonstd_lite_in_place_t( T) nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag<T> )
+#define nonstd_lite_in_place_type_t( T) nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag<T> )
+#define nonstd_lite_in_place_index_t(K) nonstd::in_place_t(&)( nonstd::detail::in_place_index_tag<K> )
+
+#define nonstd_lite_in_place( T) nonstd::in_place_type<T>
+#define nonstd_lite_in_place_type( T) nonstd::in_place_type<T>
+#define nonstd_lite_in_place_index(K) nonstd::in_place_index<K>
+
+} // namespace nonstd
+
+#endif // optional_CPP17_OR_GREATER
+#endif // nonstd_lite_HAVE_IN_PLACE_TYPES
+
+//
+// Using std::optional:
+//
+
+#if optional_USES_STD_OPTIONAL
+
+#include <optional>
+
+namespace nonstd {
+
+ using std::optional;
+ using std::bad_optional_access;
+ using std::hash;
+
+ using std::nullopt;
+ using std::nullopt_t;
+
+ using std::operator==;
+ using std::operator!=;
+ using std::operator<;
+ using std::operator<=;
+ using std::operator>;
+ using std::operator>=;
+ using std::make_optional;
+ using std::swap;
+}
+
+#else // optional_USES_STD_OPTIONAL
+
+#include <cassert>
+#include <utility>
+
+// optional-lite alignment configuration:
+
+#ifndef optional_CONFIG_MAX_ALIGN_HACK
+# define optional_CONFIG_MAX_ALIGN_HACK 0
+#endif
+
+#ifndef optional_CONFIG_ALIGN_AS
+// no default, used in #if defined()
+#endif
+
+#ifndef optional_CONFIG_ALIGN_AS_FALLBACK
+# define optional_CONFIG_ALIGN_AS_FALLBACK double
+#endif
+
+// Compiler warning suppression:
+
+#if defined(__clang__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wundef"
+#elif defined(__GNUC__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wundef"
+#elif defined(_MSC_VER )
+# pragma warning( push )
+#endif
+
+// half-open range [lo..hi):
+#define optional_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) )
+
+// Compiler versions:
+//
+// MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0)
+// MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002)
+// MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003)
+// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005)
+// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008)
+// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
+// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
+// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
+// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
+// MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017)
+
+#if defined(_MSC_VER ) && !defined(__clang__)
+# define optional_COMPILER_MSVC_VER (_MSC_VER )
+# define optional_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) )
+#else
+# define optional_COMPILER_MSVC_VER 0
+# define optional_COMPILER_MSVC_VERSION 0
+#endif
+
+#define optional_COMPILER_VERSION( major, minor, patch ) ( 10 * (10 * (major) + (minor) ) + (patch) )
+
+#if defined(__GNUC__) && !defined(__clang__)
+# define optional_COMPILER_GNUC_VERSION optional_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#else
+# define optional_COMPILER_GNUC_VERSION 0
+#endif
+
+#if defined(__clang__)
+# define optional_COMPILER_CLANG_VERSION optional_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
+#else
+# define optional_COMPILER_CLANG_VERSION 0
+#endif
+
+#if optional_BETWEEN(optional_COMPILER_MSVC_VERSION, 70, 140 )
+# pragma warning( disable: 4345 ) // initialization behavior changed
+#endif
+
+#if optional_BETWEEN(optional_COMPILER_MSVC_VERSION, 70, 150 )
+# pragma warning( disable: 4814 ) // in C++14 'constexpr' will not imply 'const'
+#endif
+
+// Presence of language and library features:
+
+#define optional_HAVE(FEATURE) ( optional_HAVE_##FEATURE )
+
+#ifdef _HAS_CPP0X
+# define optional_HAS_CPP0X _HAS_CPP0X
+#else
+# define optional_HAS_CPP0X 0
+#endif
+
+// Unless defined otherwise below, consider VC14 as C++11 for optional-lite:
+
+#if optional_COMPILER_MSVC_VER >= 1900
+# undef optional_CPP11_OR_GREATER
+# define optional_CPP11_OR_GREATER 1
+#endif
+
+#define optional_CPP11_90 (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1500)
+#define optional_CPP11_100 (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1600)
+#define optional_CPP11_110 (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1700)
+#define optional_CPP11_120 (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1800)
+#define optional_CPP11_140 (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1900)
+#define optional_CPP11_141 (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1910)
+
+#define optional_CPP14_000 (optional_CPP14_OR_GREATER)
+#define optional_CPP17_000 (optional_CPP17_OR_GREATER)
+
+// Presence of C++11 language features:
+
+#define optional_HAVE_CONSTEXPR_11 optional_CPP11_140
+#define optional_HAVE_IS_DEFAULT optional_CPP11_140
+#define optional_HAVE_NOEXCEPT optional_CPP11_140
+#define optional_HAVE_NULLPTR optional_CPP11_100
+#define optional_HAVE_REF_QUALIFIER optional_CPP11_140
+
+// Presence of C++14 language features:
+
+#define optional_HAVE_CONSTEXPR_14 optional_CPP14_000
+
+// Presence of C++17 language features:
+
+#define optional_HAVE_NODISCARD optional_CPP17_000
+
+// Presence of C++ library features:
+
+#define optional_HAVE_CONDITIONAL optional_CPP11_120
+#define optional_HAVE_REMOVE_CV optional_CPP11_120
+#define optional_HAVE_TYPE_TRAITS optional_CPP11_90
+
+#define optional_HAVE_TR1_TYPE_TRAITS (!! optional_COMPILER_GNUC_VERSION )
+#define optional_HAVE_TR1_ADD_POINTER (!! optional_COMPILER_GNUC_VERSION )
+
+// C++ feature usage:
+
+#if optional_HAVE( CONSTEXPR_11 )
+# define optional_constexpr constexpr
+#else
+# define optional_constexpr /*constexpr*/
+#endif
+
+#if optional_HAVE( IS_DEFAULT )
+# define optional_is_default = default;
+#else
+# define optional_is_default {}
+#endif
+
+#if optional_HAVE( CONSTEXPR_14 )
+# define optional_constexpr14 constexpr
+#else
+# define optional_constexpr14 /*constexpr*/
+#endif
+
+#if optional_HAVE( NODISCARD )
+# define optional_nodiscard [[nodiscard]]
+#else
+# define optional_nodiscard /*[[nodiscard]]*/
+#endif
+
+#if optional_HAVE( NOEXCEPT )
+# define optional_noexcept noexcept
+#else
+# define optional_noexcept /*noexcept*/
+#endif
+
+#if optional_HAVE( NULLPTR )
+# define optional_nullptr nullptr
+#else
+# define optional_nullptr NULL
+#endif
+
+#if optional_HAVE( REF_QUALIFIER )
+// NOLINTNEXTLINE( bugprone-macro-parentheses )
+# define optional_ref_qual &
+# define optional_refref_qual &&
+#else
+# define optional_ref_qual /*&*/
+# define optional_refref_qual /*&&*/
+#endif
+
+// additional includes:
+
+#if optional_CONFIG_NO_EXCEPTIONS
+// already included: <cassert>
+#else
+# include <stdexcept>
+#endif
+
+#if optional_CPP11_OR_GREATER
+# include <functional>
+#endif
+
+#if optional_HAVE( INITIALIZER_LIST )
+# include <initializer_list>
+#endif
+
+#if optional_HAVE( TYPE_TRAITS )
+# include <type_traits>
+#elif optional_HAVE( TR1_TYPE_TRAITS )
+# include <tr1/type_traits>
+#endif
+
+// Method enabling
+
+#if optional_CPP11_OR_GREATER
+
+#define optional_REQUIRES_0(...) \
+ template< bool B = (__VA_ARGS__), typename std::enable_if<B, int>::type = 0 >
+
+#define optional_REQUIRES_T(...) \
+ , typename = typename std::enable_if< (__VA_ARGS__), nonstd::optional_lite::detail::enabler >::type
+
+#define optional_REQUIRES_R(R, ...) \
+ typename std::enable_if< (__VA_ARGS__), R>::type
+
+#define optional_REQUIRES_A(...) \
+ , typename std::enable_if< (__VA_ARGS__), void*>::type = nullptr
+
+#endif
+
+//
+// optional:
+//
+
+namespace nonstd { namespace optional_lite {
+
+namespace std11 {
+
+#if optional_CPP11_OR_GREATER
+ using std::move;
+#else
+ template< typename T > T & move( T & t ) { return t; }
+#endif
+
+#if optional_HAVE( CONDITIONAL )
+ using std::conditional;
+#else
+ template< bool B, typename T, typename F > struct conditional { typedef T type; };
+ template< typename T, typename F > struct conditional<false, T, F> { typedef F type; };
+#endif // optional_HAVE_CONDITIONAL
+
+} // namespace std11
+
+#if optional_CPP11_OR_GREATER
+
+/// type traits C++17:
+
+namespace std17 {
+
+#if optional_CPP17_OR_GREATER
+
+using std::is_swappable;
+using std::is_nothrow_swappable;
+
+#elif optional_CPP11_OR_GREATER
+
+namespace detail {
+
+using std::swap;
+
+struct is_swappable
+{
+ template< typename T, typename = decltype( swap( std::declval<T&>(), std::declval<T&>() ) ) >
+ static std::true_type test( int /*unused*/ );
+
+ template< typename >
+ static std::false_type test(...);
+};
+
+struct is_nothrow_swappable
+{
+ // wrap noexcept(expr) in separate function as work-around for VC140 (VS2015):
+
+ template< typename T >
+ static constexpr bool satisfies()
+ {
+ return noexcept( swap( std::declval<T&>(), std::declval<T&>() ) );
+ }
+
+ template< typename T >
+ static auto test( int /*unused*/ ) -> std::integral_constant<bool, satisfies<T>()>{}
+
+ template< typename >
+ static auto test(...) -> std::false_type;
+};
+
+} // namespace detail
+
+// is [nothow] swappable:
+
+template< typename T >
+struct is_swappable : decltype( detail::is_swappable::test<T>(0) ){};
+
+template< typename T >
+struct is_nothrow_swappable : decltype( detail::is_nothrow_swappable::test<T>(0) ){};
+
+#endif // optional_CPP17_OR_GREATER
+
+} // namespace std17
+
+/// type traits C++20:
+
+namespace std20 {
+
+template< typename T >
+struct remove_cvref
+{
+ typedef typename std::remove_cv< typename std::remove_reference<T>::type >::type type;
+};
+
+} // namespace std20
+
+#endif // optional_CPP11_OR_GREATER
+
+/// class optional
+
+template< typename T >
+class optional;
+
+namespace detail {
+
+// for optional_REQUIRES_T
+
+#if optional_CPP11_OR_GREATER
+enum class enabler{};
+#endif
+
+// C++11 emulation:
+
+struct nulltype{};
+
+template< typename Head, typename Tail >
+struct typelist
+{
+ typedef Head head;
+ typedef Tail tail;
+};
+
+#if optional_CONFIG_MAX_ALIGN_HACK
+
+// Max align, use most restricted type for alignment:
+
+#define optional_UNIQUE( name ) optional_UNIQUE2( name, __LINE__ )
+#define optional_UNIQUE2( name, line ) optional_UNIQUE3( name, line )
+#define optional_UNIQUE3( name, line ) name ## line
+
+#define optional_ALIGN_TYPE( type ) \
+ type optional_UNIQUE( _t ); struct_t< type > optional_UNIQUE( _st )
+
+template< typename T >
+struct struct_t { T _; };
+
+union max_align_t
+{
+ optional_ALIGN_TYPE( char );
+ optional_ALIGN_TYPE( short int );
+ optional_ALIGN_TYPE( int );
+ optional_ALIGN_TYPE( long int );
+ optional_ALIGN_TYPE( float );
+ optional_ALIGN_TYPE( double );
+ optional_ALIGN_TYPE( long double );
+ optional_ALIGN_TYPE( char * );
+ optional_ALIGN_TYPE( short int * );
+ optional_ALIGN_TYPE( int * );
+ optional_ALIGN_TYPE( long int * );
+ optional_ALIGN_TYPE( float * );
+ optional_ALIGN_TYPE( double * );
+ optional_ALIGN_TYPE( long double * );
+ optional_ALIGN_TYPE( void * );
+
+#ifdef HAVE_LONG_LONG
+ optional_ALIGN_TYPE( long long );
+#endif
+
+ struct Unknown;
+
+ Unknown ( * optional_UNIQUE(_) )( Unknown );
+ Unknown * Unknown::* optional_UNIQUE(_);
+ Unknown ( Unknown::* optional_UNIQUE(_) )( Unknown );
+
+ struct_t< Unknown ( * )( Unknown) > optional_UNIQUE(_);
+ struct_t< Unknown * Unknown::* > optional_UNIQUE(_);
+ struct_t< Unknown ( Unknown::* )(Unknown) > optional_UNIQUE(_);
+};
+
+#undef optional_UNIQUE
+#undef optional_UNIQUE2
+#undef optional_UNIQUE3
+
+#undef optional_ALIGN_TYPE
+
+#elif defined( optional_CONFIG_ALIGN_AS ) // optional_CONFIG_MAX_ALIGN_HACK
+
+// Use user-specified type for alignment:
+
+#define optional_ALIGN_AS( unused ) \
+ optional_CONFIG_ALIGN_AS
+
+#else // optional_CONFIG_MAX_ALIGN_HACK
+
+// Determine POD type to use for alignment:
+
+#define optional_ALIGN_AS( to_align ) \
+ typename type_of_size< alignment_types, alignment_of< to_align >::value >::type
+
+template< typename T >
+struct alignment_of;
+
+template< typename T >
+struct alignment_of_hack
+{
+ char c;
+ T t;
+ alignment_of_hack();
+};
+
+template< size_t A, size_t S >
+struct alignment_logic
+{
+ enum { value = A < S ? A : S };
+};
+
+template< typename T >
+struct alignment_of
+{
+ enum { value = alignment_logic<
+ sizeof( alignment_of_hack<T> ) - sizeof(T), sizeof(T) >::value };
+};
+
+template< typename List, size_t N >
+struct type_of_size
+{
+ typedef typename std11::conditional<
+ N == sizeof( typename List::head ),
+ typename List::head,
+ typename type_of_size<typename List::tail, N >::type >::type type;
+};
+
+template< size_t N >
+struct type_of_size< nulltype, N >
+{
+ typedef optional_CONFIG_ALIGN_AS_FALLBACK type;
+};
+
+template< typename T>
+struct struct_t { T _; };
+
+#define optional_ALIGN_TYPE( type ) \
+ typelist< type , typelist< struct_t< type >
+
+struct Unknown;
+
+typedef
+ optional_ALIGN_TYPE( char ),
+ optional_ALIGN_TYPE( short ),
+ optional_ALIGN_TYPE( int ),
+ optional_ALIGN_TYPE( long ),
+ optional_ALIGN_TYPE( float ),
+ optional_ALIGN_TYPE( double ),
+ optional_ALIGN_TYPE( long double ),
+
+ optional_ALIGN_TYPE( char *),
+ optional_ALIGN_TYPE( short * ),
+ optional_ALIGN_TYPE( int * ),
+ optional_ALIGN_TYPE( long * ),
+ optional_ALIGN_TYPE( float * ),
+ optional_ALIGN_TYPE( double * ),
+ optional_ALIGN_TYPE( long double * ),
+
+ optional_ALIGN_TYPE( Unknown ( * )( Unknown ) ),
+ optional_ALIGN_TYPE( Unknown * Unknown::* ),
+ optional_ALIGN_TYPE( Unknown ( Unknown::* )( Unknown ) ),
+
+ nulltype
+ > > > > > > > > > > > > > >
+ > > > > > > > > > > > > > >
+ > > > > > >
+ alignment_types;
+
+#undef optional_ALIGN_TYPE
+
+#endif // optional_CONFIG_MAX_ALIGN_HACK
+
+/// C++03 constructed union to hold value.
+
+template< typename T >
+union storage_t
+{
+//private:
+// template< typename > friend class optional;
+
+ typedef T value_type;
+
+ storage_t() optional_is_default
+
+ explicit storage_t( value_type const & v )
+ {
+ construct_value( v );
+ }
+
+ void construct_value( value_type const & v )
+ {
+ ::new( value_ptr() ) value_type( v );
+ }
+
+#if optional_CPP11_OR_GREATER
+
+ explicit storage_t( value_type && v )
+ {
+ construct_value( std::move( v ) );
+ }
+
+ void construct_value( value_type && v )
+ {
+ ::new( value_ptr() ) value_type( std::move( v ) );
+ }
+
+ template< class... Args >
+ void emplace( Args&&... args )
+ {
+ ::new( value_ptr() ) value_type( std::forward<Args>(args)... );
+ }
+
+ template< class U, class... Args >
+ void emplace( std::initializer_list<U> il, Args&&... args )
+ {
+ ::new( value_ptr() ) value_type( il, std::forward<Args>(args)... );
+ }
+
+#endif
+
+ void destruct_value()
+ {
+ value_ptr()->~T();
+ }
+
+ optional_nodiscard value_type const * value_ptr() const
+ {
+ return as<value_type>();
+ }
+
+ value_type * value_ptr()
+ {
+ return as<value_type>();
+ }
+
+ optional_nodiscard value_type const & value() const optional_ref_qual
+ {
+ return * value_ptr();
+ }
+
+ value_type & value() optional_ref_qual
+ {
+ return * value_ptr();
+ }
+
+#if optional_CPP11_OR_GREATER
+
+ optional_nodiscard value_type const && value() const optional_refref_qual
+ {
+ return std::move( value() );
+ }
+
+ value_type && value() optional_refref_qual
+ {
+ return std::move( value() );
+ }
+
+#endif
+
+#if optional_CPP11_OR_GREATER
+
+ using aligned_storage_t = typename std::aligned_storage< sizeof(value_type), alignof(value_type) >::type;
+ aligned_storage_t data;
+
+#elif optional_CONFIG_MAX_ALIGN_HACK
+
+ typedef struct { unsigned char data[ sizeof(value_type) ]; } aligned_storage_t;
+
+ max_align_t hack;
+ aligned_storage_t data;
+
+#else
+ typedef optional_ALIGN_AS(value_type) align_as_type;
+
+ typedef struct { align_as_type data[ 1 + ( sizeof(value_type) - 1 ) / sizeof(align_as_type) ]; } aligned_storage_t;
+ aligned_storage_t data;
+
+# undef optional_ALIGN_AS
+
+#endif // optional_CONFIG_MAX_ALIGN_HACK
+
+ optional_nodiscard void * ptr() optional_noexcept
+ {
+ return &data;
+ }
+
+ optional_nodiscard void const * ptr() const optional_noexcept
+ {
+ return &data;
+ }
+
+ template <typename U>
+ optional_nodiscard U * as()
+ {
+ return reinterpret_cast<U*>( ptr() );
+ }
+
+ template <typename U>
+ optional_nodiscard U const * as() const
+ {
+ return reinterpret_cast<U const *>( ptr() );
+ }
+};
+
+} // namespace detail
+
+/// disengaged state tag
+
+struct nullopt_t
+{
+ struct init{};
+ explicit optional_constexpr nullopt_t( init /*unused*/ ) optional_noexcept {}
+};
+
+#if optional_HAVE( CONSTEXPR_11 )
+constexpr nullopt_t nullopt{ nullopt_t::init{} };
+#else
+// extra parenthesis to prevent the most vexing parse:
+const nullopt_t nullopt(( nullopt_t::init() ));
+#endif
+
+/// optional access error
+
+#if ! optional_CONFIG_NO_EXCEPTIONS
+
+class bad_optional_access : public std::logic_error
+{
+public:
+ explicit bad_optional_access()
+ : logic_error( "bad optional access" ) {}
+};
+
+#endif //optional_CONFIG_NO_EXCEPTIONS
+
+/// optional
+
+template< typename T>
+class optional
+{
+private:
+ template< typename > friend class optional;
+
+ typedef void (optional::*safe_bool)() const;
+
+public:
+ typedef T value_type;
+
+ // x.x.3.1, constructors
+
+ // 1a - default construct
+ optional_constexpr optional() optional_noexcept
+ : has_value_( false )
+ , contained()
+ {}
+
+ // 1b - construct explicitly empty
+ // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions )
+ optional_constexpr optional( nullopt_t /*unused*/ ) optional_noexcept
+ : has_value_( false )
+ , contained()
+ {}
+
+ // 2 - copy-construct
+ optional_constexpr14 optional( optional const & other
+#if optional_CPP11_OR_GREATER
+ optional_REQUIRES_A(
+ true || std::is_copy_constructible<T>::value
+ )
+#endif
+ )
+ : has_value_( other.has_value() )
+ {
+ if ( other.has_value() )
+ {
+ contained.construct_value( other.contained.value() );
+ }
+ }
+
+#if optional_CPP11_OR_GREATER
+
+ // 3 (C++11) - move-construct from optional
+ optional_constexpr14 optional( optional && other
+ optional_REQUIRES_A(
+ true || std::is_move_constructible<T>::value
+ )
+ // NOLINTNEXTLINE( performance-noexcept-move-constructor )
+ ) noexcept( std::is_nothrow_move_constructible<T>::value )
+ : has_value_( other.has_value() )
+ {
+ if ( other.has_value() )
+ {
+ contained.construct_value( std::move( other.contained.value() ) );
+ }
+ }
+
+ // 4a (C++11) - explicit converting copy-construct from optional
+ template< typename U >
+ explicit optional( optional<U> const & other
+ optional_REQUIRES_A(
+ std::is_constructible<T, U const &>::value
+ && !std::is_constructible<T, optional<U> & >::value
+ && !std::is_constructible<T, optional<U> && >::value
+ && !std::is_constructible<T, optional<U> const & >::value
+ && !std::is_constructible<T, optional<U> const && >::value
+ && !std::is_convertible< optional<U> & , T>::value
+ && !std::is_convertible< optional<U> && , T>::value
+ && !std::is_convertible< optional<U> const & , T>::value
+ && !std::is_convertible< optional<U> const &&, T>::value
+ && !std::is_convertible< U const & , T>::value /*=> explicit */
+ )
+ )
+ : has_value_( other.has_value() )
+ {
+ if ( other.has_value() )
+ {
+ contained.construct_value( T{ other.contained.value() } );
+ }
+ }
+#endif // optional_CPP11_OR_GREATER
+
+ // 4b (C++98 and later) - non-explicit converting copy-construct from optional
+ template< typename U >
+ // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions )
+ optional( optional<U> const & other
+#if optional_CPP11_OR_GREATER
+ optional_REQUIRES_A(
+ std::is_constructible<T, U const &>::value
+ && !std::is_constructible<T, optional<U> & >::value
+ && !std::is_constructible<T, optional<U> && >::value
+ && !std::is_constructible<T, optional<U> const & >::value
+ && !std::is_constructible<T, optional<U> const && >::value
+ && !std::is_convertible< optional<U> & , T>::value
+ && !std::is_convertible< optional<U> && , T>::value
+ && !std::is_convertible< optional<U> const & , T>::value
+ && !std::is_convertible< optional<U> const &&, T>::value
+ && std::is_convertible< U const & , T>::value /*=> non-explicit */
+ )
+#endif // optional_CPP11_OR_GREATER
+ )
+ : has_value_( other.has_value() )
+ {
+ if ( other.has_value() )
+ {
+ contained.construct_value( other.contained.value() );
+ }
+ }
+
+#if optional_CPP11_OR_GREATER
+
+ // 5a (C++11) - explicit converting move-construct from optional
+ template< typename U >
+ explicit optional( optional<U> && other
+ optional_REQUIRES_A(
+ std::is_constructible<T, U &&>::value
+ && !std::is_constructible<T, optional<U> & >::value
+ && !std::is_constructible<T, optional<U> && >::value
+ && !std::is_constructible<T, optional<U> const & >::value
+ && !std::is_constructible<T, optional<U> const && >::value
+ && !std::is_convertible< optional<U> & , T>::value
+ && !std::is_convertible< optional<U> && , T>::value
+ && !std::is_convertible< optional<U> const & , T>::value
+ && !std::is_convertible< optional<U> const &&, T>::value
+ && !std::is_convertible< U &&, T>::value /*=> explicit */
+ )
+ )
+ : has_value_( other.has_value() )
+ {
+ if ( other.has_value() )
+ {
+ contained.construct_value( T{ std::move( other.contained.value() ) } );
+ }
+ }
+
+ // 5a (C++11) - non-explicit converting move-construct from optional
+ template< typename U >
+ // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions )
+ optional( optional<U> && other
+ optional_REQUIRES_A(
+ std::is_constructible<T, U &&>::value
+ && !std::is_constructible<T, optional<U> & >::value
+ && !std::is_constructible<T, optional<U> && >::value
+ && !std::is_constructible<T, optional<U> const & >::value
+ && !std::is_constructible<T, optional<U> const && >::value
+ && !std::is_convertible< optional<U> & , T>::value
+ && !std::is_convertible< optional<U> && , T>::value
+ && !std::is_convertible< optional<U> const & , T>::value
+ && !std::is_convertible< optional<U> const &&, T>::value
+ && std::is_convertible< U &&, T>::value /*=> non-explicit */
+ )
+ )
+ : has_value_( other.has_value() )
+ {
+ if ( other.has_value() )
+ {
+ contained.construct_value( std::move( other.contained.value() ) );
+ }
+ }
+
+ // 6 (C++11) - in-place construct
+ template< typename... Args
+ optional_REQUIRES_T(
+ std::is_constructible<T, Args&&...>::value
+ )
+ >
+ optional_constexpr explicit optional( nonstd_lite_in_place_t(T), Args&&... args )
+ : has_value_( true )
+ , contained( T( std::forward<Args>(args)...) )
+ {}
+
+ // 7 (C++11) - in-place construct, initializer-list
+ template< typename U, typename... Args
+ optional_REQUIRES_T(
+ std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value
+ )
+ >
+ optional_constexpr explicit optional( nonstd_lite_in_place_t(T), std::initializer_list<U> il, Args&&... args )
+ : has_value_( true )
+ , contained( T( il, std::forward<Args>(args)...) )
+ {}
+
+ // 8a (C++11) - explicit move construct from value
+ template< typename U = value_type >
+ optional_constexpr explicit optional( U && value
+ optional_REQUIRES_A(
+ std::is_constructible<T, U&&>::value
+ && !std::is_same<typename std20::remove_cvref<U>::type, nonstd_lite_in_place_t(U)>::value
+ && !std::is_same<typename std20::remove_cvref<U>::type, optional<T>>::value
+ && !std::is_convertible<U&&, T>::value /*=> explicit */
+ )
+ )
+ : has_value_( true )
+ , contained( T{ std::forward<U>( value ) } )
+ {}
+
+ // 8b (C++11) - non-explicit move construct from value
+ template< typename U = value_type >
+ // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions )
+ optional_constexpr optional( U && value
+ optional_REQUIRES_A(
+ std::is_constructible<T, U&&>::value
+ && !std::is_same<typename std20::remove_cvref<U>::type, nonstd_lite_in_place_t(U)>::value
+ && !std::is_same<typename std20::remove_cvref<U>::type, optional<T>>::value
+ && std::is_convertible<U&&, T>::value /*=> non-explicit */
+ )
+ )
+ : has_value_( true )
+ , contained( std::forward<U>( value ) )
+ {}
+
+#else // optional_CPP11_OR_GREATER
+
+ // 8 (C++98)
+ optional( value_type const & value )
+ : has_value_( true )
+ , contained( value )
+ {}
+
+#endif // optional_CPP11_OR_GREATER
+
+ // x.x.3.2, destructor
+
+ ~optional()
+ {
+ if ( has_value() )
+ {
+ contained.destruct_value();
+ }
+ }
+
+ // x.x.3.3, assignment
+
+ // 1 (C++98and later) - assign explicitly empty
+ optional & operator=( nullopt_t /*unused*/) optional_noexcept
+ {
+ reset();
+ return *this;
+ }
+
+ // 2 (C++98and later) - copy-assign from optional
+#if optional_CPP11_OR_GREATER
+ // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator )
+ optional_REQUIRES_R(
+ optional &,
+ true
+// std::is_copy_constructible<T>::value
+// && std::is_copy_assignable<T>::value
+ )
+ operator=( optional const & other )
+ noexcept(
+ std::is_nothrow_move_assignable<T>::value
+ && std::is_nothrow_move_constructible<T>::value
+ )
+#else
+ optional & operator=( optional const & other )
+#endif
+ {
+ if ( (has_value() == true ) && (other.has_value() == false) ) { reset(); }
+ else if ( (has_value() == false) && (other.has_value() == true ) ) { initialize( *other ); }
+ else if ( (has_value() == true ) && (other.has_value() == true ) ) { contained.value() = *other; }
+ return *this;
+ }
+
+#if optional_CPP11_OR_GREATER
+
+ // 3 (C++11) - move-assign from optional
+ // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator )
+ optional_REQUIRES_R(
+ optional &,
+ true
+// std::is_move_constructible<T>::value
+// && std::is_move_assignable<T>::value
+ )
+ operator=( optional && other ) noexcept
+ {
+ if ( (has_value() == true ) && (other.has_value() == false) ) { reset(); }
+ else if ( (has_value() == false) && (other.has_value() == true ) ) { initialize( std::move( *other ) ); }
+ else if ( (has_value() == true ) && (other.has_value() == true ) ) { contained.value() = std::move( *other ); }
+ return *this;
+ }
+
+ // 4 (C++11) - move-assign from value
+ template< typename U = T >
+ // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator )
+ optional_REQUIRES_R(
+ optional &,
+ std::is_constructible<T , U>::value
+ && std::is_assignable<T&, U>::value
+ && !std::is_same<typename std20::remove_cvref<U>::type, nonstd_lite_in_place_t(U)>::value
+ && !std::is_same<typename std20::remove_cvref<U>::type, optional<T>>::value
+ && !(std::is_scalar<T>::value && std::is_same<T, typename std::decay<U>::type>::value)
+ )
+ operator=( U && value )
+ {
+ if ( has_value() )
+ {
+ contained.value() = std::forward<U>( value );
+ }
+ else
+ {
+ initialize( T( std::forward<U>( value ) ) );
+ }
+ return *this;
+ }
+
+#else // optional_CPP11_OR_GREATER
+
+ // 4 (C++98) - copy-assign from value
+ template< typename U /*= T*/ >
+ optional & operator=( U const & value )
+ {
+ if ( has_value() ) contained.value() = value;
+ else initialize( T( value ) );
+ return *this;
+ }
+
+#endif // optional_CPP11_OR_GREATER
+
+ // 5 (C++98 and later) - converting copy-assign from optional
+ template< typename U >
+#if optional_CPP11_OR_GREATER
+ // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator )
+ optional_REQUIRES_R(
+ optional&,
+ std::is_constructible< T , U const &>::value
+ && std::is_assignable< T&, U const &>::value
+ && !std::is_constructible<T, optional<U> & >::value
+ && !std::is_constructible<T, optional<U> && >::value
+ && !std::is_constructible<T, optional<U> const & >::value
+ && !std::is_constructible<T, optional<U> const && >::value
+ && !std::is_convertible< optional<U> & , T>::value
+ && !std::is_convertible< optional<U> && , T>::value
+ && !std::is_convertible< optional<U> const & , T>::value
+ && !std::is_convertible< optional<U> const &&, T>::value
+ && !std::is_assignable< T&, optional<U> & >::value
+ && !std::is_assignable< T&, optional<U> && >::value
+ && !std::is_assignable< T&, optional<U> const & >::value
+ && !std::is_assignable< T&, optional<U> const && >::value
+ )
+#else
+ optional&
+#endif // optional_CPP11_OR_GREATER
+ operator=( optional<U> const & other )
+ {
+ return *this = optional( other );
+ }
+
+#if optional_CPP11_OR_GREATER
+
+ // 6 (C++11) - converting move-assign from optional
+ template< typename U >
+ // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator )
+ optional_REQUIRES_R(
+ optional&,
+ std::is_constructible< T , U>::value
+ && std::is_assignable< T&, U>::value
+ && !std::is_constructible<T, optional<U> & >::value
+ && !std::is_constructible<T, optional<U> && >::value
+ && !std::is_constructible<T, optional<U> const & >::value
+ && !std::is_constructible<T, optional<U> const && >::value
+ && !std::is_convertible< optional<U> & , T>::value
+ && !std::is_convertible< optional<U> && , T>::value
+ && !std::is_convertible< optional<U> const & , T>::value
+ && !std::is_convertible< optional<U> const &&, T>::value
+ && !std::is_assignable< T&, optional<U> & >::value
+ && !std::is_assignable< T&, optional<U> && >::value
+ && !std::is_assignable< T&, optional<U> const & >::value
+ && !std::is_assignable< T&, optional<U> const && >::value
+ )
+ operator=( optional<U> && other )
+ {
+ return *this = optional( std::move( other ) );
+ }
+
+ // 7 (C++11) - emplace
+ template< typename... Args
+ optional_REQUIRES_T(
+ std::is_constructible<T, Args&&...>::value
+ )
+ >
+ T& emplace( Args&&... args )
+ {
+ *this = nullopt;
+ contained.emplace( std::forward<Args>(args)... );
+ has_value_ = true;
+ return contained.value();
+ }
+
+ // 8 (C++11) - emplace, initializer-list
+ template< typename U, typename... Args
+ optional_REQUIRES_T(
+ std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value
+ )
+ >
+ T& emplace( std::initializer_list<U> il, Args&&... args )
+ {
+ *this = nullopt;
+ contained.emplace( il, std::forward<Args>(args)... );
+ has_value_ = true;
+ return contained.value();
+ }
+
+#endif // optional_CPP11_OR_GREATER
+
+ // x.x.3.4, swap
+
+ void swap( optional & other )
+#if optional_CPP11_OR_GREATER
+ noexcept(
+ std::is_nothrow_move_constructible<T>::value
+ && std17::is_nothrow_swappable<T>::value
+ )
+#endif
+ {
+ using std::swap;
+ if ( (has_value() == true ) && (other.has_value() == true ) ) { swap( **this, *other ); }
+ else if ( (has_value() == false) && (other.has_value() == true ) ) { initialize( std11::move(*other) ); other.reset(); }
+ else if ( (has_value() == true ) && (other.has_value() == false) ) { other.initialize( std11::move(**this) ); reset(); }
+ }
+
+ // x.x.3.5, observers
+
+ optional_constexpr value_type const * operator ->() const
+ {
+ return assert( has_value() ),
+ contained.value_ptr();
+ }
+
+ optional_constexpr14 value_type * operator ->()
+ {
+ return assert( has_value() ),
+ contained.value_ptr();
+ }
+
+ optional_constexpr value_type const & operator *() const optional_ref_qual
+ {
+ return assert( has_value() ),
+ contained.value();
+ }
+
+ optional_constexpr14 value_type & operator *() optional_ref_qual
+ {
+ return assert( has_value() ),
+ contained.value();
+ }
+
+#if optional_HAVE( REF_QUALIFIER ) && ( !optional_COMPILER_GNUC_VERSION || optional_COMPILER_GNUC_VERSION >= 490 )
+
+ optional_constexpr value_type const && operator *() const optional_refref_qual
+ {
+ return std::move( **this );
+ }
+
+ optional_constexpr14 value_type && operator *() optional_refref_qual
+ {
+ return std::move( **this );
+ }
+
+#endif
+
+#if optional_CPP11_OR_GREATER
+ optional_constexpr explicit operator bool() const optional_noexcept
+ {
+ return has_value();
+ }
+#else
+ optional_constexpr operator safe_bool() const optional_noexcept
+ {
+ return has_value() ? &optional::this_type_does_not_support_comparisons : 0;
+ }
+#endif
+
+ // NOLINTNEXTLINE( modernize-use-nodiscard )
+ /*optional_nodiscard*/ optional_constexpr bool has_value() const optional_noexcept
+ {
+ return has_value_;
+ }
+
+ // NOLINTNEXTLINE( modernize-use-nodiscard )
+ /*optional_nodiscard*/ optional_constexpr14 value_type const & value() const optional_ref_qual
+ {
+#if optional_CONFIG_NO_EXCEPTIONS
+ assert( has_value() );
+#else
+ if ( ! has_value() )
+ {
+ throw bad_optional_access();
+ }
+#endif
+ return contained.value();
+ }
+
+ optional_constexpr14 value_type & value() optional_ref_qual
+ {
+#if optional_CONFIG_NO_EXCEPTIONS
+ assert( has_value() );
+#else
+ if ( ! has_value() )
+ {
+ throw bad_optional_access();
+ }
+#endif
+ return contained.value();
+ }
+
+#if optional_HAVE( REF_QUALIFIER ) && ( !optional_COMPILER_GNUC_VERSION || optional_COMPILER_GNUC_VERSION >= 490 )
+
+ // NOLINTNEXTLINE( modernize-use-nodiscard )
+ /*optional_nodiscard*/ optional_constexpr value_type const && value() const optional_refref_qual
+ {
+ return std::move( value() );
+ }
+
+ optional_constexpr14 value_type && value() optional_refref_qual
+ {
+ return std::move( value() );
+ }
+
+#endif
+
+#if optional_CPP11_OR_GREATER
+
+ template< typename U >
+ optional_constexpr value_type value_or( U && v ) const optional_ref_qual
+ {
+ return has_value() ? contained.value() : static_cast<T>(std::forward<U>( v ) );
+ }
+
+ template< typename U >
+ optional_constexpr14 value_type value_or( U && v ) optional_refref_qual
+ {
+ return has_value() ? std::move( contained.value() ) : static_cast<T>(std::forward<U>( v ) );
+ }
+
+#else
+
+ template< typename U >
+ optional_constexpr value_type value_or( U const & v ) const
+ {
+ return has_value() ? contained.value() : static_cast<value_type>( v );
+ }
+
+#endif // optional_CPP11_OR_GREATER
+
+ // x.x.3.6, modifiers
+
+ void reset() optional_noexcept
+ {
+ if ( has_value() )
+ {
+ contained.destruct_value();
+ }
+
+ has_value_ = false;
+ }
+
+private:
+ void this_type_does_not_support_comparisons() const {}
+
+ template< typename V >
+ void initialize( V const & value )
+ {
+ assert( ! has_value() );
+ contained.construct_value( value );
+ has_value_ = true;
+ }
+
+#if optional_CPP11_OR_GREATER
+ template< typename V >
+ void initialize( V && value )
+ {
+ assert( ! has_value() );
+ contained.construct_value( std::move( value ) );
+ has_value_ = true;
+ }
+
+#endif
+
+private:
+ bool has_value_;
+ detail::storage_t< value_type > contained;
+
+};
+
+// Relational operators
+
+template< typename T, typename U >
+inline optional_constexpr bool operator==( optional<T> const & x, optional<U> const & y )
+{
+ return bool(x) != bool(y) ? false : !bool( x ) ? true : *x == *y;
+}
+
+template< typename T, typename U >
+inline optional_constexpr bool operator!=( optional<T> const & x, optional<U> const & y )
+{
+ return !(x == y);
+}
+
+template< typename T, typename U >
+inline optional_constexpr bool operator<( optional<T> const & x, optional<U> const & y )
+{
+ return (!y) ? false : (!x) ? true : *x < *y;
+}
+
+template< typename T, typename U >
+inline optional_constexpr bool operator>( optional<T> const & x, optional<U> const & y )
+{
+ return (y < x);
+}
+
+template< typename T, typename U >
+inline optional_constexpr bool operator<=( optional<T> const & x, optional<U> const & y )
+{
+ return !(y < x);
+}
+
+template< typename T, typename U >
+inline optional_constexpr bool operator>=( optional<T> const & x, optional<U> const & y )
+{
+ return !(x < y);
+}
+
+// Comparison with nullopt
+
+template< typename T >
+inline optional_constexpr bool operator==( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
+{
+ return (!x);
+}
+
+template< typename T >
+inline optional_constexpr bool operator==( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
+{
+ return (!x);
+}
+
+template< typename T >
+inline optional_constexpr bool operator!=( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
+{
+ return bool(x);
+}
+
+template< typename T >
+inline optional_constexpr bool operator!=( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
+{
+ return bool(x);
+}
+
+template< typename T >
+inline optional_constexpr bool operator<( optional<T> const & /*unused*/, nullopt_t /*unused*/ ) optional_noexcept
+{
+ return false;
+}
+
+template< typename T >
+inline optional_constexpr bool operator<( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
+{
+ return bool(x);
+}
+
+template< typename T >
+inline optional_constexpr bool operator<=( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
+{
+ return (!x);
+}
+
+template< typename T >
+inline optional_constexpr bool operator<=( nullopt_t /*unused*/, optional<T> const & /*unused*/ ) optional_noexcept
+{
+ return true;
+}
+
+template< typename T >
+inline optional_constexpr bool operator>( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
+{
+ return bool(x);
+}
+
+template< typename T >
+inline optional_constexpr bool operator>( nullopt_t /*unused*/, optional<T> const & /*unused*/ ) optional_noexcept
+{
+ return false;
+}
+
+template< typename T >
+inline optional_constexpr bool operator>=( optional<T> const & /*unused*/, nullopt_t /*unused*/ ) optional_noexcept
+{
+ return true;
+}
+
+template< typename T >
+inline optional_constexpr bool operator>=( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
+{
+ return (!x);
+}
+
+// Comparison with T
+
+template< typename T, typename U >
+inline optional_constexpr bool operator==( optional<T> const & x, U const & v )
+{
+ return bool(x) ? *x == v : false;
+}
+
+template< typename T, typename U >
+inline optional_constexpr bool operator==( U const & v, optional<T> const & x )
+{
+ return bool(x) ? v == *x : false;
+}
+
+template< typename T, typename U >
+inline optional_constexpr bool operator!=( optional<T> const & x, U const & v )
+{
+ return bool(x) ? *x != v : true;
+}
+
+template< typename T, typename U >
+inline optional_constexpr bool operator!=( U const & v, optional<T> const & x )
+{
+ return bool(x) ? v != *x : true;
+}
+
+template< typename T, typename U >
+inline optional_constexpr bool operator<( optional<T> const & x, U const & v )
+{
+ return bool(x) ? *x < v : true;
+}
+
+template< typename T, typename U >
+inline optional_constexpr bool operator<( U const & v, optional<T> const & x )
+{
+ return bool(x) ? v < *x : false;
+}
+
+template< typename T, typename U >
+inline optional_constexpr bool operator<=( optional<T> const & x, U const & v )
+{
+ return bool(x) ? *x <= v : true;
+}
+
+template< typename T, typename U >
+inline optional_constexpr bool operator<=( U const & v, optional<T> const & x )
+{
+ return bool(x) ? v <= *x : false;
+}
+
+template< typename T, typename U >
+inline optional_constexpr bool operator>( optional<T> const & x, U const & v )
+{
+ return bool(x) ? *x > v : false;
+}
+
+template< typename T, typename U >
+inline optional_constexpr bool operator>( U const & v, optional<T> const & x )
+{
+ return bool(x) ? v > *x : true;
+}
+
+template< typename T, typename U >
+inline optional_constexpr bool operator>=( optional<T> const & x, U const & v )
+{
+ return bool(x) ? *x >= v : false;
+}
+
+template< typename T, typename U >
+inline optional_constexpr bool operator>=( U const & v, optional<T> const & x )
+{
+ return bool(x) ? v >= *x : true;
+}
+
+// Specialized algorithms
+
+template< typename T
+#if optional_CPP11_OR_GREATER
+ optional_REQUIRES_T(
+ std::is_move_constructible<T>::value
+ && std17::is_swappable<T>::value )
+#endif
+>
+void swap( optional<T> & x, optional<T> & y )
+#if optional_CPP11_OR_GREATER
+ noexcept( noexcept( x.swap(y) ) )
+#endif
+{
+ x.swap( y );
+}
+
+#if optional_CPP11_OR_GREATER
+
+template< typename T >
+optional_constexpr optional< typename std::decay<T>::type > make_optional( T && value )
+{
+ return optional< typename std::decay<T>::type >( std::forward<T>( value ) );
+}
+
+template< typename T, typename...Args >
+optional_constexpr optional<T> make_optional( Args&&... args )
+{
+ return optional<T>( nonstd_lite_in_place(T), std::forward<Args>(args)...);
+}
+
+template< typename T, typename U, typename... Args >
+optional_constexpr optional<T> make_optional( std::initializer_list<U> il, Args&&... args )
+{
+ return optional<T>( nonstd_lite_in_place(T), il, std::forward<Args>(args)...);
+}
+
+#else
+
+template< typename T >
+optional<T> make_optional( T const & value )
+{
+ return optional<T>( value );
+}
+
+#endif // optional_CPP11_OR_GREATER
+
+} // namespace optional_lite
+
+using optional_lite::optional;
+using optional_lite::nullopt_t;
+using optional_lite::nullopt;
+using optional_lite::bad_optional_access;
+
+using optional_lite::make_optional;
+
+} // namespace nonstd
+
+#if optional_CPP11_OR_GREATER
+
+// specialize the std::hash algorithm:
+
+namespace std {
+
+template< class T >
+struct hash< nonstd::optional<T> >
+{
+public:
+ std::size_t operator()( nonstd::optional<T> const & v ) const optional_noexcept
+ {
+ return bool( v ) ? std::hash<T>{}( *v ) : 0;
+ }
+};
+
+} //namespace std
+
+#endif // optional_CPP11_OR_GREATER
+
+#if defined(__clang__)
+# pragma clang diagnostic pop
+#elif defined(__GNUC__)
+# pragma GCC diagnostic pop
+#elif defined(_MSC_VER )
+# pragma warning( pop )
+#endif
+
+#endif // optional_USES_STD_OPTIONAL
+
+#endif // NONSTD_OPTIONAL_LITE_HPP
--- /dev/null
+// Copyright 2017-2020 by Martin Moene
+//
+// string-view lite, a C++17-like string_view for C++98 and later.
+// For more information see https://github.com/martinmoene/string-view-lite
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#pragma once
+
+#ifndef NONSTD_SV_LITE_H_INCLUDED
+#define NONSTD_SV_LITE_H_INCLUDED
+
+#define string_view_lite_MAJOR 1
+#define string_view_lite_MINOR 6
+#define string_view_lite_PATCH 0
+
+#define string_view_lite_VERSION nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY(string_view_lite_PATCH)
+
+#define nssv_STRINGIFY( x ) nssv_STRINGIFY_( x )
+#define nssv_STRINGIFY_( x ) #x
+
+// string-view lite configuration:
+
+#define nssv_STRING_VIEW_DEFAULT 0
+#define nssv_STRING_VIEW_NONSTD 1
+#define nssv_STRING_VIEW_STD 2
+
+// tweak header support:
+
+#ifdef __has_include
+# if __has_include(<nonstd/string_view.tweak.hpp>)
+# include <nonstd/string_view.tweak.hpp>
+# endif
+#define nssv_HAVE_TWEAK_HEADER 1
+#else
+#define nssv_HAVE_TWEAK_HEADER 0
+//# pragma message("string_view.hpp: Note: Tweak header not supported.")
+#endif
+
+// string_view selection and configuration:
+
+#if !defined( nssv_CONFIG_SELECT_STRING_VIEW )
+# define nssv_CONFIG_SELECT_STRING_VIEW ( nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD )
+#endif
+
+#if defined( nssv_CONFIG_SELECT_STD_STRING_VIEW ) || defined( nssv_CONFIG_SELECT_NONSTD_STRING_VIEW )
+# error nssv_CONFIG_SELECT_STD_STRING_VIEW and nssv_CONFIG_SELECT_NONSTD_STRING_VIEW are deprecated and removed, please use nssv_CONFIG_SELECT_STRING_VIEW=nssv_STRING_VIEW_...
+#endif
+
+#ifndef nssv_CONFIG_STD_SV_OPERATOR
+# define nssv_CONFIG_STD_SV_OPERATOR 0
+#endif
+
+#ifndef nssv_CONFIG_USR_SV_OPERATOR
+# define nssv_CONFIG_USR_SV_OPERATOR 1
+#endif
+
+#ifdef nssv_CONFIG_CONVERSION_STD_STRING
+# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING
+# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING
+#endif
+
+#ifndef nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
+# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1
+#endif
+
+#ifndef nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
+# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1
+#endif
+
+// Control presence of exception handling (try and auto discover):
+
+#ifndef nssv_CONFIG_NO_EXCEPTIONS
+# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
+# define nssv_CONFIG_NO_EXCEPTIONS 0
+# else
+# define nssv_CONFIG_NO_EXCEPTIONS 1
+# endif
+#endif
+
+// C++ language version detection (C++20 is speculative):
+// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
+
+#ifndef nssv_CPLUSPLUS
+# if defined(_MSVC_LANG ) && !defined(__clang__)
+# define nssv_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
+# else
+# define nssv_CPLUSPLUS __cplusplus
+# endif
+#endif
+
+#define nssv_CPP98_OR_GREATER ( nssv_CPLUSPLUS >= 199711L )
+#define nssv_CPP11_OR_GREATER ( nssv_CPLUSPLUS >= 201103L )
+#define nssv_CPP11_OR_GREATER_ ( nssv_CPLUSPLUS >= 201103L )
+#define nssv_CPP14_OR_GREATER ( nssv_CPLUSPLUS >= 201402L )
+#define nssv_CPP17_OR_GREATER ( nssv_CPLUSPLUS >= 201703L )
+#define nssv_CPP20_OR_GREATER ( nssv_CPLUSPLUS >= 202000L )
+
+// use C++17 std::string_view if available and requested:
+
+#if nssv_CPP17_OR_GREATER && defined(__has_include )
+# if __has_include( <string_view> )
+# define nssv_HAVE_STD_STRING_VIEW 1
+# else
+# define nssv_HAVE_STD_STRING_VIEW 0
+# endif
+#else
+# define nssv_HAVE_STD_STRING_VIEW 0
+#endif
+
+#define nssv_USES_STD_STRING_VIEW ( (nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW) )
+
+#define nssv_HAVE_STARTS_WITH ( nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW )
+#define nssv_HAVE_ENDS_WITH nssv_HAVE_STARTS_WITH
+
+//
+// Use C++17 std::string_view:
+//
+
+#if nssv_USES_STD_STRING_VIEW
+
+#include <string_view>
+
+// Extensions for std::string:
+
+#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
+
+namespace nonstd {
+
+template< class CharT, class Traits, class Allocator = std::allocator<CharT> >
+std::basic_string<CharT, Traits, Allocator>
+to_string( std::basic_string_view<CharT, Traits> v, Allocator const & a = Allocator() )
+{
+ return std::basic_string<CharT,Traits, Allocator>( v.begin(), v.end(), a );
+}
+
+template< class CharT, class Traits, class Allocator >
+std::basic_string_view<CharT, Traits>
+to_string_view( std::basic_string<CharT, Traits, Allocator> const & s )
+{
+ return std::basic_string_view<CharT, Traits>( s.data(), s.size() );
+}
+
+// Literal operators sv and _sv:
+
+#if nssv_CONFIG_STD_SV_OPERATOR
+
+using namespace std::literals::string_view_literals;
+
+#endif
+
+#if nssv_CONFIG_USR_SV_OPERATOR
+
+inline namespace literals {
+inline namespace string_view_literals {
+
+
+constexpr std::string_view operator "" _sv( const char* str, size_t len ) noexcept // (1)
+{
+ return std::string_view{ str, len };
+}
+
+constexpr std::u16string_view operator "" _sv( const char16_t* str, size_t len ) noexcept // (2)
+{
+ return std::u16string_view{ str, len };
+}
+
+constexpr std::u32string_view operator "" _sv( const char32_t* str, size_t len ) noexcept // (3)
+{
+ return std::u32string_view{ str, len };
+}
+
+constexpr std::wstring_view operator "" _sv( const wchar_t* str, size_t len ) noexcept // (4)
+{
+ return std::wstring_view{ str, len };
+}
+
+}} // namespace literals::string_view_literals
+
+#endif // nssv_CONFIG_USR_SV_OPERATOR
+
+} // namespace nonstd
+
+#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
+
+namespace nonstd {
+
+using std::string_view;
+using std::wstring_view;
+using std::u16string_view;
+using std::u32string_view;
+using std::basic_string_view;
+
+// literal "sv" and "_sv", see above
+
+using std::operator==;
+using std::operator!=;
+using std::operator<;
+using std::operator<=;
+using std::operator>;
+using std::operator>=;
+
+using std::operator<<;
+
+} // namespace nonstd
+
+#else // nssv_HAVE_STD_STRING_VIEW
+
+//
+// Before C++17: use string_view lite:
+//
+
+// Compiler versions:
+//
+// MSVC++ 6.0 _MSC_VER == 1200 nssv_COMPILER_MSVC_VERSION == 60 (Visual Studio 6.0)
+// MSVC++ 7.0 _MSC_VER == 1300 nssv_COMPILER_MSVC_VERSION == 70 (Visual Studio .NET 2002)
+// MSVC++ 7.1 _MSC_VER == 1310 nssv_COMPILER_MSVC_VERSION == 71 (Visual Studio .NET 2003)
+// MSVC++ 8.0 _MSC_VER == 1400 nssv_COMPILER_MSVC_VERSION == 80 (Visual Studio 2005)
+// MSVC++ 9.0 _MSC_VER == 1500 nssv_COMPILER_MSVC_VERSION == 90 (Visual Studio 2008)
+// MSVC++ 10.0 _MSC_VER == 1600 nssv_COMPILER_MSVC_VERSION == 100 (Visual Studio 2010)
+// MSVC++ 11.0 _MSC_VER == 1700 nssv_COMPILER_MSVC_VERSION == 110 (Visual Studio 2012)
+// MSVC++ 12.0 _MSC_VER == 1800 nssv_COMPILER_MSVC_VERSION == 120 (Visual Studio 2013)
+// MSVC++ 14.0 _MSC_VER == 1900 nssv_COMPILER_MSVC_VERSION == 140 (Visual Studio 2015)
+// MSVC++ 14.1 _MSC_VER >= 1910 nssv_COMPILER_MSVC_VERSION == 141 (Visual Studio 2017)
+// MSVC++ 14.2 _MSC_VER >= 1920 nssv_COMPILER_MSVC_VERSION == 142 (Visual Studio 2019)
+
+#if defined(_MSC_VER ) && !defined(__clang__)
+# define nssv_COMPILER_MSVC_VER (_MSC_VER )
+# define nssv_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) )
+#else
+# define nssv_COMPILER_MSVC_VER 0
+# define nssv_COMPILER_MSVC_VERSION 0
+#endif
+
+#define nssv_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) )
+
+#if defined( __apple_build_version__ )
+# define nssv_COMPILER_APPLECLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
+# define nssv_COMPILER_CLANG_VERSION 0
+#elif defined( __clang__ )
+# define nssv_COMPILER_APPLECLANG_VERSION 0
+# define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
+#else
+# define nssv_COMPILER_APPLECLANG_VERSION 0
+# define nssv_COMPILER_CLANG_VERSION 0
+#endif
+
+#if defined(__GNUC__) && !defined(__clang__)
+# define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#else
+# define nssv_COMPILER_GNUC_VERSION 0
+#endif
+
+// half-open range [lo..hi):
+#define nssv_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) )
+
+// Presence of language and library features:
+
+#ifdef _HAS_CPP0X
+# define nssv_HAS_CPP0X _HAS_CPP0X
+#else
+# define nssv_HAS_CPP0X 0
+#endif
+
+// Unless defined otherwise below, consider VC14 as C++11 for variant-lite:
+
+#if nssv_COMPILER_MSVC_VER >= 1900
+# undef nssv_CPP11_OR_GREATER
+# define nssv_CPP11_OR_GREATER 1
+#endif
+
+#define nssv_CPP11_90 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500)
+#define nssv_CPP11_100 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600)
+#define nssv_CPP11_110 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700)
+#define nssv_CPP11_120 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800)
+#define nssv_CPP11_140 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900)
+#define nssv_CPP11_141 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910)
+
+#define nssv_CPP14_000 (nssv_CPP14_OR_GREATER)
+#define nssv_CPP17_000 (nssv_CPP17_OR_GREATER)
+
+// Presence of C++11 language features:
+
+#define nssv_HAVE_CONSTEXPR_11 nssv_CPP11_140
+#define nssv_HAVE_EXPLICIT_CONVERSION nssv_CPP11_140
+#define nssv_HAVE_INLINE_NAMESPACE nssv_CPP11_140
+#define nssv_HAVE_NOEXCEPT nssv_CPP11_140
+#define nssv_HAVE_NULLPTR nssv_CPP11_100
+#define nssv_HAVE_REF_QUALIFIER nssv_CPP11_140
+#define nssv_HAVE_UNICODE_LITERALS nssv_CPP11_140
+#define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140
+#define nssv_HAVE_WCHAR16_T nssv_CPP11_100
+#define nssv_HAVE_WCHAR32_T nssv_CPP11_100
+
+#if ! ( ( nssv_CPP11_OR_GREATER && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) )
+# define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140
+#else
+# define nssv_HAVE_STD_DEFINED_LITERALS 0
+#endif
+
+// Presence of C++14 language features:
+
+#define nssv_HAVE_CONSTEXPR_14 nssv_CPP14_000
+
+// Presence of C++17 language features:
+
+#define nssv_HAVE_NODISCARD nssv_CPP17_000
+
+// Presence of C++ library features:
+
+#define nssv_HAVE_STD_HASH nssv_CPP11_120
+
+// Presence of compiler intrinsics:
+
+// Providing char-type specializations for compare() and length() that
+// use compiler intrinsics can improve compile- and run-time performance.
+//
+// The challenge is in using the right combinations of builtin availablity
+// and its constexpr-ness.
+//
+// | compiler | __builtin_memcmp (constexpr) | memcmp (constexpr) |
+// |----------|------------------------------|---------------------|
+// | clang | 4.0 (>= 4.0 ) | any (? ) |
+// | clang-a | 9.0 (>= 9.0 ) | any (? ) |
+// | gcc | any (constexpr) | any (? ) |
+// | msvc | >= 14.2 C++17 (>= 14.2 ) | any (? ) |
+
+#define nssv_HAVE_BUILTIN_VER ( (nssv_CPP17_000 && nssv_COMPILER_MSVC_VERSION >= 142) || nssv_COMPILER_GNUC_VERSION > 0 || nssv_COMPILER_CLANG_VERSION >= 400 || nssv_COMPILER_APPLECLANG_VERSION >= 900 )
+#define nssv_HAVE_BUILTIN_CE ( nssv_HAVE_BUILTIN_VER )
+
+#define nssv_HAVE_BUILTIN_MEMCMP ( (nssv_HAVE_CONSTEXPR_14 && nssv_HAVE_BUILTIN_CE) || !nssv_HAVE_CONSTEXPR_14 )
+#define nssv_HAVE_BUILTIN_STRLEN ( (nssv_HAVE_CONSTEXPR_11 && nssv_HAVE_BUILTIN_CE) || !nssv_HAVE_CONSTEXPR_11 )
+
+#ifdef __has_builtin
+# define nssv_HAVE_BUILTIN( x ) __has_builtin( x )
+#else
+# define nssv_HAVE_BUILTIN( x ) 0
+#endif
+
+#if nssv_HAVE_BUILTIN(__builtin_memcmp) || nssv_HAVE_BUILTIN_VER
+# define nssv_BUILTIN_MEMCMP __builtin_memcmp
+#else
+# define nssv_BUILTIN_MEMCMP memcmp
+#endif
+
+#if nssv_HAVE_BUILTIN(__builtin_strlen) || nssv_HAVE_BUILTIN_VER
+# define nssv_BUILTIN_STRLEN __builtin_strlen
+#else
+# define nssv_BUILTIN_STRLEN strlen
+#endif
+
+// C++ feature usage:
+
+#if nssv_HAVE_CONSTEXPR_11
+# define nssv_constexpr constexpr
+#else
+# define nssv_constexpr /*constexpr*/
+#endif
+
+#if nssv_HAVE_CONSTEXPR_14
+# define nssv_constexpr14 constexpr
+#else
+# define nssv_constexpr14 /*constexpr*/
+#endif
+
+#if nssv_HAVE_EXPLICIT_CONVERSION
+# define nssv_explicit explicit
+#else
+# define nssv_explicit /*explicit*/
+#endif
+
+#if nssv_HAVE_INLINE_NAMESPACE
+# define nssv_inline_ns inline
+#else
+# define nssv_inline_ns /*inline*/
+#endif
+
+#if nssv_HAVE_NOEXCEPT
+# define nssv_noexcept noexcept
+#else
+# define nssv_noexcept /*noexcept*/
+#endif
+
+//#if nssv_HAVE_REF_QUALIFIER
+//# define nssv_ref_qual &
+//# define nssv_refref_qual &&
+//#else
+//# define nssv_ref_qual /*&*/
+//# define nssv_refref_qual /*&&*/
+//#endif
+
+#if nssv_HAVE_NULLPTR
+# define nssv_nullptr nullptr
+#else
+# define nssv_nullptr NULL
+#endif
+
+#if nssv_HAVE_NODISCARD
+# define nssv_nodiscard [[nodiscard]]
+#else
+# define nssv_nodiscard /*[[nodiscard]]*/
+#endif
+
+// Additional includes:
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <limits>
+#include <ostream>
+#include <string> // std::char_traits<>
+
+#if ! nssv_CONFIG_NO_EXCEPTIONS
+# include <stdexcept>
+#endif
+
+#if nssv_CPP11_OR_GREATER
+# include <type_traits>
+#endif
+
+// Clang, GNUC, MSVC warning suppression macros:
+
+#if defined(__clang__)
+# pragma clang diagnostic ignored "-Wreserved-user-defined-literal"
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wuser-defined-literals"
+#elif defined(__GNUC__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wliteral-suffix"
+#endif // __clang__
+
+#if nssv_COMPILER_MSVC_VERSION >= 140
+# define nssv_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]]
+# define nssv_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress: code) )
+# define nssv_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable: codes))
+#else
+# define nssv_SUPPRESS_MSGSL_WARNING(expr)
+# define nssv_SUPPRESS_MSVC_WARNING(code, descr)
+# define nssv_DISABLE_MSVC_WARNINGS(codes)
+#endif
+
+#if defined(__clang__)
+# define nssv_RESTORE_WARNINGS() _Pragma("clang diagnostic pop")
+#elif defined(__GNUC__)
+# define nssv_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop")
+#elif nssv_COMPILER_MSVC_VERSION >= 140
+# define nssv_RESTORE_WARNINGS() __pragma(warning(pop ))
+#else
+# define nssv_RESTORE_WARNINGS()
+#endif
+
+// Suppress the following MSVC (GSL) warnings:
+// - C4455, non-gsl : 'operator ""sv': literal suffix identifiers that do not
+// start with an underscore are reserved
+// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions;
+// use brace initialization, gsl::narrow_cast or gsl::narow
+// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead
+
+nssv_DISABLE_MSVC_WARNINGS( 4455 26481 26472 )
+//nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" )
+//nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix )
+
+namespace nonstd { namespace sv_lite {
+
+namespace detail {
+
+// support constexpr comparison in C++14;
+// for C++17 and later, use provided traits:
+
+template< typename CharT >
+inline nssv_constexpr14 int compare( CharT const * s1, CharT const * s2, std::size_t count )
+{
+ while ( count-- != 0 )
+ {
+ if ( *s1 < *s2 ) return -1;
+ if ( *s1 > *s2 ) return +1;
+ ++s1; ++s2;
+ }
+ return 0;
+}
+
+#if nssv_HAVE_BUILTIN_MEMCMP
+
+// specialization of compare() for char, see also generic compare() above:
+
+inline nssv_constexpr14 int compare( char const * s1, char const * s2, std::size_t count )
+{
+ return nssv_BUILTIN_MEMCMP( s1, s2, count );
+}
+
+#endif
+
+#if nssv_HAVE_BUILTIN_STRLEN
+
+// specialization of length() for char, see also generic length() further below:
+
+inline nssv_constexpr std::size_t length( char const * s )
+{
+ return nssv_BUILTIN_STRLEN( s );
+}
+
+#endif
+
+#if defined(__OPTIMIZE__)
+
+// gcc, clang provide __OPTIMIZE__
+// Expect tail call optimization to make length() non-recursive:
+
+template< typename CharT >
+inline nssv_constexpr std::size_t length( CharT * s, std::size_t result = 0 )
+{
+ return *s == '\0' ? result : length( s + 1, result + 1 );
+}
+
+#else // OPTIMIZE
+
+// non-recursive:
+
+template< typename CharT >
+inline nssv_constexpr14 std::size_t length( CharT * s )
+{
+ std::size_t result = 0;
+ while ( *s++ != '\0' )
+ {
+ ++result;
+ }
+ return result;
+}
+
+#endif // OPTIMIZE
+
+} // namespace detail
+
+template
+<
+ class CharT,
+ class Traits = std::char_traits<CharT>
+>
+class basic_string_view;
+
+//
+// basic_string_view:
+//
+
+template
+<
+ class CharT,
+ class Traits /* = std::char_traits<CharT> */
+>
+class basic_string_view
+{
+public:
+ // Member types:
+
+ typedef Traits traits_type;
+ typedef CharT value_type;
+
+ typedef CharT * pointer;
+ typedef CharT const * const_pointer;
+ typedef CharT & reference;
+ typedef CharT const & const_reference;
+
+ typedef const_pointer iterator;
+ typedef const_pointer const_iterator;
+ typedef std::reverse_iterator< const_iterator > reverse_iterator;
+ typedef std::reverse_iterator< const_iterator > const_reverse_iterator;
+
+ typedef std::size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+
+ // 24.4.2.1 Construction and assignment:
+
+ nssv_constexpr basic_string_view() nssv_noexcept
+ : data_( nssv_nullptr )
+ , size_( 0 )
+ {}
+
+#if nssv_CPP11_OR_GREATER
+ nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept = default;
+#else
+ nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept
+ : data_( other.data_)
+ , size_( other.size_)
+ {}
+#endif
+
+ nssv_constexpr basic_string_view( CharT const * s, size_type count ) nssv_noexcept // non-standard noexcept
+ : data_( s )
+ , size_( count )
+ {}
+
+ nssv_constexpr basic_string_view( CharT const * s) nssv_noexcept // non-standard noexcept
+ : data_( s )
+#if nssv_CPP17_OR_GREATER
+ , size_( Traits::length(s) )
+#elif nssv_CPP11_OR_GREATER
+ , size_( detail::length(s) )
+#else
+ , size_( Traits::length(s) )
+#endif
+ {}
+
+ // Assignment:
+
+#if nssv_CPP11_OR_GREATER
+ nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept = default;
+#else
+ nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept
+ {
+ data_ = other.data_;
+ size_ = other.size_;
+ return *this;
+ }
+#endif
+
+ // 24.4.2.2 Iterator support:
+
+ nssv_constexpr const_iterator begin() const nssv_noexcept { return data_; }
+ nssv_constexpr const_iterator end() const nssv_noexcept { return data_ + size_; }
+
+ nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); }
+ nssv_constexpr const_iterator cend() const nssv_noexcept { return end(); }
+
+ nssv_constexpr const_reverse_iterator rbegin() const nssv_noexcept { return const_reverse_iterator( end() ); }
+ nssv_constexpr const_reverse_iterator rend() const nssv_noexcept { return const_reverse_iterator( begin() ); }
+
+ nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); }
+ nssv_constexpr const_reverse_iterator crend() const nssv_noexcept { return rend(); }
+
+ // 24.4.2.3 Capacity:
+
+ nssv_constexpr size_type size() const nssv_noexcept { return size_; }
+ nssv_constexpr size_type length() const nssv_noexcept { return size_; }
+ nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits< size_type >::max)(); }
+
+ // since C++20
+ nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept
+ {
+ return 0 == size_;
+ }
+
+ // 24.4.2.4 Element access:
+
+ nssv_constexpr const_reference operator[]( size_type pos ) const
+ {
+ return data_at( pos );
+ }
+
+ nssv_constexpr14 const_reference at( size_type pos ) const
+ {
+#if nssv_CONFIG_NO_EXCEPTIONS
+ assert( pos < size() );
+#else
+ if ( pos >= size() )
+ {
+ throw std::out_of_range("nonstd::string_view::at()");
+ }
+#endif
+ return data_at( pos );
+ }
+
+ nssv_constexpr const_reference front() const { return data_at( 0 ); }
+ nssv_constexpr const_reference back() const { return data_at( size() - 1 ); }
+
+ nssv_constexpr const_pointer data() const nssv_noexcept { return data_; }
+
+ // 24.4.2.5 Modifiers:
+
+ nssv_constexpr14 void remove_prefix( size_type n )
+ {
+ assert( n <= size() );
+ data_ += n;
+ size_ -= n;
+ }
+
+ nssv_constexpr14 void remove_suffix( size_type n )
+ {
+ assert( n <= size() );
+ size_ -= n;
+ }
+
+ nssv_constexpr14 void swap( basic_string_view & other ) nssv_noexcept
+ {
+ using std::swap;
+ swap( data_, other.data_ );
+ swap( size_, other.size_ );
+ }
+
+ // 24.4.2.6 String operations:
+
+ size_type copy( CharT * dest, size_type n, size_type pos = 0 ) const
+ {
+#if nssv_CONFIG_NO_EXCEPTIONS
+ assert( pos <= size() );
+#else
+ if ( pos > size() )
+ {
+ throw std::out_of_range("nonstd::string_view::copy()");
+ }
+#endif
+ const size_type rlen = (std::min)( n, size() - pos );
+
+ (void) Traits::copy( dest, data() + pos, rlen );
+
+ return rlen;
+ }
+
+ nssv_constexpr14 basic_string_view substr( size_type pos = 0, size_type n = npos ) const
+ {
+#if nssv_CONFIG_NO_EXCEPTIONS
+ assert( pos <= size() );
+#else
+ if ( pos > size() )
+ {
+ throw std::out_of_range("nonstd::string_view::substr()");
+ }
+#endif
+ return basic_string_view( data() + pos, (std::min)( n, size() - pos ) );
+ }
+
+ // compare(), 6x:
+
+ nssv_constexpr14 int compare( basic_string_view other ) const nssv_noexcept // (1)
+ {
+#if nssv_CPP17_OR_GREATER
+ if ( const int result = Traits::compare( data(), other.data(), (std::min)( size(), other.size() ) ) )
+#else
+ if ( const int result = detail::compare( data(), other.data(), (std::min)( size(), other.size() ) ) )
+#endif
+ {
+ return result;
+ }
+
+ return size() == other.size() ? 0 : size() < other.size() ? -1 : 1;
+ }
+
+ nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other ) const // (2)
+ {
+ return substr( pos1, n1 ).compare( other );
+ }
+
+ nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other, size_type pos2, size_type n2 ) const // (3)
+ {
+ return substr( pos1, n1 ).compare( other.substr( pos2, n2 ) );
+ }
+
+ nssv_constexpr int compare( CharT const * s ) const // (4)
+ {
+ return compare( basic_string_view( s ) );
+ }
+
+ nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s ) const // (5)
+ {
+ return substr( pos1, n1 ).compare( basic_string_view( s ) );
+ }
+
+ nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s, size_type n2 ) const // (6)
+ {
+ return substr( pos1, n1 ).compare( basic_string_view( s, n2 ) );
+ }
+
+ // 24.4.2.7 Searching:
+
+ // starts_with(), 3x, since C++20:
+
+ nssv_constexpr bool starts_with( basic_string_view v ) const nssv_noexcept // (1)
+ {
+ return size() >= v.size() && compare( 0, v.size(), v ) == 0;
+ }
+
+ nssv_constexpr bool starts_with( CharT c ) const nssv_noexcept // (2)
+ {
+ return starts_with( basic_string_view( &c, 1 ) );
+ }
+
+ nssv_constexpr bool starts_with( CharT const * s ) const // (3)
+ {
+ return starts_with( basic_string_view( s ) );
+ }
+
+ // ends_with(), 3x, since C++20:
+
+ nssv_constexpr bool ends_with( basic_string_view v ) const nssv_noexcept // (1)
+ {
+ return size() >= v.size() && compare( size() - v.size(), npos, v ) == 0;
+ }
+
+ nssv_constexpr bool ends_with( CharT c ) const nssv_noexcept // (2)
+ {
+ return ends_with( basic_string_view( &c, 1 ) );
+ }
+
+ nssv_constexpr bool ends_with( CharT const * s ) const // (3)
+ {
+ return ends_with( basic_string_view( s ) );
+ }
+
+ // find(), 4x:
+
+ nssv_constexpr14 size_type find( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1)
+ {
+ return assert( v.size() == 0 || v.data() != nssv_nullptr )
+ , pos >= size()
+ ? npos
+ : to_pos( std::search( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) );
+ }
+
+ nssv_constexpr14 size_type find( CharT c, size_type pos = 0 ) const nssv_noexcept // (2)
+ {
+ return find( basic_string_view( &c, 1 ), pos );
+ }
+
+ nssv_constexpr14 size_type find( CharT const * s, size_type pos, size_type n ) const // (3)
+ {
+ return find( basic_string_view( s, n ), pos );
+ }
+
+ nssv_constexpr14 size_type find( CharT const * s, size_type pos = 0 ) const // (4)
+ {
+ return find( basic_string_view( s ), pos );
+ }
+
+ // rfind(), 4x:
+
+ nssv_constexpr14 size_type rfind( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1)
+ {
+ if ( size() < v.size() )
+ {
+ return npos;
+ }
+
+ if ( v.empty() )
+ {
+ return (std::min)( size(), pos );
+ }
+
+ const_iterator last = cbegin() + (std::min)( size() - v.size(), pos ) + v.size();
+ const_iterator result = std::find_end( cbegin(), last, v.cbegin(), v.cend(), Traits::eq );
+
+ return result != last ? size_type( result - cbegin() ) : npos;
+ }
+
+ nssv_constexpr14 size_type rfind( CharT c, size_type pos = npos ) const nssv_noexcept // (2)
+ {
+ return rfind( basic_string_view( &c, 1 ), pos );
+ }
+
+ nssv_constexpr14 size_type rfind( CharT const * s, size_type pos, size_type n ) const // (3)
+ {
+ return rfind( basic_string_view( s, n ), pos );
+ }
+
+ nssv_constexpr14 size_type rfind( CharT const * s, size_type pos = npos ) const // (4)
+ {
+ return rfind( basic_string_view( s ), pos );
+ }
+
+ // find_first_of(), 4x:
+
+ nssv_constexpr size_type find_first_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1)
+ {
+ return pos >= size()
+ ? npos
+ : to_pos( std::find_first_of( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) );
+ }
+
+ nssv_constexpr size_type find_first_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2)
+ {
+ return find_first_of( basic_string_view( &c, 1 ), pos );
+ }
+
+ nssv_constexpr size_type find_first_of( CharT const * s, size_type pos, size_type n ) const // (3)
+ {
+ return find_first_of( basic_string_view( s, n ), pos );
+ }
+
+ nssv_constexpr size_type find_first_of( CharT const * s, size_type pos = 0 ) const // (4)
+ {
+ return find_first_of( basic_string_view( s ), pos );
+ }
+
+ // find_last_of(), 4x:
+
+ nssv_constexpr size_type find_last_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1)
+ {
+ return empty()
+ ? npos
+ : pos >= size()
+ ? find_last_of( v, size() - 1 )
+ : to_pos( std::find_first_of( const_reverse_iterator( cbegin() + pos + 1 ), crend(), v.cbegin(), v.cend(), Traits::eq ) );
+ }
+
+ nssv_constexpr size_type find_last_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2)
+ {
+ return find_last_of( basic_string_view( &c, 1 ), pos );
+ }
+
+ nssv_constexpr size_type find_last_of( CharT const * s, size_type pos, size_type count ) const // (3)
+ {
+ return find_last_of( basic_string_view( s, count ), pos );
+ }
+
+ nssv_constexpr size_type find_last_of( CharT const * s, size_type pos = npos ) const // (4)
+ {
+ return find_last_of( basic_string_view( s ), pos );
+ }
+
+ // find_first_not_of(), 4x:
+
+ nssv_constexpr size_type find_first_not_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1)
+ {
+ return pos >= size()
+ ? npos
+ : to_pos( std::find_if( cbegin() + pos, cend(), not_in_view( v ) ) );
+ }
+
+ nssv_constexpr size_type find_first_not_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2)
+ {
+ return find_first_not_of( basic_string_view( &c, 1 ), pos );
+ }
+
+ nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos, size_type count ) const // (3)
+ {
+ return find_first_not_of( basic_string_view( s, count ), pos );
+ }
+
+ nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos = 0 ) const // (4)
+ {
+ return find_first_not_of( basic_string_view( s ), pos );
+ }
+
+ // find_last_not_of(), 4x:
+
+ nssv_constexpr size_type find_last_not_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1)
+ {
+ return empty()
+ ? npos
+ : pos >= size()
+ ? find_last_not_of( v, size() - 1 )
+ : to_pos( std::find_if( const_reverse_iterator( cbegin() + pos + 1 ), crend(), not_in_view( v ) ) );
+ }
+
+ nssv_constexpr size_type find_last_not_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2)
+ {
+ return find_last_not_of( basic_string_view( &c, 1 ), pos );
+ }
+
+ nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos, size_type count ) const // (3)
+ {
+ return find_last_not_of( basic_string_view( s, count ), pos );
+ }
+
+ nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos = npos ) const // (4)
+ {
+ return find_last_not_of( basic_string_view( s ), pos );
+ }
+
+ // Constants:
+
+#if nssv_CPP17_OR_GREATER
+ static nssv_constexpr size_type npos = size_type(-1);
+#elif nssv_CPP11_OR_GREATER
+ enum : size_type { npos = size_type(-1) };
+#else
+ enum { npos = size_type(-1) };
+#endif
+
+private:
+ struct not_in_view
+ {
+ const basic_string_view v;
+
+ nssv_constexpr explicit not_in_view( basic_string_view v_ ) : v( v_ ) {}
+
+ nssv_constexpr bool operator()( CharT c ) const
+ {
+ return npos == v.find_first_of( c );
+ }
+ };
+
+ nssv_constexpr size_type to_pos( const_iterator it ) const
+ {
+ return it == cend() ? npos : size_type( it - cbegin() );
+ }
+
+ nssv_constexpr size_type to_pos( const_reverse_iterator it ) const
+ {
+ return it == crend() ? npos : size_type( crend() - it - 1 );
+ }
+
+ nssv_constexpr const_reference data_at( size_type pos ) const
+ {
+#if nssv_BETWEEN( nssv_COMPILER_GNUC_VERSION, 1, 500 )
+ return data_[pos];
+#else
+ return assert( pos < size() ), data_[pos];
+#endif
+ }
+
+private:
+ const_pointer data_;
+ size_type size_;
+
+public:
+#if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
+
+ template< class Allocator >
+ basic_string_view( std::basic_string<CharT, Traits, Allocator> const & s ) nssv_noexcept
+ : data_( s.data() )
+ , size_( s.size() )
+ {}
+
+#if nssv_HAVE_EXPLICIT_CONVERSION
+
+ template< class Allocator >
+ explicit operator std::basic_string<CharT, Traits, Allocator>() const
+ {
+ return to_string( Allocator() );
+ }
+
+#endif // nssv_HAVE_EXPLICIT_CONVERSION
+
+#if nssv_CPP11_OR_GREATER
+
+ template< class Allocator = std::allocator<CharT> >
+ std::basic_string<CharT, Traits, Allocator>
+ to_string( Allocator const & a = Allocator() ) const
+ {
+ return std::basic_string<CharT, Traits, Allocator>( begin(), end(), a );
+ }
+
+#else
+
+ std::basic_string<CharT, Traits>
+ to_string() const
+ {
+ return std::basic_string<CharT, Traits>( begin(), end() );
+ }
+
+ template< class Allocator >
+ std::basic_string<CharT, Traits, Allocator>
+ to_string( Allocator const & a ) const
+ {
+ return std::basic_string<CharT, Traits, Allocator>( begin(), end(), a );
+ }
+
+#endif // nssv_CPP11_OR_GREATER
+
+#endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
+};
+
+//
+// Non-member functions:
+//
+
+// 24.4.3 Non-member comparison functions:
+// lexicographically compare two string views (function template):
+
+template< class CharT, class Traits >
+nssv_constexpr bool operator== (
+ basic_string_view <CharT, Traits> lhs,
+ basic_string_view <CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
+
+template< class CharT, class Traits >
+nssv_constexpr bool operator!= (
+ basic_string_view <CharT, Traits> lhs,
+ basic_string_view <CharT, Traits> rhs ) nssv_noexcept
+{ return !( lhs == rhs ); }
+
+template< class CharT, class Traits >
+nssv_constexpr bool operator< (
+ basic_string_view <CharT, Traits> lhs,
+ basic_string_view <CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) < 0; }
+
+template< class CharT, class Traits >
+nssv_constexpr bool operator<= (
+ basic_string_view <CharT, Traits> lhs,
+ basic_string_view <CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) <= 0; }
+
+template< class CharT, class Traits >
+nssv_constexpr bool operator> (
+ basic_string_view <CharT, Traits> lhs,
+ basic_string_view <CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) > 0; }
+
+template< class CharT, class Traits >
+nssv_constexpr bool operator>= (
+ basic_string_view <CharT, Traits> lhs,
+ basic_string_view <CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) >= 0; }
+
+// Let S be basic_string_view<CharT, Traits>, and sv be an instance of S.
+// Implementations shall provide sufficient additional overloads marked
+// constexpr and noexcept so that an object t with an implicit conversion
+// to S can be compared according to Table 67.
+
+#if ! nssv_CPP11_OR_GREATER || nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 )
+
+// accomodate for older compilers:
+
+// ==
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator==(
+ basic_string_view<CharT, Traits> lhs,
+ CharT const * rhs ) nssv_noexcept
+{ return lhs.size() == detail::length( rhs ) && lhs.compare( rhs ) == 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator==(
+ CharT const * lhs,
+ basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return detail::length( lhs ) == rhs.size() && rhs.compare( lhs ) == 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator==(
+ basic_string_view<CharT, Traits> lhs,
+ std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator==(
+ std::basic_string<CharT, Traits> rhs,
+ basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
+
+// !=
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator!=(
+ basic_string_view<CharT, Traits> lhs,
+ char const * rhs ) nssv_noexcept
+{ return !( lhs == rhs ); }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator!=(
+ char const * lhs,
+ basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return !( lhs == rhs ); }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator!=(
+ basic_string_view<CharT, Traits> lhs,
+ std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return !( lhs == rhs ); }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator!=(
+ std::basic_string<CharT, Traits> rhs,
+ basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return !( lhs == rhs ); }
+
+// <
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<(
+ basic_string_view<CharT, Traits> lhs,
+ char const * rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) < 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<(
+ char const * lhs,
+ basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return rhs.compare( lhs ) > 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<(
+ basic_string_view<CharT, Traits> lhs,
+ std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) < 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<(
+ std::basic_string<CharT, Traits> rhs,
+ basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return rhs.compare( lhs ) > 0; }
+
+// <=
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<=(
+ basic_string_view<CharT, Traits> lhs,
+ char const * rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) <= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<=(
+ char const * lhs,
+ basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return rhs.compare( lhs ) >= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<=(
+ basic_string_view<CharT, Traits> lhs,
+ std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) <= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator<=(
+ std::basic_string<CharT, Traits> rhs,
+ basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return rhs.compare( lhs ) >= 0; }
+
+// >
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>(
+ basic_string_view<CharT, Traits> lhs,
+ char const * rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) > 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>(
+ char const * lhs,
+ basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return rhs.compare( lhs ) < 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>(
+ basic_string_view<CharT, Traits> lhs,
+ std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) > 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>(
+ std::basic_string<CharT, Traits> rhs,
+ basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return rhs.compare( lhs ) < 0; }
+
+// >=
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>=(
+ basic_string_view<CharT, Traits> lhs,
+ char const * rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) >= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>=(
+ char const * lhs,
+ basic_string_view<CharT, Traits> rhs ) nssv_noexcept
+{ return rhs.compare( lhs ) <= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>=(
+ basic_string_view<CharT, Traits> lhs,
+ std::basic_string<CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) >= 0; }
+
+template< class CharT, class Traits>
+nssv_constexpr bool operator>=(
+ std::basic_string<CharT, Traits> rhs,
+ basic_string_view<CharT, Traits> lhs ) nssv_noexcept
+{ return rhs.compare( lhs ) <= 0; }
+
+#else // newer compilers:
+
+#define nssv_BASIC_STRING_VIEW_I(T,U) typename std::decay< basic_string_view<T,U> >::type
+
+#if nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 140, 150 )
+# define nssv_MSVC_ORDER(x) , int=x
+#else
+# define nssv_MSVC_ORDER(x) /*, int=x*/
+#endif
+
+// ==
+
+template< class CharT, class Traits nssv_MSVC_ORDER(1) >
+nssv_constexpr bool operator==(
+ basic_string_view <CharT, Traits> lhs,
+ nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs ) nssv_noexcept
+{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
+
+template< class CharT, class Traits nssv_MSVC_ORDER(2) >
+nssv_constexpr bool operator==(
+ nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
+ basic_string_view <CharT, Traits> rhs ) nssv_noexcept
+{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
+
+// !=
+
+template< class CharT, class Traits nssv_MSVC_ORDER(1) >
+nssv_constexpr bool operator!= (
+ basic_string_view < CharT, Traits > lhs,
+ nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
+{ return !( lhs == rhs ); }
+
+template< class CharT, class Traits nssv_MSVC_ORDER(2) >
+nssv_constexpr bool operator!= (
+ nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
+ basic_string_view < CharT, Traits > rhs ) nssv_noexcept
+{ return !( lhs == rhs ); }
+
+// <
+
+template< class CharT, class Traits nssv_MSVC_ORDER(1) >
+nssv_constexpr bool operator< (
+ basic_string_view < CharT, Traits > lhs,
+ nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) < 0; }
+
+template< class CharT, class Traits nssv_MSVC_ORDER(2) >
+nssv_constexpr bool operator< (
+ nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
+ basic_string_view < CharT, Traits > rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) < 0; }
+
+// <=
+
+template< class CharT, class Traits nssv_MSVC_ORDER(1) >
+nssv_constexpr bool operator<= (
+ basic_string_view < CharT, Traits > lhs,
+ nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) <= 0; }
+
+template< class CharT, class Traits nssv_MSVC_ORDER(2) >
+nssv_constexpr bool operator<= (
+ nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
+ basic_string_view < CharT, Traits > rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) <= 0; }
+
+// >
+
+template< class CharT, class Traits nssv_MSVC_ORDER(1) >
+nssv_constexpr bool operator> (
+ basic_string_view < CharT, Traits > lhs,
+ nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) > 0; }
+
+template< class CharT, class Traits nssv_MSVC_ORDER(2) >
+nssv_constexpr bool operator> (
+ nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
+ basic_string_view < CharT, Traits > rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) > 0; }
+
+// >=
+
+template< class CharT, class Traits nssv_MSVC_ORDER(1) >
+nssv_constexpr bool operator>= (
+ basic_string_view < CharT, Traits > lhs,
+ nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) >= 0; }
+
+template< class CharT, class Traits nssv_MSVC_ORDER(2) >
+nssv_constexpr bool operator>= (
+ nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
+ basic_string_view < CharT, Traits > rhs ) nssv_noexcept
+{ return lhs.compare( rhs ) >= 0; }
+
+#undef nssv_MSVC_ORDER
+#undef nssv_BASIC_STRING_VIEW_I
+
+#endif // compiler-dependent approach to comparisons
+
+// 24.4.4 Inserters and extractors:
+
+namespace detail {
+
+template< class Stream >
+void write_padding( Stream & os, std::streamsize n )
+{
+ for ( std::streamsize i = 0; i < n; ++i )
+ os.rdbuf()->sputc( os.fill() );
+}
+
+template< class Stream, class View >
+Stream & write_to_stream( Stream & os, View const & sv )
+{
+ typename Stream::sentry sentry( os );
+
+ if ( !os )
+ return os;
+
+ const std::streamsize length = static_cast<std::streamsize>( sv.length() );
+
+ // Whether, and how, to pad:
+ const bool pad = ( length < os.width() );
+ const bool left_pad = pad && ( os.flags() & std::ios_base::adjustfield ) == std::ios_base::right;
+
+ if ( left_pad )
+ write_padding( os, os.width() - length );
+
+ // Write span characters:
+ os.rdbuf()->sputn( sv.begin(), length );
+
+ if ( pad && !left_pad )
+ write_padding( os, os.width() - length );
+
+ // Reset output stream width:
+ os.width( 0 );
+
+ return os;
+}
+
+} // namespace detail
+
+template< class CharT, class Traits >
+std::basic_ostream<CharT, Traits> &
+operator<<(
+ std::basic_ostream<CharT, Traits>& os,
+ basic_string_view <CharT, Traits> sv )
+{
+ return detail::write_to_stream( os, sv );
+}
+
+// Several typedefs for common character types are provided:
+
+typedef basic_string_view<char> string_view;
+typedef basic_string_view<wchar_t> wstring_view;
+#if nssv_HAVE_WCHAR16_T
+typedef basic_string_view<char16_t> u16string_view;
+typedef basic_string_view<char32_t> u32string_view;
+#endif
+
+}} // namespace nonstd::sv_lite
+
+//
+// 24.4.6 Suffix for basic_string_view literals:
+//
+
+#if nssv_HAVE_USER_DEFINED_LITERALS
+
+namespace nonstd {
+nssv_inline_ns namespace literals {
+nssv_inline_ns namespace string_view_literals {
+
+#if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
+
+nssv_constexpr nonstd::sv_lite::string_view operator "" sv( const char* str, size_t len ) nssv_noexcept // (1)
+{
+ return nonstd::sv_lite::string_view{ str, len };
+}
+
+nssv_constexpr nonstd::sv_lite::u16string_view operator "" sv( const char16_t* str, size_t len ) nssv_noexcept // (2)
+{
+ return nonstd::sv_lite::u16string_view{ str, len };
+}
+
+nssv_constexpr nonstd::sv_lite::u32string_view operator "" sv( const char32_t* str, size_t len ) nssv_noexcept // (3)
+{
+ return nonstd::sv_lite::u32string_view{ str, len };
+}
+
+nssv_constexpr nonstd::sv_lite::wstring_view operator "" sv( const wchar_t* str, size_t len ) nssv_noexcept // (4)
+{
+ return nonstd::sv_lite::wstring_view{ str, len };
+}
+
+#endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
+
+#if nssv_CONFIG_USR_SV_OPERATOR
+
+nssv_constexpr nonstd::sv_lite::string_view operator "" _sv( const char* str, size_t len ) nssv_noexcept // (1)
+{
+ return nonstd::sv_lite::string_view{ str, len };
+}
+
+nssv_constexpr nonstd::sv_lite::u16string_view operator "" _sv( const char16_t* str, size_t len ) nssv_noexcept // (2)
+{
+ return nonstd::sv_lite::u16string_view{ str, len };
+}
+
+nssv_constexpr nonstd::sv_lite::u32string_view operator "" _sv( const char32_t* str, size_t len ) nssv_noexcept // (3)
+{
+ return nonstd::sv_lite::u32string_view{ str, len };
+}
+
+nssv_constexpr nonstd::sv_lite::wstring_view operator "" _sv( const wchar_t* str, size_t len ) nssv_noexcept // (4)
+{
+ return nonstd::sv_lite::wstring_view{ str, len };
+}
+
+#endif // nssv_CONFIG_USR_SV_OPERATOR
+
+}}} // namespace nonstd::literals::string_view_literals
+
+#endif
+
+//
+// Extensions for std::string:
+//
+
+#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
+
+namespace nonstd {
+namespace sv_lite {
+
+// Exclude MSVC 14 (19.00): it yields ambiguous to_string():
+
+#if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140
+
+template< class CharT, class Traits, class Allocator = std::allocator<CharT> >
+std::basic_string<CharT, Traits, Allocator>
+to_string( basic_string_view<CharT, Traits> v, Allocator const & a = Allocator() )
+{
+ return std::basic_string<CharT,Traits, Allocator>( v.begin(), v.end(), a );
+}
+
+#else
+
+template< class CharT, class Traits >
+std::basic_string<CharT, Traits>
+to_string( basic_string_view<CharT, Traits> v )
+{
+ return std::basic_string<CharT, Traits>( v.begin(), v.end() );
+}
+
+template< class CharT, class Traits, class Allocator >
+std::basic_string<CharT, Traits, Allocator>
+to_string( basic_string_view<CharT, Traits> v, Allocator const & a )
+{
+ return std::basic_string<CharT, Traits, Allocator>( v.begin(), v.end(), a );
+}
+
+#endif // nssv_CPP11_OR_GREATER
+
+template< class CharT, class Traits, class Allocator >
+basic_string_view<CharT, Traits>
+to_string_view( std::basic_string<CharT, Traits, Allocator> const & s )
+{
+ return basic_string_view<CharT, Traits>( s.data(), s.size() );
+}
+
+}} // namespace nonstd::sv_lite
+
+#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
+
+//
+// make types and algorithms available in namespace nonstd:
+//
+
+namespace nonstd {
+
+using sv_lite::basic_string_view;
+using sv_lite::string_view;
+using sv_lite::wstring_view;
+
+#if nssv_HAVE_WCHAR16_T
+using sv_lite::u16string_view;
+#endif
+#if nssv_HAVE_WCHAR32_T
+using sv_lite::u32string_view;
+#endif
+
+// literal "sv"
+
+using sv_lite::operator==;
+using sv_lite::operator!=;
+using sv_lite::operator<;
+using sv_lite::operator<=;
+using sv_lite::operator>;
+using sv_lite::operator>=;
+
+using sv_lite::operator<<;
+
+#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
+using sv_lite::to_string;
+using sv_lite::to_string_view;
+#endif
+
+} // namespace nonstd
+
+// 24.4.5 Hash support (C++11):
+
+// Note: The hash value of a string view object is equal to the hash value of
+// the corresponding string object.
+
+#if nssv_HAVE_STD_HASH
+
+#include <functional>
+
+namespace std {
+
+template<>
+struct hash< nonstd::string_view >
+{
+public:
+ std::size_t operator()( nonstd::string_view v ) const nssv_noexcept
+ {
+ return std::hash<std::string>()( std::string( v.data(), v.size() ) );
+ }
+};
+
+template<>
+struct hash< nonstd::wstring_view >
+{
+public:
+ std::size_t operator()( nonstd::wstring_view v ) const nssv_noexcept
+ {
+ return std::hash<std::wstring>()( std::wstring( v.data(), v.size() ) );
+ }
+};
+
+template<>
+struct hash< nonstd::u16string_view >
+{
+public:
+ std::size_t operator()( nonstd::u16string_view v ) const nssv_noexcept
+ {
+ return std::hash<std::u16string>()( std::u16string( v.data(), v.size() ) );
+ }
+};
+
+template<>
+struct hash< nonstd::u32string_view >
+{
+public:
+ std::size_t operator()( nonstd::u32string_view v ) const nssv_noexcept
+ {
+ return std::hash<std::u32string>()( std::u32string( v.data(), v.size() ) );
+ }
+};
+
+} // namespace std
+
+#endif // nssv_HAVE_STD_HASH
+
+nssv_RESTORE_WARNINGS()
+
+#endif // nssv_HAVE_STD_STRING_VIEW
+#endif // NONSTD_SV_LITE_H_INCLUDED
--- /dev/null
+/* Getopt for Microsoft C
+This code is a modification of the Free Software Foundation, Inc.
+Getopt library for parsing command line argument the purpose was
+to provide a Microsoft Visual C friendly derivative. This code
+provides functionality for both Unicode and Multibyte builds.
+
+Date: 02/03/2011 - Ludvik Jerabek - Initial Release
+Version: 1.0
+Comment: Supports getopt, getopt_long, and getopt_long_only
+and POSIXLY_CORRECT environment flag
+License: LGPL
+
+Revisions:
+
+02/03/2011 - Ludvik Jerabek - Initial Release
+02/20/2011 - Ludvik Jerabek - Fixed compiler warnings at Level 4
+07/05/2011 - Ludvik Jerabek - Added no_argument, required_argument, optional_argument defs
+08/03/2011 - Ludvik Jerabek - Fixed non-argument runtime bug which caused runtime exception
+08/09/2011 - Ludvik Jerabek - Added code to export functions for DLL and LIB
+02/15/2012 - Ludvik Jerabek - Fixed _GETOPT_THROW definition missing in implementation file
+08/01/2012 - Ludvik Jerabek - Created separate functions for char and wchar_t characters so single dll can do both unicode and ansi
+10/15/2012 - Ludvik Jerabek - Modified to match latest GNU features
+06/19/2015 - Ludvik Jerabek - Fixed maximum option limitation caused by option_a (255) and option_w (65535) structure val variable
+
+**DISCLAIMER**
+THIS MATERIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
+EITHER EXPRESS OR IMPLIED, INCLUDING, BUT Not LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE, OR NON-INFRINGEMENT. SOME JURISDICTIONS DO NOT ALLOW THE
+EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT
+APPLY TO YOU. IN NO EVENT WILL I BE LIABLE TO ANY PARTY FOR ANY
+DIRECT, INDIRECT, SPECIAL OR OTHER CONSEQUENTIAL DAMAGES FOR ANY
+USE OF THIS MATERIAL INCLUDING, WITHOUT LIMITATION, ANY LOST
+PROFITS, BUSINESS INTERRUPTION, LOSS OF PROGRAMS OR OTHER DATA ON
+YOUR INFORMATION HANDLING SYSTEM OR OTHERWISE, EVEN If WE ARE
+EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+#ifndef _CRT_SECURE_NO_WARNINGS
+ #define _CRT_SECURE_NO_WARNINGS
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <malloc.h>
+#include "getopt.h"
+
+#ifdef __cplusplus
+ #define _GETOPT_THROW throw()
+#else
+ #define _GETOPT_THROW
+#endif
+
+int optind = 1;
+int opterr = 1;
+int optopt = '?';
+enum ENUM_ORDERING { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER };
+
+//
+//
+// Ansi structures and functions follow
+//
+//
+
+static struct _getopt_data_a
+{
+ int optind;
+ int opterr;
+ int optopt;
+ char *optarg;
+ int __initialized;
+ char *__nextchar;
+ enum ENUM_ORDERING __ordering;
+ int __posixly_correct;
+ int __first_nonopt;
+ int __last_nonopt;
+} getopt_data_a;
+char *optarg_a;
+
+static void exchange_a(char **argv, struct _getopt_data_a *d)
+{
+ int bottom = d->__first_nonopt;
+ int middle = d->__last_nonopt;
+ int top = d->optind;
+ char *tem;
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ int len = middle - bottom;
+ register int i;
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ }
+ top -= len;
+ }
+ else
+ {
+ int len = top - middle;
+ register int i;
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ }
+ bottom += len;
+ }
+ }
+ d->__first_nonopt += (d->optind - d->__last_nonopt);
+ d->__last_nonopt = d->optind;
+}
+static const char *_getopt_initialize_a (const char *optstring, struct _getopt_data_a *d, int posixly_correct)
+{
+ d->__first_nonopt = d->__last_nonopt = d->optind;
+ d->__nextchar = NULL;
+ d->__posixly_correct = posixly_correct | !!getenv("POSIXLY_CORRECT");
+ if (optstring[0] == '-')
+ {
+ d->__ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ d->__ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (d->__posixly_correct)
+ d->__ordering = REQUIRE_ORDER;
+ else
+ d->__ordering = PERMUTE;
+ return optstring;
+}
+int _getopt_internal_r_a (int argc, char *const *argv, const char *optstring, const struct option_a *longopts, int *longind, int long_only, struct _getopt_data_a *d, int posixly_correct)
+{
+ int print_errors = d->opterr;
+ if (argc < 1)
+ return -1;
+ d->optarg = NULL;
+ if (d->optind == 0 || !d->__initialized)
+ {
+ if (d->optind == 0)
+ d->optind = 1;
+ optstring = _getopt_initialize_a (optstring, d, posixly_correct);
+ d->__initialized = 1;
+ }
+ else if (optstring[0] == '-' || optstring[0] == '+')
+ optstring++;
+ if (optstring[0] == ':')
+ print_errors = 0;
+ if (d->__nextchar == NULL || *d->__nextchar == '\0')
+ {
+ if (d->__last_nonopt > d->optind)
+ d->__last_nonopt = d->optind;
+ if (d->__first_nonopt > d->optind)
+ d->__first_nonopt = d->optind;
+ if (d->__ordering == PERMUTE)
+ {
+ if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind)
+ exchange_a ((char **) argv, d);
+ else if (d->__last_nonopt != d->optind)
+ d->__first_nonopt = d->optind;
+ while (d->optind < argc && (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0'))
+ d->optind++;
+ d->__last_nonopt = d->optind;
+ }
+ if (d->optind != argc && !strcmp(argv[d->optind], "--"))
+ {
+ d->optind++;
+ if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind)
+ exchange_a((char **) argv, d);
+ else if (d->__first_nonopt == d->__last_nonopt)
+ d->__first_nonopt = d->optind;
+ d->__last_nonopt = argc;
+ d->optind = argc;
+ }
+ if (d->optind == argc)
+ {
+ if (d->__first_nonopt != d->__last_nonopt)
+ d->optind = d->__first_nonopt;
+ return -1;
+ }
+ if ((argv[d->optind][0] != '-' || argv[d->optind][1] == '\0'))
+ {
+ if (d->__ordering == REQUIRE_ORDER)
+ return -1;
+ d->optarg = argv[d->optind++];
+ return 1;
+ }
+ d->__nextchar = (argv[d->optind] + 1 + (longopts != NULL && argv[d->optind][1] == '-'));
+ }
+ if (longopts != NULL && (argv[d->optind][1] == '-' || (long_only && (argv[d->optind][2] || !strchr(optstring, argv[d->optind][1])))))
+ {
+ char *nameend;
+ unsigned int namelen;
+ const struct option_a *p;
+ const struct option_a *pfound = NULL;
+ struct option_list
+ {
+ const struct option_a *p;
+ struct option_list *next;
+ } *ambig_list = NULL;
+ int exact = 0;
+ int indfound = -1;
+ int option_index;
+ for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++);
+ namelen = (unsigned int)(nameend - d->__nextchar);
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp(p->name, d->__nextchar, namelen))
+ {
+ if (namelen == (unsigned int)strlen(p->name))
+ {
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ pfound = p;
+ indfound = option_index;
+ }
+ else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val)
+ {
+ struct option_list *newp = (struct option_list*)alloca(sizeof(*newp));
+ newp->p = p;
+ newp->next = ambig_list;
+ ambig_list = newp;
+ }
+ }
+ if (ambig_list != NULL && !exact)
+ {
+ if (print_errors)
+ {
+ struct option_list first;
+ first.p = pfound;
+ first.next = ambig_list;
+ ambig_list = &first;
+ fprintf (stderr, "%s: option '%s' is ambiguous; possibilities:", argv[0], argv[d->optind]);
+ do
+ {
+ fprintf (stderr, " '--%s'", ambig_list->p->name);
+ ambig_list = ambig_list->next;
+ }
+ while (ambig_list != NULL);
+ fputc ('\n', stderr);
+ }
+ d->__nextchar += strlen(d->__nextchar);
+ d->optind++;
+ d->optopt = 0;
+ return '?';
+ }
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ d->optind++;
+ if (*nameend)
+ {
+ if (pfound->has_arg)
+ d->optarg = nameend + 1;
+ else
+ {
+ if (print_errors)
+ {
+ if (argv[d->optind - 1][1] == '-')
+ {
+ fprintf(stderr, "%s: option '--%s' doesn't allow an argument\n",argv[0], pfound->name);
+ }
+ else
+ {
+ fprintf(stderr, "%s: option '%c%s' doesn't allow an argument\n",argv[0], argv[d->optind - 1][0],pfound->name);
+ }
+ }
+ d->__nextchar += strlen(d->__nextchar);
+ d->optopt = pfound->val;
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (d->optind < argc)
+ d->optarg = argv[d->optind++];
+ else
+ {
+ if (print_errors)
+ {
+ fprintf(stderr,"%s: option '--%s' requires an argument\n",argv[0], pfound->name);
+ }
+ d->__nextchar += strlen(d->__nextchar);
+ d->optopt = pfound->val;
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ d->__nextchar += strlen(d->__nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ if (!long_only || argv[d->optind][1] == '-' || strchr(optstring, *d->__nextchar) == NULL)
+ {
+ if (print_errors)
+ {
+ if (argv[d->optind][1] == '-')
+ {
+ fprintf(stderr, "%s: unrecognized option '--%s'\n",argv[0], d->__nextchar);
+ }
+ else
+ {
+ fprintf(stderr, "%s: unrecognized option '%c%s'\n",argv[0], argv[d->optind][0], d->__nextchar);
+ }
+ }
+ d->__nextchar = (char *)"";
+ d->optind++;
+ d->optopt = 0;
+ return '?';
+ }
+ }
+ {
+ char c = *d->__nextchar++;
+ char *temp = (char*)strchr(optstring, c);
+ if (*d->__nextchar == '\0')
+ ++d->optind;
+ if (temp == NULL || c == ':' || c == ';')
+ {
+ if (print_errors)
+ {
+ fprintf(stderr, "%s: invalid option -- '%c'\n", argv[0], c);
+ }
+ d->optopt = c;
+ return '?';
+ }
+ if (temp[0] == 'W' && temp[1] == ';')
+ {
+ char *nameend;
+ const struct option_a *p;
+ const struct option_a *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = 0;
+ int option_index;
+ if (longopts == NULL)
+ goto no_longs;
+ if (*d->__nextchar != '\0')
+ {
+ d->optarg = d->__nextchar;
+ d->optind++;
+ }
+ else if (d->optind == argc)
+ {
+ if (print_errors)
+ {
+ fprintf(stderr,"%s: option requires an argument -- '%c'\n",argv[0], c);
+ }
+ d->optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ return c;
+ }
+ else
+ d->optarg = argv[d->optind++];
+ for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != '='; nameend++);
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp(p->name, d->__nextchar, nameend - d->__nextchar))
+ {
+ if ((unsigned int) (nameend - d->__nextchar) == strlen(p->name))
+ {
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ pfound = p;
+ indfound = option_index;
+ }
+ else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val)
+ ambig = 1;
+ }
+ if (ambig && !exact)
+ {
+ if (print_errors)
+ {
+ fprintf(stderr, "%s: option '-W %s' is ambiguous\n",argv[0], d->optarg);
+ }
+ d->__nextchar += strlen(d->__nextchar);
+ d->optind++;
+ return '?';
+ }
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ if (*nameend)
+ {
+ if (pfound->has_arg)
+ d->optarg = nameend + 1;
+ else
+ {
+ if (print_errors)
+ {
+ fprintf(stderr, "%s: option '-W %s' doesn't allow an argument\n",argv[0], pfound->name);
+ }
+ d->__nextchar += strlen(d->__nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (d->optind < argc)
+ d->optarg = argv[d->optind++];
+ else
+ {
+ if (print_errors)
+ {
+ fprintf(stderr, "%s: option '-W %s' requires an argument\n",argv[0], pfound->name);
+ }
+ d->__nextchar += strlen(d->__nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ else
+ d->optarg = NULL;
+ d->__nextchar += strlen(d->__nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+no_longs:
+ d->__nextchar = NULL;
+ return 'W';
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ if (*d->__nextchar != '\0')
+ {
+ d->optarg = d->__nextchar;
+ d->optind++;
+ }
+ else
+ d->optarg = NULL;
+ d->__nextchar = NULL;
+ }
+ else
+ {
+ if (*d->__nextchar != '\0')
+ {
+ d->optarg = d->__nextchar;
+ d->optind++;
+ }
+ else if (d->optind == argc)
+ {
+ if (print_errors)
+ {
+ fprintf(stderr,"%s: option requires an argument -- '%c'\n",argv[0], c);
+ }
+ d->optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ d->optarg = argv[d->optind++];
+ d->__nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+int _getopt_internal_a (int argc, char *const *argv, const char *optstring, const struct option_a *longopts, int *longind, int long_only, int posixly_correct)
+{
+ int result;
+ getopt_data_a.optind = optind;
+ getopt_data_a.opterr = opterr;
+ result = _getopt_internal_r_a (argc, argv, optstring, longopts,longind, long_only, &getopt_data_a,posixly_correct);
+ optind = getopt_data_a.optind;
+ optarg_a = getopt_data_a.optarg;
+ optopt = getopt_data_a.optopt;
+ return result;
+}
+int getopt_a (int argc, char *const *argv, const char *optstring) _GETOPT_THROW
+{
+ return _getopt_internal_a (argc, argv, optstring, (const struct option_a *) 0, (int *) 0, 0, 0);
+}
+int getopt_long_a (int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW
+{
+ return _getopt_internal_a (argc, argv, options, long_options, opt_index, 0, 0);
+}
+int getopt_long_only_a (int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW
+{
+ return _getopt_internal_a (argc, argv, options, long_options, opt_index, 1, 0);
+}
+int _getopt_long_r_a (int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index, struct _getopt_data_a *d)
+{
+ return _getopt_internal_r_a (argc, argv, options, long_options, opt_index,0, d, 0);
+}
+int _getopt_long_only_r_a (int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index, struct _getopt_data_a *d)
+{
+ return _getopt_internal_r_a (argc, argv, options, long_options, opt_index, 1, d, 0);
+}
+
+//
+//
+// Unicode Structures and Functions
+//
+//
+
+static struct _getopt_data_w
+{
+ int optind;
+ int opterr;
+ int optopt;
+ wchar_t *optarg;
+ int __initialized;
+ wchar_t *__nextchar;
+ enum ENUM_ORDERING __ordering;
+ int __posixly_correct;
+ int __first_nonopt;
+ int __last_nonopt;
+} getopt_data_w;
+wchar_t *optarg_w;
+
+static void exchange_w(wchar_t **argv, struct _getopt_data_w *d)
+{
+ int bottom = d->__first_nonopt;
+ int middle = d->__last_nonopt;
+ int top = d->optind;
+ wchar_t *tem;
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ int len = middle - bottom;
+ register int i;
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ }
+ top -= len;
+ }
+ else
+ {
+ int len = top - middle;
+ register int i;
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ }
+ bottom += len;
+ }
+ }
+ d->__first_nonopt += (d->optind - d->__last_nonopt);
+ d->__last_nonopt = d->optind;
+}
+static const wchar_t *_getopt_initialize_w (const wchar_t *optstring, struct _getopt_data_w *d, int posixly_correct)
+{
+ d->__first_nonopt = d->__last_nonopt = d->optind;
+ d->__nextchar = NULL;
+ d->__posixly_correct = posixly_correct | !!_wgetenv(L"POSIXLY_CORRECT");
+ if (optstring[0] == L'-')
+ {
+ d->__ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == L'+')
+ {
+ d->__ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (d->__posixly_correct)
+ d->__ordering = REQUIRE_ORDER;
+ else
+ d->__ordering = PERMUTE;
+ return optstring;
+}
+int _getopt_internal_r_w (int argc, wchar_t *const *argv, const wchar_t *optstring, const struct option_w *longopts, int *longind, int long_only, struct _getopt_data_w *d, int posixly_correct)
+{
+ int print_errors = d->opterr;
+ if (argc < 1)
+ return -1;
+ d->optarg = NULL;
+ if (d->optind == 0 || !d->__initialized)
+ {
+ if (d->optind == 0)
+ d->optind = 1;
+ optstring = _getopt_initialize_w (optstring, d, posixly_correct);
+ d->__initialized = 1;
+ }
+ else if (optstring[0] == L'-' || optstring[0] == L'+')
+ optstring++;
+ if (optstring[0] == L':')
+ print_errors = 0;
+ if (d->__nextchar == NULL || *d->__nextchar == L'\0')
+ {
+ if (d->__last_nonopt > d->optind)
+ d->__last_nonopt = d->optind;
+ if (d->__first_nonopt > d->optind)
+ d->__first_nonopt = d->optind;
+ if (d->__ordering == PERMUTE)
+ {
+ if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind)
+ exchange_w((wchar_t **) argv, d);
+ else if (d->__last_nonopt != d->optind)
+ d->__first_nonopt = d->optind;
+ while (d->optind < argc && (argv[d->optind][0] != L'-' || argv[d->optind][1] == L'\0'))
+ d->optind++;
+ d->__last_nonopt = d->optind;
+ }
+ if (d->optind != argc && !wcscmp(argv[d->optind], L"--"))
+ {
+ d->optind++;
+ if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind)
+ exchange_w((wchar_t **) argv, d);
+ else if (d->__first_nonopt == d->__last_nonopt)
+ d->__first_nonopt = d->optind;
+ d->__last_nonopt = argc;
+ d->optind = argc;
+ }
+ if (d->optind == argc)
+ {
+ if (d->__first_nonopt != d->__last_nonopt)
+ d->optind = d->__first_nonopt;
+ return -1;
+ }
+ if ((argv[d->optind][0] != L'-' || argv[d->optind][1] == L'\0'))
+ {
+ if (d->__ordering == REQUIRE_ORDER)
+ return -1;
+ d->optarg = argv[d->optind++];
+ return 1;
+ }
+ d->__nextchar = (argv[d->optind] + 1 + (longopts != NULL && argv[d->optind][1] == L'-'));
+ }
+ if (longopts != NULL && (argv[d->optind][1] == L'-' || (long_only && (argv[d->optind][2] || !wcschr(optstring, argv[d->optind][1])))))
+ {
+ wchar_t *nameend;
+ unsigned int namelen;
+ const struct option_w *p;
+ const struct option_w *pfound = NULL;
+ struct option_list
+ {
+ const struct option_w *p;
+ struct option_list *next;
+ } *ambig_list = NULL;
+ int exact = 0;
+ int indfound = -1;
+ int option_index;
+ for (nameend = d->__nextchar; *nameend && *nameend != L'='; nameend++);
+ namelen = (unsigned int)(nameend - d->__nextchar);
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!wcsncmp(p->name, d->__nextchar, namelen))
+ {
+ if (namelen == (unsigned int)wcslen(p->name))
+ {
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ pfound = p;
+ indfound = option_index;
+ }
+ else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val)
+ {
+ struct option_list *newp = (struct option_list*)alloca(sizeof(*newp));
+ newp->p = p;
+ newp->next = ambig_list;
+ ambig_list = newp;
+ }
+ }
+ if (ambig_list != NULL && !exact)
+ {
+ if (print_errors)
+ {
+ struct option_list first;
+ first.p = pfound;
+ first.next = ambig_list;
+ ambig_list = &first;
+ fwprintf(stderr, L"%s: option '%s' is ambiguous; possibilities:", argv[0], argv[d->optind]);
+ do
+ {
+ fwprintf (stderr, L" '--%s'", ambig_list->p->name);
+ ambig_list = ambig_list->next;
+ }
+ while (ambig_list != NULL);
+ fputwc (L'\n', stderr);
+ }
+ d->__nextchar += wcslen(d->__nextchar);
+ d->optind++;
+ d->optopt = 0;
+ return L'?';
+ }
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ d->optind++;
+ if (*nameend)
+ {
+ if (pfound->has_arg)
+ d->optarg = nameend + 1;
+ else
+ {
+ if (print_errors)
+ {
+ if (argv[d->optind - 1][1] == L'-')
+ {
+ fwprintf(stderr, L"%s: option '--%s' doesn't allow an argument\n",argv[0], pfound->name);
+ }
+ else
+ {
+ fwprintf(stderr, L"%s: option '%c%s' doesn't allow an argument\n",argv[0], argv[d->optind - 1][0],pfound->name);
+ }
+ }
+ d->__nextchar += wcslen(d->__nextchar);
+ d->optopt = pfound->val;
+ return L'?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (d->optind < argc)
+ d->optarg = argv[d->optind++];
+ else
+ {
+ if (print_errors)
+ {
+ fwprintf(stderr,L"%s: option '--%s' requires an argument\n",argv[0], pfound->name);
+ }
+ d->__nextchar += wcslen(d->__nextchar);
+ d->optopt = pfound->val;
+ return optstring[0] == L':' ? L':' : L'?';
+ }
+ }
+ d->__nextchar += wcslen(d->__nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ if (!long_only || argv[d->optind][1] == L'-' || wcschr(optstring, *d->__nextchar) == NULL)
+ {
+ if (print_errors)
+ {
+ if (argv[d->optind][1] == L'-')
+ {
+ fwprintf(stderr, L"%s: unrecognized option '--%s'\n",argv[0], d->__nextchar);
+ }
+ else
+ {
+ fwprintf(stderr, L"%s: unrecognized option '%c%s'\n",argv[0], argv[d->optind][0], d->__nextchar);
+ }
+ }
+ d->__nextchar = (wchar_t *)L"";
+ d->optind++;
+ d->optopt = 0;
+ return L'?';
+ }
+ }
+ {
+ wchar_t c = *d->__nextchar++;
+ wchar_t *temp = (wchar_t*)wcschr(optstring, c);
+ if (*d->__nextchar == L'\0')
+ ++d->optind;
+ if (temp == NULL || c == L':' || c == L';')
+ {
+ if (print_errors)
+ {
+ fwprintf(stderr, L"%s: invalid option -- '%c'\n", argv[0], c);
+ }
+ d->optopt = c;
+ return L'?';
+ }
+ if (temp[0] == L'W' && temp[1] == L';')
+ {
+ wchar_t *nameend;
+ const struct option_w *p;
+ const struct option_w *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = 0;
+ int option_index;
+ if (longopts == NULL)
+ goto no_longs;
+ if (*d->__nextchar != L'\0')
+ {
+ d->optarg = d->__nextchar;
+ d->optind++;
+ }
+ else if (d->optind == argc)
+ {
+ if (print_errors)
+ {
+ fwprintf(stderr,L"%s: option requires an argument -- '%c'\n",argv[0], c);
+ }
+ d->optopt = c;
+ if (optstring[0] == L':')
+ c = L':';
+ else
+ c = L'?';
+ return c;
+ }
+ else
+ d->optarg = argv[d->optind++];
+ for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != L'='; nameend++);
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!wcsncmp(p->name, d->__nextchar, nameend - d->__nextchar))
+ {
+ if ((unsigned int) (nameend - d->__nextchar) == wcslen(p->name))
+ {
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ pfound = p;
+ indfound = option_index;
+ }
+ else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val)
+ ambig = 1;
+ }
+ if (ambig && !exact)
+ {
+ if (print_errors)
+ {
+ fwprintf(stderr, L"%s: option '-W %s' is ambiguous\n",argv[0], d->optarg);
+ }
+ d->__nextchar += wcslen(d->__nextchar);
+ d->optind++;
+ return L'?';
+ }
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ if (*nameend)
+ {
+ if (pfound->has_arg)
+ d->optarg = nameend + 1;
+ else
+ {
+ if (print_errors)
+ {
+ fwprintf(stderr, L"%s: option '-W %s' doesn't allow an argument\n",argv[0], pfound->name);
+ }
+ d->__nextchar += wcslen(d->__nextchar);
+ return L'?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (d->optind < argc)
+ d->optarg = argv[d->optind++];
+ else
+ {
+ if (print_errors)
+ {
+ fwprintf(stderr, L"%s: option '-W %s' requires an argument\n",argv[0], pfound->name);
+ }
+ d->__nextchar += wcslen(d->__nextchar);
+ return optstring[0] == L':' ? L':' : L'?';
+ }
+ }
+ else
+ d->optarg = NULL;
+ d->__nextchar += wcslen(d->__nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+no_longs:
+ d->__nextchar = NULL;
+ return L'W';
+ }
+ if (temp[1] == L':')
+ {
+ if (temp[2] == L':')
+ {
+ if (*d->__nextchar != L'\0')
+ {
+ d->optarg = d->__nextchar;
+ d->optind++;
+ }
+ else
+ d->optarg = NULL;
+ d->__nextchar = NULL;
+ }
+ else
+ {
+ if (*d->__nextchar != L'\0')
+ {
+ d->optarg = d->__nextchar;
+ d->optind++;
+ }
+ else if (d->optind == argc)
+ {
+ if (print_errors)
+ {
+ fwprintf(stderr,L"%s: option requires an argument -- '%c'\n",argv[0], c);
+ }
+ d->optopt = c;
+ if (optstring[0] == L':')
+ c = L':';
+ else
+ c = L'?';
+ }
+ else
+ d->optarg = argv[d->optind++];
+ d->__nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+int _getopt_internal_w (int argc, wchar_t *const *argv, const wchar_t *optstring, const struct option_w *longopts, int *longind, int long_only, int posixly_correct)
+{
+ int result;
+ getopt_data_w.optind = optind;
+ getopt_data_w.opterr = opterr;
+ result = _getopt_internal_r_w (argc, argv, optstring, longopts,longind, long_only, &getopt_data_w,posixly_correct);
+ optind = getopt_data_w.optind;
+ optarg_w = getopt_data_w.optarg;
+ optopt = getopt_data_w.optopt;
+ return result;
+}
+int getopt_w (int argc, wchar_t *const *argv, const wchar_t *optstring) _GETOPT_THROW
+{
+ return _getopt_internal_w (argc, argv, optstring, (const struct option_w *) 0, (int *) 0, 0, 0);
+}
+int getopt_long_w (int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW
+{
+ return _getopt_internal_w (argc, argv, options, long_options, opt_index, 0, 0);
+}
+int getopt_long_only_w (int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW
+{
+ return _getopt_internal_w (argc, argv, options, long_options, opt_index, 1, 0);
+}
+int _getopt_long_r_w (int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index, struct _getopt_data_w *d)
+{
+ return _getopt_internal_r_w (argc, argv, options, long_options, opt_index,0, d, 0);
+}
+int _getopt_long_only_r_w (int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index, struct _getopt_data_w *d)
+{
+ return _getopt_internal_r_w (argc, argv, options, long_options, opt_index, 1, d, 0);
+}
--- /dev/null
+/* Getopt for Microsoft C
+This code is a modification of the Free Software Foundation, Inc.
+Getopt library for parsing command line argument the purpose was
+to provide a Microsoft Visual C friendly derivative. This code
+provides functionality for both Unicode and Multibyte builds.
+
+Date: 02/03/2011 - Ludvik Jerabek - Initial Release
+Version: 1.0
+Comment: Supports getopt, getopt_long, and getopt_long_only
+and POSIXLY_CORRECT environment flag
+License: LGPL
+
+Revisions:
+
+02/03/2011 - Ludvik Jerabek - Initial Release
+02/20/2011 - Ludvik Jerabek - Fixed compiler warnings at Level 4
+07/05/2011 - Ludvik Jerabek - Added no_argument, required_argument, optional_argument defs
+08/03/2011 - Ludvik Jerabek - Fixed non-argument runtime bug which caused runtime exception
+08/09/2011 - Ludvik Jerabek - Added code to export functions for DLL and LIB
+02/15/2012 - Ludvik Jerabek - Fixed _GETOPT_THROW definition missing in implementation file
+08/01/2012 - Ludvik Jerabek - Created separate functions for char and wchar_t characters so single dll can do both unicode and ansi
+10/15/2012 - Ludvik Jerabek - Modified to match latest GNU features
+06/19/2015 - Ludvik Jerabek - Fixed maximum option limitation caused by option_a (255) and option_w (65535) structure val variable
+
+**DISCLAIMER**
+THIS MATERIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
+EITHER EXPRESS OR IMPLIED, INCLUDING, BUT Not LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE, OR NON-INFRINGEMENT. SOME JURISDICTIONS DO NOT ALLOW THE
+EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT
+APPLY TO YOU. IN NO EVENT WILL I BE LIABLE TO ANY PARTY FOR ANY
+DIRECT, INDIRECT, SPECIAL OR OTHER CONSEQUENTIAL DAMAGES FOR ANY
+USE OF THIS MATERIAL INCLUDING, WITHOUT LIMITATION, ANY LOST
+PROFITS, BUSINESS INTERRUPTION, LOSS OF PROGRAMS OR OTHER DATA ON
+YOUR INFORMATION HANDLING SYSTEM OR OTHERWISE, EVEN If WE ARE
+EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+#ifndef __GETOPT_H_
+ #define __GETOPT_H_
+
+ #ifdef _GETOPT_API
+ #undef _GETOPT_API
+ #endif
+
+ #if defined(EXPORTS_GETOPT) && defined(STATIC_GETOPT)
+ #error "The preprocessor definitions of EXPORTS_GETOPT and STATIC_GETOPT can only be used individually"
+ #elif defined(STATIC_GETOPT)
+// #pragma message("Warning static builds of getopt violate the Lesser GNU Public License")
+ #define _GETOPT_API
+ #elif defined(EXPORTS_GETOPT)
+ #pragma message("Exporting getopt library")
+ #define _GETOPT_API __declspec(dllexport)
+ #else
+ #pragma message("Importing getopt library")
+ #define _GETOPT_API __declspec(dllimport)
+ #endif
+
+ // Change behavior for C\C++
+ #ifdef __cplusplus
+ #define _BEGIN_EXTERN_C extern "C" {
+ #define _END_EXTERN_C }
+ #define _GETOPT_THROW throw()
+ #else
+ #define _BEGIN_EXTERN_C
+ #define _END_EXTERN_C
+ #define _GETOPT_THROW
+ #endif
+
+ // Standard GNU options
+ #define null_argument 0 /*Argument Null*/
+ #define no_argument 0 /*Argument Switch Only*/
+ #define required_argument 1 /*Argument Required*/
+ #define optional_argument 2 /*Argument Optional*/
+
+ // Shorter Options
+ #define ARG_NULL 0 /*Argument Null*/
+ #define ARG_NONE 0 /*Argument Switch Only*/
+ #define ARG_REQ 1 /*Argument Required*/
+ #define ARG_OPT 2 /*Argument Optional*/
+
+ #include <string.h>
+ #include <wchar.h>
+
+_BEGIN_EXTERN_C
+
+ extern _GETOPT_API int optind;
+ extern _GETOPT_API int opterr;
+ extern _GETOPT_API int optopt;
+
+ // Ansi
+ struct option_a
+ {
+ const char* name;
+ int has_arg;
+ int *flag;
+ int val;
+ };
+ extern _GETOPT_API char *optarg_a;
+ extern _GETOPT_API int getopt_a(int argc, char *const *argv, const char *optstring) _GETOPT_THROW;
+ extern _GETOPT_API int getopt_long_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW;
+ extern _GETOPT_API int getopt_long_only_a(int argc, char *const *argv, const char *options, const struct option_a *long_options, int *opt_index) _GETOPT_THROW;
+
+ // Unicode
+ struct option_w
+ {
+ const wchar_t* name;
+ int has_arg;
+ int *flag;
+ int val;
+ };
+ extern _GETOPT_API wchar_t *optarg_w;
+ extern _GETOPT_API int getopt_w(int argc, wchar_t *const *argv, const wchar_t *optstring) _GETOPT_THROW;
+ extern _GETOPT_API int getopt_long_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW;
+ extern _GETOPT_API int getopt_long_only_w(int argc, wchar_t *const *argv, const wchar_t *options, const struct option_w *long_options, int *opt_index) _GETOPT_THROW;
+
+_END_EXTERN_C
+
+ #undef _BEGIN_EXTERN_C
+ #undef _END_EXTERN_C
+ #undef _GETOPT_THROW
+ #undef _GETOPT_API
+
+ #ifdef _UNICODE
+ #define getopt getopt_w
+ #define getopt_long getopt_long_w
+ #define getopt_long_only getopt_long_only_w
+ #define option option_w
+ #define optarg optarg_w
+ #else
+ #define getopt getopt_a
+ #define getopt_long getopt_long_a
+ #define getopt_long_only getopt_long_only_a
+ #define option option_a
+ #define optarg optarg_a
+ #endif
+#endif // __GETOPT_H_
--- /dev/null
+/*
+ * xxHash - Extremely Fast Hash algorithm
+ * Copyright (C) 2020 Yann Collet
+ *
+ * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You can contact the author at:
+ * - xxHash homepage: https://www.xxhash.com
+ * - xxHash source repository: https://github.com/Cyan4973/xxHash
+ */
+
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+/*
+ * Dispatcher code for XXH3 on x86-based targets.
+ */
+#if !(defined(__x86_64__) || defined(__i386__) || defined(_M_IX86) || defined(_M_X64))
+# error "Dispatching is currently only supported on x86 and x86_64."
+#endif
+
+#ifndef __GNUC__
+# error "Dispatching requires __attribute__((__target__)) capability"
+#endif
+
+#define XXH_DISPATCH_AVX2 /* enable dispatch towards AVX2 */
+#define XXH_DISPATCH_AVX512 /* enable dispatch towards AVX512 */
+
+#ifdef XXH_DISPATCH_DEBUG
+/* debug logging */
+# include <stdio.h>
+# define XXH_debugPrint(str) { fprintf(stderr, "DEBUG: xxHash dispatch: %s \n", str); fflush(NULL); }
+#else
+# define XXH_debugPrint(str) ((void)0)
+# undef NDEBUG /* avoid redefinition */
+# define NDEBUG
+#endif
+#include <assert.h>
+
+#if defined(__GNUC__)
+# include <immintrin.h> /* sse2 */
+# include <emmintrin.h> /* avx2 */
+#elif defined(_MSC_VER)
+# include <intrin.h>
+#endif
+
+#define XXH_INLINE_ALL
+#define XXH_X86DISPATCH
+#define XXH_TARGET_AVX512 __attribute__((__target__("avx512f")))
+#define XXH_TARGET_AVX2 __attribute__((__target__("avx2")))
+#define XXH_TARGET_SSE2 __attribute__((__target__("sse2")))
+#include "xxhash.h"
+
+/*
+ * Modified version of Intel's guide
+ * https://software.intel.com/en-us/articles/how-to-detect-new-instruction-support-in-the-4th-generation-intel-core-processor-family
+ */
+#if defined(_MSC_VER)
+# include <intrin.h>
+#endif
+
+/*
+ * Support both AT&T and Intel dialects
+ *
+ * GCC doesn't convert AT&T syntax to Intel syntax, and will error out if
+ * compiled with -masm=intel. Instead, it supports dialect switching with
+ * curly braces: { AT&T syntax | Intel syntax }
+ *
+ * Clang's integrated assembler automatically converts AT&T syntax to Intel if
+ * needed, making the dialect switching useless (it isn't even supported).
+ *
+ * Note: Comments are written in the inline assembly itself.
+ */
+#ifdef __clang__
+# define I_ATT(intel, att) att "\n\t"
+#else
+# define I_ATT(intel, att) "{" att "|" intel "}\n\t"
+#endif
+
+
+static void XXH_cpuid(xxh_u32 eax, xxh_u32 ecx, xxh_u32* abcd)
+{
+#if defined(_MSC_VER)
+ __cpuidex(abcd, eax, ecx);
+#else
+ xxh_u32 ebx, edx;
+# if defined(__i386__) && defined(__PIC__)
+ __asm__(
+ "# Call CPUID\n\t"
+ "#\n\t"
+ "# On 32-bit x86 with PIC enabled, we are not allowed to overwrite\n\t"
+ "# EBX, so we use EDI instead.\n\t"
+ I_ATT("mov edi, ebx", "movl %%ebx, %%edi")
+ I_ATT("cpuid", "cpuid" )
+ I_ATT("xchg edi, ebx", "xchgl %%ebx, %%edi")
+ : "=D" (ebx),
+# else
+ __asm__(
+ "# Call CPUID\n\t"
+ I_ATT("cpuid", "cpuid")
+ : "=b" (ebx),
+# endif
+ "+a" (eax), "+c" (ecx), "=d" (edx));
+ abcd[0] = eax;
+ abcd[1] = ebx;
+ abcd[2] = ecx;
+ abcd[3] = edx;
+#endif
+}
+
+#if defined(XXH_DISPATCH_AVX2) || defined(XXH_DISPATCH_AVX512)
+/*
+ * While the CPU may support AVX2, the operating system might not properly save
+ * the full YMM/ZMM registers.
+ *
+ * xgetbv is used for detecting this: Any compliant operating system will define
+ * a set of flags in the xcr0 register indicating how it saves the AVX registers.
+ *
+ * You can manually disable this flag on Windows by running, as admin:
+ *
+ * bcdedit.exe /set xsavedisable 1
+ *
+ * and rebooting. Run the same command with 0 to re-enable it.
+ */
+static xxh_u64 XXH_xgetbv(void)
+{
+#if defined(_MSC_VER)
+ return _xgetbv(0); /* min VS2010 SP1 compiler is required */
+#else
+ xxh_u32 xcr0_lo, xcr0_hi;
+ __asm__(
+ "# Call XGETBV\n\t"
+ "#\n\t"
+ "# Older assemblers (e.g. macOS's ancient GAS version) don't support\n\t"
+ "# the XGETBV opcode, so we encode it by hand instead.\n\t"
+ "# See <https://github.com/asmjit/asmjit/issues/78> for details.\n\t"
+ ".byte 0x0f, 0x01, 0xd0\n\t"
+ : "=a" (xcr0_lo), "=d" (xcr0_hi) : "c" (0));
+ return xcr0_lo | ((xxh_u64)xcr0_hi << 32);
+#endif
+}
+#endif
+
+#define SSE2_CPUID_MASK (1 << 26)
+#define OSXSAVE_CPUID_MASK ((1 << 26) | (1 << 27))
+#define AVX2_CPUID_MASK (1 << 5)
+#define AVX2_XGETBV_MASK ((1 << 2) | (1 << 1))
+#define AVX512F_CPUID_MASK (1 << 16)
+#define AVX512F_XGETBV_MASK ((7 << 5) | (1 << 2) | (1 << 1))
+
+/* Returns the best XXH3 implementation */
+static int XXH_featureTest(void)
+{
+ xxh_u32 abcd[4];
+ xxh_u32 max_leaves;
+ int best = XXH_SCALAR;
+#if defined(XXH_DISPATCH_AVX2) || defined(XXH_DISPATCH_AVX512)
+ xxh_u64 xgetbv_val;
+#endif
+#if defined(__GNUC__) && defined(__i386__)
+ xxh_u32 cpuid_supported;
+ __asm__(
+ "# For the sake of ruthless backwards compatibility, check if CPUID\n\t"
+ "# is supported in the EFLAGS on i386.\n\t"
+ "# This is not necessary on x86_64 - CPUID is mandatory.\n\t"
+ "# The ID flag (bit 21) in the EFLAGS register indicates support\n\t"
+ "# for the CPUID instruction. If a software procedure can set and\n\t"
+ "# clear this flag, the processor executing the procedure supports\n\t"
+ "# the CPUID instruction.\n\t"
+ "# <https://c9x.me/x86/html/file_module_x86_id_45.html>\n\t"
+ "#\n\t"
+ "# Routine is from <https://wiki.osdev.org/CPUID>.\n\t"
+
+ "# Save EFLAGS\n\t"
+ I_ATT("pushfd", "pushfl" )
+ "# Store EFLAGS\n\t"
+ I_ATT("pushfd", "pushfl" )
+ "# Invert the ID bit in stored EFLAGS\n\t"
+ I_ATT("xor dword ptr[esp], 0x200000", "xorl $0x200000, (%%esp)")
+ "# Load stored EFLAGS (with ID bit inverted)\n\t"
+ I_ATT("popfd", "popfl" )
+ "# Store EFLAGS again (ID bit may or not be inverted)\n\t"
+ I_ATT("pushfd", "pushfl" )
+ "# eax = modified EFLAGS (ID bit may or may not be inverted)\n\t"
+ I_ATT("pop eax", "popl %%eax" )
+ "# eax = whichever bits were changed\n\t"
+ I_ATT("xor eax, dword ptr[esp]", "xorl (%%esp), %%eax" )
+ "# Restore original EFLAGS\n\t"
+ I_ATT("popfd", "popfl" )
+ "# eax = zero if ID bit can't be changed, else non-zero\n\t"
+ I_ATT("and eax, 0x200000", "andl $0x200000, %%eax" )
+ : "=a" (cpuid_supported) :: "cc");
+
+ if (XXH_unlikely(!cpuid_supported)) {
+ XXH_debugPrint("CPUID support is not detected!");
+ return best;
+ }
+
+#endif
+ /* Check how many CPUID pages we have */
+ XXH_cpuid(0, 0, abcd);
+ max_leaves = abcd[0];
+
+ /* Shouldn't happen on hardware, but happens on some QEMU configs. */
+ if (XXH_unlikely(max_leaves == 0)) {
+ XXH_debugPrint("Max CPUID leaves == 0!");
+ return best;
+ }
+
+ /* Check for SSE2, OSXSAVE and xgetbv */
+ XXH_cpuid(1, 0, abcd);
+
+ /*
+ * Test for SSE2. The check is redundant on x86_64, but it doesn't hurt.
+ */
+ if (XXH_unlikely((abcd[3] & SSE2_CPUID_MASK) != SSE2_CPUID_MASK))
+ return best;
+
+ XXH_debugPrint("SSE2 support detected.");
+
+ best = XXH_SSE2;
+#if defined(XXH_DISPATCH_AVX2) || defined(XXH_DISPATCH_AVX512)
+ /* Make sure we have enough leaves */
+ if (XXH_unlikely(max_leaves < 7))
+ return best;
+
+ /* Test for OSXSAVE and XGETBV */
+ if ((abcd[2] & OSXSAVE_CPUID_MASK) != OSXSAVE_CPUID_MASK)
+ return best;
+
+ /* CPUID check for AVX features */
+ XXH_cpuid(7, 0, abcd);
+
+ xgetbv_val = XXH_xgetbv();
+#if defined(XXH_DISPATCH_AVX2)
+ /* Validate that AVX2 is supported by the CPU */
+ if ((abcd[1] & AVX2_CPUID_MASK) != AVX2_CPUID_MASK)
+ return best;
+
+ /* Validate that the OS supports YMM registers */
+ if ((xgetbv_val & AVX2_XGETBV_MASK) != AVX2_XGETBV_MASK) {
+ XXH_debugPrint("AVX2 supported by the CPU, but not the OS.");
+ return best;
+ }
+
+ /* AVX2 supported */
+ XXH_debugPrint("AVX2 support detected.");
+ best = XXH_AVX2;
+#endif
+#if defined(XXH_DISPATCH_AVX512)
+ /* Check if AVX512F is supported by the CPU */
+ if ((abcd[1] & AVX512F_CPUID_MASK) != AVX512F_CPUID_MASK) {
+ XXH_debugPrint("AVX512F not supported by CPU");
+ return best;
+ }
+
+ /* Validate that the OS supports ZMM registers */
+ if ((xgetbv_val & AVX512F_XGETBV_MASK) != AVX512F_XGETBV_MASK) {
+ XXH_debugPrint("AVX512F supported by the CPU, but not the OS.");
+ return best;
+ }
+
+ /* AVX512F supported */
+ XXH_debugPrint("AVX512F support detected.");
+ best = XXH_AVX512;
+#endif
+#endif
+ return best;
+}
+
+
+/* === Vector implementations === */
+
+/* === XXH3, default variants === */
+
+XXH_NO_INLINE XXH64_hash_t
+XXHL64_default_scalar(const void* XXH_RESTRICT input, size_t len)
+{
+ return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate_512_scalar, XXH3_scrambleAcc_scalar);
+}
+
+XXH_NO_INLINE XXH_TARGET_SSE2 XXH64_hash_t
+XXHL64_default_sse2(const void* XXH_RESTRICT input, size_t len)
+{
+ return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate_512_sse2, XXH3_scrambleAcc_sse2);
+}
+
+#ifdef XXH_DISPATCH_AVX2
+XXH_NO_INLINE XXH_TARGET_AVX2 XXH64_hash_t
+XXHL64_default_avx2(const void* XXH_RESTRICT input, size_t len)
+{
+ return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate_512_avx2, XXH3_scrambleAcc_avx2);
+}
+#endif
+
+#ifdef XXH_DISPATCH_AVX512
+XXH_NO_INLINE XXH_TARGET_AVX512 XXH64_hash_t
+XXHL64_default_avx512(const void* XXH_RESTRICT input, size_t len)
+{
+ return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate_512_avx512, XXH3_scrambleAcc_avx512);
+}
+#endif
+
+/* === XXH3, Seeded variants === */
+
+XXH_NO_INLINE XXH64_hash_t
+XXHL64_seed_scalar(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed)
+{
+ return XXH3_hashLong_64b_withSeed_internal(input, len, seed,
+ XXH3_accumulate_512_scalar, XXH3_scrambleAcc_scalar, XXH3_initCustomSecret_scalar);
+}
+
+XXH_NO_INLINE XXH_TARGET_SSE2 XXH64_hash_t
+XXHL64_seed_sse2(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed)
+{
+ return XXH3_hashLong_64b_withSeed_internal(input, len, seed,
+ XXH3_accumulate_512_sse2, XXH3_scrambleAcc_sse2, XXH3_initCustomSecret_sse2);
+}
+
+#ifdef XXH_DISPATCH_AVX2
+XXH_NO_INLINE XXH_TARGET_AVX2 XXH64_hash_t
+XXHL64_seed_avx2(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed)
+{
+ return XXH3_hashLong_64b_withSeed_internal(input, len, seed,
+ XXH3_accumulate_512_avx2, XXH3_scrambleAcc_avx2, XXH3_initCustomSecret_avx2);
+}
+#endif
+
+#ifdef XXH_DISPATCH_AVX512
+XXH_NO_INLINE XXH_TARGET_AVX512 XXH64_hash_t
+XXHL64_seed_avx512(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed)
+{
+ return XXH3_hashLong_64b_withSeed_internal(input, len, seed,
+ XXH3_accumulate_512_avx512, XXH3_scrambleAcc_avx512, XXH3_initCustomSecret_avx512);
+}
+#endif
+
+/* === XXH3, Secret variants === */
+
+XXH_NO_INLINE XXH64_hash_t
+XXHL64_secret_scalar(const void* XXH_RESTRICT input, size_t len, const void* secret, size_t secretLen)
+{
+ return XXH3_hashLong_64b_internal(input, len, secret, secretLen,
+ XXH3_accumulate_512_scalar, XXH3_scrambleAcc_scalar);
+}
+
+XXH_NO_INLINE XXH_TARGET_SSE2 XXH64_hash_t
+XXHL64_secret_sse2(const void* XXH_RESTRICT input, size_t len, const void* secret, size_t secretLen)
+{
+ return XXH3_hashLong_64b_internal(input, len, secret, secretLen,
+ XXH3_accumulate_512_sse2, XXH3_scrambleAcc_sse2);
+}
+
+#ifdef XXH_DISPATCH_AVX2
+XXH_NO_INLINE XXH_TARGET_AVX2 XXH64_hash_t
+XXHL64_secret_avx2(const void* XXH_RESTRICT input, size_t len, const void* secret, size_t secretLen)
+{
+ return XXH3_hashLong_64b_internal(input, len, secret, secretLen,
+ XXH3_accumulate_512_avx2, XXH3_scrambleAcc_avx2);
+}
+#endif
+
+#ifdef XXH_DISPATCH_AVX512
+XXH_NO_INLINE XXH_TARGET_AVX512 XXH64_hash_t
+XXHL64_secret_avx512(const void* XXH_RESTRICT input, size_t len, const void* secret, size_t secretLen)
+{
+ return XXH3_hashLong_64b_internal(input, len, secret, secretLen,
+ XXH3_accumulate_512_avx512, XXH3_scrambleAcc_avx512);
+}
+#endif
+
+/* === XXH3 update variants === */
+
+XXH_NO_INLINE XXH_errorcode
+XXH3_64bits_update_scalar(XXH3_state_t* state, const void* input, size_t len)
+{
+ return XXH3_update(state, (const xxh_u8*)input, len,
+ XXH3_accumulate_512_scalar, XXH3_scrambleAcc_scalar);
+}
+
+XXH_NO_INLINE XXH_TARGET_SSE2 XXH_errorcode
+XXH3_64bits_update_sse2(XXH3_state_t* state, const void* input, size_t len)
+{
+ return XXH3_update(state, (const xxh_u8*)input, len,
+ XXH3_accumulate_512_sse2, XXH3_scrambleAcc_sse2);
+}
+
+#ifdef XXH_DISPATCH_AVX2
+XXH_NO_INLINE XXH_TARGET_AVX2 XXH_errorcode
+XXH3_64bits_update_avx2(XXH3_state_t* state, const void* input, size_t len)
+{
+ return XXH3_update(state, (const xxh_u8*)input, len,
+ XXH3_accumulate_512_avx2, XXH3_scrambleAcc_avx2);
+}
+#endif
+
+#ifdef XXH_DISPATCH_AVX512
+XXH_NO_INLINE XXH_TARGET_AVX512 XXH_errorcode
+XXH3_64bits_update_avx512(XXH3_state_t* state, const void* input, size_t len)
+{
+ return XXH3_update(state, (const xxh_u8*)input, len,
+ XXH3_accumulate_512_avx512, XXH3_scrambleAcc_avx512);
+}
+#endif
+
+/* === XXH128 default variants === */
+
+XXH_NO_INLINE XXH128_hash_t
+XXHL128_default_scalar(const void* XXH_RESTRICT input, size_t len)
+{
+ return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate_512_scalar, XXH3_scrambleAcc_scalar);
+}
+
+XXH_NO_INLINE XXH_TARGET_SSE2 XXH128_hash_t
+XXHL128_default_sse2(const void* XXH_RESTRICT input, size_t len)
+{
+ return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate_512_sse2, XXH3_scrambleAcc_sse2);
+}
+
+#ifdef XXH_DISPATCH_AVX2
+XXH_NO_INLINE XXH_TARGET_AVX2 XXH128_hash_t
+XXHL128_default_avx2(const void* XXH_RESTRICT input, size_t len)
+{
+ return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate_512_avx2, XXH3_scrambleAcc_avx2);
+}
+#endif
+
+#ifdef XXH_DISPATCH_AVX512
+XXH_NO_INLINE XXH_TARGET_AVX512 XXH128_hash_t
+XXHL128_default_avx512(const void* XXH_RESTRICT input, size_t len)
+{
+ return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate_512_avx512, XXH3_scrambleAcc_avx512);
+}
+#endif
+
+/* === XXH128 Secret variants === */
+
+XXH_NO_INLINE XXH128_hash_t
+XXHL128_secret_scalar(const void* XXH_RESTRICT input, size_t len, const void* XXH_RESTRICT secret, size_t secretLen)
+{
+ return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, secretLen,
+ XXH3_accumulate_512_scalar, XXH3_scrambleAcc_scalar);
+}
+
+XXH_NO_INLINE XXH_TARGET_SSE2 XXH128_hash_t
+XXHL128_secret_sse2(const void* XXH_RESTRICT input, size_t len, const void* XXH_RESTRICT secret, size_t secretLen)
+{
+ return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, secretLen,
+ XXH3_accumulate_512_sse2, XXH3_scrambleAcc_sse2);
+}
+
+#ifdef XXH_DISPATCH_AVX2
+XXH_NO_INLINE XXH_TARGET_AVX2 XXH128_hash_t
+XXHL128_secret_avx2(const void* XXH_RESTRICT input, size_t len, const void* XXH_RESTRICT secret, size_t secretLen)
+{
+ return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, secretLen,
+ XXH3_accumulate_512_avx2, XXH3_scrambleAcc_avx2);
+}
+#endif
+
+#ifdef XXH_DISPATCH_AVX512
+XXH_NO_INLINE XXH_TARGET_AVX512 XXH128_hash_t
+XXHL128_secret_avx512(const void* XXH_RESTRICT input, size_t len, const void* XXH_RESTRICT secret, size_t secretLen)
+{
+ return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, secretLen,
+ XXH3_accumulate_512_avx512, XXH3_scrambleAcc_avx512);
+}
+#endif
+
+/* === XXH128 Seeded variants === */
+
+XXH_NO_INLINE XXH128_hash_t
+XXHL128_seed_scalar(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed)
+{
+ return XXH3_hashLong_128b_withSeed_internal(input, len, seed,
+ XXH3_accumulate_512_scalar, XXH3_scrambleAcc_scalar, XXH3_initCustomSecret_scalar);
+}
+
+XXH_NO_INLINE XXH_TARGET_SSE2 XXH128_hash_t
+XXHL128_seed_sse2(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed)
+{
+ return XXH3_hashLong_128b_withSeed_internal(input, len, seed,
+ XXH3_accumulate_512_sse2, XXH3_scrambleAcc_sse2, XXH3_initCustomSecret_sse2);
+}
+
+#ifdef XXH_DISPATCH_AVX2
+XXH_NO_INLINE XXH_TARGET_AVX2 XXH128_hash_t
+XXHL128_seed_avx2(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed)
+{
+ return XXH3_hashLong_128b_withSeed_internal(input, len, seed,
+ XXH3_accumulate_512_avx2, XXH3_scrambleAcc_avx2, XXH3_initCustomSecret_avx2);
+}
+#endif
+
+#ifdef XXH_DISPATCH_AVX512
+XXH_NO_INLINE XXH_TARGET_AVX512 XXH128_hash_t
+XXHL128_seed_avx512(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed)
+{
+ return XXH3_hashLong_128b_withSeed_internal(input, len, seed,
+ XXH3_accumulate_512_avx512, XXH3_scrambleAcc_avx512, XXH3_initCustomSecret_avx512);
+}
+#endif
+
+/* === XXH128 update variants === */
+
+XXH_NO_INLINE XXH_errorcode
+XXH3_128bits_update_scalar(XXH3_state_t* state, const void* input, size_t len)
+{
+ return XXH3_update(state, (const xxh_u8*)input, len,
+ XXH3_accumulate_512_scalar, XXH3_scrambleAcc_scalar);
+}
+
+XXH_NO_INLINE XXH_TARGET_SSE2 XXH_errorcode
+XXH3_128bits_update_sse2(XXH3_state_t* state, const void* input, size_t len)
+{
+ return XXH3_update(state, (const xxh_u8*)input, len,
+ XXH3_accumulate_512_sse2, XXH3_scrambleAcc_sse2);
+}
+
+#ifdef XXH_DISPATCH_AVX2
+XXH_NO_INLINE XXH_TARGET_AVX2 XXH_errorcode
+XXH3_128bits_update_avx2(XXH3_state_t* state, const void* input, size_t len)
+{
+ return XXH3_update(state, (const xxh_u8*)input, len,
+ XXH3_accumulate_512_avx2, XXH3_scrambleAcc_avx2);
+}
+#endif
+
+#ifdef XXH_DISPATCH_AVX512
+XXH_NO_INLINE XXH_TARGET_AVX512 XXH_errorcode
+XXH3_128bits_update_avx512(XXH3_state_t* state, const void* input, size_t len)
+{
+ return XXH3_update(state, (const xxh_u8*)input, len,
+ XXH3_accumulate_512_avx512, XXH3_scrambleAcc_avx512);
+}
+#endif
+
+/* ==== Dispatchers ==== */
+
+typedef XXH64_hash_t (*XXH3_dispatchx86_hashLong64_default)(const void* XXH_RESTRICT, size_t);
+
+typedef XXH64_hash_t (*XXH3_dispatchx86_hashLong64_withSeed)(const void* XXH_RESTRICT, size_t, XXH64_hash_t);
+
+typedef XXH64_hash_t (*XXH3_dispatchx86_hashLong64_withSecret)(const void* XXH_RESTRICT, size_t, const void* XXH_RESTRICT, size_t);
+
+typedef XXH_errorcode (*XXH3_dispatchx86_update)(XXH3_state_t*, const void*, size_t);
+
+typedef struct {
+ XXH3_dispatchx86_hashLong64_default hashLong64_default;
+ XXH3_dispatchx86_hashLong64_withSeed hashLong64_seed;
+ XXH3_dispatchx86_hashLong64_withSecret hashLong64_secret;
+ XXH3_dispatchx86_update update;
+} dispatchFunctions_s;
+
+static dispatchFunctions_s g_dispatch = { NULL, NULL, NULL, NULL};
+
+#define NB_DISPATCHES 4
+static const dispatchFunctions_s k_dispatch[NB_DISPATCHES] = {
+ /* scalar */ { XXHL64_default_scalar, XXHL64_seed_scalar, XXHL64_secret_scalar, XXH3_64bits_update_scalar },
+ /* sse2 */ { XXHL64_default_sse2, XXHL64_seed_sse2, XXHL64_secret_sse2, XXH3_64bits_update_sse2 },
+#ifdef XXH_DISPATCH_AVX2
+ /* avx2 */ { XXHL64_default_avx2, XXHL64_seed_avx2, XXHL64_secret_avx2, XXH3_64bits_update_avx2 },
+#else
+ /* avx2 */ { NULL, NULL, NULL, NULL },
+#endif
+#ifdef XXH_DISPATCH_AVX512
+ /* avx512 */ { XXHL64_default_avx512, XXHL64_seed_avx512, XXHL64_secret_avx512, XXH3_64bits_update_avx512 }
+#else
+ /* avx512 */ { NULL, NULL, NULL, NULL }
+#endif
+};
+
+typedef XXH128_hash_t (*XXH3_dispatchx86_hashLong128_default)(const void* XXH_RESTRICT, size_t);
+
+typedef XXH128_hash_t (*XXH3_dispatchx86_hashLong128_withSeed)(const void* XXH_RESTRICT, size_t, XXH64_hash_t);
+
+typedef XXH128_hash_t (*XXH3_dispatchx86_hashLong128_withSecret)(const void* XXH_RESTRICT, size_t, const void* XXH_RESTRICT, size_t);
+
+typedef struct {
+ XXH3_dispatchx86_hashLong128_default hashLong128_default;
+ XXH3_dispatchx86_hashLong128_withSeed hashLong128_seed;
+ XXH3_dispatchx86_hashLong128_withSecret hashLong128_secret;
+ XXH3_dispatchx86_update update;
+} dispatch128Functions_s;
+
+static dispatch128Functions_s g_dispatch128 = { NULL, NULL, NULL, NULL };
+
+static const dispatch128Functions_s k_dispatch128[NB_DISPATCHES] = {
+ /* scalar */ { XXHL128_default_scalar, XXHL128_seed_scalar, XXHL128_secret_scalar, XXH3_128bits_update_scalar },
+ /* sse2 */ { XXHL128_default_sse2, XXHL128_seed_sse2, XXHL128_secret_sse2, XXH3_128bits_update_sse2 },
+#ifdef XXH_DISPATCH_AVX2
+ /* avx2 */ { XXHL128_default_avx2, XXHL128_seed_avx2, XXHL128_secret_avx2, XXH3_128bits_update_avx2 },
+#else
+ /* avx2 */ { NULL, NULL, NULL, NULL },
+#endif
+#ifdef XXH_DISPATCH_AVX512
+ /* avx512 */ { XXHL128_default_avx512, XXHL128_seed_avx512, XXHL128_secret_avx512, XXH3_128bits_update_avx512 }
+#else
+ /* avx512 */ { NULL, NULL, NULL, NULL }
+#endif
+};
+
+static void setDispatch(void)
+{
+ int vecID = XXH_featureTest();
+ XXH_STATIC_ASSERT(XXH_AVX512 == NB_DISPATCHES-1);
+ assert(XXH_SCALAR <= vecID && vecID <= XXH_AVX512);
+#ifndef XXH_DISPATCH_AVX512
+ assert(vecID != XXH_AVX512);
+#endif
+#ifndef XXH_DISPATCH_AVX2
+ assert(vecID != XXH_AVX2);
+#endif
+ g_dispatch = k_dispatch[vecID];
+ g_dispatch128 = k_dispatch128[vecID];
+}
+
+
+/* ==== XXH3 public functions ==== */
+
+static XXH64_hash_t
+XXH3_hashLong_64b_defaultSecret_selection(const void* input, size_t len,
+ XXH64_hash_t seed64, const xxh_u8* secret, size_t secretLen)
+{
+ (void)seed64; (void)secret; (void)secretLen;
+ if (g_dispatch.hashLong64_default == NULL) setDispatch();
+ return g_dispatch.hashLong64_default(input, len);
+}
+
+XXH64_hash_t XXH3_64bits_dispatch(const void* input, size_t len)
+{
+ return XXH3_64bits_internal(input, len, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_defaultSecret_selection);
+}
+
+static XXH64_hash_t
+XXH3_hashLong_64b_withSeed_selection(const void* input, size_t len,
+ XXH64_hash_t seed64, const xxh_u8* secret, size_t secretLen)
+{
+ (void)secret; (void)secretLen;
+ if (g_dispatch.hashLong64_seed == NULL) setDispatch();
+ return g_dispatch.hashLong64_seed(input, len, seed64);
+}
+
+XXH64_hash_t XXH3_64bits_withSeed_dispatch(const void* input, size_t len, XXH64_hash_t seed)
+{
+ return XXH3_64bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_withSeed_selection);
+}
+
+static XXH64_hash_t
+XXH3_hashLong_64b_withSecret_selection(const void* input, size_t len,
+ XXH64_hash_t seed64, const xxh_u8* secret, size_t secretLen)
+{
+ (void)seed64;
+ if (g_dispatch.hashLong64_secret == NULL) setDispatch();
+ return g_dispatch.hashLong64_secret(input, len, secret, secretLen);
+}
+
+XXH64_hash_t XXH3_64bits_withSecret_dispatch(const void* input, size_t len, const void* secret, size_t secretLen)
+{
+ return XXH3_64bits_internal(input, len, 0, secret, secretLen, XXH3_hashLong_64b_withSecret_selection);
+}
+
+XXH_errorcode
+XXH3_64bits_update_dispatch(XXH3_state_t* state, const void* input, size_t len)
+{
+ if (g_dispatch.update == NULL) setDispatch();
+ return g_dispatch.update(state, (const xxh_u8*)input, len);
+}
+
+
+/* ==== XXH128 public functions ==== */
+
+static XXH128_hash_t
+XXH3_hashLong_128b_defaultSecret_selection(const void* input, size_t len,
+ XXH64_hash_t seed64, const void* secret, size_t secretLen)
+{
+ (void)seed64; (void)secret; (void)secretLen;
+ if (g_dispatch128.hashLong128_default == NULL) setDispatch();
+ return g_dispatch128.hashLong128_default(input, len);
+}
+
+XXH128_hash_t XXH3_128bits_dispatch(const void* input, size_t len)
+{
+ return XXH3_128bits_internal(input, len, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_128b_defaultSecret_selection);
+}
+
+static XXH128_hash_t
+XXH3_hashLong_128b_withSeed_selection(const void* input, size_t len,
+ XXH64_hash_t seed64, const void* secret, size_t secretLen)
+{
+ (void)secret; (void)secretLen;
+ if (g_dispatch128.hashLong128_seed == NULL) setDispatch();
+ return g_dispatch128.hashLong128_seed(input, len, seed64);
+}
+
+XXH128_hash_t XXH3_128bits_withSeed_dispatch(const void* input, size_t len, XXH64_hash_t seed)
+{
+ return XXH3_128bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_128b_withSeed_selection);
+}
+
+static XXH128_hash_t
+XXH3_hashLong_128b_withSecret_selection(const void* input, size_t len,
+ XXH64_hash_t seed64, const void* secret, size_t secretLen)
+{
+ (void)seed64;
+ if (g_dispatch128.hashLong128_secret == NULL) setDispatch();
+ return g_dispatch128.hashLong128_secret(input, len, secret, secretLen);
+}
+
+XXH128_hash_t XXH3_128bits_withSecret_dispatch(const void* input, size_t len, const void* secret, size_t secretLen)
+{
+ return XXH3_128bits_internal(input, len, 0, secret, secretLen, XXH3_hashLong_128b_withSecret_selection);
+}
+
+XXH_errorcode
+XXH3_128bits_update_dispatch(XXH3_state_t* state, const void* input, size_t len)
+{
+ if (g_dispatch128.update == NULL) setDispatch();
+ return g_dispatch128.update(state, (const xxh_u8*)input, len);
+}
+
+#if defined (__cplusplus)
+}
+#endif
--- /dev/null
+/*
+ * xxHash - XXH3 Dispatcher for x86-based targets
+ * Copyright (C) 2020 Yann Collet
+ *
+ * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You can contact the author at:
+ * - xxHash homepage: https://www.xxhash.com
+ * - xxHash source repository: https://github.com/Cyan4973/xxHash
+ */
+
+#ifndef XXH_X86DISPATCH_H_13563687684
+#define XXH_X86DISPATCH_H_13563687684
+
+#include "xxhash.h" /* XXH64_hash_t, XXH3_state_t */
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_dispatch(const void* input, size_t len);
+XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSeed_dispatch(const void* input, size_t len, XXH64_hash_t seed);
+XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret_dispatch(const void* input, size_t len, const void* secret, size_t secretLen);
+XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update_dispatch(XXH3_state_t* state, const void* input, size_t len);
+
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_dispatch(const void* input, size_t len);
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSeed_dispatch(const void* input, size_t len, XXH64_hash_t seed);
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecret_dispatch(const void* input, size_t len, const void* secret, size_t secretLen);
+XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update_dispatch(XXH3_state_t* state, const void* input, size_t len);
+
+#if defined (__cplusplus)
+}
+#endif
+
+
+/* automatic replacement of XXH3 functions.
+ * can be disabled by setting XXH_DISPATCH_DISABLE_REPLACE */
+#ifndef XXH_DISPATCH_DISABLE_REPLACE
+
+# undef XXH3_64bits
+# define XXH3_64bits XXH3_64bits_dispatch
+# undef XXH3_64bits_withSeed
+# define XXH3_64bits_withSeed XXH3_64bits_withSeed_dispatch
+# undef XXH3_64bits_withSecret
+# define XXH3_64bits_withSecret XXH3_64bits_withSecret_dispatch
+# undef XXH3_64bits_update
+# define XXH3_64bits_update XXH3_64bits_update_dispatch
+
+# undef XXH128
+# define XXH128 XXH3_128bits_withSeed_dispatch
+# define XXH3_128bits XXH3_128bits_dispatch
+# undef XXH3_128bits
+# define XXH3_128bits XXH3_128bits_dispatch
+# undef XXH3_128bits_withSeed
+# define XXH3_128bits_withSeed XXH3_128bits_withSeed_dispatch
+# undef XXH3_128bits_withSecret
+# define XXH3_128bits_withSecret XXH3_128bits_withSecret_dispatch
+# undef XXH3_128bits_update
+# define XXH3_128bits_update XXH3_128bits_update_dispatch
+
+#endif /* XXH_DISPATCH_DISABLE_REPLACE */
+
+#endif /* XXH_X86DISPATCH_H_13563687684 */
--- /dev/null
+/*
+ * xxHash - Extremely Fast Hash algorithm
+ * Copyright (C) 2012-2020 Yann Collet
+ *
+ * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You can contact the author at:
+ * - xxHash homepage: https://www.xxhash.com
+ * - xxHash source repository: https://github.com/Cyan4973/xxHash
+ */
+
+
+/*
+ * xxhash.c instantiates functions defined in xxhash.h
+ */
+
+#define XXH_STATIC_LINKING_ONLY /* access advanced declarations */
+#define XXH_IMPLEMENTATION /* access definitions */
+
+#include "xxhash.h"
--- /dev/null
+/*
+ * xxHash - Extremely Fast Hash algorithm
+ * Header File
+ * Copyright (C) 2012-2020 Yann Collet
+ *
+ * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You can contact the author at:
+ * - xxHash homepage: https://www.xxhash.com
+ * - xxHash source repository: https://github.com/Cyan4973/xxHash
+ */
+
+/* TODO: update */
+/* Notice extracted from xxHash homepage:
+
+xxHash is an extremely fast hash algorithm, running at RAM speed limits.
+It also successfully passes all tests from the SMHasher suite.
+
+Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz)
+
+Name Speed Q.Score Author
+xxHash 5.4 GB/s 10
+CrapWow 3.2 GB/s 2 Andrew
+MumurHash 3a 2.7 GB/s 10 Austin Appleby
+SpookyHash 2.0 GB/s 10 Bob Jenkins
+SBox 1.4 GB/s 9 Bret Mulvey
+Lookup3 1.2 GB/s 9 Bob Jenkins
+SuperFastHash 1.2 GB/s 1 Paul Hsieh
+CityHash64 1.05 GB/s 10 Pike & Alakuijala
+FNV 0.55 GB/s 5 Fowler, Noll, Vo
+CRC32 0.43 GB/s 9
+MD5-32 0.33 GB/s 10 Ronald L. Rivest
+SHA1-32 0.28 GB/s 10
+
+Q.Score is a measure of quality of the hash function.
+It depends on successfully passing SMHasher test set.
+10 is a perfect score.
+
+Note: SMHasher's CRC32 implementation is not the fastest one.
+Other speed-oriented implementations can be faster,
+especially in combination with PCLMUL instruction:
+https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html?showComment=1552696407071#c3490092340461170735
+
+A 64-bit version, named XXH64, is available since r35.
+It offers much better speed, but for 64-bit applications only.
+Name Speed on 64 bits Speed on 32 bits
+XXH64 13.8 GB/s 1.9 GB/s
+XXH32 6.8 GB/s 6.0 GB/s
+*/
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+/* ****************************
+ * INLINE mode
+ ******************************/
+/*!
+ * XXH_INLINE_ALL (and XXH_PRIVATE_API)
+ * Use these build macros to inline xxhash into the target unit.
+ * Inlining improves performance on small inputs, especially when the length is
+ * expressed as a compile-time constant:
+ *
+ * https://fastcompression.blogspot.com/2018/03/xxhash-for-small-keys-impressive-power.html
+ *
+ * It also keeps xxHash symbols private to the unit, so they are not exported.
+ *
+ * Usage:
+ * #define XXH_INLINE_ALL
+ * #include "xxhash.h"
+ *
+ * Do not compile and link xxhash.o as a separate object, as it is not useful.
+ */
+#if (defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)) \
+ && !defined(XXH_INLINE_ALL_31684351384)
+ /* this section should be traversed only once */
+# define XXH_INLINE_ALL_31684351384
+ /* give access to the advanced API, required to compile implementations */
+# undef XXH_STATIC_LINKING_ONLY /* avoid macro redef */
+# define XXH_STATIC_LINKING_ONLY
+ /* make all functions private */
+# undef XXH_PUBLIC_API
+# if defined(__GNUC__)
+# define XXH_PUBLIC_API static __inline __attribute__((unused))
+# elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
+# define XXH_PUBLIC_API static inline
+# elif defined(_MSC_VER)
+# define XXH_PUBLIC_API static __inline
+# else
+ /* note: this version may generate warnings for unused static functions */
+# define XXH_PUBLIC_API static
+# endif
+
+ /*
+ * This part deals with the special case where a unit wants to inline xxHash,
+ * but "xxhash.h" has previously been included without XXH_INLINE_ALL, such
+ * as part of some previously included *.h header file.
+ * Without further action, the new include would just be ignored,
+ * and functions would effectively _not_ be inlined (silent failure).
+ * The following macros solve this situation by prefixing all inlined names,
+ * avoiding naming collision with previous inclusions.
+ */
+# ifdef XXH_NAMESPACE
+# error "XXH_INLINE_ALL with XXH_NAMESPACE is not supported"
+ /*
+ * Note: Alternative: #undef all symbols (it's a pretty large list).
+ * Without #error: it compiles, but functions are actually not inlined.
+ */
+# endif
+# define XXH_NAMESPACE XXH_INLINE_
+ /*
+ * Some identifiers (enums, type names) are not symbols, but they must
+ * still be renamed to avoid redeclaration.
+ * Alternative solution: do not redeclare them.
+ * However, this requires some #ifdefs, and is a more dispersed action.
+ * Meanwhile, renaming can be achieved in a single block
+ */
+# define XXH_IPREF(Id) XXH_INLINE_ ## Id
+# define XXH_OK XXH_IPREF(XXH_OK)
+# define XXH_ERROR XXH_IPREF(XXH_ERROR)
+# define XXH_errorcode XXH_IPREF(XXH_errorcode)
+# define XXH32_canonical_t XXH_IPREF(XXH32_canonical_t)
+# define XXH64_canonical_t XXH_IPREF(XXH64_canonical_t)
+# define XXH128_canonical_t XXH_IPREF(XXH128_canonical_t)
+# define XXH32_state_s XXH_IPREF(XXH32_state_s)
+# define XXH32_state_t XXH_IPREF(XXH32_state_t)
+# define XXH64_state_s XXH_IPREF(XXH64_state_s)
+# define XXH64_state_t XXH_IPREF(XXH64_state_t)
+# define XXH3_state_s XXH_IPREF(XXH3_state_s)
+# define XXH3_state_t XXH_IPREF(XXH3_state_t)
+# define XXH128_hash_t XXH_IPREF(XXH128_hash_t)
+ /* Ensure the header is parsed again, even if it was previously included */
+# undef XXHASH_H_5627135585666179
+# undef XXHASH_H_STATIC_13879238742
+#endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */
+
+
+
+/* ****************************************************************
+ * Stable API
+ *****************************************************************/
+#ifndef XXHASH_H_5627135585666179
+#define XXHASH_H_5627135585666179 1
+
+/* specific declaration modes for Windows */
+#if !defined(XXH_INLINE_ALL) && !defined(XXH_PRIVATE_API)
+# if defined(WIN32) && defined(_MSC_VER) && (defined(XXH_IMPORT) || defined(XXH_EXPORT))
+# ifdef XXH_EXPORT
+# define XXH_PUBLIC_API __declspec(dllexport)
+# elif XXH_IMPORT
+# define XXH_PUBLIC_API __declspec(dllimport)
+# endif
+# else
+# define XXH_PUBLIC_API /* do nothing */
+# endif
+#endif
+
+/*!
+ * XXH_NAMESPACE, aka Namespace Emulation:
+ *
+ * If you want to include _and expose_ xxHash functions from within your own
+ * library, but also want to avoid symbol collisions with other libraries which
+ * may also include xxHash, you can use XXH_NAMESPACE to automatically prefix
+ * any public symbol from xxhash library with the value of XXH_NAMESPACE
+ * (therefore, avoid empty or numeric values).
+ *
+ * Note that no change is required within the calling program as long as it
+ * includes `xxhash.h`: Regular symbol names will be automatically translated
+ * by this header.
+ */
+#ifdef XXH_NAMESPACE
+# define XXH_CAT(A,B) A##B
+# define XXH_NAME2(A,B) XXH_CAT(A,B)
+# define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber)
+/* XXH32 */
+# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32)
+# define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState)
+# define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState)
+# define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset)
+# define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update)
+# define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest)
+# define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState)
+# define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash)
+# define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical)
+/* XXH64 */
+# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64)
+# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState)
+# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState)
+# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset)
+# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update)
+# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest)
+# define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState)
+# define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash)
+# define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical)
+/* XXH3_64bits */
+# define XXH3_64bits XXH_NAME2(XXH_NAMESPACE, XXH3_64bits)
+# define XXH3_64bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecret)
+# define XXH3_64bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSeed)
+# define XXH3_createState XXH_NAME2(XXH_NAMESPACE, XXH3_createState)
+# define XXH3_freeState XXH_NAME2(XXH_NAMESPACE, XXH3_freeState)
+# define XXH3_copyState XXH_NAME2(XXH_NAMESPACE, XXH3_copyState)
+# define XXH3_64bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset)
+# define XXH3_64bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSeed)
+# define XXH3_64bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecret)
+# define XXH3_64bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_update)
+# define XXH3_64bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_digest)
+# define XXH3_generateSecret XXH_NAME2(XXH_NAMESPACE, XXH3_generateSecret)
+/* XXH3_128bits */
+# define XXH128 XXH_NAME2(XXH_NAMESPACE, XXH128)
+# define XXH3_128bits XXH_NAME2(XXH_NAMESPACE, XXH3_128bits)
+# define XXH3_128bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSeed)
+# define XXH3_128bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecret)
+# define XXH3_128bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset)
+# define XXH3_128bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSeed)
+# define XXH3_128bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecret)
+# define XXH3_128bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_update)
+# define XXH3_128bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_digest)
+# define XXH128_isEqual XXH_NAME2(XXH_NAMESPACE, XXH128_isEqual)
+# define XXH128_cmp XXH_NAME2(XXH_NAMESPACE, XXH128_cmp)
+# define XXH128_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH128_canonicalFromHash)
+# define XXH128_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH128_hashFromCanonical)
+#endif
+
+
+/* *************************************
+* Version
+***************************************/
+#define XXH_VERSION_MAJOR 0
+#define XXH_VERSION_MINOR 8
+#define XXH_VERSION_RELEASE 0
+#define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE)
+XXH_PUBLIC_API unsigned XXH_versionNumber (void);
+
+
+/* ****************************
+* Definitions
+******************************/
+#include <stddef.h> /* size_t */
+typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;
+
+
+/*-**********************************************************************
+* 32-bit hash
+************************************************************************/
+#if !defined (__VMS) \
+ && (defined (__cplusplus) \
+ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
+# include <stdint.h>
+ typedef uint32_t XXH32_hash_t;
+#else
+# include <limits.h>
+# if UINT_MAX == 0xFFFFFFFFUL
+ typedef unsigned int XXH32_hash_t;
+# else
+# if ULONG_MAX == 0xFFFFFFFFUL
+ typedef unsigned long XXH32_hash_t;
+# else
+# error "unsupported platform: need a 32-bit type"
+# endif
+# endif
+#endif
+
+/*!
+ * XXH32():
+ * Calculate the 32-bit hash of sequence "length" bytes stored at memory address "input".
+ * The memory between input & input+length must be valid (allocated and read-accessible).
+ * "seed" can be used to alter the result predictably.
+ * Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark): 5.4 GB/s
+ *
+ * Note: XXH3 provides competitive speed for both 32-bit and 64-bit systems,
+ * and offers true 64/128 bit hash results. It provides a superior level of
+ * dispersion, and greatly reduces the risks of collisions.
+ */
+XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, XXH32_hash_t seed);
+
+/******* Streaming *******/
+
+/*
+ * Streaming functions generate the xxHash value from an incrememtal input.
+ * This method is slower than single-call functions, due to state management.
+ * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized.
+ *
+ * An XXH state must first be allocated using `XXH*_createState()`.
+ *
+ * Start a new hash by initializing the state with a seed using `XXH*_reset()`.
+ *
+ * Then, feed the hash state by calling `XXH*_update()` as many times as necessary.
+ *
+ * The function returns an error code, with 0 meaning OK, and any other value
+ * meaning there is an error.
+ *
+ * Finally, a hash value can be produced anytime, by using `XXH*_digest()`.
+ * This function returns the nn-bits hash as an int or long long.
+ *
+ * It's still possible to continue inserting input into the hash state after a
+ * digest, and generate new hash values later on by invoking `XXH*_digest()`.
+ *
+ * When done, release the state using `XXH*_freeState()`.
+ */
+
+typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */
+XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void);
+XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr);
+XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state);
+
+XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, XXH32_hash_t seed);
+XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length);
+XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr);
+
+/******* Canonical representation *******/
+
+/*
+ * The default return values from XXH functions are unsigned 32 and 64 bit
+ * integers.
+ * This the simplest and fastest format for further post-processing.
+ *
+ * However, this leaves open the question of what is the order on the byte level,
+ * since little and big endian conventions will store the same number differently.
+ *
+ * The canonical representation settles this issue by mandating big-endian
+ * convention, the same convention as human-readable numbers (large digits first).
+ *
+ * When writing hash values to storage, sending them over a network, or printing
+ * them, it's highly recommended to use the canonical representation to ensure
+ * portability across a wider range of systems, present and future.
+ *
+ * The following functions allow transformation of hash values to and from
+ * canonical format.
+ */
+
+typedef struct { unsigned char digest[4]; } XXH32_canonical_t;
+XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash);
+XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src);
+
+
+#ifndef XXH_NO_LONG_LONG
+/*-**********************************************************************
+* 64-bit hash
+************************************************************************/
+#if !defined (__VMS) \
+ && (defined (__cplusplus) \
+ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
+# include <stdint.h>
+ typedef uint64_t XXH64_hash_t;
+#else
+ /* the following type must have a width of 64-bit */
+ typedef unsigned long long XXH64_hash_t;
+#endif
+
+/*!
+ * XXH64():
+ * Returns the 64-bit hash of sequence of length @length stored at memory
+ * address @input.
+ * @seed can be used to alter the result predictably.
+ *
+ * This function usually runs faster on 64-bit systems, but slower on 32-bit
+ * systems (see benchmark).
+ *
+ * Note: XXH3 provides competitive speed for both 32-bit and 64-bit systems,
+ * and offers true 64/128 bit hash results. It provides a superior level of
+ * dispersion, and greatly reduces the risks of collisions.
+ */
+XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, XXH64_hash_t seed);
+
+/******* Streaming *******/
+typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */
+XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void);
+XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr);
+XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state);
+
+XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, XXH64_hash_t seed);
+XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length);
+XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr);
+
+/******* Canonical representation *******/
+typedef struct { unsigned char digest[sizeof(XXH64_hash_t)]; } XXH64_canonical_t;
+XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash);
+XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src);
+
+
+/*-**********************************************************************
+* XXH3 64-bit variant
+************************************************************************/
+
+/* ************************************************************************
+ * XXH3 is a new hash algorithm featuring:
+ * - Improved speed for both small and large inputs
+ * - True 64-bit and 128-bit outputs
+ * - SIMD acceleration
+ * - Improved 32-bit viability
+ *
+ * Speed analysis methodology is explained here:
+ *
+ * https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html
+ *
+ * In general, expect XXH3 to run about ~2x faster on large inputs and >3x
+ * faster on small ones compared to XXH64, though exact differences depend on
+ * the platform.
+ *
+ * The algorithm is portable: Like XXH32 and XXH64, it generates the same hash
+ * on all platforms.
+ *
+ * It benefits greatly from SIMD and 64-bit arithmetic, but does not require it.
+ *
+ * Almost all 32-bit and 64-bit targets that can run XXH32 smoothly can run
+ * XXH3 at competitive speeds, even if XXH64 runs slowly. Further details are
+ * explained in the implementation.
+ *
+ * Optimized implementations are provided for AVX512, AVX2, SSE2, NEON, POWER8,
+ * ZVector and scalar targets. This can be controlled with the XXH_VECTOR macro.
+ *
+ * XXH3 offers 2 variants, _64bits and _128bits.
+ * When only 64 bits are needed, prefer calling the _64bits variant, as it
+ * reduces the amount of mixing, resulting in faster speed on small inputs.
+ *
+ * It's also generally simpler to manipulate a scalar return type than a struct.
+ *
+ * The 128-bit version adds additional strength, but it is slightly slower.
+ *
+ * The XXH3 algorithm is still in development.
+ * The results it produces may still change in future versions.
+ *
+ * Results produced by v0.7.x are not comparable with results from v0.7.y.
+ * However, the API is completely stable, and it can safely be used for
+ * ephemeral data (local sessions).
+ *
+ * Avoid storing values in long-term storage until the algorithm is finalized.
+ * XXH3's return values will be officially finalized upon reaching v0.8.0.
+ *
+ * After which, return values of XXH3 and XXH128 will no longer change in
+ * future versions.
+ *
+ * The API supports one-shot hashing, streaming mode, and custom secrets.
+ */
+
+/* XXH3_64bits():
+ * default 64-bit variant, using default secret and default seed of 0.
+ * It's the fastest variant. */
+XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* data, size_t len);
+
+/*
+ * XXH3_64bits_withSeed():
+ * This variant generates a custom secret on the fly
+ * based on default secret altered using the `seed` value.
+ * While this operation is decently fast, note that it's not completely free.
+ * Note: seed==0 produces the same results as XXH3_64bits().
+ */
+XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSeed(const void* data, size_t len, XXH64_hash_t seed);
+
+/*
+ * XXH3_64bits_withSecret():
+ * It's possible to provide any blob of bytes as a "secret" to generate the hash.
+ * This makes it more difficult for an external actor to prepare an intentional collision.
+ * The main condition is that secretSize *must* be large enough (>= XXH3_SECRET_SIZE_MIN).
+ * However, the quality of produced hash values depends on secret's entropy.
+ * Technically, the secret must look like a bunch of random bytes.
+ * Avoid "trivial" or structured data such as repeated sequences or a text document.
+ * Whenever unsure about the "randomness" of the blob of bytes,
+ * consider relabelling it as a "custom seed" instead,
+ * and employ "XXH3_generateSecret()" (see below)
+ * to generate a high entropy secret derived from the custom seed.
+ */
+#define XXH3_SECRET_SIZE_MIN 136
+XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize);
+
+
+/******* Streaming *******/
+/*
+ * Streaming requires state maintenance.
+ * This operation costs memory and CPU.
+ * As a consequence, streaming is slower than one-shot hashing.
+ * For better performance, prefer one-shot functions whenever applicable.
+ */
+typedef struct XXH3_state_s XXH3_state_t;
+XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void);
+XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr);
+XXH_PUBLIC_API void XXH3_copyState(XXH3_state_t* dst_state, const XXH3_state_t* src_state);
+
+/*
+ * XXH3_64bits_reset():
+ * Initialize with default parameters.
+ * digest will be equivalent to `XXH3_64bits()`.
+ */
+XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH3_state_t* statePtr);
+/*
+ * XXH3_64bits_reset_withSeed():
+ * Generate a custom secret from `seed`, and store it into `statePtr`.
+ * digest will be equivalent to `XXH3_64bits_withSeed()`.
+ */
+XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed);
+/*
+ * XXH3_64bits_reset_withSecret():
+ * `secret` is referenced, it _must outlive_ the hash streaming session.
+ * Similar to one-shot API, `secretSize` must be >= `XXH3_SECRET_SIZE_MIN`,
+ * and the quality of produced hash values depends on secret's entropy
+ * (secret's content should look like a bunch of random bytes).
+ * When in doubt about the randomness of a candidate `secret`,
+ * consider employing `XXH3_generateSecret()` instead (see below).
+ */
+XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize);
+
+XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update (XXH3_state_t* statePtr, const void* input, size_t length);
+XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* statePtr);
+
+/* note : canonical representation of XXH3 is the same as XXH64
+ * since they both produce XXH64_hash_t values */
+
+
+/*-**********************************************************************
+* XXH3 128-bit variant
+************************************************************************/
+
+typedef struct {
+ XXH64_hash_t low64;
+ XXH64_hash_t high64;
+} XXH128_hash_t;
+
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void* data, size_t len);
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSeed(const void* data, size_t len, XXH64_hash_t seed);
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize);
+
+/******* Streaming *******/
+/*
+ * Streaming requires state maintenance.
+ * This operation costs memory and CPU.
+ * As a consequence, streaming is slower than one-shot hashing.
+ * For better performance, prefer one-shot functions whenever applicable.
+ *
+ * XXH3_128bits uses the same XXH3_state_t as XXH3_64bits().
+ * Use already declared XXH3_createState() and XXH3_freeState().
+ *
+ * All reset and streaming functions have same meaning as their 64-bit counterpart.
+ */
+
+XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH3_state_t* statePtr);
+XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed);
+XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize);
+
+XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update (XXH3_state_t* statePtr, const void* input, size_t length);
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (const XXH3_state_t* statePtr);
+
+/* Following helper functions make it possible to compare XXH128_hast_t values.
+ * Since XXH128_hash_t is a structure, this capability is not offered by the language.
+ * Note: For better performance, these functions can be inlined using XXH_INLINE_ALL */
+
+/*!
+ * XXH128_isEqual():
+ * Return: 1 if `h1` and `h2` are equal, 0 if they are not.
+ */
+XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2);
+
+/*!
+ * XXH128_cmp():
+ *
+ * This comparator is compatible with stdlib's `qsort()`/`bsearch()`.
+ *
+ * return: >0 if *h128_1 > *h128_2
+ * =0 if *h128_1 == *h128_2
+ * <0 if *h128_1 < *h128_2
+ */
+XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2);
+
+
+/******* Canonical representation *******/
+typedef struct { unsigned char digest[sizeof(XXH128_hash_t)]; } XXH128_canonical_t;
+XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash);
+XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(const XXH128_canonical_t* src);
+
+
+#endif /* XXH_NO_LONG_LONG */
+
+#endif /* XXHASH_H_5627135585666179 */
+
+
+
+#if defined(XXH_STATIC_LINKING_ONLY) && !defined(XXHASH_H_STATIC_13879238742)
+#define XXHASH_H_STATIC_13879238742
+/* ****************************************************************************
+ * This section contains declarations which are not guaranteed to remain stable.
+ * They may change in future versions, becoming incompatible with a different
+ * version of the library.
+ * These declarations should only be used with static linking.
+ * Never use them in association with dynamic linking!
+ ***************************************************************************** */
+
+/*
+ * These definitions are only present to allow static allocation
+ * of XXH states, on stack or in a struct, for example.
+ * Never **ever** access their members directly.
+ */
+
+struct XXH32_state_s {
+ XXH32_hash_t total_len_32;
+ XXH32_hash_t large_len;
+ XXH32_hash_t v1;
+ XXH32_hash_t v2;
+ XXH32_hash_t v3;
+ XXH32_hash_t v4;
+ XXH32_hash_t mem32[4];
+ XXH32_hash_t memsize;
+ XXH32_hash_t reserved; /* never read nor write, might be removed in a future version */
+}; /* typedef'd to XXH32_state_t */
+
+
+#ifndef XXH_NO_LONG_LONG /* defined when there is no 64-bit support */
+
+struct XXH64_state_s {
+ XXH64_hash_t total_len;
+ XXH64_hash_t v1;
+ XXH64_hash_t v2;
+ XXH64_hash_t v3;
+ XXH64_hash_t v4;
+ XXH64_hash_t mem64[4];
+ XXH32_hash_t memsize;
+ XXH32_hash_t reserved32; /* required for padding anyway */
+ XXH64_hash_t reserved64; /* never read nor write, might be removed in a future version */
+}; /* typedef'd to XXH64_state_t */
+
+#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11+ */
+# include <stdalign.h>
+# define XXH_ALIGN(n) alignas(n)
+#elif defined(__GNUC__)
+# define XXH_ALIGN(n) __attribute__ ((aligned(n)))
+#elif defined(_MSC_VER)
+# define XXH_ALIGN(n) __declspec(align(n))
+#else
+# define XXH_ALIGN(n) /* disabled */
+#endif
+
+/* Old GCC versions only accept the attribute after the type in structures. */
+#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) /* C11+ */ \
+ && defined(__GNUC__)
+# define XXH_ALIGN_MEMBER(align, type) type XXH_ALIGN(align)
+#else
+# define XXH_ALIGN_MEMBER(align, type) XXH_ALIGN(align) type
+#endif
+
+#define XXH3_INTERNALBUFFER_SIZE 256
+#define XXH3_SECRET_DEFAULT_SIZE 192
+struct XXH3_state_s {
+ XXH_ALIGN_MEMBER(64, XXH64_hash_t acc[8]);
+ /* used to store a custom secret generated from a seed */
+ XXH_ALIGN_MEMBER(64, unsigned char customSecret[XXH3_SECRET_DEFAULT_SIZE]);
+ XXH_ALIGN_MEMBER(64, unsigned char buffer[XXH3_INTERNALBUFFER_SIZE]);
+ XXH32_hash_t bufferedSize;
+ XXH32_hash_t reserved32;
+ size_t nbStripesSoFar;
+ XXH64_hash_t totalLen;
+ size_t nbStripesPerBlock;
+ size_t secretLimit;
+ XXH64_hash_t seed;
+ XXH64_hash_t reserved64;
+ const unsigned char* extSecret; /* reference to external secret;
+ * if == NULL, use .customSecret instead */
+ /* note: there may be some padding at the end due to alignment on 64 bytes */
+}; /* typedef'd to XXH3_state_t */
+
+#undef XXH_ALIGN_MEMBER
+
+/* When the XXH3_state_t structure is merely emplaced on stack,
+ * it should be initialized with XXH3_INITSTATE() or a memset()
+ * in case its first reset uses XXH3_NNbits_reset_withSeed().
+ * This init can be omitted if the first reset uses default or _withSecret mode.
+ * This operation isn't necessary when the state is created with XXH3_createState().
+ * Note that this doesn't prepare the state for a streaming operation,
+ * it's still necessary to use XXH3_NNbits_reset*() afterwards.
+ */
+#define XXH3_INITSTATE(XXH3_state_ptr) { (XXH3_state_ptr)->seed = 0; }
+
+
+/* === Experimental API === */
+/* Symbols defined below must be considered tied to a specific library version. */
+
+/*
+ * XXH3_generateSecret():
+ *
+ * Derive a high-entropy secret from any user-defined content, named customSeed.
+ * The generated secret can be used in combination with `*_withSecret()` functions.
+ * The `_withSecret()` variants are useful to provide a higher level of protection than 64-bit seed,
+ * as it becomes much more difficult for an external actor to guess how to impact the calculation logic.
+ *
+ * The function accepts as input a custom seed of any length and any content,
+ * and derives from it a high-entropy secret of length XXH3_SECRET_DEFAULT_SIZE
+ * into an already allocated buffer secretBuffer.
+ * The generated secret is _always_ XXH_SECRET_DEFAULT_SIZE bytes long.
+ *
+ * The generated secret can then be used with any `*_withSecret()` variant.
+ * Functions `XXH3_128bits_withSecret()`, `XXH3_64bits_withSecret()`,
+ * `XXH3_128bits_reset_withSecret()` and `XXH3_64bits_reset_withSecret()`
+ * are part of this list. They all accept a `secret` parameter
+ * which must be very long for implementation reasons (>= XXH3_SECRET_SIZE_MIN)
+ * _and_ feature very high entropy (consist of random-looking bytes).
+ * These conditions can be a high bar to meet, so
+ * this function can be used to generate a secret of proper quality.
+ *
+ * customSeed can be anything. It can have any size, even small ones,
+ * and its content can be anything, even stupidly "low entropy" source such as a bunch of zeroes.
+ * The resulting `secret` will nonetheless provide all expected qualities.
+ *
+ * Supplying NULL as the customSeed copies the default secret into `secretBuffer`.
+ * When customSeedSize > 0, supplying NULL as customSeed is undefined behavior.
+ */
+XXH_PUBLIC_API void XXH3_generateSecret(void* secretBuffer, const void* customSeed, size_t customSeedSize);
+
+
+/* simple short-cut to pre-selected XXH3_128bits variant */
+XXH_PUBLIC_API XXH128_hash_t XXH128(const void* data, size_t len, XXH64_hash_t seed);
+
+
+#endif /* XXH_NO_LONG_LONG */
+
+
+#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)
+# define XXH_IMPLEMENTATION
+#endif
+
+#endif /* defined(XXH_STATIC_LINKING_ONLY) && !defined(XXHASH_H_STATIC_13879238742) */
+
+
+/* ======================================================================== */
+/* ======================================================================== */
+/* ======================================================================== */
+
+
+/*-**********************************************************************
+ * xxHash implementation
+ *-**********************************************************************
+ * xxHash's implementation used to be hosted inside xxhash.c.
+ *
+ * However, inlining requires implementation to be visible to the compiler,
+ * hence be included alongside the header.
+ * Previously, implementation was hosted inside xxhash.c,
+ * which was then #included when inlining was activated.
+ * This construction created issues with a few build and install systems,
+ * as it required xxhash.c to be stored in /include directory.
+ *
+ * xxHash implementation is now directly integrated within xxhash.h.
+ * As a consequence, xxhash.c is no longer needed in /include.
+ *
+ * xxhash.c is still available and is still useful.
+ * In a "normal" setup, when xxhash is not inlined,
+ * xxhash.h only exposes the prototypes and public symbols,
+ * while xxhash.c can be built into an object file xxhash.o
+ * which can then be linked into the final binary.
+ ************************************************************************/
+
+#if ( defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) \
+ || defined(XXH_IMPLEMENTATION) ) && !defined(XXH_IMPLEM_13a8737387)
+# define XXH_IMPLEM_13a8737387
+
+/* *************************************
+* Tuning parameters
+***************************************/
+/*!
+ * XXH_FORCE_MEMORY_ACCESS:
+ * By default, access to unaligned memory is controlled by `memcpy()`, which is
+ * safe and portable.
+ *
+ * Unfortunately, on some target/compiler combinations, the generated assembly
+ * is sub-optimal.
+ *
+ * The below switch allow selection of a different access method
+ * in the search for improved performance.
+ * Method 0 (default):
+ * Use `memcpy()`. Safe and portable. Default.
+ * Method 1:
+ * `__attribute__((packed))` statement. It depends on compiler extensions
+ * and is therefore not portable.
+ * This method is safe if your compiler supports it, and *generally* as
+ * fast or faster than `memcpy`.
+ * Method 2:
+ * Direct access via cast. This method doesn't depend on the compiler but
+ * violates the C standard.
+ * It can generate buggy code on targets which do not support unaligned
+ * memory accesses.
+ * But in some circumstances, it's the only known way to get the most
+ * performance (example: GCC + ARMv6)
+ * Method 3:
+ * Byteshift. This can generate the best code on old compilers which don't
+ * inline small `memcpy()` calls, and it might also be faster on big-endian
+ * systems which lack a native byteswap instruction.
+ * See https://stackoverflow.com/a/32095106/646947 for details.
+ * Prefer these methods in priority order (0 > 1 > 2 > 3)
+ */
+#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */
+# if !defined(__clang__) && defined(__GNUC__) && defined(__ARM_FEATURE_UNALIGNED) && defined(__ARM_ARCH) && (__ARM_ARCH == 6)
+# define XXH_FORCE_MEMORY_ACCESS 2
+# elif !defined(__clang__) && ((defined(__INTEL_COMPILER) && !defined(_WIN32)) || \
+ (defined(__GNUC__) && (defined(__ARM_ARCH) && __ARM_ARCH >= 7)))
+# define XXH_FORCE_MEMORY_ACCESS 1
+# endif
+#endif
+
+/*!
+ * XXH_ACCEPT_NULL_INPUT_POINTER:
+ * If the input pointer is NULL, xxHash's default behavior is to dereference it,
+ * triggering a segfault.
+ * When this macro is enabled, xxHash actively checks the input for a null pointer.
+ * If it is, the result for null input pointers is the same as a zero-length input.
+ */
+#ifndef XXH_ACCEPT_NULL_INPUT_POINTER /* can be defined externally */
+# define XXH_ACCEPT_NULL_INPUT_POINTER 0
+#endif
+
+/*!
+ * XXH_FORCE_ALIGN_CHECK:
+ * This is an important performance trick
+ * for architectures without decent unaligned memory access performance.
+ * It checks for input alignment, and when conditions are met,
+ * uses a "fast path" employing direct 32-bit/64-bit read,
+ * resulting in _dramatically faster_ read speed.
+ *
+ * The check costs one initial branch per hash, which is generally negligible, but not zero.
+ * Moreover, it's not useful to generate binary for an additional code path
+ * if memory access uses same instruction for both aligned and unaligned adresses.
+ *
+ * In these cases, the alignment check can be removed by setting this macro to 0.
+ * Then the code will always use unaligned memory access.
+ * Align check is automatically disabled on x86, x64 & arm64,
+ * which are platforms known to offer good unaligned memory accesses performance.
+ *
+ * This option does not affect XXH3 (only XXH32 and XXH64).
+ */
+#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */
+# if defined(__i386) || defined(__x86_64__) || defined(__aarch64__) \
+ || defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM64) /* visual */
+# define XXH_FORCE_ALIGN_CHECK 0
+# else
+# define XXH_FORCE_ALIGN_CHECK 1
+# endif
+#endif
+
+/*!
+ * XXH_NO_INLINE_HINTS:
+ *
+ * By default, xxHash tries to force the compiler to inline almost all internal
+ * functions.
+ *
+ * This can usually improve performance due to reduced jumping and improved
+ * constant folding, but significantly increases the size of the binary which
+ * might not be favorable.
+ *
+ * Additionally, sometimes the forced inlining can be detrimental to performance,
+ * depending on the architecture.
+ *
+ * XXH_NO_INLINE_HINTS marks all internal functions as static, giving the
+ * compiler full control on whether to inline or not.
+ *
+ * When not optimizing (-O0), optimizing for size (-Os, -Oz), or using
+ * -fno-inline with GCC or Clang, this will automatically be defined.
+ */
+#ifndef XXH_NO_INLINE_HINTS
+# if defined(__OPTIMIZE_SIZE__) /* -Os, -Oz */ \
+ || defined(__NO_INLINE__) /* -O0, -fno-inline */
+# define XXH_NO_INLINE_HINTS 1
+# else
+# define XXH_NO_INLINE_HINTS 0
+# endif
+#endif
+
+/*!
+ * XXH_REROLL:
+ * Whether to reroll XXH32_finalize, and XXH64_finalize,
+ * instead of using an unrolled jump table/if statement loop.
+ *
+ * This is automatically defined on -Os/-Oz on GCC and Clang.
+ */
+#ifndef XXH_REROLL
+# if defined(__OPTIMIZE_SIZE__)
+# define XXH_REROLL 1
+# else
+# define XXH_REROLL 0
+# endif
+#endif
+
+
+/* *************************************
+* Includes & Memory related functions
+***************************************/
+/*!
+ * Modify the local functions below should you wish to use
+ * different memory routines for malloc() and free()
+ */
+#include <stdlib.h>
+
+static void* XXH_malloc(size_t s) { return malloc(s); }
+static void XXH_free(void* p) { free(p); }
+
+/*! and for memcpy() */
+#include <string.h>
+static void* XXH_memcpy(void* dest, const void* src, size_t size)
+{
+ return memcpy(dest,src,size);
+}
+
+#include <limits.h> /* ULLONG_MAX */
+
+
+/* *************************************
+* Compiler Specific Options
+***************************************/
+#ifdef _MSC_VER /* Visual Studio warning fix */
+# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
+#endif
+
+#if XXH_NO_INLINE_HINTS /* disable inlining hints */
+# if defined(__GNUC__)
+# define XXH_FORCE_INLINE static __attribute__((unused))
+# else
+# define XXH_FORCE_INLINE static
+# endif
+# define XXH_NO_INLINE static
+/* enable inlining hints */
+#elif defined(_MSC_VER) /* Visual Studio */
+# define XXH_FORCE_INLINE static __forceinline
+# define XXH_NO_INLINE static __declspec(noinline)
+#elif defined(__GNUC__)
+# define XXH_FORCE_INLINE static __inline__ __attribute__((always_inline, unused))
+# define XXH_NO_INLINE static __attribute__((noinline))
+#elif defined (__cplusplus) \
+ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) /* C99 */
+# define XXH_FORCE_INLINE static inline
+# define XXH_NO_INLINE static
+#else
+# define XXH_FORCE_INLINE static
+# define XXH_NO_INLINE static
+#endif
+
+
+
+/* *************************************
+* Debug
+***************************************/
+/*
+ * XXH_DEBUGLEVEL is expected to be defined externally, typically via the
+ * compiler's command line options. The value must be a number.
+ */
+#ifndef XXH_DEBUGLEVEL
+# ifdef DEBUGLEVEL /* backwards compat */
+# define XXH_DEBUGLEVEL DEBUGLEVEL
+# else
+# define XXH_DEBUGLEVEL 0
+# endif
+#endif
+
+#if (XXH_DEBUGLEVEL>=1)
+# include <assert.h> /* note: can still be disabled with NDEBUG */
+# define XXH_ASSERT(c) assert(c)
+#else
+# define XXH_ASSERT(c) ((void)0)
+#endif
+
+/* note: use after variable declarations */
+#define XXH_STATIC_ASSERT(c) do { enum { XXH_sa = 1/(int)(!!(c)) }; } while (0)
+
+
+/* *************************************
+* Basic Types
+***************************************/
+#if !defined (__VMS) \
+ && (defined (__cplusplus) \
+ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
+# include <stdint.h>
+ typedef uint8_t xxh_u8;
+#else
+ typedef unsigned char xxh_u8;
+#endif
+typedef XXH32_hash_t xxh_u32;
+
+#ifdef XXH_OLD_NAMES
+# define BYTE xxh_u8
+# define U8 xxh_u8
+# define U32 xxh_u32
+#endif
+
+/* *** Memory access *** */
+
+#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3))
+/*
+ * Manual byteshift. Best for old compilers which don't inline memcpy.
+ * We actually directly use XXH_readLE32 and XXH_readBE32.
+ */
+#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))
+
+/*
+ * Force direct memory access. Only works on CPU which support unaligned memory
+ * access in hardware.
+ */
+static xxh_u32 XXH_read32(const void* memPtr) { return *(const xxh_u32*) memPtr; }
+
+#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))
+
+/*
+ * __pack instructions are safer but compiler specific, hence potentially
+ * problematic for some compilers.
+ *
+ * Currently only defined for GCC and ICC.
+ */
+#ifdef XXH_OLD_NAMES
+typedef union { xxh_u32 u32; } __attribute__((packed)) unalign;
+#endif
+static xxh_u32 XXH_read32(const void* ptr)
+{
+ typedef union { xxh_u32 u32; } __attribute__((packed)) xxh_unalign;
+ return ((const xxh_unalign*)ptr)->u32;
+}
+
+#else
+
+/*
+ * Portable and safe solution. Generally efficient.
+ * see: https://stackoverflow.com/a/32095106/646947
+ */
+static xxh_u32 XXH_read32(const void* memPtr)
+{
+ xxh_u32 val;
+ memcpy(&val, memPtr, sizeof(val));
+ return val;
+}
+
+#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */
+
+
+/* *** Endianess *** */
+typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess;
+
+/*!
+ * XXH_CPU_LITTLE_ENDIAN:
+ * Defined to 1 if the target is little endian, or 0 if it is big endian.
+ * It can be defined externally, for example on the compiler command line.
+ *
+ * If it is not defined, a runtime check (which is usually constant folded)
+ * is used instead.
+ */
+#ifndef XXH_CPU_LITTLE_ENDIAN
+/*
+ * Try to detect endianness automatically, to avoid the nonstandard behavior
+ * in `XXH_isLittleEndian()`
+ */
+# if defined(_WIN32) /* Windows is always little endian */ \
+ || defined(__LITTLE_ENDIAN__) \
+ || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+# define XXH_CPU_LITTLE_ENDIAN 1
+# elif defined(__BIG_ENDIAN__) \
+ || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+# define XXH_CPU_LITTLE_ENDIAN 0
+# else
+/*
+ * runtime test, presumed to simplify to a constant by compiler
+ */
+static int XXH_isLittleEndian(void)
+{
+ /*
+ * Portable and well-defined behavior.
+ * Don't use static: it is detrimental to performance.
+ */
+ const union { xxh_u32 u; xxh_u8 c[4]; } one = { 1 };
+ return one.c[0];
+}
+# define XXH_CPU_LITTLE_ENDIAN XXH_isLittleEndian()
+# endif
+#endif
+
+
+
+
+/* ****************************************
+* Compiler-specific Functions and Macros
+******************************************/
+#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+
+#ifdef __has_builtin
+# define XXH_HAS_BUILTIN(x) __has_builtin(x)
+#else
+# define XXH_HAS_BUILTIN(x) 0
+#endif
+
+#if !defined(NO_CLANG_BUILTIN) && XXH_HAS_BUILTIN(__builtin_rotateleft32) \
+ && XXH_HAS_BUILTIN(__builtin_rotateleft64)
+# define XXH_rotl32 __builtin_rotateleft32
+# define XXH_rotl64 __builtin_rotateleft64
+/* Note: although _rotl exists for minGW (GCC under windows), performance seems poor */
+#elif defined(_MSC_VER)
+# define XXH_rotl32(x,r) _rotl(x,r)
+# define XXH_rotl64(x,r) _rotl64(x,r)
+#else
+# define XXH_rotl32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))
+# define XXH_rotl64(x,r) (((x) << (r)) | ((x) >> (64 - (r))))
+#endif
+
+#if defined(_MSC_VER) /* Visual Studio */
+# define XXH_swap32 _byteswap_ulong
+#elif XXH_GCC_VERSION >= 403
+# define XXH_swap32 __builtin_bswap32
+#else
+static xxh_u32 XXH_swap32 (xxh_u32 x)
+{
+ return ((x << 24) & 0xff000000 ) |
+ ((x << 8) & 0x00ff0000 ) |
+ ((x >> 8) & 0x0000ff00 ) |
+ ((x >> 24) & 0x000000ff );
+}
+#endif
+
+
+/* ***************************
+* Memory reads
+*****************************/
+typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment;
+
+/*
+ * XXH_FORCE_MEMORY_ACCESS==3 is an endian-independent byteshift load.
+ *
+ * This is ideal for older compilers which don't inline memcpy.
+ */
+#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3))
+
+XXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void* memPtr)
+{
+ const xxh_u8* bytePtr = (const xxh_u8 *)memPtr;
+ return bytePtr[0]
+ | ((xxh_u32)bytePtr[1] << 8)
+ | ((xxh_u32)bytePtr[2] << 16)
+ | ((xxh_u32)bytePtr[3] << 24);
+}
+
+XXH_FORCE_INLINE xxh_u32 XXH_readBE32(const void* memPtr)
+{
+ const xxh_u8* bytePtr = (const xxh_u8 *)memPtr;
+ return bytePtr[3]
+ | ((xxh_u32)bytePtr[2] << 8)
+ | ((xxh_u32)bytePtr[1] << 16)
+ | ((xxh_u32)bytePtr[0] << 24);
+}
+
+#else
+XXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void* ptr)
+{
+ return XXH_CPU_LITTLE_ENDIAN ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr));
+}
+
+static xxh_u32 XXH_readBE32(const void* ptr)
+{
+ return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr);
+}
+#endif
+
+XXH_FORCE_INLINE xxh_u32
+XXH_readLE32_align(const void* ptr, XXH_alignment align)
+{
+ if (align==XXH_unaligned) {
+ return XXH_readLE32(ptr);
+ } else {
+ return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u32*)ptr : XXH_swap32(*(const xxh_u32*)ptr);
+ }
+}
+
+
+/* *************************************
+* Misc
+***************************************/
+XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; }
+
+
+/* *******************************************************************
+* 32-bit hash functions
+*********************************************************************/
+static const xxh_u32 XXH_PRIME32_1 = 0x9E3779B1U; /* 0b10011110001101110111100110110001 */
+static const xxh_u32 XXH_PRIME32_2 = 0x85EBCA77U; /* 0b10000101111010111100101001110111 */
+static const xxh_u32 XXH_PRIME32_3 = 0xC2B2AE3DU; /* 0b11000010101100101010111000111101 */
+static const xxh_u32 XXH_PRIME32_4 = 0x27D4EB2FU; /* 0b00100111110101001110101100101111 */
+static const xxh_u32 XXH_PRIME32_5 = 0x165667B1U; /* 0b00010110010101100110011110110001 */
+
+#ifdef XXH_OLD_NAMES
+# define PRIME32_1 XXH_PRIME32_1
+# define PRIME32_2 XXH_PRIME32_2
+# define PRIME32_3 XXH_PRIME32_3
+# define PRIME32_4 XXH_PRIME32_4
+# define PRIME32_5 XXH_PRIME32_5
+#endif
+
+static xxh_u32 XXH32_round(xxh_u32 acc, xxh_u32 input)
+{
+ acc += input * XXH_PRIME32_2;
+ acc = XXH_rotl32(acc, 13);
+ acc *= XXH_PRIME32_1;
+#if defined(__GNUC__) && defined(__SSE4_1__) && !defined(XXH_ENABLE_AUTOVECTORIZE)
+ /*
+ * UGLY HACK:
+ * This inline assembly hack forces acc into a normal register. This is the
+ * only thing that prevents GCC and Clang from autovectorizing the XXH32
+ * loop (pragmas and attributes don't work for some resason) without globally
+ * disabling SSE4.1.
+ *
+ * The reason we want to avoid vectorization is because despite working on
+ * 4 integers at a time, there are multiple factors slowing XXH32 down on
+ * SSE4:
+ * - There's a ridiculous amount of lag from pmulld (10 cycles of latency on
+ * newer chips!) making it slightly slower to multiply four integers at
+ * once compared to four integers independently. Even when pmulld was
+ * fastest, Sandy/Ivy Bridge, it is still not worth it to go into SSE
+ * just to multiply unless doing a long operation.
+ *
+ * - Four instructions are required to rotate,
+ * movqda tmp, v // not required with VEX encoding
+ * pslld tmp, 13 // tmp <<= 13
+ * psrld v, 19 // x >>= 19
+ * por v, tmp // x |= tmp
+ * compared to one for scalar:
+ * roll v, 13 // reliably fast across the board
+ * shldl v, v, 13 // Sandy Bridge and later prefer this for some reason
+ *
+ * - Instruction level parallelism is actually more beneficial here because
+ * the SIMD actually serializes this operation: While v1 is rotating, v2
+ * can load data, while v3 can multiply. SSE forces them to operate
+ * together.
+ *
+ * How this hack works:
+ * __asm__("" // Declare an assembly block but don't declare any instructions
+ * : // However, as an Input/Output Operand,
+ * "+r" // constrain a read/write operand (+) as a general purpose register (r).
+ * (acc) // and set acc as the operand
+ * );
+ *
+ * Because of the 'r', the compiler has promised that seed will be in a
+ * general purpose register and the '+' says that it will be 'read/write',
+ * so it has to assume it has changed. It is like volatile without all the
+ * loads and stores.
+ *
+ * Since the argument has to be in a normal register (not an SSE register),
+ * each time XXH32_round is called, it is impossible to vectorize.
+ */
+ __asm__("" : "+r" (acc));
+#endif
+ return acc;
+}
+
+/* mix all bits */
+static xxh_u32 XXH32_avalanche(xxh_u32 h32)
+{
+ h32 ^= h32 >> 15;
+ h32 *= XXH_PRIME32_2;
+ h32 ^= h32 >> 13;
+ h32 *= XXH_PRIME32_3;
+ h32 ^= h32 >> 16;
+ return(h32);
+}
+
+#define XXH_get32bits(p) XXH_readLE32_align(p, align)
+
+static xxh_u32
+XXH32_finalize(xxh_u32 h32, const xxh_u8* ptr, size_t len, XXH_alignment align)
+{
+#define XXH_PROCESS1 do { \
+ h32 += (*ptr++) * XXH_PRIME32_5; \
+ h32 = XXH_rotl32(h32, 11) * XXH_PRIME32_1; \
+} while (0)
+
+#define XXH_PROCESS4 do { \
+ h32 += XXH_get32bits(ptr) * XXH_PRIME32_3; \
+ ptr += 4; \
+ h32 = XXH_rotl32(h32, 17) * XXH_PRIME32_4; \
+} while (0)
+
+ /* Compact rerolled version */
+ if (XXH_REROLL) {
+ len &= 15;
+ while (len >= 4) {
+ XXH_PROCESS4;
+ len -= 4;
+ }
+ while (len > 0) {
+ XXH_PROCESS1;
+ --len;
+ }
+ return XXH32_avalanche(h32);
+ } else {
+ switch(len&15) /* or switch(bEnd - p) */ {
+ case 12: XXH_PROCESS4;
+ /* fallthrough */
+ case 8: XXH_PROCESS4;
+ /* fallthrough */
+ case 4: XXH_PROCESS4;
+ return XXH32_avalanche(h32);
+
+ case 13: XXH_PROCESS4;
+ /* fallthrough */
+ case 9: XXH_PROCESS4;
+ /* fallthrough */
+ case 5: XXH_PROCESS4;
+ XXH_PROCESS1;
+ return XXH32_avalanche(h32);
+
+ case 14: XXH_PROCESS4;
+ /* fallthrough */
+ case 10: XXH_PROCESS4;
+ /* fallthrough */
+ case 6: XXH_PROCESS4;
+ XXH_PROCESS1;
+ XXH_PROCESS1;
+ return XXH32_avalanche(h32);
+
+ case 15: XXH_PROCESS4;
+ /* fallthrough */
+ case 11: XXH_PROCESS4;
+ /* fallthrough */
+ case 7: XXH_PROCESS4;
+ /* fallthrough */
+ case 3: XXH_PROCESS1;
+ /* fallthrough */
+ case 2: XXH_PROCESS1;
+ /* fallthrough */
+ case 1: XXH_PROCESS1;
+ /* fallthrough */
+ case 0: return XXH32_avalanche(h32);
+ }
+ XXH_ASSERT(0);
+ return h32; /* reaching this point is deemed impossible */
+ }
+}
+
+#ifdef XXH_OLD_NAMES
+# define PROCESS1 XXH_PROCESS1
+# define PROCESS4 XXH_PROCESS4
+#else
+# undef XXH_PROCESS1
+# undef XXH_PROCESS4
+#endif
+
+XXH_FORCE_INLINE xxh_u32
+XXH32_endian_align(const xxh_u8* input, size_t len, xxh_u32 seed, XXH_alignment align)
+{
+ const xxh_u8* bEnd = input + len;
+ xxh_u32 h32;
+
+#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)
+ if (input==NULL) {
+ len=0;
+ bEnd=input=(const xxh_u8*)(size_t)16;
+ }
+#endif
+
+ if (len>=16) {
+ const xxh_u8* const limit = bEnd - 15;
+ xxh_u32 v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2;
+ xxh_u32 v2 = seed + XXH_PRIME32_2;
+ xxh_u32 v3 = seed + 0;
+ xxh_u32 v4 = seed - XXH_PRIME32_1;
+
+ do {
+ v1 = XXH32_round(v1, XXH_get32bits(input)); input += 4;
+ v2 = XXH32_round(v2, XXH_get32bits(input)); input += 4;
+ v3 = XXH32_round(v3, XXH_get32bits(input)); input += 4;
+ v4 = XXH32_round(v4, XXH_get32bits(input)); input += 4;
+ } while (input < limit);
+
+ h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7)
+ + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);
+ } else {
+ h32 = seed + XXH_PRIME32_5;
+ }
+
+ h32 += (xxh_u32)len;
+
+ return XXH32_finalize(h32, input, len&15, align);
+}
+
+
+XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t len, XXH32_hash_t seed)
+{
+#if 0
+ /* Simple version, good for code maintenance, but unfortunately slow for small inputs */
+ XXH32_state_t state;
+ XXH32_reset(&state, seed);
+ XXH32_update(&state, (const xxh_u8*)input, len);
+ return XXH32_digest(&state);
+
+#else
+
+ if (XXH_FORCE_ALIGN_CHECK) {
+ if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */
+ return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_aligned);
+ } }
+
+ return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_unaligned);
+#endif
+}
+
+
+
+/******* Hash streaming *******/
+
+XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void)
+{
+ return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t));
+}
+XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr)
+{
+ XXH_free(statePtr);
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState)
+{
+ memcpy(dstState, srcState, sizeof(*dstState));
+}
+
+XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, XXH32_hash_t seed)
+{
+ XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */
+ memset(&state, 0, sizeof(state));
+ state.v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2;
+ state.v2 = seed + XXH_PRIME32_2;
+ state.v3 = seed + 0;
+ state.v4 = seed - XXH_PRIME32_1;
+ /* do not write into reserved, planned to be removed in a future version */
+ memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved));
+ return XXH_OK;
+}
+
+
+XXH_PUBLIC_API XXH_errorcode
+XXH32_update(XXH32_state_t* state, const void* input, size_t len)
+{
+ if (input==NULL)
+#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)
+ return XXH_OK;
+#else
+ return XXH_ERROR;
+#endif
+
+ { const xxh_u8* p = (const xxh_u8*)input;
+ const xxh_u8* const bEnd = p + len;
+
+ state->total_len_32 += (XXH32_hash_t)len;
+ state->large_len |= (XXH32_hash_t)((len>=16) | (state->total_len_32>=16));
+
+ if (state->memsize + len < 16) { /* fill in tmp buffer */
+ XXH_memcpy((xxh_u8*)(state->mem32) + state->memsize, input, len);
+ state->memsize += (XXH32_hash_t)len;
+ return XXH_OK;
+ }
+
+ if (state->memsize) { /* some data left from previous update */
+ XXH_memcpy((xxh_u8*)(state->mem32) + state->memsize, input, 16-state->memsize);
+ { const xxh_u32* p32 = state->mem32;
+ state->v1 = XXH32_round(state->v1, XXH_readLE32(p32)); p32++;
+ state->v2 = XXH32_round(state->v2, XXH_readLE32(p32)); p32++;
+ state->v3 = XXH32_round(state->v3, XXH_readLE32(p32)); p32++;
+ state->v4 = XXH32_round(state->v4, XXH_readLE32(p32));
+ }
+ p += 16-state->memsize;
+ state->memsize = 0;
+ }
+
+ if (p <= bEnd-16) {
+ const xxh_u8* const limit = bEnd - 16;
+ xxh_u32 v1 = state->v1;
+ xxh_u32 v2 = state->v2;
+ xxh_u32 v3 = state->v3;
+ xxh_u32 v4 = state->v4;
+
+ do {
+ v1 = XXH32_round(v1, XXH_readLE32(p)); p+=4;
+ v2 = XXH32_round(v2, XXH_readLE32(p)); p+=4;
+ v3 = XXH32_round(v3, XXH_readLE32(p)); p+=4;
+ v4 = XXH32_round(v4, XXH_readLE32(p)); p+=4;
+ } while (p<=limit);
+
+ state->v1 = v1;
+ state->v2 = v2;
+ state->v3 = v3;
+ state->v4 = v4;
+ }
+
+ if (p < bEnd) {
+ XXH_memcpy(state->mem32, p, (size_t)(bEnd-p));
+ state->memsize = (unsigned)(bEnd-p);
+ }
+ }
+
+ return XXH_OK;
+}
+
+
+XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* state)
+{
+ xxh_u32 h32;
+
+ if (state->large_len) {
+ h32 = XXH_rotl32(state->v1, 1)
+ + XXH_rotl32(state->v2, 7)
+ + XXH_rotl32(state->v3, 12)
+ + XXH_rotl32(state->v4, 18);
+ } else {
+ h32 = state->v3 /* == seed */ + XXH_PRIME32_5;
+ }
+
+ h32 += state->total_len_32;
+
+ return XXH32_finalize(h32, (const xxh_u8*)state->mem32, state->memsize, XXH_aligned);
+}
+
+
+/******* Canonical representation *******/
+
+/*
+ * The default return values from XXH functions are unsigned 32 and 64 bit
+ * integers.
+ *
+ * The canonical representation uses big endian convention, the same convention
+ * as human-readable numbers (large digits first).
+ *
+ * This way, hash values can be written into a file or buffer, remaining
+ * comparable across different systems.
+ *
+ * The following functions allow transformation of hash values to and from their
+ * canonical format.
+ */
+XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash)
+{
+ XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t));
+ if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash);
+ memcpy(dst, &hash, sizeof(*dst));
+}
+
+XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src)
+{
+ return XXH_readBE32(src);
+}
+
+
+#ifndef XXH_NO_LONG_LONG
+
+/* *******************************************************************
+* 64-bit hash functions
+*********************************************************************/
+
+/******* Memory access *******/
+
+typedef XXH64_hash_t xxh_u64;
+
+#ifdef XXH_OLD_NAMES
+# define U64 xxh_u64
+#endif
+
+/*!
+ * XXH_REROLL_XXH64:
+ * Whether to reroll the XXH64_finalize() loop.
+ *
+ * Just like XXH32, we can unroll the XXH64_finalize() loop. This can be a
+ * performance gain on 64-bit hosts, as only one jump is required.
+ *
+ * However, on 32-bit hosts, because arithmetic needs to be done with two 32-bit
+ * registers, and 64-bit arithmetic needs to be simulated, it isn't beneficial
+ * to unroll. The code becomes ridiculously large (the largest function in the
+ * binary on i386!), and rerolling it saves anywhere from 3kB to 20kB. It is
+ * also slightly faster because it fits into cache better and is more likely
+ * to be inlined by the compiler.
+ *
+ * If XXH_REROLL is defined, this is ignored and the loop is always rerolled.
+ */
+#ifndef XXH_REROLL_XXH64
+# if (defined(__ILP32__) || defined(_ILP32)) /* ILP32 is often defined on 32-bit GCC family */ \
+ || !(defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64) /* x86-64 */ \
+ || defined(_M_ARM64) || defined(__aarch64__) || defined(__arm64__) /* aarch64 */ \
+ || defined(__PPC64__) || defined(__PPC64LE__) || defined(__ppc64__) || defined(__powerpc64__) /* ppc64 */ \
+ || defined(__mips64__) || defined(__mips64)) /* mips64 */ \
+ || (!defined(SIZE_MAX) || SIZE_MAX < ULLONG_MAX) /* check limits */
+# define XXH_REROLL_XXH64 1
+# else
+# define XXH_REROLL_XXH64 0
+# endif
+#endif /* !defined(XXH_REROLL_XXH64) */
+
+#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3))
+/*
+ * Manual byteshift. Best for old compilers which don't inline memcpy.
+ * We actually directly use XXH_readLE64 and XXH_readBE64.
+ */
+#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))
+
+/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */
+static xxh_u64 XXH_read64(const void* memPtr) { return *(const xxh_u64*) memPtr; }
+
+#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))
+
+/*
+ * __pack instructions are safer, but compiler specific, hence potentially
+ * problematic for some compilers.
+ *
+ * Currently only defined for GCC and ICC.
+ */
+#ifdef XXH_OLD_NAMES
+typedef union { xxh_u32 u32; xxh_u64 u64; } __attribute__((packed)) unalign64;
+#endif
+static xxh_u64 XXH_read64(const void* ptr)
+{
+ typedef union { xxh_u32 u32; xxh_u64 u64; } __attribute__((packed)) xxh_unalign64;
+ return ((const xxh_unalign64*)ptr)->u64;
+}
+
+#else
+
+/*
+ * Portable and safe solution. Generally efficient.
+ * see: https://stackoverflow.com/a/32095106/646947
+ */
+static xxh_u64 XXH_read64(const void* memPtr)
+{
+ xxh_u64 val;
+ memcpy(&val, memPtr, sizeof(val));
+ return val;
+}
+
+#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */
+
+#if defined(_MSC_VER) /* Visual Studio */
+# define XXH_swap64 _byteswap_uint64
+#elif XXH_GCC_VERSION >= 403
+# define XXH_swap64 __builtin_bswap64
+#else
+static xxh_u64 XXH_swap64 (xxh_u64 x)
+{
+ return ((x << 56) & 0xff00000000000000ULL) |
+ ((x << 40) & 0x00ff000000000000ULL) |
+ ((x << 24) & 0x0000ff0000000000ULL) |
+ ((x << 8) & 0x000000ff00000000ULL) |
+ ((x >> 8) & 0x00000000ff000000ULL) |
+ ((x >> 24) & 0x0000000000ff0000ULL) |
+ ((x >> 40) & 0x000000000000ff00ULL) |
+ ((x >> 56) & 0x00000000000000ffULL);
+}
+#endif
+
+
+/* XXH_FORCE_MEMORY_ACCESS==3 is an endian-independent byteshift load. */
+#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3))
+
+XXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void* memPtr)
+{
+ const xxh_u8* bytePtr = (const xxh_u8 *)memPtr;
+ return bytePtr[0]
+ | ((xxh_u64)bytePtr[1] << 8)
+ | ((xxh_u64)bytePtr[2] << 16)
+ | ((xxh_u64)bytePtr[3] << 24)
+ | ((xxh_u64)bytePtr[4] << 32)
+ | ((xxh_u64)bytePtr[5] << 40)
+ | ((xxh_u64)bytePtr[6] << 48)
+ | ((xxh_u64)bytePtr[7] << 56);
+}
+
+XXH_FORCE_INLINE xxh_u64 XXH_readBE64(const void* memPtr)
+{
+ const xxh_u8* bytePtr = (const xxh_u8 *)memPtr;
+ return bytePtr[7]
+ | ((xxh_u64)bytePtr[6] << 8)
+ | ((xxh_u64)bytePtr[5] << 16)
+ | ((xxh_u64)bytePtr[4] << 24)
+ | ((xxh_u64)bytePtr[3] << 32)
+ | ((xxh_u64)bytePtr[2] << 40)
+ | ((xxh_u64)bytePtr[1] << 48)
+ | ((xxh_u64)bytePtr[0] << 56);
+}
+
+#else
+XXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void* ptr)
+{
+ return XXH_CPU_LITTLE_ENDIAN ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr));
+}
+
+static xxh_u64 XXH_readBE64(const void* ptr)
+{
+ return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr);
+}
+#endif
+
+XXH_FORCE_INLINE xxh_u64
+XXH_readLE64_align(const void* ptr, XXH_alignment align)
+{
+ if (align==XXH_unaligned)
+ return XXH_readLE64(ptr);
+ else
+ return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u64*)ptr : XXH_swap64(*(const xxh_u64*)ptr);
+}
+
+
+/******* xxh64 *******/
+
+static const xxh_u64 XXH_PRIME64_1 = 0x9E3779B185EBCA87ULL; /* 0b1001111000110111011110011011000110000101111010111100101010000111 */
+static const xxh_u64 XXH_PRIME64_2 = 0xC2B2AE3D27D4EB4FULL; /* 0b1100001010110010101011100011110100100111110101001110101101001111 */
+static const xxh_u64 XXH_PRIME64_3 = 0x165667B19E3779F9ULL; /* 0b0001011001010110011001111011000110011110001101110111100111111001 */
+static const xxh_u64 XXH_PRIME64_4 = 0x85EBCA77C2B2AE63ULL; /* 0b1000010111101011110010100111011111000010101100101010111001100011 */
+static const xxh_u64 XXH_PRIME64_5 = 0x27D4EB2F165667C5ULL; /* 0b0010011111010100111010110010111100010110010101100110011111000101 */
+
+#ifdef XXH_OLD_NAMES
+# define PRIME64_1 XXH_PRIME64_1
+# define PRIME64_2 XXH_PRIME64_2
+# define PRIME64_3 XXH_PRIME64_3
+# define PRIME64_4 XXH_PRIME64_4
+# define PRIME64_5 XXH_PRIME64_5
+#endif
+
+static xxh_u64 XXH64_round(xxh_u64 acc, xxh_u64 input)
+{
+ acc += input * XXH_PRIME64_2;
+ acc = XXH_rotl64(acc, 31);
+ acc *= XXH_PRIME64_1;
+ return acc;
+}
+
+static xxh_u64 XXH64_mergeRound(xxh_u64 acc, xxh_u64 val)
+{
+ val = XXH64_round(0, val);
+ acc ^= val;
+ acc = acc * XXH_PRIME64_1 + XXH_PRIME64_4;
+ return acc;
+}
+
+static xxh_u64 XXH64_avalanche(xxh_u64 h64)
+{
+ h64 ^= h64 >> 33;
+ h64 *= XXH_PRIME64_2;
+ h64 ^= h64 >> 29;
+ h64 *= XXH_PRIME64_3;
+ h64 ^= h64 >> 32;
+ return h64;
+}
+
+
+#define XXH_get64bits(p) XXH_readLE64_align(p, align)
+
+static xxh_u64
+XXH64_finalize(xxh_u64 h64, const xxh_u8* ptr, size_t len, XXH_alignment align)
+{
+#define XXH_PROCESS1_64 do { \
+ h64 ^= (*ptr++) * XXH_PRIME64_5; \
+ h64 = XXH_rotl64(h64, 11) * XXH_PRIME64_1; \
+} while (0)
+
+#define XXH_PROCESS4_64 do { \
+ h64 ^= (xxh_u64)(XXH_get32bits(ptr)) * XXH_PRIME64_1; \
+ ptr += 4; \
+ h64 = XXH_rotl64(h64, 23) * XXH_PRIME64_2 + XXH_PRIME64_3; \
+} while (0)
+
+#define XXH_PROCESS8_64 do { \
+ xxh_u64 const k1 = XXH64_round(0, XXH_get64bits(ptr)); \
+ ptr += 8; \
+ h64 ^= k1; \
+ h64 = XXH_rotl64(h64,27) * XXH_PRIME64_1 + XXH_PRIME64_4; \
+} while (0)
+
+ /* Rerolled version for 32-bit targets is faster and much smaller. */
+ if (XXH_REROLL || XXH_REROLL_XXH64) {
+ len &= 31;
+ while (len >= 8) {
+ XXH_PROCESS8_64;
+ len -= 8;
+ }
+ if (len >= 4) {
+ XXH_PROCESS4_64;
+ len -= 4;
+ }
+ while (len > 0) {
+ XXH_PROCESS1_64;
+ --len;
+ }
+ return XXH64_avalanche(h64);
+ } else {
+ switch(len & 31) {
+ case 24: XXH_PROCESS8_64;
+ /* fallthrough */
+ case 16: XXH_PROCESS8_64;
+ /* fallthrough */
+ case 8: XXH_PROCESS8_64;
+ return XXH64_avalanche(h64);
+
+ case 28: XXH_PROCESS8_64;
+ /* fallthrough */
+ case 20: XXH_PROCESS8_64;
+ /* fallthrough */
+ case 12: XXH_PROCESS8_64;
+ /* fallthrough */
+ case 4: XXH_PROCESS4_64;
+ return XXH64_avalanche(h64);
+
+ case 25: XXH_PROCESS8_64;
+ /* fallthrough */
+ case 17: XXH_PROCESS8_64;
+ /* fallthrough */
+ case 9: XXH_PROCESS8_64;
+ XXH_PROCESS1_64;
+ return XXH64_avalanche(h64);
+
+ case 29: XXH_PROCESS8_64;
+ /* fallthrough */
+ case 21: XXH_PROCESS8_64;
+ /* fallthrough */
+ case 13: XXH_PROCESS8_64;
+ /* fallthrough */
+ case 5: XXH_PROCESS4_64;
+ XXH_PROCESS1_64;
+ return XXH64_avalanche(h64);
+
+ case 26: XXH_PROCESS8_64;
+ /* fallthrough */
+ case 18: XXH_PROCESS8_64;
+ /* fallthrough */
+ case 10: XXH_PROCESS8_64;
+ XXH_PROCESS1_64;
+ XXH_PROCESS1_64;
+ return XXH64_avalanche(h64);
+
+ case 30: XXH_PROCESS8_64;
+ /* fallthrough */
+ case 22: XXH_PROCESS8_64;
+ /* fallthrough */
+ case 14: XXH_PROCESS8_64;
+ /* fallthrough */
+ case 6: XXH_PROCESS4_64;
+ XXH_PROCESS1_64;
+ XXH_PROCESS1_64;
+ return XXH64_avalanche(h64);
+
+ case 27: XXH_PROCESS8_64;
+ /* fallthrough */
+ case 19: XXH_PROCESS8_64;
+ /* fallthrough */
+ case 11: XXH_PROCESS8_64;
+ XXH_PROCESS1_64;
+ XXH_PROCESS1_64;
+ XXH_PROCESS1_64;
+ return XXH64_avalanche(h64);
+
+ case 31: XXH_PROCESS8_64;
+ /* fallthrough */
+ case 23: XXH_PROCESS8_64;
+ /* fallthrough */
+ case 15: XXH_PROCESS8_64;
+ /* fallthrough */
+ case 7: XXH_PROCESS4_64;
+ /* fallthrough */
+ case 3: XXH_PROCESS1_64;
+ /* fallthrough */
+ case 2: XXH_PROCESS1_64;
+ /* fallthrough */
+ case 1: XXH_PROCESS1_64;
+ /* fallthrough */
+ case 0: return XXH64_avalanche(h64);
+ }
+ }
+ /* impossible to reach */
+ XXH_ASSERT(0);
+ return 0; /* unreachable, but some compilers complain without it */
+}
+
+#ifdef XXH_OLD_NAMES
+# define PROCESS1_64 XXH_PROCESS1_64
+# define PROCESS4_64 XXH_PROCESS4_64
+# define PROCESS8_64 XXH_PROCESS8_64
+#else
+# undef XXH_PROCESS1_64
+# undef XXH_PROCESS4_64
+# undef XXH_PROCESS8_64
+#endif
+
+XXH_FORCE_INLINE xxh_u64
+XXH64_endian_align(const xxh_u8* input, size_t len, xxh_u64 seed, XXH_alignment align)
+{
+ const xxh_u8* bEnd = input + len;
+ xxh_u64 h64;
+
+#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)
+ if (input==NULL) {
+ len=0;
+ bEnd=input=(const xxh_u8*)(size_t)32;
+ }
+#endif
+
+ if (len>=32) {
+ const xxh_u8* const limit = bEnd - 32;
+ xxh_u64 v1 = seed + XXH_PRIME64_1 + XXH_PRIME64_2;
+ xxh_u64 v2 = seed + XXH_PRIME64_2;
+ xxh_u64 v3 = seed + 0;
+ xxh_u64 v4 = seed - XXH_PRIME64_1;
+
+ do {
+ v1 = XXH64_round(v1, XXH_get64bits(input)); input+=8;
+ v2 = XXH64_round(v2, XXH_get64bits(input)); input+=8;
+ v3 = XXH64_round(v3, XXH_get64bits(input)); input+=8;
+ v4 = XXH64_round(v4, XXH_get64bits(input)); input+=8;
+ } while (input<=limit);
+
+ h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);
+ h64 = XXH64_mergeRound(h64, v1);
+ h64 = XXH64_mergeRound(h64, v2);
+ h64 = XXH64_mergeRound(h64, v3);
+ h64 = XXH64_mergeRound(h64, v4);
+
+ } else {
+ h64 = seed + XXH_PRIME64_5;
+ }
+
+ h64 += (xxh_u64) len;
+
+ return XXH64_finalize(h64, input, len, align);
+}
+
+
+XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t len, XXH64_hash_t seed)
+{
+#if 0
+ /* Simple version, good for code maintenance, but unfortunately slow for small inputs */
+ XXH64_state_t state;
+ XXH64_reset(&state, seed);
+ XXH64_update(&state, (const xxh_u8*)input, len);
+ return XXH64_digest(&state);
+
+#else
+
+ if (XXH_FORCE_ALIGN_CHECK) {
+ if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */
+ return XXH64_endian_align((const xxh_u8*)input, len, seed, XXH_aligned);
+ } }
+
+ return XXH64_endian_align((const xxh_u8*)input, len, seed, XXH_unaligned);
+
+#endif
+}
+
+/******* Hash Streaming *******/
+
+XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void)
+{
+ return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t));
+}
+XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr)
+{
+ XXH_free(statePtr);
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState)
+{
+ memcpy(dstState, srcState, sizeof(*dstState));
+}
+
+XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, XXH64_hash_t seed)
+{
+ XXH64_state_t state; /* use a local state to memcpy() in order to avoid strict-aliasing warnings */
+ memset(&state, 0, sizeof(state));
+ state.v1 = seed + XXH_PRIME64_1 + XXH_PRIME64_2;
+ state.v2 = seed + XXH_PRIME64_2;
+ state.v3 = seed + 0;
+ state.v4 = seed - XXH_PRIME64_1;
+ /* do not write into reserved64, might be removed in a future version */
+ memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved64));
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API XXH_errorcode
+XXH64_update (XXH64_state_t* state, const void* input, size_t len)
+{
+ if (input==NULL)
+#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)
+ return XXH_OK;
+#else
+ return XXH_ERROR;
+#endif
+
+ { const xxh_u8* p = (const xxh_u8*)input;
+ const xxh_u8* const bEnd = p + len;
+
+ state->total_len += len;
+
+ if (state->memsize + len < 32) { /* fill in tmp buffer */
+ XXH_memcpy(((xxh_u8*)state->mem64) + state->memsize, input, len);
+ state->memsize += (xxh_u32)len;
+ return XXH_OK;
+ }
+
+ if (state->memsize) { /* tmp buffer is full */
+ XXH_memcpy(((xxh_u8*)state->mem64) + state->memsize, input, 32-state->memsize);
+ state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0));
+ state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1));
+ state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2));
+ state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3));
+ p += 32-state->memsize;
+ state->memsize = 0;
+ }
+
+ if (p+32 <= bEnd) {
+ const xxh_u8* const limit = bEnd - 32;
+ xxh_u64 v1 = state->v1;
+ xxh_u64 v2 = state->v2;
+ xxh_u64 v3 = state->v3;
+ xxh_u64 v4 = state->v4;
+
+ do {
+ v1 = XXH64_round(v1, XXH_readLE64(p)); p+=8;
+ v2 = XXH64_round(v2, XXH_readLE64(p)); p+=8;
+ v3 = XXH64_round(v3, XXH_readLE64(p)); p+=8;
+ v4 = XXH64_round(v4, XXH_readLE64(p)); p+=8;
+ } while (p<=limit);
+
+ state->v1 = v1;
+ state->v2 = v2;
+ state->v3 = v3;
+ state->v4 = v4;
+ }
+
+ if (p < bEnd) {
+ XXH_memcpy(state->mem64, p, (size_t)(bEnd-p));
+ state->memsize = (unsigned)(bEnd-p);
+ }
+ }
+
+ return XXH_OK;
+}
+
+
+XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* state)
+{
+ xxh_u64 h64;
+
+ if (state->total_len >= 32) {
+ xxh_u64 const v1 = state->v1;
+ xxh_u64 const v2 = state->v2;
+ xxh_u64 const v3 = state->v3;
+ xxh_u64 const v4 = state->v4;
+
+ h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);
+ h64 = XXH64_mergeRound(h64, v1);
+ h64 = XXH64_mergeRound(h64, v2);
+ h64 = XXH64_mergeRound(h64, v3);
+ h64 = XXH64_mergeRound(h64, v4);
+ } else {
+ h64 = state->v3 /*seed*/ + XXH_PRIME64_5;
+ }
+
+ h64 += (xxh_u64) state->total_len;
+
+ return XXH64_finalize(h64, (const xxh_u8*)state->mem64, (size_t)state->total_len, XXH_aligned);
+}
+
+
+/******* Canonical representation *******/
+
+XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash)
+{
+ XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t));
+ if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash);
+ memcpy(dst, &hash, sizeof(*dst));
+}
+
+XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src)
+{
+ return XXH_readBE64(src);
+}
+
+
+
+/* *********************************************************************
+* XXH3
+* New generation hash designed for speed on small keys and vectorization
+************************************************************************ */
+
+/* === Compiler specifics === */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* >= C99 */
+# define XXH_RESTRICT restrict
+#else
+/* Note: it might be useful to define __restrict or __restrict__ for some C++ compilers */
+# define XXH_RESTRICT /* disable */
+#endif
+
+#if (defined(__GNUC__) && (__GNUC__ >= 3)) \
+ || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) \
+ || defined(__clang__)
+# define XXH_likely(x) __builtin_expect(x, 1)
+# define XXH_unlikely(x) __builtin_expect(x, 0)
+#else
+# define XXH_likely(x) (x)
+# define XXH_unlikely(x) (x)
+#endif
+
+#if defined(__GNUC__)
+# if defined(__AVX2__)
+# include <immintrin.h>
+# elif defined(__SSE2__)
+# include <emmintrin.h>
+# elif defined(__ARM_NEON__) || defined(__ARM_NEON)
+# define inline __inline__ /* circumvent a clang bug */
+# include <arm_neon.h>
+# undef inline
+# endif
+#elif defined(_MSC_VER)
+# include <intrin.h>
+#endif
+
+/*
+ * One goal of XXH3 is to make it fast on both 32-bit and 64-bit, while
+ * remaining a true 64-bit/128-bit hash function.
+ *
+ * This is done by prioritizing a subset of 64-bit operations that can be
+ * emulated without too many steps on the average 32-bit machine.
+ *
+ * For example, these two lines seem similar, and run equally fast on 64-bit:
+ *
+ * xxh_u64 x;
+ * x ^= (x >> 47); // good
+ * x ^= (x >> 13); // bad
+ *
+ * However, to a 32-bit machine, there is a major difference.
+ *
+ * x ^= (x >> 47) looks like this:
+ *
+ * x.lo ^= (x.hi >> (47 - 32));
+ *
+ * while x ^= (x >> 13) looks like this:
+ *
+ * // note: funnel shifts are not usually cheap.
+ * x.lo ^= (x.lo >> 13) | (x.hi << (32 - 13));
+ * x.hi ^= (x.hi >> 13);
+ *
+ * The first one is significantly faster than the second, simply because the
+ * shift is larger than 32. This means:
+ * - All the bits we need are in the upper 32 bits, so we can ignore the lower
+ * 32 bits in the shift.
+ * - The shift result will always fit in the lower 32 bits, and therefore,
+ * we can ignore the upper 32 bits in the xor.
+ *
+ * Thanks to this optimization, XXH3 only requires these features to be efficient:
+ *
+ * - Usable unaligned access
+ * - A 32-bit or 64-bit ALU
+ * - If 32-bit, a decent ADC instruction
+ * - A 32 or 64-bit multiply with a 64-bit result
+ * - For the 128-bit variant, a decent byteswap helps short inputs.
+ *
+ * The first two are already required by XXH32, and almost all 32-bit and 64-bit
+ * platforms which can run XXH32 can run XXH3 efficiently.
+ *
+ * Thumb-1, the classic 16-bit only subset of ARM's instruction set, is one
+ * notable exception.
+ *
+ * First of all, Thumb-1 lacks support for the UMULL instruction which
+ * performs the important long multiply. This means numerous __aeabi_lmul
+ * calls.
+ *
+ * Second of all, the 8 functional registers are just not enough.
+ * Setup for __aeabi_lmul, byteshift loads, pointers, and all arithmetic need
+ * Lo registers, and this shuffling results in thousands more MOVs than A32.
+ *
+ * A32 and T32 don't have this limitation. They can access all 14 registers,
+ * do a 32->64 multiply with UMULL, and the flexible operand allowing free
+ * shifts is helpful, too.
+ *
+ * Therefore, we do a quick sanity check.
+ *
+ * If compiling Thumb-1 for a target which supports ARM instructions, we will
+ * emit a warning, as it is not a "sane" platform to compile for.
+ *
+ * Usually, if this happens, it is because of an accident and you probably need
+ * to specify -march, as you likely meant to compile for a newer architecture.
+ *
+ * Credit: large sections of the vectorial and asm source code paths
+ * have been contributed by @easyaspi314
+ */
+#if defined(__thumb__) && !defined(__thumb2__) && defined(__ARM_ARCH_ISA_ARM)
+# warning "XXH3 is highly inefficient without ARM or Thumb-2."
+#endif
+
+/* ==========================================
+ * Vectorization detection
+ * ========================================== */
+#define XXH_SCALAR 0 /* Portable scalar version */
+#define XXH_SSE2 1 /* SSE2 for Pentium 4 and all x86_64 */
+#define XXH_AVX2 2 /* AVX2 for Haswell and Bulldozer */
+#define XXH_AVX512 3 /* AVX512 for Skylake and Icelake */
+#define XXH_NEON 4 /* NEON for most ARMv7-A and all AArch64 */
+#define XXH_VSX 5 /* VSX and ZVector for POWER8/z13 */
+
+#ifndef XXH_VECTOR /* can be defined on command line */
+# if defined(__AVX512F__)
+# define XXH_VECTOR XXH_AVX512
+# elif defined(__AVX2__)
+# define XXH_VECTOR XXH_AVX2
+# elif defined(__SSE2__) || defined(_M_AMD64) || defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP == 2))
+# define XXH_VECTOR XXH_SSE2
+# elif defined(__GNUC__) /* msvc support maybe later */ \
+ && (defined(__ARM_NEON__) || defined(__ARM_NEON)) \
+ && (defined(__LITTLE_ENDIAN__) /* We only support little endian NEON */ \
+ || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
+# define XXH_VECTOR XXH_NEON
+# elif (defined(__PPC64__) && defined(__POWER8_VECTOR__)) \
+ || (defined(__s390x__) && defined(__VEC__)) \
+ && defined(__GNUC__) /* TODO: IBM XL */
+# define XXH_VECTOR XXH_VSX
+# else
+# define XXH_VECTOR XXH_SCALAR
+# endif
+#endif
+
+/*
+ * Controls the alignment of the accumulator,
+ * for compatibility with aligned vector loads, which are usually faster.
+ */
+#ifndef XXH_ACC_ALIGN
+# if defined(XXH_X86DISPATCH)
+# define XXH_ACC_ALIGN 64 /* for compatibility with avx512 */
+# elif XXH_VECTOR == XXH_SCALAR /* scalar */
+# define XXH_ACC_ALIGN 8
+# elif XXH_VECTOR == XXH_SSE2 /* sse2 */
+# define XXH_ACC_ALIGN 16
+# elif XXH_VECTOR == XXH_AVX2 /* avx2 */
+# define XXH_ACC_ALIGN 32
+# elif XXH_VECTOR == XXH_NEON /* neon */
+# define XXH_ACC_ALIGN 16
+# elif XXH_VECTOR == XXH_VSX /* vsx */
+# define XXH_ACC_ALIGN 16
+# elif XXH_VECTOR == XXH_AVX512 /* avx512 */
+# define XXH_ACC_ALIGN 64
+# endif
+#endif
+
+#if defined(XXH_X86DISPATCH) || XXH_VECTOR == XXH_SSE2 \
+ || XXH_VECTOR == XXH_AVX2 || XXH_VECTOR == XXH_AVX512
+# define XXH_SEC_ALIGN XXH_ACC_ALIGN
+#else
+# define XXH_SEC_ALIGN 8
+#endif
+
+/*
+ * UGLY HACK:
+ * GCC usually generates the best code with -O3 for xxHash.
+ *
+ * However, when targeting AVX2, it is overzealous in its unrolling resulting
+ * in code roughly 3/4 the speed of Clang.
+ *
+ * There are other issues, such as GCC splitting _mm256_loadu_si256 into
+ * _mm_loadu_si128 + _mm256_inserti128_si256. This is an optimization which
+ * only applies to Sandy and Ivy Bridge... which don't even support AVX2.
+ *
+ * That is why when compiling the AVX2 version, it is recommended to use either
+ * -O2 -mavx2 -march=haswell
+ * or
+ * -O2 -mavx2 -mno-avx256-split-unaligned-load
+ * for decent performance, or to use Clang instead.
+ *
+ * Fortunately, we can control the first one with a pragma that forces GCC into
+ * -O2, but the other one we can't control without "failed to inline always
+ * inline function due to target mismatch" warnings.
+ */
+#if XXH_VECTOR == XXH_AVX2 /* AVX2 */ \
+ && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \
+ && defined(__OPTIMIZE__) && !defined(__OPTIMIZE_SIZE__) /* respect -O0 and -Os */
+# pragma GCC push_options
+# pragma GCC optimize("-O2")
+#endif
+
+
+#if XXH_VECTOR == XXH_NEON
+/*
+ * NEON's setup for vmlal_u32 is a little more complicated than it is on
+ * SSE2, AVX2, and VSX.
+ *
+ * While PMULUDQ and VMULEUW both perform a mask, VMLAL.U32 performs an upcast.
+ *
+ * To do the same operation, the 128-bit 'Q' register needs to be split into
+ * two 64-bit 'D' registers, performing this operation::
+ *
+ * [ a | b ]
+ * | '---------. .--------' |
+ * | x |
+ * | .---------' '--------. |
+ * [ a & 0xFFFFFFFF | b & 0xFFFFFFFF ],[ a >> 32 | b >> 32 ]
+ *
+ * Due to significant changes in aarch64, the fastest method for aarch64 is
+ * completely different than the fastest method for ARMv7-A.
+ *
+ * ARMv7-A treats D registers as unions overlaying Q registers, so modifying
+ * D11 will modify the high half of Q5. This is similar to how modifying AH
+ * will only affect bits 8-15 of AX on x86.
+ *
+ * VZIP takes two registers, and puts even lanes in one register and odd lanes
+ * in the other.
+ *
+ * On ARMv7-A, this strangely modifies both parameters in place instead of
+ * taking the usual 3-operand form.
+ *
+ * Therefore, if we want to do this, we can simply use a D-form VZIP.32 on the
+ * lower and upper halves of the Q register to end up with the high and low
+ * halves where we want - all in one instruction.
+ *
+ * vzip.32 d10, d11 @ d10 = { d10[0], d11[0] }; d11 = { d10[1], d11[1] }
+ *
+ * Unfortunately we need inline assembly for this: Instructions modifying two
+ * registers at once is not possible in GCC or Clang's IR, and they have to
+ * create a copy.
+ *
+ * aarch64 requires a different approach.
+ *
+ * In order to make it easier to write a decent compiler for aarch64, many
+ * quirks were removed, such as conditional execution.
+ *
+ * NEON was also affected by this.
+ *
+ * aarch64 cannot access the high bits of a Q-form register, and writes to a
+ * D-form register zero the high bits, similar to how writes to W-form scalar
+ * registers (or DWORD registers on x86_64) work.
+ *
+ * The formerly free vget_high intrinsics now require a vext (with a few
+ * exceptions)
+ *
+ * Additionally, VZIP was replaced by ZIP1 and ZIP2, which are the equivalent
+ * of PUNPCKL* and PUNPCKH* in SSE, respectively, in order to only modify one
+ * operand.
+ *
+ * The equivalent of the VZIP.32 on the lower and upper halves would be this
+ * mess:
+ *
+ * ext v2.4s, v0.4s, v0.4s, #2 // v2 = { v0[2], v0[3], v0[0], v0[1] }
+ * zip1 v1.2s, v0.2s, v2.2s // v1 = { v0[0], v2[0] }
+ * zip2 v0.2s, v0.2s, v1.2s // v0 = { v0[1], v2[1] }
+ *
+ * Instead, we use a literal downcast, vmovn_u64 (XTN), and vshrn_n_u64 (SHRN):
+ *
+ * shrn v1.2s, v0.2d, #32 // v1 = (uint32x2_t)(v0 >> 32);
+ * xtn v0.2s, v0.2d // v0 = (uint32x2_t)(v0 & 0xFFFFFFFF);
+ *
+ * This is available on ARMv7-A, but is less efficient than a single VZIP.32.
+ */
+
+/*
+ * Function-like macro:
+ * void XXH_SPLIT_IN_PLACE(uint64x2_t &in, uint32x2_t &outLo, uint32x2_t &outHi)
+ * {
+ * outLo = (uint32x2_t)(in & 0xFFFFFFFF);
+ * outHi = (uint32x2_t)(in >> 32);
+ * in = UNDEFINED;
+ * }
+ */
+# if !defined(XXH_NO_VZIP_HACK) /* define to disable */ \
+ && defined(__GNUC__) \
+ && !defined(__aarch64__) && !defined(__arm64__)
+# define XXH_SPLIT_IN_PLACE(in, outLo, outHi) \
+ do { \
+ /* Undocumented GCC/Clang operand modifier: %e0 = lower D half, %f0 = upper D half */ \
+ /* https://github.com/gcc-mirror/gcc/blob/38cf91e5/gcc/config/arm/arm.c#L22486 */ \
+ /* https://github.com/llvm-mirror/llvm/blob/2c4ca683/lib/Target/ARM/ARMAsmPrinter.cpp#L399 */ \
+ __asm__("vzip.32 %e0, %f0" : "+w" (in)); \
+ (outLo) = vget_low_u32 (vreinterpretq_u32_u64(in)); \
+ (outHi) = vget_high_u32(vreinterpretq_u32_u64(in)); \
+ } while (0)
+# else
+# define XXH_SPLIT_IN_PLACE(in, outLo, outHi) \
+ do { \
+ (outLo) = vmovn_u64 (in); \
+ (outHi) = vshrn_n_u64 ((in), 32); \
+ } while (0)
+# endif
+#endif /* XXH_VECTOR == XXH_NEON */
+
+/*
+ * VSX and Z Vector helpers.
+ *
+ * This is very messy, and any pull requests to clean this up are welcome.
+ *
+ * There are a lot of problems with supporting VSX and s390x, due to
+ * inconsistent intrinsics, spotty coverage, and multiple endiannesses.
+ */
+#if XXH_VECTOR == XXH_VSX
+# if defined(__s390x__)
+# include <s390intrin.h>
+# else
+/* gcc's altivec.h can have the unwanted consequence to unconditionally
+ * #define bool, vector, and pixel keywords,
+ * with bad consequences for programs already using these keywords for other purposes.
+ * The paragraph defining these macros is skipped when __APPLE_ALTIVEC__ is defined.
+ * __APPLE_ALTIVEC__ is _generally_ defined automatically by the compiler,
+ * but it seems that, in some cases, it isn't.
+ * Force the build macro to be defined, so that keywords are not altered.
+ */
+# if defined(__GNUC__) && !defined(__APPLE_ALTIVEC__)
+# define __APPLE_ALTIVEC__
+# endif
+# include <altivec.h>
+# endif
+
+typedef __vector unsigned long long xxh_u64x2;
+typedef __vector unsigned char xxh_u8x16;
+typedef __vector unsigned xxh_u32x4;
+
+# ifndef XXH_VSX_BE
+# if defined(__BIG_ENDIAN__) \
+ || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+# define XXH_VSX_BE 1
+# elif defined(__VEC_ELEMENT_REG_ORDER__) && __VEC_ELEMENT_REG_ORDER__ == __ORDER_BIG_ENDIAN__
+# warning "-maltivec=be is not recommended. Please use native endianness."
+# define XXH_VSX_BE 1
+# else
+# define XXH_VSX_BE 0
+# endif
+# endif /* !defined(XXH_VSX_BE) */
+
+# if XXH_VSX_BE
+/* A wrapper for POWER9's vec_revb. */
+# if defined(__POWER9_VECTOR__) || (defined(__clang__) && defined(__s390x__))
+# define XXH_vec_revb vec_revb
+# else
+XXH_FORCE_INLINE xxh_u64x2 XXH_vec_revb(xxh_u64x2 val)
+{
+ xxh_u8x16 const vByteSwap = { 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+ 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08 };
+ return vec_perm(val, val, vByteSwap);
+}
+# endif
+# endif /* XXH_VSX_BE */
+
+/*
+ * Performs an unaligned load and byte swaps it on big endian.
+ */
+XXH_FORCE_INLINE xxh_u64x2 XXH_vec_loadu(const void *ptr)
+{
+ xxh_u64x2 ret;
+ memcpy(&ret, ptr, sizeof(xxh_u64x2));
+# if XXH_VSX_BE
+ ret = XXH_vec_revb(ret);
+# endif
+ return ret;
+}
+
+/*
+ * vec_mulo and vec_mule are very problematic intrinsics on PowerPC
+ *
+ * These intrinsics weren't added until GCC 8, despite existing for a while,
+ * and they are endian dependent. Also, their meaning swap depending on version.
+ * */
+# if defined(__s390x__)
+ /* s390x is always big endian, no issue on this platform */
+# define XXH_vec_mulo vec_mulo
+# define XXH_vec_mule vec_mule
+# elif defined(__clang__) && XXH_HAS_BUILTIN(__builtin_altivec_vmuleuw)
+/* Clang has a better way to control this, we can just use the builtin which doesn't swap. */
+# define XXH_vec_mulo __builtin_altivec_vmulouw
+# define XXH_vec_mule __builtin_altivec_vmuleuw
+# else
+/* gcc needs inline assembly */
+/* Adapted from https://github.com/google/highwayhash/blob/master/highwayhash/hh_vsx.h. */
+XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mulo(xxh_u32x4 a, xxh_u32x4 b)
+{
+ xxh_u64x2 result;
+ __asm__("vmulouw %0, %1, %2" : "=v" (result) : "v" (a), "v" (b));
+ return result;
+}
+XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mule(xxh_u32x4 a, xxh_u32x4 b)
+{
+ xxh_u64x2 result;
+ __asm__("vmuleuw %0, %1, %2" : "=v" (result) : "v" (a), "v" (b));
+ return result;
+}
+# endif /* XXH_vec_mulo, XXH_vec_mule */
+#endif /* XXH_VECTOR == XXH_VSX */
+
+
+/* prefetch
+ * can be disabled, by declaring XXH_NO_PREFETCH build macro */
+#if defined(XXH_NO_PREFETCH)
+# define XXH_PREFETCH(ptr) (void)(ptr) /* disabled */
+#else
+# if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_I86)) /* _mm_prefetch() is not defined outside of x86/x64 */
+# include <mmintrin.h> /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */
+# define XXH_PREFETCH(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T0)
+# elif defined(__GNUC__) && ( (__GNUC__ >= 4) || ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) ) )
+# define XXH_PREFETCH(ptr) __builtin_prefetch((ptr), 0 /* rw==read */, 3 /* locality */)
+# else
+# define XXH_PREFETCH(ptr) (void)(ptr) /* disabled */
+# endif
+#endif /* XXH_NO_PREFETCH */
+
+
+/* ==========================================
+ * XXH3 default settings
+ * ========================================== */
+
+#define XXH_SECRET_DEFAULT_SIZE 192 /* minimum XXH3_SECRET_SIZE_MIN */
+
+#if (XXH_SECRET_DEFAULT_SIZE < XXH3_SECRET_SIZE_MIN)
+# error "default keyset is not large enough"
+#endif
+
+/* Pseudorandom secret taken directly from FARSH */
+XXH_ALIGN(64) static const xxh_u8 XXH3_kSecret[XXH_SECRET_DEFAULT_SIZE] = {
+ 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c,
+ 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f,
+ 0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21,
+ 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c,
+ 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3,
+ 0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8,
+ 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d,
+ 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64,
+ 0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb,
+ 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e,
+ 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce,
+ 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e,
+};
+
+
+#ifdef XXH_OLD_NAMES
+# define kSecret XXH3_kSecret
+#endif
+
+/*
+ * Calculates a 32-bit to 64-bit long multiply.
+ *
+ * Wraps __emulu on MSVC x86 because it tends to call __allmul when it doesn't
+ * need to (but it shouldn't need to anyways, it is about 7 instructions to do
+ * a 64x64 multiply...). Since we know that this will _always_ emit MULL, we
+ * use that instead of the normal method.
+ *
+ * If you are compiling for platforms like Thumb-1 and don't have a better option,
+ * you may also want to write your own long multiply routine here.
+ *
+ * XXH_FORCE_INLINE xxh_u64 XXH_mult32to64(xxh_u64 x, xxh_u64 y)
+ * {
+ * return (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF);
+ * }
+ */
+#if defined(_MSC_VER) && defined(_M_IX86)
+# include <intrin.h>
+# define XXH_mult32to64(x, y) __emulu((unsigned)(x), (unsigned)(y))
+#else
+/*
+ * Downcast + upcast is usually better than masking on older compilers like
+ * GCC 4.2 (especially 32-bit ones), all without affecting newer compilers.
+ *
+ * The other method, (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF), will AND both operands
+ * and perform a full 64x64 multiply -- entirely redundant on 32-bit.
+ */
+# define XXH_mult32to64(x, y) ((xxh_u64)(xxh_u32)(x) * (xxh_u64)(xxh_u32)(y))
+#endif
+
+/*
+ * Calculates a 64->128-bit long multiply.
+ *
+ * Uses __uint128_t and _umul128 if available, otherwise uses a scalar version.
+ */
+static XXH128_hash_t
+XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs)
+{
+ /*
+ * GCC/Clang __uint128_t method.
+ *
+ * On most 64-bit targets, GCC and Clang define a __uint128_t type.
+ * This is usually the best way as it usually uses a native long 64-bit
+ * multiply, such as MULQ on x86_64 or MUL + UMULH on aarch64.
+ *
+ * Usually.
+ *
+ * Despite being a 32-bit platform, Clang (and emscripten) define this type
+ * despite not having the arithmetic for it. This results in a laggy
+ * compiler builtin call which calculates a full 128-bit multiply.
+ * In that case it is best to use the portable one.
+ * https://github.com/Cyan4973/xxHash/issues/211#issuecomment-515575677
+ */
+#if defined(__GNUC__) && !defined(__wasm__) \
+ && defined(__SIZEOF_INT128__) \
+ || (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128)
+
+ __uint128_t const product = (__uint128_t)lhs * (__uint128_t)rhs;
+ XXH128_hash_t r128;
+ r128.low64 = (xxh_u64)(product);
+ r128.high64 = (xxh_u64)(product >> 64);
+ return r128;
+
+ /*
+ * MSVC for x64's _umul128 method.
+ *
+ * xxh_u64 _umul128(xxh_u64 Multiplier, xxh_u64 Multiplicand, xxh_u64 *HighProduct);
+ *
+ * This compiles to single operand MUL on x64.
+ */
+#elif defined(_M_X64) || defined(_M_IA64)
+
+#ifndef _MSC_VER
+# pragma intrinsic(_umul128)
+#endif
+ xxh_u64 product_high;
+ xxh_u64 const product_low = _umul128(lhs, rhs, &product_high);
+ XXH128_hash_t r128;
+ r128.low64 = product_low;
+ r128.high64 = product_high;
+ return r128;
+
+#else
+ /*
+ * Portable scalar method. Optimized for 32-bit and 64-bit ALUs.
+ *
+ * This is a fast and simple grade school multiply, which is shown below
+ * with base 10 arithmetic instead of base 0x100000000.
+ *
+ * 9 3 // D2 lhs = 93
+ * x 7 5 // D2 rhs = 75
+ * ----------
+ * 1 5 // D2 lo_lo = (93 % 10) * (75 % 10) = 15
+ * 4 5 | // D2 hi_lo = (93 / 10) * (75 % 10) = 45
+ * 2 1 | // D2 lo_hi = (93 % 10) * (75 / 10) = 21
+ * + 6 3 | | // D2 hi_hi = (93 / 10) * (75 / 10) = 63
+ * ---------
+ * 2 7 | // D2 cross = (15 / 10) + (45 % 10) + 21 = 27
+ * + 6 7 | | // D2 upper = (27 / 10) + (45 / 10) + 63 = 67
+ * ---------
+ * 6 9 7 5 // D4 res = (27 * 10) + (15 % 10) + (67 * 100) = 6975
+ *
+ * The reasons for adding the products like this are:
+ * 1. It avoids manual carry tracking. Just like how
+ * (9 * 9) + 9 + 9 = 99, the same applies with this for UINT64_MAX.
+ * This avoids a lot of complexity.
+ *
+ * 2. It hints for, and on Clang, compiles to, the powerful UMAAL
+ * instruction available in ARM's Digital Signal Processing extension
+ * in 32-bit ARMv6 and later, which is shown below:
+ *
+ * void UMAAL(xxh_u32 *RdLo, xxh_u32 *RdHi, xxh_u32 Rn, xxh_u32 Rm)
+ * {
+ * xxh_u64 product = (xxh_u64)*RdLo * (xxh_u64)*RdHi + Rn + Rm;
+ * *RdLo = (xxh_u32)(product & 0xFFFFFFFF);
+ * *RdHi = (xxh_u32)(product >> 32);
+ * }
+ *
+ * This instruction was designed for efficient long multiplication, and
+ * allows this to be calculated in only 4 instructions at speeds
+ * comparable to some 64-bit ALUs.
+ *
+ * 3. It isn't terrible on other platforms. Usually this will be a couple
+ * of 32-bit ADD/ADCs.
+ */
+
+ /* First calculate all of the cross products. */
+ xxh_u64 const lo_lo = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs & 0xFFFFFFFF);
+ xxh_u64 const hi_lo = XXH_mult32to64(lhs >> 32, rhs & 0xFFFFFFFF);
+ xxh_u64 const lo_hi = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs >> 32);
+ xxh_u64 const hi_hi = XXH_mult32to64(lhs >> 32, rhs >> 32);
+
+ /* Now add the products together. These will never overflow. */
+ xxh_u64 const cross = (lo_lo >> 32) + (hi_lo & 0xFFFFFFFF) + lo_hi;
+ xxh_u64 const upper = (hi_lo >> 32) + (cross >> 32) + hi_hi;
+ xxh_u64 const lower = (cross << 32) | (lo_lo & 0xFFFFFFFF);
+
+ XXH128_hash_t r128;
+ r128.low64 = lower;
+ r128.high64 = upper;
+ return r128;
+#endif
+}
+
+/*
+ * Does a 64-bit to 128-bit multiply, then XOR folds it.
+ *
+ * The reason for the separate function is to prevent passing too many structs
+ * around by value. This will hopefully inline the multiply, but we don't force it.
+ */
+static xxh_u64
+XXH3_mul128_fold64(xxh_u64 lhs, xxh_u64 rhs)
+{
+ XXH128_hash_t product = XXH_mult64to128(lhs, rhs);
+ return product.low64 ^ product.high64;
+}
+
+/* Seems to produce slightly better code on GCC for some reason. */
+XXH_FORCE_INLINE xxh_u64 XXH_xorshift64(xxh_u64 v64, int shift)
+{
+ XXH_ASSERT(0 <= shift && shift < 64);
+ return v64 ^ (v64 >> shift);
+}
+
+/*
+ * This is a fast avalanche stage,
+ * suitable when input bits are already partially mixed
+ */
+static XXH64_hash_t XXH3_avalanche(xxh_u64 h64)
+{
+ h64 = XXH_xorshift64(h64, 37);
+ h64 *= 0x165667919E3779F9ULL;
+ h64 = XXH_xorshift64(h64, 32);
+ return h64;
+}
+
+/*
+ * This is a stronger avalanche,
+ * inspired by Pelle Evensen's rrmxmx
+ * preferable when input has not been previously mixed
+ */
+static XXH64_hash_t XXH3_rrmxmx(xxh_u64 h64, xxh_u64 len)
+{
+ /* this mix is inspired by Pelle Evensen's rrmxmx */
+ h64 ^= XXH_rotl64(h64, 49) ^ XXH_rotl64(h64, 24);
+ h64 *= 0x9FB21C651E98DF25ULL;
+ h64 ^= (h64 >> 35) + len ;
+ h64 *= 0x9FB21C651E98DF25ULL;
+ return XXH_xorshift64(h64, 28);
+}
+
+
+/* ==========================================
+ * Short keys
+ * ==========================================
+ * One of the shortcomings of XXH32 and XXH64 was that their performance was
+ * sub-optimal on short lengths. It used an iterative algorithm which strongly
+ * favored lengths that were a multiple of 4 or 8.
+ *
+ * Instead of iterating over individual inputs, we use a set of single shot
+ * functions which piece together a range of lengths and operate in constant time.
+ *
+ * Additionally, the number of multiplies has been significantly reduced. This
+ * reduces latency, especially when emulating 64-bit multiplies on 32-bit.
+ *
+ * Depending on the platform, this may or may not be faster than XXH32, but it
+ * is almost guaranteed to be faster than XXH64.
+ */
+
+/*
+ * At very short lengths, there isn't enough input to fully hide secrets, or use
+ * the entire secret.
+ *
+ * There is also only a limited amount of mixing we can do before significantly
+ * impacting performance.
+ *
+ * Therefore, we use different sections of the secret and always mix two secret
+ * samples with an XOR. This should have no effect on performance on the
+ * seedless or withSeed variants because everything _should_ be constant folded
+ * by modern compilers.
+ *
+ * The XOR mixing hides individual parts of the secret and increases entropy.
+ *
+ * This adds an extra layer of strength for custom secrets.
+ */
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_len_1to3_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
+{
+ XXH_ASSERT(input != NULL);
+ XXH_ASSERT(1 <= len && len <= 3);
+ XXH_ASSERT(secret != NULL);
+ /*
+ * len = 1: combined = { input[0], 0x01, input[0], input[0] }
+ * len = 2: combined = { input[1], 0x02, input[0], input[1] }
+ * len = 3: combined = { input[2], 0x03, input[0], input[1] }
+ */
+ { xxh_u8 const c1 = input[0];
+ xxh_u8 const c2 = input[len >> 1];
+ xxh_u8 const c3 = input[len - 1];
+ xxh_u32 const combined = ((xxh_u32)c1 << 16) | ((xxh_u32)c2 << 24)
+ | ((xxh_u32)c3 << 0) | ((xxh_u32)len << 8);
+ xxh_u64 const bitflip = (XXH_readLE32(secret) ^ XXH_readLE32(secret+4)) + seed;
+ xxh_u64 const keyed = (xxh_u64)combined ^ bitflip;
+ return XXH64_avalanche(keyed);
+ }
+}
+
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_len_4to8_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
+{
+ XXH_ASSERT(input != NULL);
+ XXH_ASSERT(secret != NULL);
+ XXH_ASSERT(4 <= len && len < 8);
+ seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32;
+ { xxh_u32 const input1 = XXH_readLE32(input);
+ xxh_u32 const input2 = XXH_readLE32(input + len - 4);
+ xxh_u64 const bitflip = (XXH_readLE64(secret+8) ^ XXH_readLE64(secret+16)) - seed;
+ xxh_u64 const input64 = input2 + (((xxh_u64)input1) << 32);
+ xxh_u64 const keyed = input64 ^ bitflip;
+ return XXH3_rrmxmx(keyed, len);
+ }
+}
+
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_len_9to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
+{
+ XXH_ASSERT(input != NULL);
+ XXH_ASSERT(secret != NULL);
+ XXH_ASSERT(8 <= len && len <= 16);
+ { xxh_u64 const bitflip1 = (XXH_readLE64(secret+24) ^ XXH_readLE64(secret+32)) + seed;
+ xxh_u64 const bitflip2 = (XXH_readLE64(secret+40) ^ XXH_readLE64(secret+48)) - seed;
+ xxh_u64 const input_lo = XXH_readLE64(input) ^ bitflip1;
+ xxh_u64 const input_hi = XXH_readLE64(input + len - 8) ^ bitflip2;
+ xxh_u64 const acc = len
+ + XXH_swap64(input_lo) + input_hi
+ + XXH3_mul128_fold64(input_lo, input_hi);
+ return XXH3_avalanche(acc);
+ }
+}
+
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_len_0to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
+{
+ XXH_ASSERT(len <= 16);
+ { if (XXH_likely(len > 8)) return XXH3_len_9to16_64b(input, len, secret, seed);
+ if (XXH_likely(len >= 4)) return XXH3_len_4to8_64b(input, len, secret, seed);
+ if (len) return XXH3_len_1to3_64b(input, len, secret, seed);
+ return XXH64_avalanche(seed ^ (XXH_readLE64(secret+56) ^ XXH_readLE64(secret+64)));
+ }
+}
+
+/*
+ * DISCLAIMER: There are known *seed-dependent* multicollisions here due to
+ * multiplication by zero, affecting hashes of lengths 17 to 240.
+ *
+ * However, they are very unlikely.
+ *
+ * Keep this in mind when using the unseeded XXH3_64bits() variant: As with all
+ * unseeded non-cryptographic hashes, it does not attempt to defend itself
+ * against specially crafted inputs, only random inputs.
+ *
+ * Compared to classic UMAC where a 1 in 2^31 chance of 4 consecutive bytes
+ * cancelling out the secret is taken an arbitrary number of times (addressed
+ * in XXH3_accumulate_512), this collision is very unlikely with random inputs
+ * and/or proper seeding:
+ *
+ * This only has a 1 in 2^63 chance of 8 consecutive bytes cancelling out, in a
+ * function that is only called up to 16 times per hash with up to 240 bytes of
+ * input.
+ *
+ * This is not too bad for a non-cryptographic hash function, especially with
+ * only 64 bit outputs.
+ *
+ * The 128-bit variant (which trades some speed for strength) is NOT affected
+ * by this, although it is always a good idea to use a proper seed if you care
+ * about strength.
+ */
+XXH_FORCE_INLINE xxh_u64 XXH3_mix16B(const xxh_u8* XXH_RESTRICT input,
+ const xxh_u8* XXH_RESTRICT secret, xxh_u64 seed64)
+{
+#if defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \
+ && defined(__i386__) && defined(__SSE2__) /* x86 + SSE2 */ \
+ && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable like XXH32 hack */
+ /*
+ * UGLY HACK:
+ * GCC for x86 tends to autovectorize the 128-bit multiply, resulting in
+ * slower code.
+ *
+ * By forcing seed64 into a register, we disrupt the cost model and
+ * cause it to scalarize. See `XXH32_round()`
+ *
+ * FIXME: Clang's output is still _much_ faster -- On an AMD Ryzen 3600,
+ * XXH3_64bits @ len=240 runs at 4.6 GB/s with Clang 9, but 3.3 GB/s on
+ * GCC 9.2, despite both emitting scalar code.
+ *
+ * GCC generates much better scalar code than Clang for the rest of XXH3,
+ * which is why finding a more optimal codepath is an interest.
+ */
+ __asm__ ("" : "+r" (seed64));
+#endif
+ { xxh_u64 const input_lo = XXH_readLE64(input);
+ xxh_u64 const input_hi = XXH_readLE64(input+8);
+ return XXH3_mul128_fold64(
+ input_lo ^ (XXH_readLE64(secret) + seed64),
+ input_hi ^ (XXH_readLE64(secret+8) - seed64)
+ );
+ }
+}
+
+/* For mid range keys, XXH3 uses a Mum-hash variant. */
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_len_17to128_64b(const xxh_u8* XXH_RESTRICT input, size_t len,
+ const xxh_u8* XXH_RESTRICT secret, size_t secretSize,
+ XXH64_hash_t seed)
+{
+ XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize;
+ XXH_ASSERT(16 < len && len <= 128);
+
+ { xxh_u64 acc = len * XXH_PRIME64_1;
+ if (len > 32) {
+ if (len > 64) {
+ if (len > 96) {
+ acc += XXH3_mix16B(input+48, secret+96, seed);
+ acc += XXH3_mix16B(input+len-64, secret+112, seed);
+ }
+ acc += XXH3_mix16B(input+32, secret+64, seed);
+ acc += XXH3_mix16B(input+len-48, secret+80, seed);
+ }
+ acc += XXH3_mix16B(input+16, secret+32, seed);
+ acc += XXH3_mix16B(input+len-32, secret+48, seed);
+ }
+ acc += XXH3_mix16B(input+0, secret+0, seed);
+ acc += XXH3_mix16B(input+len-16, secret+16, seed);
+
+ return XXH3_avalanche(acc);
+ }
+}
+
+#define XXH3_MIDSIZE_MAX 240
+
+XXH_NO_INLINE XXH64_hash_t
+XXH3_len_129to240_64b(const xxh_u8* XXH_RESTRICT input, size_t len,
+ const xxh_u8* XXH_RESTRICT secret, size_t secretSize,
+ XXH64_hash_t seed)
+{
+ XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize;
+ XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX);
+
+ #define XXH3_MIDSIZE_STARTOFFSET 3
+ #define XXH3_MIDSIZE_LASTOFFSET 17
+
+ { xxh_u64 acc = len * XXH_PRIME64_1;
+ int const nbRounds = (int)len / 16;
+ int i;
+ for (i=0; i<8; i++) {
+ acc += XXH3_mix16B(input+(16*i), secret+(16*i), seed);
+ }
+ acc = XXH3_avalanche(acc);
+ XXH_ASSERT(nbRounds >= 8);
+#if defined(__clang__) /* Clang */ \
+ && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \
+ && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable */
+ /*
+ * UGLY HACK:
+ * Clang for ARMv7-A tries to vectorize this loop, similar to GCC x86.
+ * In everywhere else, it uses scalar code.
+ *
+ * For 64->128-bit multiplies, even if the NEON was 100% optimal, it
+ * would still be slower than UMAAL (see XXH_mult64to128).
+ *
+ * Unfortunately, Clang doesn't handle the long multiplies properly and
+ * converts them to the nonexistent "vmulq_u64" intrinsic, which is then
+ * scalarized into an ugly mess of VMOV.32 instructions.
+ *
+ * This mess is difficult to avoid without turning autovectorization
+ * off completely, but they are usually relatively minor and/or not
+ * worth it to fix.
+ *
+ * This loop is the easiest to fix, as unlike XXH32, this pragma
+ * _actually works_ because it is a loop vectorization instead of an
+ * SLP vectorization.
+ */
+ #pragma clang loop vectorize(disable)
+#endif
+ for (i=8 ; i < nbRounds; i++) {
+ acc += XXH3_mix16B(input+(16*i), secret+(16*(i-8)) + XXH3_MIDSIZE_STARTOFFSET, seed);
+ }
+ /* last bytes */
+ acc += XXH3_mix16B(input + len - 16, secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET, seed);
+ return XXH3_avalanche(acc);
+ }
+}
+
+
+/* ======= Long Keys ======= */
+
+#define XXH_STRIPE_LEN 64
+#define XXH_SECRET_CONSUME_RATE 8 /* nb of secret bytes consumed at each accumulation */
+#define XXH_ACC_NB (XXH_STRIPE_LEN / sizeof(xxh_u64))
+
+#ifdef XXH_OLD_NAMES
+# define STRIPE_LEN XXH_STRIPE_LEN
+# define ACC_NB XXH_ACC_NB
+#endif
+
+XXH_FORCE_INLINE void XXH_writeLE64(void* dst, xxh_u64 v64)
+{
+ if (!XXH_CPU_LITTLE_ENDIAN) v64 = XXH_swap64(v64);
+ memcpy(dst, &v64, sizeof(v64));
+}
+
+/* Several intrinsic functions below are supposed to accept __int64 as argument,
+ * as documented in https://software.intel.com/sites/landingpage/IntrinsicsGuide/ .
+ * However, several environments do not define __int64 type,
+ * requiring a workaround.
+ */
+#if !defined (__VMS) \
+ && (defined (__cplusplus) \
+ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
+ typedef int64_t xxh_i64;
+#else
+ /* the following type must have a width of 64-bit */
+ typedef long long xxh_i64;
+#endif
+
+/*
+ * XXH3_accumulate_512 is the tightest loop for long inputs, and it is the most optimized.
+ *
+ * It is a hardened version of UMAC, based off of FARSH's implementation.
+ *
+ * This was chosen because it adapts quite well to 32-bit, 64-bit, and SIMD
+ * implementations, and it is ridiculously fast.
+ *
+ * We harden it by mixing the original input to the accumulators as well as the product.
+ *
+ * This means that in the (relatively likely) case of a multiply by zero, the
+ * original input is preserved.
+ *
+ * On 128-bit inputs, we swap 64-bit pairs when we add the input to improve
+ * cross-pollination, as otherwise the upper and lower halves would be
+ * essentially independent.
+ *
+ * This doesn't matter on 64-bit hashes since they all get merged together in
+ * the end, so we skip the extra step.
+ *
+ * Both XXH3_64bits and XXH3_128bits use this subroutine.
+ */
+
+#if (XXH_VECTOR == XXH_AVX512) || defined(XXH_X86DISPATCH)
+
+#ifndef XXH_TARGET_AVX512
+# define XXH_TARGET_AVX512 /* disable attribute target */
+#endif
+
+XXH_FORCE_INLINE XXH_TARGET_AVX512 void
+XXH3_accumulate_512_avx512(void* XXH_RESTRICT acc,
+ const void* XXH_RESTRICT input,
+ const void* XXH_RESTRICT secret)
+{
+ XXH_ALIGN(64) __m512i* const xacc = (__m512i *) acc;
+ XXH_ASSERT((((size_t)acc) & 63) == 0);
+ XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i));
+
+ {
+ /* data_vec = input[0]; */
+ __m512i const data_vec = _mm512_loadu_si512 (input);
+ /* key_vec = secret[0]; */
+ __m512i const key_vec = _mm512_loadu_si512 (secret);
+ /* data_key = data_vec ^ key_vec; */
+ __m512i const data_key = _mm512_xor_si512 (data_vec, key_vec);
+ /* data_key_lo = data_key >> 32; */
+ __m512i const data_key_lo = _mm512_shuffle_epi32 (data_key, (_MM_PERM_ENUM)_MM_SHUFFLE(0, 3, 0, 1));
+ /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */
+ __m512i const product = _mm512_mul_epu32 (data_key, data_key_lo);
+ /* xacc[0] += swap(data_vec); */
+ __m512i const data_swap = _mm512_shuffle_epi32(data_vec, (_MM_PERM_ENUM)_MM_SHUFFLE(1, 0, 3, 2));
+ __m512i const sum = _mm512_add_epi64(*xacc, data_swap);
+ /* xacc[0] += product; */
+ *xacc = _mm512_add_epi64(product, sum);
+ }
+}
+
+/*
+ * XXH3_scrambleAcc: Scrambles the accumulators to improve mixing.
+ *
+ * Multiplication isn't perfect, as explained by Google in HighwayHash:
+ *
+ * // Multiplication mixes/scrambles bytes 0-7 of the 64-bit result to
+ * // varying degrees. In descending order of goodness, bytes
+ * // 3 4 2 5 1 6 0 7 have quality 228 224 164 160 100 96 36 32.
+ * // As expected, the upper and lower bytes are much worse.
+ *
+ * Source: https://github.com/google/highwayhash/blob/0aaf66b/highwayhash/hh_avx2.h#L291
+ *
+ * Since our algorithm uses a pseudorandom secret to add some variance into the
+ * mix, we don't need to (or want to) mix as often or as much as HighwayHash does.
+ *
+ * This isn't as tight as XXH3_accumulate, but still written in SIMD to avoid
+ * extraction.
+ *
+ * Both XXH3_64bits and XXH3_128bits use this subroutine.
+ */
+
+XXH_FORCE_INLINE XXH_TARGET_AVX512 void
+XXH3_scrambleAcc_avx512(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)
+{
+ XXH_ASSERT((((size_t)acc) & 63) == 0);
+ XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i));
+ { XXH_ALIGN(64) __m512i* const xacc = (__m512i*) acc;
+ const __m512i prime32 = _mm512_set1_epi32((int)XXH_PRIME32_1);
+
+ /* xacc[0] ^= (xacc[0] >> 47) */
+ __m512i const acc_vec = *xacc;
+ __m512i const shifted = _mm512_srli_epi64 (acc_vec, 47);
+ __m512i const data_vec = _mm512_xor_si512 (acc_vec, shifted);
+ /* xacc[0] ^= secret; */
+ __m512i const key_vec = _mm512_loadu_si512 (secret);
+ __m512i const data_key = _mm512_xor_si512 (data_vec, key_vec);
+
+ /* xacc[0] *= XXH_PRIME32_1; */
+ __m512i const data_key_hi = _mm512_shuffle_epi32 (data_key, (_MM_PERM_ENUM)_MM_SHUFFLE(0, 3, 0, 1));
+ __m512i const prod_lo = _mm512_mul_epu32 (data_key, prime32);
+ __m512i const prod_hi = _mm512_mul_epu32 (data_key_hi, prime32);
+ *xacc = _mm512_add_epi64(prod_lo, _mm512_slli_epi64(prod_hi, 32));
+ }
+}
+
+XXH_FORCE_INLINE XXH_TARGET_AVX512 void
+XXH3_initCustomSecret_avx512(void* XXH_RESTRICT customSecret, xxh_u64 seed64)
+{
+ XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 63) == 0);
+ XXH_STATIC_ASSERT(XXH_SEC_ALIGN == 64);
+ XXH_ASSERT(((size_t)customSecret & 63) == 0);
+ (void)(&XXH_writeLE64);
+ { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m512i);
+ __m512i const seed = _mm512_mask_set1_epi64(_mm512_set1_epi64((xxh_i64)seed64), 0xAA, -(xxh_i64)seed64);
+
+ XXH_ALIGN(64) const __m512i* const src = (const __m512i*) XXH3_kSecret;
+ XXH_ALIGN(64) __m512i* const dest = ( __m512i*) customSecret;
+ int i;
+ for (i=0; i < nbRounds; ++i) {
+ /* GCC has a bug, _mm512_stream_load_si512 accepts 'void*', not 'void const*',
+ * this will warn "discards ‘const’ qualifier". */
+ union {
+ XXH_ALIGN(64) const __m512i* cp;
+ XXH_ALIGN(64) void* p;
+ } remote_const_void;
+ remote_const_void.cp = src + i;
+ dest[i] = _mm512_add_epi64(_mm512_stream_load_si512(remote_const_void.p), seed);
+ } }
+}
+
+#endif
+
+#if (XXH_VECTOR == XXH_AVX2) || defined(XXH_X86DISPATCH)
+
+#ifndef XXH_TARGET_AVX2
+# define XXH_TARGET_AVX2 /* disable attribute target */
+#endif
+
+XXH_FORCE_INLINE XXH_TARGET_AVX2 void
+XXH3_accumulate_512_avx2( void* XXH_RESTRICT acc,
+ const void* XXH_RESTRICT input,
+ const void* XXH_RESTRICT secret)
+{
+ XXH_ASSERT((((size_t)acc) & 31) == 0);
+ { XXH_ALIGN(32) __m256i* const xacc = (__m256i *) acc;
+ /* Unaligned. This is mainly for pointer arithmetic, and because
+ * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */
+ const __m256i* const xinput = (const __m256i *) input;
+ /* Unaligned. This is mainly for pointer arithmetic, and because
+ * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */
+ const __m256i* const xsecret = (const __m256i *) secret;
+
+ size_t i;
+ for (i=0; i < XXH_STRIPE_LEN/sizeof(__m256i); i++) {
+ /* data_vec = xinput[i]; */
+ __m256i const data_vec = _mm256_loadu_si256 (xinput+i);
+ /* key_vec = xsecret[i]; */
+ __m256i const key_vec = _mm256_loadu_si256 (xsecret+i);
+ /* data_key = data_vec ^ key_vec; */
+ __m256i const data_key = _mm256_xor_si256 (data_vec, key_vec);
+ /* data_key_lo = data_key >> 32; */
+ __m256i const data_key_lo = _mm256_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1));
+ /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */
+ __m256i const product = _mm256_mul_epu32 (data_key, data_key_lo);
+ /* xacc[i] += swap(data_vec); */
+ __m256i const data_swap = _mm256_shuffle_epi32(data_vec, _MM_SHUFFLE(1, 0, 3, 2));
+ __m256i const sum = _mm256_add_epi64(xacc[i], data_swap);
+ /* xacc[i] += product; */
+ xacc[i] = _mm256_add_epi64(product, sum);
+ } }
+}
+
+XXH_FORCE_INLINE XXH_TARGET_AVX2 void
+XXH3_scrambleAcc_avx2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)
+{
+ XXH_ASSERT((((size_t)acc) & 31) == 0);
+ { XXH_ALIGN(32) __m256i* const xacc = (__m256i*) acc;
+ /* Unaligned. This is mainly for pointer arithmetic, and because
+ * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */
+ const __m256i* const xsecret = (const __m256i *) secret;
+ const __m256i prime32 = _mm256_set1_epi32((int)XXH_PRIME32_1);
+
+ size_t i;
+ for (i=0; i < XXH_STRIPE_LEN/sizeof(__m256i); i++) {
+ /* xacc[i] ^= (xacc[i] >> 47) */
+ __m256i const acc_vec = xacc[i];
+ __m256i const shifted = _mm256_srli_epi64 (acc_vec, 47);
+ __m256i const data_vec = _mm256_xor_si256 (acc_vec, shifted);
+ /* xacc[i] ^= xsecret; */
+ __m256i const key_vec = _mm256_loadu_si256 (xsecret+i);
+ __m256i const data_key = _mm256_xor_si256 (data_vec, key_vec);
+
+ /* xacc[i] *= XXH_PRIME32_1; */
+ __m256i const data_key_hi = _mm256_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1));
+ __m256i const prod_lo = _mm256_mul_epu32 (data_key, prime32);
+ __m256i const prod_hi = _mm256_mul_epu32 (data_key_hi, prime32);
+ xacc[i] = _mm256_add_epi64(prod_lo, _mm256_slli_epi64(prod_hi, 32));
+ }
+ }
+}
+
+XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_initCustomSecret_avx2(void* XXH_RESTRICT customSecret, xxh_u64 seed64)
+{
+ XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 31) == 0);
+ XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE / sizeof(__m256i)) == 6);
+ XXH_STATIC_ASSERT(XXH_SEC_ALIGN <= 64);
+ (void)(&XXH_writeLE64);
+ XXH_PREFETCH(customSecret);
+ { __m256i const seed = _mm256_set_epi64x(-(xxh_i64)seed64, (xxh_i64)seed64, -(xxh_i64)seed64, (xxh_i64)seed64);
+
+ XXH_ALIGN(64) const __m256i* const src = (const __m256i*) XXH3_kSecret;
+ XXH_ALIGN(64) __m256i* dest = ( __m256i*) customSecret;
+
+# if defined(__GNUC__) || defined(__clang__)
+ /*
+ * On GCC & Clang, marking 'dest' as modified will cause the compiler:
+ * - do not extract the secret from sse registers in the internal loop
+ * - use less common registers, and avoid pushing these reg into stack
+ * The asm hack causes Clang to assume that XXH3_kSecretPtr aliases with
+ * customSecret, and on aarch64, this prevented LDP from merging two
+ * loads together for free. Putting the loads together before the stores
+ * properly generates LDP.
+ */
+ __asm__("" : "+r" (dest));
+# endif
+
+ /* GCC -O2 need unroll loop manually */
+ dest[0] = _mm256_add_epi64(_mm256_stream_load_si256(src+0), seed);
+ dest[1] = _mm256_add_epi64(_mm256_stream_load_si256(src+1), seed);
+ dest[2] = _mm256_add_epi64(_mm256_stream_load_si256(src+2), seed);
+ dest[3] = _mm256_add_epi64(_mm256_stream_load_si256(src+3), seed);
+ dest[4] = _mm256_add_epi64(_mm256_stream_load_si256(src+4), seed);
+ dest[5] = _mm256_add_epi64(_mm256_stream_load_si256(src+5), seed);
+ }
+}
+
+#endif
+
+#if (XXH_VECTOR == XXH_SSE2) || defined(XXH_X86DISPATCH)
+
+#ifndef XXH_TARGET_SSE2
+# define XXH_TARGET_SSE2 /* disable attribute target */
+#endif
+
+XXH_FORCE_INLINE XXH_TARGET_SSE2 void
+XXH3_accumulate_512_sse2( void* XXH_RESTRICT acc,
+ const void* XXH_RESTRICT input,
+ const void* XXH_RESTRICT secret)
+{
+ /* SSE2 is just a half-scale version of the AVX2 version. */
+ XXH_ASSERT((((size_t)acc) & 15) == 0);
+ { XXH_ALIGN(16) __m128i* const xacc = (__m128i *) acc;
+ /* Unaligned. This is mainly for pointer arithmetic, and because
+ * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */
+ const __m128i* const xinput = (const __m128i *) input;
+ /* Unaligned. This is mainly for pointer arithmetic, and because
+ * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */
+ const __m128i* const xsecret = (const __m128i *) secret;
+
+ size_t i;
+ for (i=0; i < XXH_STRIPE_LEN/sizeof(__m128i); i++) {
+ /* data_vec = xinput[i]; */
+ __m128i const data_vec = _mm_loadu_si128 (xinput+i);
+ /* key_vec = xsecret[i]; */
+ __m128i const key_vec = _mm_loadu_si128 (xsecret+i);
+ /* data_key = data_vec ^ key_vec; */
+ __m128i const data_key = _mm_xor_si128 (data_vec, key_vec);
+ /* data_key_lo = data_key >> 32; */
+ __m128i const data_key_lo = _mm_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1));
+ /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */
+ __m128i const product = _mm_mul_epu32 (data_key, data_key_lo);
+ /* xacc[i] += swap(data_vec); */
+ __m128i const data_swap = _mm_shuffle_epi32(data_vec, _MM_SHUFFLE(1,0,3,2));
+ __m128i const sum = _mm_add_epi64(xacc[i], data_swap);
+ /* xacc[i] += product; */
+ xacc[i] = _mm_add_epi64(product, sum);
+ } }
+}
+
+XXH_FORCE_INLINE XXH_TARGET_SSE2 void
+XXH3_scrambleAcc_sse2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)
+{
+ XXH_ASSERT((((size_t)acc) & 15) == 0);
+ { XXH_ALIGN(16) __m128i* const xacc = (__m128i*) acc;
+ /* Unaligned. This is mainly for pointer arithmetic, and because
+ * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */
+ const __m128i* const xsecret = (const __m128i *) secret;
+ const __m128i prime32 = _mm_set1_epi32((int)XXH_PRIME32_1);
+
+ size_t i;
+ for (i=0; i < XXH_STRIPE_LEN/sizeof(__m128i); i++) {
+ /* xacc[i] ^= (xacc[i] >> 47) */
+ __m128i const acc_vec = xacc[i];
+ __m128i const shifted = _mm_srli_epi64 (acc_vec, 47);
+ __m128i const data_vec = _mm_xor_si128 (acc_vec, shifted);
+ /* xacc[i] ^= xsecret[i]; */
+ __m128i const key_vec = _mm_loadu_si128 (xsecret+i);
+ __m128i const data_key = _mm_xor_si128 (data_vec, key_vec);
+
+ /* xacc[i] *= XXH_PRIME32_1; */
+ __m128i const data_key_hi = _mm_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1));
+ __m128i const prod_lo = _mm_mul_epu32 (data_key, prime32);
+ __m128i const prod_hi = _mm_mul_epu32 (data_key_hi, prime32);
+ xacc[i] = _mm_add_epi64(prod_lo, _mm_slli_epi64(prod_hi, 32));
+ }
+ }
+}
+
+XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_initCustomSecret_sse2(void* XXH_RESTRICT customSecret, xxh_u64 seed64)
+{
+ XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0);
+ (void)(&XXH_writeLE64);
+ { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m128i);
+
+# if defined(_MSC_VER) && defined(_M_IX86) && _MSC_VER < 1900
+ // MSVC 32bit mode does not support _mm_set_epi64x before 2015
+ XXH_ALIGN(16) const xxh_i64 seed64x2[2] = { (xxh_i64)seed64, -(xxh_i64)seed64 };
+ __m128i const seed = _mm_load_si128((__m128i const*)seed64x2);
+# else
+ __m128i const seed = _mm_set_epi64x(-(xxh_i64)seed64, (xxh_i64)seed64);
+# endif
+ int i;
+
+ XXH_ALIGN(64) const float* const src = (float const*) XXH3_kSecret;
+ XXH_ALIGN(XXH_SEC_ALIGN) __m128i* dest = (__m128i*) customSecret;
+# if defined(__GNUC__) || defined(__clang__)
+ /*
+ * On GCC & Clang, marking 'dest' as modified will cause the compiler:
+ * - do not extract the secret from sse registers in the internal loop
+ * - use less common registers, and avoid pushing these reg into stack
+ */
+ __asm__("" : "+r" (dest));
+# endif
+
+ for (i=0; i < nbRounds; ++i) {
+ dest[i] = _mm_add_epi64(_mm_castps_si128(_mm_load_ps(src+i*4)), seed);
+ } }
+}
+
+#endif
+
+#if (XXH_VECTOR == XXH_NEON)
+
+XXH_FORCE_INLINE void
+XXH3_accumulate_512_neon( void* XXH_RESTRICT acc,
+ const void* XXH_RESTRICT input,
+ const void* XXH_RESTRICT secret)
+{
+ XXH_ASSERT((((size_t)acc) & 15) == 0);
+ {
+ XXH_ALIGN(16) uint64x2_t* const xacc = (uint64x2_t *) acc;
+ /* We don't use a uint32x4_t pointer because it causes bus errors on ARMv7. */
+ uint8_t const* const xinput = (const uint8_t *) input;
+ uint8_t const* const xsecret = (const uint8_t *) secret;
+
+ size_t i;
+ for (i=0; i < XXH_STRIPE_LEN / sizeof(uint64x2_t); i++) {
+ /* data_vec = xinput[i]; */
+ uint8x16_t data_vec = vld1q_u8(xinput + (i * 16));
+ /* key_vec = xsecret[i]; */
+ uint8x16_t key_vec = vld1q_u8(xsecret + (i * 16));
+ uint64x2_t data_key;
+ uint32x2_t data_key_lo, data_key_hi;
+ /* xacc[i] += swap(data_vec); */
+ uint64x2_t const data64 = vreinterpretq_u64_u8(data_vec);
+ uint64x2_t const swapped = vextq_u64(data64, data64, 1);
+ xacc[i] = vaddq_u64 (xacc[i], swapped);
+ /* data_key = data_vec ^ key_vec; */
+ data_key = vreinterpretq_u64_u8(veorq_u8(data_vec, key_vec));
+ /* data_key_lo = (uint32x2_t) (data_key & 0xFFFFFFFF);
+ * data_key_hi = (uint32x2_t) (data_key >> 32);
+ * data_key = UNDEFINED; */
+ XXH_SPLIT_IN_PLACE(data_key, data_key_lo, data_key_hi);
+ /* xacc[i] += (uint64x2_t) data_key_lo * (uint64x2_t) data_key_hi; */
+ xacc[i] = vmlal_u32 (xacc[i], data_key_lo, data_key_hi);
+
+ }
+ }
+}
+
+XXH_FORCE_INLINE void
+XXH3_scrambleAcc_neon(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)
+{
+ XXH_ASSERT((((size_t)acc) & 15) == 0);
+
+ { uint64x2_t* xacc = (uint64x2_t*) acc;
+ uint8_t const* xsecret = (uint8_t const*) secret;
+ uint32x2_t prime = vdup_n_u32 (XXH_PRIME32_1);
+
+ size_t i;
+ for (i=0; i < XXH_STRIPE_LEN/sizeof(uint64x2_t); i++) {
+ /* xacc[i] ^= (xacc[i] >> 47); */
+ uint64x2_t acc_vec = xacc[i];
+ uint64x2_t shifted = vshrq_n_u64 (acc_vec, 47);
+ uint64x2_t data_vec = veorq_u64 (acc_vec, shifted);
+
+ /* xacc[i] ^= xsecret[i]; */
+ uint8x16_t key_vec = vld1q_u8(xsecret + (i * 16));
+ uint64x2_t data_key = veorq_u64(data_vec, vreinterpretq_u64_u8(key_vec));
+
+ /* xacc[i] *= XXH_PRIME32_1 */
+ uint32x2_t data_key_lo, data_key_hi;
+ /* data_key_lo = (uint32x2_t) (xacc[i] & 0xFFFFFFFF);
+ * data_key_hi = (uint32x2_t) (xacc[i] >> 32);
+ * xacc[i] = UNDEFINED; */
+ XXH_SPLIT_IN_PLACE(data_key, data_key_lo, data_key_hi);
+ { /*
+ * prod_hi = (data_key >> 32) * XXH_PRIME32_1;
+ *
+ * Avoid vmul_u32 + vshll_n_u32 since Clang 6 and 7 will
+ * incorrectly "optimize" this:
+ * tmp = vmul_u32(vmovn_u64(a), vmovn_u64(b));
+ * shifted = vshll_n_u32(tmp, 32);
+ * to this:
+ * tmp = "vmulq_u64"(a, b); // no such thing!
+ * shifted = vshlq_n_u64(tmp, 32);
+ *
+ * However, unlike SSE, Clang lacks a 64-bit multiply routine
+ * for NEON, and it scalarizes two 64-bit multiplies instead.
+ *
+ * vmull_u32 has the same timing as vmul_u32, and it avoids
+ * this bug completely.
+ * See https://bugs.llvm.org/show_bug.cgi?id=39967
+ */
+ uint64x2_t prod_hi = vmull_u32 (data_key_hi, prime);
+ /* xacc[i] = prod_hi << 32; */
+ xacc[i] = vshlq_n_u64(prod_hi, 32);
+ /* xacc[i] += (prod_hi & 0xFFFFFFFF) * XXH_PRIME32_1; */
+ xacc[i] = vmlal_u32(xacc[i], data_key_lo, prime);
+ }
+ } }
+}
+
+#endif
+
+#if (XXH_VECTOR == XXH_VSX)
+
+XXH_FORCE_INLINE void
+XXH3_accumulate_512_vsx( void* XXH_RESTRICT acc,
+ const void* XXH_RESTRICT input,
+ const void* XXH_RESTRICT secret)
+{
+ xxh_u64x2* const xacc = (xxh_u64x2*) acc; /* presumed aligned */
+ xxh_u64x2 const* const xinput = (xxh_u64x2 const*) input; /* no alignment restriction */
+ xxh_u64x2 const* const xsecret = (xxh_u64x2 const*) secret; /* no alignment restriction */
+ xxh_u64x2 const v32 = { 32, 32 };
+ size_t i;
+ for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) {
+ /* data_vec = xinput[i]; */
+ xxh_u64x2 const data_vec = XXH_vec_loadu(xinput + i);
+ /* key_vec = xsecret[i]; */
+ xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + i);
+ xxh_u64x2 const data_key = data_vec ^ key_vec;
+ /* shuffled = (data_key << 32) | (data_key >> 32); */
+ xxh_u32x4 const shuffled = (xxh_u32x4)vec_rl(data_key, v32);
+ /* product = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)shuffled & 0xFFFFFFFF); */
+ xxh_u64x2 const product = XXH_vec_mulo((xxh_u32x4)data_key, shuffled);
+ xacc[i] += product;
+
+ /* swap high and low halves */
+#ifdef __s390x__
+ xacc[i] += vec_permi(data_vec, data_vec, 2);
+#else
+ xacc[i] += vec_xxpermdi(data_vec, data_vec, 2);
+#endif
+ }
+}
+
+XXH_FORCE_INLINE void
+XXH3_scrambleAcc_vsx(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)
+{
+ XXH_ASSERT((((size_t)acc) & 15) == 0);
+
+ { xxh_u64x2* const xacc = (xxh_u64x2*) acc;
+ const xxh_u64x2* const xsecret = (const xxh_u64x2*) secret;
+ /* constants */
+ xxh_u64x2 const v32 = { 32, 32 };
+ xxh_u64x2 const v47 = { 47, 47 };
+ xxh_u32x4 const prime = { XXH_PRIME32_1, XXH_PRIME32_1, XXH_PRIME32_1, XXH_PRIME32_1 };
+ size_t i;
+ for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) {
+ /* xacc[i] ^= (xacc[i] >> 47); */
+ xxh_u64x2 const acc_vec = xacc[i];
+ xxh_u64x2 const data_vec = acc_vec ^ (acc_vec >> v47);
+
+ /* xacc[i] ^= xsecret[i]; */
+ xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + i);
+ xxh_u64x2 const data_key = data_vec ^ key_vec;
+
+ /* xacc[i] *= XXH_PRIME32_1 */
+ /* prod_lo = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)prime & 0xFFFFFFFF); */
+ xxh_u64x2 const prod_even = XXH_vec_mule((xxh_u32x4)data_key, prime);
+ /* prod_hi = ((xxh_u64x2)data_key >> 32) * ((xxh_u64x2)prime >> 32); */
+ xxh_u64x2 const prod_odd = XXH_vec_mulo((xxh_u32x4)data_key, prime);
+ xacc[i] = prod_odd + (prod_even << v32);
+ } }
+}
+
+#endif
+
+/* scalar variants - universal */
+
+XXH_FORCE_INLINE void
+XXH3_accumulate_512_scalar(void* XXH_RESTRICT acc,
+ const void* XXH_RESTRICT input,
+ const void* XXH_RESTRICT secret)
+{
+ XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64* const xacc = (xxh_u64*) acc; /* presumed aligned */
+ const xxh_u8* const xinput = (const xxh_u8*) input; /* no alignment restriction */
+ const xxh_u8* const xsecret = (const xxh_u8*) secret; /* no alignment restriction */
+ size_t i;
+ XXH_ASSERT(((size_t)acc & (XXH_ACC_ALIGN-1)) == 0);
+ for (i=0; i < XXH_ACC_NB; i++) {
+ xxh_u64 const data_val = XXH_readLE64(xinput + 8*i);
+ xxh_u64 const data_key = data_val ^ XXH_readLE64(xsecret + i*8);
+ xacc[i ^ 1] += data_val; /* swap adjacent lanes */
+ xacc[i] += XXH_mult32to64(data_key & 0xFFFFFFFF, data_key >> 32);
+ }
+}
+
+XXH_FORCE_INLINE void
+XXH3_scrambleAcc_scalar(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)
+{
+ XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64* const xacc = (xxh_u64*) acc; /* presumed aligned */
+ const xxh_u8* const xsecret = (const xxh_u8*) secret; /* no alignment restriction */
+ size_t i;
+ XXH_ASSERT((((size_t)acc) & (XXH_ACC_ALIGN-1)) == 0);
+ for (i=0; i < XXH_ACC_NB; i++) {
+ xxh_u64 const key64 = XXH_readLE64(xsecret + 8*i);
+ xxh_u64 acc64 = xacc[i];
+ acc64 = XXH_xorshift64(acc64, 47);
+ acc64 ^= key64;
+ acc64 *= XXH_PRIME32_1;
+ xacc[i] = acc64;
+ }
+}
+
+XXH_FORCE_INLINE void
+XXH3_initCustomSecret_scalar(void* XXH_RESTRICT customSecret, xxh_u64 seed64)
+{
+ /*
+ * We need a separate pointer for the hack below,
+ * which requires a non-const pointer.
+ * Any decent compiler will optimize this out otherwise.
+ */
+ const xxh_u8* kSecretPtr = XXH3_kSecret;
+ XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0);
+
+#if defined(__clang__) && defined(__aarch64__)
+ /*
+ * UGLY HACK:
+ * Clang generates a bunch of MOV/MOVK pairs for aarch64, and they are
+ * placed sequentially, in order, at the top of the unrolled loop.
+ *
+ * While MOVK is great for generating constants (2 cycles for a 64-bit
+ * constant compared to 4 cycles for LDR), long MOVK chains stall the
+ * integer pipelines:
+ * I L S
+ * MOVK
+ * MOVK
+ * MOVK
+ * MOVK
+ * ADD
+ * SUB STR
+ * STR
+ * By forcing loads from memory (as the asm line causes Clang to assume
+ * that XXH3_kSecretPtr has been changed), the pipelines are used more
+ * efficiently:
+ * I L S
+ * LDR
+ * ADD LDR
+ * SUB STR
+ * STR
+ * XXH3_64bits_withSeed, len == 256, Snapdragon 835
+ * without hack: 2654.4 MB/s
+ * with hack: 3202.9 MB/s
+ */
+ __asm__("" : "+r" (kSecretPtr));
+#endif
+ /*
+ * Note: in debug mode, this overrides the asm optimization
+ * and Clang will emit MOVK chains again.
+ */
+ XXH_ASSERT(kSecretPtr == XXH3_kSecret);
+
+ { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / 16;
+ int i;
+ for (i=0; i < nbRounds; i++) {
+ /*
+ * The asm hack causes Clang to assume that kSecretPtr aliases with
+ * customSecret, and on aarch64, this prevented LDP from merging two
+ * loads together for free. Putting the loads together before the stores
+ * properly generates LDP.
+ */
+ xxh_u64 lo = XXH_readLE64(kSecretPtr + 16*i) + seed64;
+ xxh_u64 hi = XXH_readLE64(kSecretPtr + 16*i + 8) - seed64;
+ XXH_writeLE64((xxh_u8*)customSecret + 16*i, lo);
+ XXH_writeLE64((xxh_u8*)customSecret + 16*i + 8, hi);
+ } }
+}
+
+
+typedef void (*XXH3_f_accumulate_512)(void* XXH_RESTRICT, const void*, const void*);
+typedef void (*XXH3_f_scrambleAcc)(void* XXH_RESTRICT, const void*);
+typedef void (*XXH3_f_initCustomSecret)(void* XXH_RESTRICT, xxh_u64);
+
+
+#if (XXH_VECTOR == XXH_AVX512)
+
+#define XXH3_accumulate_512 XXH3_accumulate_512_avx512
+#define XXH3_scrambleAcc XXH3_scrambleAcc_avx512
+#define XXH3_initCustomSecret XXH3_initCustomSecret_avx512
+
+#elif (XXH_VECTOR == XXH_AVX2)
+
+#define XXH3_accumulate_512 XXH3_accumulate_512_avx2
+#define XXH3_scrambleAcc XXH3_scrambleAcc_avx2
+#define XXH3_initCustomSecret XXH3_initCustomSecret_avx2
+
+#elif (XXH_VECTOR == XXH_SSE2)
+
+#define XXH3_accumulate_512 XXH3_accumulate_512_sse2
+#define XXH3_scrambleAcc XXH3_scrambleAcc_sse2
+#define XXH3_initCustomSecret XXH3_initCustomSecret_sse2
+
+#elif (XXH_VECTOR == XXH_NEON)
+
+#define XXH3_accumulate_512 XXH3_accumulate_512_neon
+#define XXH3_scrambleAcc XXH3_scrambleAcc_neon
+#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar
+
+#elif (XXH_VECTOR == XXH_VSX)
+
+#define XXH3_accumulate_512 XXH3_accumulate_512_vsx
+#define XXH3_scrambleAcc XXH3_scrambleAcc_vsx
+#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar
+
+#else /* scalar */
+
+#define XXH3_accumulate_512 XXH3_accumulate_512_scalar
+#define XXH3_scrambleAcc XXH3_scrambleAcc_scalar
+#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar
+
+#endif
+
+
+
+#ifndef XXH_PREFETCH_DIST
+# ifdef __clang__
+# define XXH_PREFETCH_DIST 320
+# else
+# if (XXH_VECTOR == XXH_AVX512)
+# define XXH_PREFETCH_DIST 512
+# else
+# define XXH_PREFETCH_DIST 384
+# endif
+# endif /* __clang__ */
+#endif /* XXH_PREFETCH_DIST */
+
+/*
+ * XXH3_accumulate()
+ * Loops over XXH3_accumulate_512().
+ * Assumption: nbStripes will not overflow the secret size
+ */
+XXH_FORCE_INLINE void
+XXH3_accumulate( xxh_u64* XXH_RESTRICT acc,
+ const xxh_u8* XXH_RESTRICT input,
+ const xxh_u8* XXH_RESTRICT secret,
+ size_t nbStripes,
+ XXH3_f_accumulate_512 f_acc512)
+{
+ size_t n;
+ for (n = 0; n < nbStripes; n++ ) {
+ const xxh_u8* const in = input + n*XXH_STRIPE_LEN;
+ XXH_PREFETCH(in + XXH_PREFETCH_DIST);
+ f_acc512(acc,
+ in,
+ secret + n*XXH_SECRET_CONSUME_RATE);
+ }
+}
+
+XXH_FORCE_INLINE void
+XXH3_hashLong_internal_loop(xxh_u64* XXH_RESTRICT acc,
+ const xxh_u8* XXH_RESTRICT input, size_t len,
+ const xxh_u8* XXH_RESTRICT secret, size_t secretSize,
+ XXH3_f_accumulate_512 f_acc512,
+ XXH3_f_scrambleAcc f_scramble)
+{
+ size_t const nbStripesPerBlock = (secretSize - XXH_STRIPE_LEN) / XXH_SECRET_CONSUME_RATE;
+ size_t const block_len = XXH_STRIPE_LEN * nbStripesPerBlock;
+ size_t const nb_blocks = (len - 1) / block_len;
+
+ size_t n;
+
+ XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN);
+
+ for (n = 0; n < nb_blocks; n++) {
+ XXH3_accumulate(acc, input + n*block_len, secret, nbStripesPerBlock, f_acc512);
+ f_scramble(acc, secret + secretSize - XXH_STRIPE_LEN);
+ }
+
+ /* last partial block */
+ XXH_ASSERT(len > XXH_STRIPE_LEN);
+ { size_t const nbStripes = ((len - 1) - (block_len * nb_blocks)) / XXH_STRIPE_LEN;
+ XXH_ASSERT(nbStripes <= (secretSize / XXH_SECRET_CONSUME_RATE));
+ XXH3_accumulate(acc, input + nb_blocks*block_len, secret, nbStripes, f_acc512);
+
+ /* last stripe */
+ { const xxh_u8* const p = input + len - XXH_STRIPE_LEN;
+#define XXH_SECRET_LASTACC_START 7 /* not aligned on 8, last secret is different from acc & scrambler */
+ f_acc512(acc, p, secret + secretSize - XXH_STRIPE_LEN - XXH_SECRET_LASTACC_START);
+ } }
+}
+
+XXH_FORCE_INLINE xxh_u64
+XXH3_mix2Accs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret)
+{
+ return XXH3_mul128_fold64(
+ acc[0] ^ XXH_readLE64(secret),
+ acc[1] ^ XXH_readLE64(secret+8) );
+}
+
+static XXH64_hash_t
+XXH3_mergeAccs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret, xxh_u64 start)
+{
+ xxh_u64 result64 = start;
+ size_t i = 0;
+
+ for (i = 0; i < 4; i++) {
+ result64 += XXH3_mix2Accs(acc+2*i, secret + 16*i);
+#if defined(__clang__) /* Clang */ \
+ && (defined(__arm__) || defined(__thumb__)) /* ARMv7 */ \
+ && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \
+ && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable */
+ /*
+ * UGLY HACK:
+ * Prevent autovectorization on Clang ARMv7-a. Exact same problem as
+ * the one in XXH3_len_129to240_64b. Speeds up shorter keys > 240b.
+ * XXH3_64bits, len == 256, Snapdragon 835:
+ * without hack: 2063.7 MB/s
+ * with hack: 2560.7 MB/s
+ */
+ __asm__("" : "+r" (result64));
+#endif
+ }
+
+ return XXH3_avalanche(result64);
+}
+
+#define XXH3_INIT_ACC { XXH_PRIME32_3, XXH_PRIME64_1, XXH_PRIME64_2, XXH_PRIME64_3, \
+ XXH_PRIME64_4, XXH_PRIME32_2, XXH_PRIME64_5, XXH_PRIME32_1 }
+
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_hashLong_64b_internal(const void* XXH_RESTRICT input, size_t len,
+ const void* XXH_RESTRICT secret, size_t secretSize,
+ XXH3_f_accumulate_512 f_acc512,
+ XXH3_f_scrambleAcc f_scramble)
+{
+ XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC;
+
+ XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, (const xxh_u8*)secret, secretSize, f_acc512, f_scramble);
+
+ /* converge into final hash */
+ XXH_STATIC_ASSERT(sizeof(acc) == 64);
+ /* do not align on 8, so that the secret is different from the accumulator */
+#define XXH_SECRET_MERGEACCS_START 11
+ XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START);
+ return XXH3_mergeAccs(acc, (const xxh_u8*)secret + XXH_SECRET_MERGEACCS_START, (xxh_u64)len * XXH_PRIME64_1);
+}
+
+/*
+ * It's important for performance that XXH3_hashLong is not inlined.
+ */
+XXH_NO_INLINE XXH64_hash_t
+XXH3_hashLong_64b_withSecret(const void* XXH_RESTRICT input, size_t len,
+ XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen)
+{
+ (void)seed64;
+ return XXH3_hashLong_64b_internal(input, len, secret, secretLen, XXH3_accumulate_512, XXH3_scrambleAcc);
+}
+
+/*
+ * It's important for performance that XXH3_hashLong is not inlined.
+ * Since the function is not inlined, the compiler may not be able to understand that,
+ * in some scenarios, its `secret` argument is actually a compile time constant.
+ * This variant enforces that the compiler can detect that,
+ * and uses this opportunity to streamline the generated code for better performance.
+ */
+XXH_NO_INLINE XXH64_hash_t
+XXH3_hashLong_64b_default(const void* XXH_RESTRICT input, size_t len,
+ XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen)
+{
+ (void)seed64; (void)secret; (void)secretLen;
+ return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate_512, XXH3_scrambleAcc);
+}
+
+/*
+ * XXH3_hashLong_64b_withSeed():
+ * Generate a custom key based on alteration of default XXH3_kSecret with the seed,
+ * and then use this key for long mode hashing.
+ *
+ * This operation is decently fast but nonetheless costs a little bit of time.
+ * Try to avoid it whenever possible (typically when seed==0).
+ *
+ * It's important for performance that XXH3_hashLong is not inlined. Not sure
+ * why (uop cache maybe?), but the difference is large and easily measurable.
+ */
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_hashLong_64b_withSeed_internal(const void* input, size_t len,
+ XXH64_hash_t seed,
+ XXH3_f_accumulate_512 f_acc512,
+ XXH3_f_scrambleAcc f_scramble,
+ XXH3_f_initCustomSecret f_initSec)
+{
+ if (seed == 0)
+ return XXH3_hashLong_64b_internal(input, len,
+ XXH3_kSecret, sizeof(XXH3_kSecret),
+ f_acc512, f_scramble);
+ { XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE];
+ f_initSec(secret, seed);
+ return XXH3_hashLong_64b_internal(input, len, secret, sizeof(secret),
+ f_acc512, f_scramble);
+ }
+}
+
+/*
+ * It's important for performance that XXH3_hashLong is not inlined.
+ */
+XXH_NO_INLINE XXH64_hash_t
+XXH3_hashLong_64b_withSeed(const void* input, size_t len,
+ XXH64_hash_t seed, const xxh_u8* secret, size_t secretLen)
+{
+ (void)secret; (void)secretLen;
+ return XXH3_hashLong_64b_withSeed_internal(input, len, seed,
+ XXH3_accumulate_512, XXH3_scrambleAcc, XXH3_initCustomSecret);
+}
+
+
+typedef XXH64_hash_t (*XXH3_hashLong64_f)(const void* XXH_RESTRICT, size_t,
+ XXH64_hash_t, const xxh_u8* XXH_RESTRICT, size_t);
+
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_64bits_internal(const void* XXH_RESTRICT input, size_t len,
+ XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen,
+ XXH3_hashLong64_f f_hashLong)
+{
+ XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN);
+ /*
+ * If an action is to be taken if `secretLen` condition is not respected,
+ * it should be done here.
+ * For now, it's a contract pre-condition.
+ * Adding a check and a branch here would cost performance at every hash.
+ * Also, note that function signature doesn't offer room to return an error.
+ */
+ if (len <= 16)
+ return XXH3_len_0to16_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, seed64);
+ if (len <= 128)
+ return XXH3_len_17to128_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64);
+ if (len <= XXH3_MIDSIZE_MAX)
+ return XXH3_len_129to240_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64);
+ return f_hashLong(input, len, seed64, (const xxh_u8*)secret, secretLen);
+}
+
+
+/* === Public entry point === */
+
+XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* input, size_t len)
+{
+ return XXH3_64bits_internal(input, len, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_default);
+}
+
+XXH_PUBLIC_API XXH64_hash_t
+XXH3_64bits_withSecret(const void* input, size_t len, const void* secret, size_t secretSize)
+{
+ return XXH3_64bits_internal(input, len, 0, secret, secretSize, XXH3_hashLong_64b_withSecret);
+}
+
+XXH_PUBLIC_API XXH64_hash_t
+XXH3_64bits_withSeed(const void* input, size_t len, XXH64_hash_t seed)
+{
+ return XXH3_64bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_withSeed);
+}
+
+
+/* === XXH3 streaming === */
+
+/*
+ * Malloc's a pointer that is always aligned to align.
+ *
+ * This must be freed with `XXH_alignedFree()`.
+ *
+ * malloc typically guarantees 16 byte alignment on 64-bit systems and 8 byte
+ * alignment on 32-bit. This isn't enough for the 32 byte aligned loads in AVX2
+ * or on 32-bit, the 16 byte aligned loads in SSE2 and NEON.
+ *
+ * This underalignment previously caused a rather obvious crash which went
+ * completely unnoticed due to XXH3_createState() not actually being tested.
+ * Credit to RedSpah for noticing this bug.
+ *
+ * The alignment is done manually: Functions like posix_memalign or _mm_malloc
+ * are avoided: To maintain portability, we would have to write a fallback
+ * like this anyways, and besides, testing for the existence of library
+ * functions without relying on external build tools is impossible.
+ *
+ * The method is simple: Overallocate, manually align, and store the offset
+ * to the original behind the returned pointer.
+ *
+ * Align must be a power of 2 and 8 <= align <= 128.
+ */
+static void* XXH_alignedMalloc(size_t s, size_t align)
+{
+ XXH_ASSERT(align <= 128 && align >= 8); /* range check */
+ XXH_ASSERT((align & (align-1)) == 0); /* power of 2 */
+ XXH_ASSERT(s != 0 && s < (s + align)); /* empty/overflow */
+ { /* Overallocate to make room for manual realignment and an offset byte */
+ xxh_u8* base = (xxh_u8*)XXH_malloc(s + align);
+ if (base != NULL) {
+ /*
+ * Get the offset needed to align this pointer.
+ *
+ * Even if the returned pointer is aligned, there will always be
+ * at least one byte to store the offset to the original pointer.
+ */
+ size_t offset = align - ((size_t)base & (align - 1)); /* base % align */
+ /* Add the offset for the now-aligned pointer */
+ xxh_u8* ptr = base + offset;
+
+ XXH_ASSERT((size_t)ptr % align == 0);
+
+ /* Store the offset immediately before the returned pointer. */
+ ptr[-1] = (xxh_u8)offset;
+ return ptr;
+ }
+ return NULL;
+ }
+}
+/*
+ * Frees an aligned pointer allocated by XXH_alignedMalloc(). Don't pass
+ * normal malloc'd pointers, XXH_alignedMalloc has a specific data layout.
+ */
+static void XXH_alignedFree(void* p)
+{
+ if (p != NULL) {
+ xxh_u8* ptr = (xxh_u8*)p;
+ /* Get the offset byte we added in XXH_malloc. */
+ xxh_u8 offset = ptr[-1];
+ /* Free the original malloc'd pointer */
+ xxh_u8* base = ptr - offset;
+ XXH_free(base);
+ }
+}
+XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void)
+{
+ XXH3_state_t* const state = (XXH3_state_t*)XXH_alignedMalloc(sizeof(XXH3_state_t), 64);
+ if (state==NULL) return NULL;
+ XXH3_INITSTATE(state);
+ return state;
+}
+
+XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr)
+{
+ XXH_alignedFree(statePtr);
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API void
+XXH3_copyState(XXH3_state_t* dst_state, const XXH3_state_t* src_state)
+{
+ memcpy(dst_state, src_state, sizeof(*dst_state));
+}
+
+static void
+XXH3_64bits_reset_internal(XXH3_state_t* statePtr,
+ XXH64_hash_t seed,
+ const void* secret, size_t secretSize)
+{
+ size_t const initStart = offsetof(XXH3_state_t, bufferedSize);
+ size_t const initLength = offsetof(XXH3_state_t, nbStripesPerBlock) - initStart;
+ XXH_ASSERT(offsetof(XXH3_state_t, nbStripesPerBlock) > initStart);
+ XXH_ASSERT(statePtr != NULL);
+ /* set members from bufferedSize to nbStripesPerBlock (excluded) to 0 */
+ memset((char*)statePtr + initStart, 0, initLength);
+ statePtr->acc[0] = XXH_PRIME32_3;
+ statePtr->acc[1] = XXH_PRIME64_1;
+ statePtr->acc[2] = XXH_PRIME64_2;
+ statePtr->acc[3] = XXH_PRIME64_3;
+ statePtr->acc[4] = XXH_PRIME64_4;
+ statePtr->acc[5] = XXH_PRIME32_2;
+ statePtr->acc[6] = XXH_PRIME64_5;
+ statePtr->acc[7] = XXH_PRIME32_1;
+ statePtr->seed = seed;
+ statePtr->extSecret = (const unsigned char*)secret;
+ XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN);
+ statePtr->secretLimit = secretSize - XXH_STRIPE_LEN;
+ statePtr->nbStripesPerBlock = statePtr->secretLimit / XXH_SECRET_CONSUME_RATE;
+}
+
+XXH_PUBLIC_API XXH_errorcode
+XXH3_64bits_reset(XXH3_state_t* statePtr)
+{
+ if (statePtr == NULL) return XXH_ERROR;
+ XXH3_64bits_reset_internal(statePtr, 0, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE);
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API XXH_errorcode
+XXH3_64bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize)
+{
+ if (statePtr == NULL) return XXH_ERROR;
+ XXH3_64bits_reset_internal(statePtr, 0, secret, secretSize);
+ if (secret == NULL) return XXH_ERROR;
+ if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR;
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API XXH_errorcode
+XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed)
+{
+ if (statePtr == NULL) return XXH_ERROR;
+ if (seed==0) return XXH3_64bits_reset(statePtr);
+ if (seed != statePtr->seed) XXH3_initCustomSecret(statePtr->customSecret, seed);
+ XXH3_64bits_reset_internal(statePtr, seed, NULL, XXH_SECRET_DEFAULT_SIZE);
+ return XXH_OK;
+}
+
+/* Note : when XXH3_consumeStripes() is invoked,
+ * there must be a guarantee that at least one more byte must be consumed from input
+ * so that the function can blindly consume all stripes using the "normal" secret segment */
+XXH_FORCE_INLINE void
+XXH3_consumeStripes(xxh_u64* XXH_RESTRICT acc,
+ size_t* XXH_RESTRICT nbStripesSoFarPtr, size_t nbStripesPerBlock,
+ const xxh_u8* XXH_RESTRICT input, size_t nbStripes,
+ const xxh_u8* XXH_RESTRICT secret, size_t secretLimit,
+ XXH3_f_accumulate_512 f_acc512,
+ XXH3_f_scrambleAcc f_scramble)
+{
+ XXH_ASSERT(nbStripes <= nbStripesPerBlock); /* can handle max 1 scramble per invocation */
+ XXH_ASSERT(*nbStripesSoFarPtr < nbStripesPerBlock);
+ if (nbStripesPerBlock - *nbStripesSoFarPtr <= nbStripes) {
+ /* need a scrambling operation */
+ size_t const nbStripesToEndofBlock = nbStripesPerBlock - *nbStripesSoFarPtr;
+ size_t const nbStripesAfterBlock = nbStripes - nbStripesToEndofBlock;
+ XXH3_accumulate(acc, input, secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, nbStripesToEndofBlock, f_acc512);
+ f_scramble(acc, secret + secretLimit);
+ XXH3_accumulate(acc, input + nbStripesToEndofBlock * XXH_STRIPE_LEN, secret, nbStripesAfterBlock, f_acc512);
+ *nbStripesSoFarPtr = nbStripesAfterBlock;
+ } else {
+ XXH3_accumulate(acc, input, secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, nbStripes, f_acc512);
+ *nbStripesSoFarPtr += nbStripes;
+ }
+}
+
+/*
+ * Both XXH3_64bits_update and XXH3_128bits_update use this routine.
+ */
+XXH_FORCE_INLINE XXH_errorcode
+XXH3_update(XXH3_state_t* state,
+ const xxh_u8* input, size_t len,
+ XXH3_f_accumulate_512 f_acc512,
+ XXH3_f_scrambleAcc f_scramble)
+{
+ if (input==NULL)
+#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)
+ return XXH_OK;
+#else
+ return XXH_ERROR;
+#endif
+
+ { const xxh_u8* const bEnd = input + len;
+ const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret;
+
+ state->totalLen += len;
+
+ if (state->bufferedSize + len <= XXH3_INTERNALBUFFER_SIZE) { /* fill in tmp buffer */
+ XXH_memcpy(state->buffer + state->bufferedSize, input, len);
+ state->bufferedSize += (XXH32_hash_t)len;
+ return XXH_OK;
+ }
+ /* total input is now > XXH3_INTERNALBUFFER_SIZE */
+
+ #define XXH3_INTERNALBUFFER_STRIPES (XXH3_INTERNALBUFFER_SIZE / XXH_STRIPE_LEN)
+ XXH_STATIC_ASSERT(XXH3_INTERNALBUFFER_SIZE % XXH_STRIPE_LEN == 0); /* clean multiple */
+
+ /*
+ * Internal buffer is partially filled (always, except at beginning)
+ * Complete it, then consume it.
+ */
+ if (state->bufferedSize) {
+ size_t const loadSize = XXH3_INTERNALBUFFER_SIZE - state->bufferedSize;
+ XXH_memcpy(state->buffer + state->bufferedSize, input, loadSize);
+ input += loadSize;
+ XXH3_consumeStripes(state->acc,
+ &state->nbStripesSoFar, state->nbStripesPerBlock,
+ state->buffer, XXH3_INTERNALBUFFER_STRIPES,
+ secret, state->secretLimit,
+ f_acc512, f_scramble);
+ state->bufferedSize = 0;
+ }
+ XXH_ASSERT(input < bEnd);
+
+ /* Consume input by a multiple of internal buffer size */
+ if (input+XXH3_INTERNALBUFFER_SIZE < bEnd) {
+ const xxh_u8* const limit = bEnd - XXH3_INTERNALBUFFER_SIZE;
+ do {
+ XXH3_consumeStripes(state->acc,
+ &state->nbStripesSoFar, state->nbStripesPerBlock,
+ input, XXH3_INTERNALBUFFER_STRIPES,
+ secret, state->secretLimit,
+ f_acc512, f_scramble);
+ input += XXH3_INTERNALBUFFER_SIZE;
+ } while (input<limit);
+ /* for last partial stripe */
+ memcpy(state->buffer + sizeof(state->buffer) - XXH_STRIPE_LEN, input - XXH_STRIPE_LEN, XXH_STRIPE_LEN);
+ }
+ XXH_ASSERT(input < bEnd);
+
+ /* Some remaining input (always) : buffer it */
+ XXH_memcpy(state->buffer, input, (size_t)(bEnd-input));
+ state->bufferedSize = (XXH32_hash_t)(bEnd-input);
+ }
+
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API XXH_errorcode
+XXH3_64bits_update(XXH3_state_t* state, const void* input, size_t len)
+{
+ return XXH3_update(state, (const xxh_u8*)input, len,
+ XXH3_accumulate_512, XXH3_scrambleAcc);
+}
+
+
+XXH_FORCE_INLINE void
+XXH3_digest_long (XXH64_hash_t* acc,
+ const XXH3_state_t* state,
+ const unsigned char* secret)
+{
+ /*
+ * Digest on a local copy. This way, the state remains unaltered, and it can
+ * continue ingesting more input afterwards.
+ */
+ memcpy(acc, state->acc, sizeof(state->acc));
+ if (state->bufferedSize >= XXH_STRIPE_LEN) {
+ size_t const nbStripes = (state->bufferedSize - 1) / XXH_STRIPE_LEN;
+ size_t nbStripesSoFar = state->nbStripesSoFar;
+ XXH3_consumeStripes(acc,
+ &nbStripesSoFar, state->nbStripesPerBlock,
+ state->buffer, nbStripes,
+ secret, state->secretLimit,
+ XXH3_accumulate_512, XXH3_scrambleAcc);
+ /* last stripe */
+ XXH3_accumulate_512(acc,
+ state->buffer + state->bufferedSize - XXH_STRIPE_LEN,
+ secret + state->secretLimit - XXH_SECRET_LASTACC_START);
+ } else { /* bufferedSize < XXH_STRIPE_LEN */
+ xxh_u8 lastStripe[XXH_STRIPE_LEN];
+ size_t const catchupSize = XXH_STRIPE_LEN - state->bufferedSize;
+ XXH_ASSERT(state->bufferedSize > 0); /* there is always some input buffered */
+ memcpy(lastStripe, state->buffer + sizeof(state->buffer) - catchupSize, catchupSize);
+ memcpy(lastStripe + catchupSize, state->buffer, state->bufferedSize);
+ XXH3_accumulate_512(acc,
+ lastStripe,
+ secret + state->secretLimit - XXH_SECRET_LASTACC_START);
+ }
+}
+
+XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* state)
+{
+ const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret;
+ if (state->totalLen > XXH3_MIDSIZE_MAX) {
+ XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB];
+ XXH3_digest_long(acc, state, secret);
+ return XXH3_mergeAccs(acc,
+ secret + XXH_SECRET_MERGEACCS_START,
+ (xxh_u64)state->totalLen * XXH_PRIME64_1);
+ }
+ /* totalLen <= XXH3_MIDSIZE_MAX: digesting a short input */
+ if (state->seed)
+ return XXH3_64bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed);
+ return XXH3_64bits_withSecret(state->buffer, (size_t)(state->totalLen),
+ secret, state->secretLimit + XXH_STRIPE_LEN);
+}
+
+
+#define XXH_MIN(x, y) (((x) > (y)) ? (y) : (x))
+
+XXH_PUBLIC_API void
+XXH3_generateSecret(void* secretBuffer, const void* customSeed, size_t customSeedSize)
+{
+ XXH_ASSERT(secretBuffer != NULL);
+ if (customSeedSize == 0) {
+ memcpy(secretBuffer, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE);
+ return;
+ }
+ XXH_ASSERT(customSeed != NULL);
+
+ { size_t const segmentSize = sizeof(XXH128_hash_t);
+ size_t const nbSegments = XXH_SECRET_DEFAULT_SIZE / segmentSize;
+ XXH128_canonical_t scrambler;
+ XXH64_hash_t seeds[12];
+ size_t segnb;
+ XXH_ASSERT(nbSegments == 12);
+ XXH_ASSERT(segmentSize * nbSegments == XXH_SECRET_DEFAULT_SIZE); /* exact multiple */
+ XXH128_canonicalFromHash(&scrambler, XXH128(customSeed, customSeedSize, 0));
+
+ /*
+ * Copy customSeed to seeds[], truncating or repeating as necessary.
+ */
+ { size_t toFill = XXH_MIN(customSeedSize, sizeof(seeds));
+ size_t filled = toFill;
+ memcpy(seeds, customSeed, toFill);
+ while (filled < sizeof(seeds)) {
+ toFill = XXH_MIN(filled, sizeof(seeds) - filled);
+ memcpy((char*)seeds + filled, seeds, toFill);
+ filled += toFill;
+ } }
+
+ /* generate secret */
+ memcpy(secretBuffer, &scrambler, sizeof(scrambler));
+ for (segnb=1; segnb < nbSegments; segnb++) {
+ size_t const segmentStart = segnb * segmentSize;
+ XXH128_canonical_t segment;
+ XXH128_canonicalFromHash(&segment,
+ XXH128(&scrambler, sizeof(scrambler), XXH_readLE64(seeds + segnb) + segnb) );
+ memcpy((char*)secretBuffer + segmentStart, &segment, sizeof(segment));
+ } }
+}
+
+
+/* ==========================================
+ * XXH3 128 bits (a.k.a XXH128)
+ * ==========================================
+ * XXH3's 128-bit variant has better mixing and strength than the 64-bit variant,
+ * even without counting the significantly larger output size.
+ *
+ * For example, extra steps are taken to avoid the seed-dependent collisions
+ * in 17-240 byte inputs (See XXH3_mix16B and XXH128_mix32B).
+ *
+ * This strength naturally comes at the cost of some speed, especially on short
+ * lengths. Note that longer hashes are about as fast as the 64-bit version
+ * due to it using only a slight modification of the 64-bit loop.
+ *
+ * XXH128 is also more oriented towards 64-bit machines. It is still extremely
+ * fast for a _128-bit_ hash on 32-bit (it usually clears XXH64).
+ */
+
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_len_1to3_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
+{
+ /* A doubled version of 1to3_64b with different constants. */
+ XXH_ASSERT(input != NULL);
+ XXH_ASSERT(1 <= len && len <= 3);
+ XXH_ASSERT(secret != NULL);
+ /*
+ * len = 1: combinedl = { input[0], 0x01, input[0], input[0] }
+ * len = 2: combinedl = { input[1], 0x02, input[0], input[1] }
+ * len = 3: combinedl = { input[2], 0x03, input[0], input[1] }
+ */
+ { xxh_u8 const c1 = input[0];
+ xxh_u8 const c2 = input[len >> 1];
+ xxh_u8 const c3 = input[len - 1];
+ xxh_u32 const combinedl = ((xxh_u32)c1 <<16) | ((xxh_u32)c2 << 24)
+ | ((xxh_u32)c3 << 0) | ((xxh_u32)len << 8);
+ xxh_u32 const combinedh = XXH_rotl32(XXH_swap32(combinedl), 13);
+ xxh_u64 const bitflipl = (XXH_readLE32(secret) ^ XXH_readLE32(secret+4)) + seed;
+ xxh_u64 const bitfliph = (XXH_readLE32(secret+8) ^ XXH_readLE32(secret+12)) - seed;
+ xxh_u64 const keyed_lo = (xxh_u64)combinedl ^ bitflipl;
+ xxh_u64 const keyed_hi = (xxh_u64)combinedh ^ bitfliph;
+ XXH128_hash_t h128;
+ h128.low64 = XXH64_avalanche(keyed_lo);
+ h128.high64 = XXH64_avalanche(keyed_hi);
+ return h128;
+ }
+}
+
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_len_4to8_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
+{
+ XXH_ASSERT(input != NULL);
+ XXH_ASSERT(secret != NULL);
+ XXH_ASSERT(4 <= len && len <= 8);
+ seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32;
+ { xxh_u32 const input_lo = XXH_readLE32(input);
+ xxh_u32 const input_hi = XXH_readLE32(input + len - 4);
+ xxh_u64 const input_64 = input_lo + ((xxh_u64)input_hi << 32);
+ xxh_u64 const bitflip = (XXH_readLE64(secret+16) ^ XXH_readLE64(secret+24)) + seed;
+ xxh_u64 const keyed = input_64 ^ bitflip;
+
+ /* Shift len to the left to ensure it is even, this avoids even multiplies. */
+ XXH128_hash_t m128 = XXH_mult64to128(keyed, XXH_PRIME64_1 + (len << 2));
+
+ m128.high64 += (m128.low64 << 1);
+ m128.low64 ^= (m128.high64 >> 3);
+
+ m128.low64 = XXH_xorshift64(m128.low64, 35);
+ m128.low64 *= 0x9FB21C651E98DF25ULL;
+ m128.low64 = XXH_xorshift64(m128.low64, 28);
+ m128.high64 = XXH3_avalanche(m128.high64);
+ return m128;
+ }
+}
+
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_len_9to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
+{
+ XXH_ASSERT(input != NULL);
+ XXH_ASSERT(secret != NULL);
+ XXH_ASSERT(9 <= len && len <= 16);
+ { xxh_u64 const bitflipl = (XXH_readLE64(secret+32) ^ XXH_readLE64(secret+40)) - seed;
+ xxh_u64 const bitfliph = (XXH_readLE64(secret+48) ^ XXH_readLE64(secret+56)) + seed;
+ xxh_u64 const input_lo = XXH_readLE64(input);
+ xxh_u64 input_hi = XXH_readLE64(input + len - 8);
+ XXH128_hash_t m128 = XXH_mult64to128(input_lo ^ input_hi ^ bitflipl, XXH_PRIME64_1);
+ /*
+ * Put len in the middle of m128 to ensure that the length gets mixed to
+ * both the low and high bits in the 128x64 multiply below.
+ */
+ m128.low64 += (xxh_u64)(len - 1) << 54;
+ input_hi ^= bitfliph;
+ /*
+ * Add the high 32 bits of input_hi to the high 32 bits of m128, then
+ * add the long product of the low 32 bits of input_hi and XXH_PRIME32_2 to
+ * the high 64 bits of m128.
+ *
+ * The best approach to this operation is different on 32-bit and 64-bit.
+ */
+ if (sizeof(void *) < sizeof(xxh_u64)) { /* 32-bit */
+ /*
+ * 32-bit optimized version, which is more readable.
+ *
+ * On 32-bit, it removes an ADC and delays a dependency between the two
+ * halves of m128.high64, but it generates an extra mask on 64-bit.
+ */
+ m128.high64 += (input_hi & 0xFFFFFFFF00000000ULL) + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2);
+ } else {
+ /*
+ * 64-bit optimized (albeit more confusing) version.
+ *
+ * Uses some properties of addition and multiplication to remove the mask:
+ *
+ * Let:
+ * a = input_hi.lo = (input_hi & 0x00000000FFFFFFFF)
+ * b = input_hi.hi = (input_hi & 0xFFFFFFFF00000000)
+ * c = XXH_PRIME32_2
+ *
+ * a + (b * c)
+ * Inverse Property: x + y - x == y
+ * a + (b * (1 + c - 1))
+ * Distributive Property: x * (y + z) == (x * y) + (x * z)
+ * a + (b * 1) + (b * (c - 1))
+ * Identity Property: x * 1 == x
+ * a + b + (b * (c - 1))
+ *
+ * Substitute a, b, and c:
+ * input_hi.hi + input_hi.lo + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1))
+ *
+ * Since input_hi.hi + input_hi.lo == input_hi, we get this:
+ * input_hi + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1))
+ */
+ m128.high64 += input_hi + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2 - 1);
+ }
+ /* m128 ^= XXH_swap64(m128 >> 64); */
+ m128.low64 ^= XXH_swap64(m128.high64);
+
+ { /* 128x64 multiply: h128 = m128 * XXH_PRIME64_2; */
+ XXH128_hash_t h128 = XXH_mult64to128(m128.low64, XXH_PRIME64_2);
+ h128.high64 += m128.high64 * XXH_PRIME64_2;
+
+ h128.low64 = XXH3_avalanche(h128.low64);
+ h128.high64 = XXH3_avalanche(h128.high64);
+ return h128;
+ } }
+}
+
+/*
+ * Assumption: `secret` size is >= XXH3_SECRET_SIZE_MIN
+ */
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_len_0to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
+{
+ XXH_ASSERT(len <= 16);
+ { if (len > 8) return XXH3_len_9to16_128b(input, len, secret, seed);
+ if (len >= 4) return XXH3_len_4to8_128b(input, len, secret, seed);
+ if (len) return XXH3_len_1to3_128b(input, len, secret, seed);
+ { XXH128_hash_t h128;
+ xxh_u64 const bitflipl = XXH_readLE64(secret+64) ^ XXH_readLE64(secret+72);
+ xxh_u64 const bitfliph = XXH_readLE64(secret+80) ^ XXH_readLE64(secret+88);
+ h128.low64 = XXH64_avalanche(seed ^ bitflipl);
+ h128.high64 = XXH64_avalanche( seed ^ bitfliph);
+ return h128;
+ } }
+}
+
+/*
+ * A bit slower than XXH3_mix16B, but handles multiply by zero better.
+ */
+XXH_FORCE_INLINE XXH128_hash_t
+XXH128_mix32B(XXH128_hash_t acc, const xxh_u8* input_1, const xxh_u8* input_2,
+ const xxh_u8* secret, XXH64_hash_t seed)
+{
+ acc.low64 += XXH3_mix16B (input_1, secret+0, seed);
+ acc.low64 ^= XXH_readLE64(input_2) + XXH_readLE64(input_2 + 8);
+ acc.high64 += XXH3_mix16B (input_2, secret+16, seed);
+ acc.high64 ^= XXH_readLE64(input_1) + XXH_readLE64(input_1 + 8);
+ return acc;
+}
+
+
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_len_17to128_128b(const xxh_u8* XXH_RESTRICT input, size_t len,
+ const xxh_u8* XXH_RESTRICT secret, size_t secretSize,
+ XXH64_hash_t seed)
+{
+ XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize;
+ XXH_ASSERT(16 < len && len <= 128);
+
+ { XXH128_hash_t acc;
+ acc.low64 = len * XXH_PRIME64_1;
+ acc.high64 = 0;
+ if (len > 32) {
+ if (len > 64) {
+ if (len > 96) {
+ acc = XXH128_mix32B(acc, input+48, input+len-64, secret+96, seed);
+ }
+ acc = XXH128_mix32B(acc, input+32, input+len-48, secret+64, seed);
+ }
+ acc = XXH128_mix32B(acc, input+16, input+len-32, secret+32, seed);
+ }
+ acc = XXH128_mix32B(acc, input, input+len-16, secret, seed);
+ { XXH128_hash_t h128;
+ h128.low64 = acc.low64 + acc.high64;
+ h128.high64 = (acc.low64 * XXH_PRIME64_1)
+ + (acc.high64 * XXH_PRIME64_4)
+ + ((len - seed) * XXH_PRIME64_2);
+ h128.low64 = XXH3_avalanche(h128.low64);
+ h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64);
+ return h128;
+ }
+ }
+}
+
+XXH_NO_INLINE XXH128_hash_t
+XXH3_len_129to240_128b(const xxh_u8* XXH_RESTRICT input, size_t len,
+ const xxh_u8* XXH_RESTRICT secret, size_t secretSize,
+ XXH64_hash_t seed)
+{
+ XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize;
+ XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX);
+
+ { XXH128_hash_t acc;
+ int const nbRounds = (int)len / 32;
+ int i;
+ acc.low64 = len * XXH_PRIME64_1;
+ acc.high64 = 0;
+ for (i=0; i<4; i++) {
+ acc = XXH128_mix32B(acc,
+ input + (32 * i),
+ input + (32 * i) + 16,
+ secret + (32 * i),
+ seed);
+ }
+ acc.low64 = XXH3_avalanche(acc.low64);
+ acc.high64 = XXH3_avalanche(acc.high64);
+ XXH_ASSERT(nbRounds >= 4);
+ for (i=4 ; i < nbRounds; i++) {
+ acc = XXH128_mix32B(acc,
+ input + (32 * i),
+ input + (32 * i) + 16,
+ secret + XXH3_MIDSIZE_STARTOFFSET + (32 * (i - 4)),
+ seed);
+ }
+ /* last bytes */
+ acc = XXH128_mix32B(acc,
+ input + len - 16,
+ input + len - 32,
+ secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET - 16,
+ 0ULL - seed);
+
+ { XXH128_hash_t h128;
+ h128.low64 = acc.low64 + acc.high64;
+ h128.high64 = (acc.low64 * XXH_PRIME64_1)
+ + (acc.high64 * XXH_PRIME64_4)
+ + ((len - seed) * XXH_PRIME64_2);
+ h128.low64 = XXH3_avalanche(h128.low64);
+ h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64);
+ return h128;
+ }
+ }
+}
+
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_hashLong_128b_internal(const void* XXH_RESTRICT input, size_t len,
+ const xxh_u8* XXH_RESTRICT secret, size_t secretSize,
+ XXH3_f_accumulate_512 f_acc512,
+ XXH3_f_scrambleAcc f_scramble)
+{
+ XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC;
+
+ XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, secret, secretSize, f_acc512, f_scramble);
+
+ /* converge into final hash */
+ XXH_STATIC_ASSERT(sizeof(acc) == 64);
+ XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START);
+ { XXH128_hash_t h128;
+ h128.low64 = XXH3_mergeAccs(acc,
+ secret + XXH_SECRET_MERGEACCS_START,
+ (xxh_u64)len * XXH_PRIME64_1);
+ h128.high64 = XXH3_mergeAccs(acc,
+ secret + secretSize
+ - sizeof(acc) - XXH_SECRET_MERGEACCS_START,
+ ~((xxh_u64)len * XXH_PRIME64_2));
+ return h128;
+ }
+}
+
+/*
+ * It's important for performance that XXH3_hashLong is not inlined.
+ */
+XXH_NO_INLINE XXH128_hash_t
+XXH3_hashLong_128b_default(const void* XXH_RESTRICT input, size_t len,
+ XXH64_hash_t seed64,
+ const void* XXH_RESTRICT secret, size_t secretLen)
+{
+ (void)seed64; (void)secret; (void)secretLen;
+ return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret),
+ XXH3_accumulate_512, XXH3_scrambleAcc);
+}
+
+/*
+ * It's important for performance that XXH3_hashLong is not inlined.
+ */
+XXH_NO_INLINE XXH128_hash_t
+XXH3_hashLong_128b_withSecret(const void* XXH_RESTRICT input, size_t len,
+ XXH64_hash_t seed64,
+ const void* XXH_RESTRICT secret, size_t secretLen)
+{
+ (void)seed64;
+ return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, secretLen,
+ XXH3_accumulate_512, XXH3_scrambleAcc);
+}
+
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_hashLong_128b_withSeed_internal(const void* XXH_RESTRICT input, size_t len,
+ XXH64_hash_t seed64,
+ XXH3_f_accumulate_512 f_acc512,
+ XXH3_f_scrambleAcc f_scramble,
+ XXH3_f_initCustomSecret f_initSec)
+{
+ if (seed64 == 0)
+ return XXH3_hashLong_128b_internal(input, len,
+ XXH3_kSecret, sizeof(XXH3_kSecret),
+ f_acc512, f_scramble);
+ { XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE];
+ f_initSec(secret, seed64);
+ return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, sizeof(secret),
+ f_acc512, f_scramble);
+ }
+}
+
+/*
+ * It's important for performance that XXH3_hashLong is not inlined.
+ */
+XXH_NO_INLINE XXH128_hash_t
+XXH3_hashLong_128b_withSeed(const void* input, size_t len,
+ XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen)
+{
+ (void)secret; (void)secretLen;
+ return XXH3_hashLong_128b_withSeed_internal(input, len, seed64,
+ XXH3_accumulate_512, XXH3_scrambleAcc, XXH3_initCustomSecret);
+}
+
+typedef XXH128_hash_t (*XXH3_hashLong128_f)(const void* XXH_RESTRICT, size_t,
+ XXH64_hash_t, const void* XXH_RESTRICT, size_t);
+
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_128bits_internal(const void* input, size_t len,
+ XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen,
+ XXH3_hashLong128_f f_hl128)
+{
+ XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN);
+ /*
+ * If an action is to be taken if `secret` conditions are not respected,
+ * it should be done here.
+ * For now, it's a contract pre-condition.
+ * Adding a check and a branch here would cost performance at every hash.
+ */
+ if (len <= 16)
+ return XXH3_len_0to16_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, seed64);
+ if (len <= 128)
+ return XXH3_len_17to128_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64);
+ if (len <= XXH3_MIDSIZE_MAX)
+ return XXH3_len_129to240_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64);
+ return f_hl128(input, len, seed64, secret, secretLen);
+}
+
+
+/* === Public XXH128 API === */
+
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void* input, size_t len)
+{
+ return XXH3_128bits_internal(input, len, 0,
+ XXH3_kSecret, sizeof(XXH3_kSecret),
+ XXH3_hashLong_128b_default);
+}
+
+XXH_PUBLIC_API XXH128_hash_t
+XXH3_128bits_withSecret(const void* input, size_t len, const void* secret, size_t secretSize)
+{
+ return XXH3_128bits_internal(input, len, 0,
+ (const xxh_u8*)secret, secretSize,
+ XXH3_hashLong_128b_withSecret);
+}
+
+XXH_PUBLIC_API XXH128_hash_t
+XXH3_128bits_withSeed(const void* input, size_t len, XXH64_hash_t seed)
+{
+ return XXH3_128bits_internal(input, len, seed,
+ XXH3_kSecret, sizeof(XXH3_kSecret),
+ XXH3_hashLong_128b_withSeed);
+}
+
+XXH_PUBLIC_API XXH128_hash_t
+XXH128(const void* input, size_t len, XXH64_hash_t seed)
+{
+ return XXH3_128bits_withSeed(input, len, seed);
+}
+
+
+/* === XXH3 128-bit streaming === */
+
+/*
+ * All the functions are actually the same as for 64-bit streaming variant.
+ * The only difference is the finalizatiom routine.
+ */
+
+static void
+XXH3_128bits_reset_internal(XXH3_state_t* statePtr,
+ XXH64_hash_t seed,
+ const void* secret, size_t secretSize)
+{
+ XXH3_64bits_reset_internal(statePtr, seed, secret, secretSize);
+}
+
+XXH_PUBLIC_API XXH_errorcode
+XXH3_128bits_reset(XXH3_state_t* statePtr)
+{
+ if (statePtr == NULL) return XXH_ERROR;
+ XXH3_128bits_reset_internal(statePtr, 0, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE);
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API XXH_errorcode
+XXH3_128bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize)
+{
+ if (statePtr == NULL) return XXH_ERROR;
+ XXH3_128bits_reset_internal(statePtr, 0, secret, secretSize);
+ if (secret == NULL) return XXH_ERROR;
+ if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR;
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API XXH_errorcode
+XXH3_128bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed)
+{
+ if (statePtr == NULL) return XXH_ERROR;
+ if (seed==0) return XXH3_128bits_reset(statePtr);
+ if (seed != statePtr->seed) XXH3_initCustomSecret(statePtr->customSecret, seed);
+ XXH3_128bits_reset_internal(statePtr, seed, NULL, XXH_SECRET_DEFAULT_SIZE);
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API XXH_errorcode
+XXH3_128bits_update(XXH3_state_t* state, const void* input, size_t len)
+{
+ return XXH3_update(state, (const xxh_u8*)input, len,
+ XXH3_accumulate_512, XXH3_scrambleAcc);
+}
+
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (const XXH3_state_t* state)
+{
+ const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret;
+ if (state->totalLen > XXH3_MIDSIZE_MAX) {
+ XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB];
+ XXH3_digest_long(acc, state, secret);
+ XXH_ASSERT(state->secretLimit + XXH_STRIPE_LEN >= sizeof(acc) + XXH_SECRET_MERGEACCS_START);
+ { XXH128_hash_t h128;
+ h128.low64 = XXH3_mergeAccs(acc,
+ secret + XXH_SECRET_MERGEACCS_START,
+ (xxh_u64)state->totalLen * XXH_PRIME64_1);
+ h128.high64 = XXH3_mergeAccs(acc,
+ secret + state->secretLimit + XXH_STRIPE_LEN
+ - sizeof(acc) - XXH_SECRET_MERGEACCS_START,
+ ~((xxh_u64)state->totalLen * XXH_PRIME64_2));
+ return h128;
+ }
+ }
+ /* len <= XXH3_MIDSIZE_MAX : short code */
+ if (state->seed)
+ return XXH3_128bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed);
+ return XXH3_128bits_withSecret(state->buffer, (size_t)(state->totalLen),
+ secret, state->secretLimit + XXH_STRIPE_LEN);
+}
+
+/* 128-bit utility functions */
+
+#include <string.h> /* memcmp, memcpy */
+
+/* return : 1 is equal, 0 if different */
+XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2)
+{
+ /* note : XXH128_hash_t is compact, it has no padding byte */
+ return !(memcmp(&h1, &h2, sizeof(h1)));
+}
+
+/* This prototype is compatible with stdlib's qsort().
+ * return : >0 if *h128_1 > *h128_2
+ * <0 if *h128_1 < *h128_2
+ * =0 if *h128_1 == *h128_2 */
+XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2)
+{
+ XXH128_hash_t const h1 = *(const XXH128_hash_t*)h128_1;
+ XXH128_hash_t const h2 = *(const XXH128_hash_t*)h128_2;
+ int const hcmp = (h1.high64 > h2.high64) - (h2.high64 > h1.high64);
+ /* note : bets that, in most cases, hash values are different */
+ if (hcmp) return hcmp;
+ return (h1.low64 > h2.low64) - (h2.low64 > h1.low64);
+}
+
+
+/*====== Canonical representation ======*/
+XXH_PUBLIC_API void
+XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash)
+{
+ XXH_STATIC_ASSERT(sizeof(XXH128_canonical_t) == sizeof(XXH128_hash_t));
+ if (XXH_CPU_LITTLE_ENDIAN) {
+ hash.high64 = XXH_swap64(hash.high64);
+ hash.low64 = XXH_swap64(hash.low64);
+ }
+ memcpy(dst, &hash.high64, sizeof(hash.high64));
+ memcpy((char*)dst + sizeof(hash.high64), &hash.low64, sizeof(hash.low64));
+}
+
+XXH_PUBLIC_API XXH128_hash_t
+XXH128_hashFromCanonical(const XXH128_canonical_t* src)
+{
+ XXH128_hash_t h;
+ h.high64 = XXH_readBE64(src);
+ h.low64 = XXH_readBE64(src->digest + 8);
+ return h;
+}
+
+/* Pop our optimization override from above */
+#if XXH_VECTOR == XXH_AVX2 /* AVX2 */ \
+ && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \
+ && defined(__OPTIMIZE__) && !defined(__OPTIMIZE_SIZE__) /* respect -O0 and -Os */
+# pragma GCC pop_options
+#endif
+
+#endif /* XXH_NO_LONG_LONG */
+
+
+#endif /* XXH_IMPLEMENTATION */
+
+
+#if defined (__cplusplus)
+}
+#endif
+++ /dev/null
-// Copyright (C) 2002 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 <zlib.h>
-
-#ifdef HAVE_PWD_H
-#include <pwd.h>
-#endif
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-
-#ifdef _WIN32
-#include <windows.h>
-#include <sys/locking.h>
-#include <psapi.h>
-#include <tchar.h>
-#endif
-
-// Destination for conf->log_file.
-static FILE *logfile;
-
-// Buffer used for logs in conf->debug mode.
-static char *debug_log_buffer;
-
-// Allocated debug_log_buffer size.
-static size_t debug_log_buffer_capacity;
-
-// The amount of log data stored in debug_log_buffer.
-static size_t debug_log_size;
-
-#define DEBUG_LOG_BUFFER_MARGIN 1024
-
-static bool
-init_log(void)
-{
- extern struct conf *conf;
-
- if (debug_log_buffer || logfile) {
- return true;
- }
- assert(conf);
- if (conf->debug) {
- debug_log_buffer_capacity = DEBUG_LOG_BUFFER_MARGIN;
- debug_log_buffer = x_malloc(debug_log_buffer_capacity);
- debug_log_size = 0;
- }
- if (str_eq(conf->log_file, "")) {
- return conf->debug;
- }
- logfile = fopen(conf->log_file, "a");
- if (logfile) {
-#ifndef _WIN32
- set_cloexec_flag(fileno(logfile));
-#endif
- return true;
- } else {
- return false;
- }
-}
-
-static void
-append_to_debug_log(const char *s, size_t len)
-{
- assert(debug_log_buffer);
- if (debug_log_size + len + 1 > debug_log_buffer_capacity) {
- debug_log_buffer_capacity += len + 1 + DEBUG_LOG_BUFFER_MARGIN;
- debug_log_buffer = x_realloc(debug_log_buffer, debug_log_buffer_capacity);
- }
- memcpy(debug_log_buffer + debug_log_size, s, len);
- debug_log_size += len;
-}
-
-static void
-log_prefix(bool log_updated_time)
-{
- static char prefix[200];
-#ifdef HAVE_GETTIMEOFDAY
- if (log_updated_time) {
- char timestamp[100];
- struct tm tm;
- struct timeval tv;
- gettimeofday(&tv, NULL);
- if (localtime_r((time_t *)&tv.tv_sec, &tm) != NULL) {
- 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, (int)tv.tv_usec, (int)getpid());
- }
-#else
- snprintf(prefix, sizeof(prefix), "[%-5d] ", (int)getpid());
-#endif
- if (logfile) {
- fputs(prefix, logfile);
- }
- if (debug_log_buffer) {
- append_to_debug_log(prefix, strlen(prefix));
- }
-}
-
-static long
-path_max(const char *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, _PC_PATH_MAX);
- return maxlen >= 4096 ? maxlen : 4096;
-#endif
-}
-
-static void warn_log_fail(void) ATTR_NORETURN;
-
-// Warn about failure writing to the log file and then exit.
-static void
-warn_log_fail(void)
-{
- extern struct conf *conf;
-
- // Note: Can't call fatal() since that would lead to recursion.
- fprintf(stderr, "ccache: error: Failed to write to %s: %s\n",
- conf->log_file, strerror(errno));
- x_exit(EXIT_FAILURE);
-}
-
-static void
-vlog(const char *format, va_list ap, bool log_updated_time)
-{
- if (!init_log()) {
- return;
- }
-
- va_list aq;
- va_copy(aq, ap);
- log_prefix(log_updated_time);
- if (logfile) {
- int rc1 = vfprintf(logfile, format, ap);
- int rc2 = fprintf(logfile, "\n");
- if (rc1 < 0 || rc2 < 0) {
- warn_log_fail();
- }
- }
- if (debug_log_buffer) {
- char buf[8192];
- int len = vsnprintf(buf, sizeof(buf), format, aq);
- if (len >= 0) {
- append_to_debug_log(buf, MIN((size_t)len, sizeof(buf) - 1));
- append_to_debug_log("\n", 1);
- }
- }
- va_end(aq);
-}
-
-// Write a message to the log file (adding a newline) and flush.
-void
-cc_log(const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- vlog(format, ap, true);
- va_end(ap);
- if (logfile) {
- fflush(logfile);
- }
-}
-
-// Write a message to the log file (adding a newline) without flushing and with
-// a reused timestamp.
-void
-cc_bulklog(const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- vlog(format, ap, false);
- va_end(ap);
-}
-
-// Log an executed command to the CCACHE_LOGFILE location.
-void
-cc_log_argv(const char *prefix, char **argv)
-{
- if (!init_log()) {
- return;
- }
-
- log_prefix(true);
- if (logfile) {
- fputs(prefix, logfile);
- print_command(logfile, argv);
- int rc = fflush(logfile);
- if (rc) {
- warn_log_fail();
- }
- }
- if (debug_log_buffer) {
- append_to_debug_log(prefix, strlen(prefix));
- char *s = format_command(argv);
- append_to_debug_log(s, strlen(s));
- free(s);
- }
-}
-
-// Copy the current log memory buffer to an output file.
-void
-cc_dump_debug_log_buffer(const char *path)
-{
- FILE *file = fopen(path, "w");
- if (file) {
- (void) fwrite(debug_log_buffer, 1, debug_log_size, file);
- fclose(file);
- } else {
- cc_log("Failed to open %s: %s", path, strerror(errno));
- }
-}
-
-// Something went badly wrong!
-void
-fatal(const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- char msg[8192];
- vsnprintf(msg, sizeof(msg), format, ap);
- va_end(ap);
-
- cc_log("FATAL: %s", msg);
- fprintf(stderr, "ccache: error: %s\n", msg);
-
- x_exit(1);
-}
-
-// Copy all data from fd_in to fd_out, decompressing data from fd_in if needed.
-void
-copy_fd(int fd_in, int fd_out)
-{
- gzFile gz_in = gzdopen(dup(fd_in), "rb");
- if (!gz_in) {
- fatal("Failed to copy fd");
- }
-
- int n;
- char buf[READ_BUFFER_SIZE];
- while ((n = gzread(gz_in, buf, sizeof(buf))) > 0) {
- ssize_t written = 0;
- do {
- ssize_t count = write(fd_out, buf + written, n - written);
- if (count == -1) {
- if (errno != EAGAIN && errno != EINTR) {
- fatal("Failed to copy fd");
- }
- } else {
- written += count;
- }
- } while (written < n);
- }
-
- gzclose(gz_in);
-}
-
-#ifndef HAVE_MKSTEMP
-// Cheap and nasty mkstemp replacement.
-int
-mkstemp(char *template)
-{
-#ifdef __GNUC__
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#endif
- mktemp(template);
-#ifdef __GNUC__
- #pragma GCC diagnostic pop
-#endif
- return open(template, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600);
-}
-#endif
-
-#ifndef _WIN32
-static mode_t
-get_umask(void)
-{
- static bool mask_retrieved = false;
- static mode_t mask;
- if (!mask_retrieved) {
- mask = umask(0);
- umask(mask);
- mask_retrieved = true;
- }
- return mask;
-}
-#endif
-
-// Copy src to dest, decompressing src if needed. compress_level > 0 decides
-// whether dest will be compressed, and with which compression level. Returns 0
-// on success and -1 on failure. On failure, errno represents the error.
-int
-copy_file(const char *src,
- const char *dest,
- int compress_level,
- bool via_tmp_file)
-{
- int fd_out = -1;
- char *tmp_name = NULL;
- gzFile gz_in = NULL;
- gzFile gz_out = NULL;
- int saved_errno = 0;
-
- // Open source file.
- int fd_in = open(src, O_RDONLY | O_BINARY);
- if (fd_in == -1) {
- saved_errno = errno;
- cc_log("open error: %s", strerror(saved_errno));
- goto error;
- }
-
- // Open destination file.
- if (via_tmp_file) {
- tmp_name = x_strdup(dest);
- fd_out = create_tmp_fd(&tmp_name);
- cc_log("Copying %s to %s via %s (%scompressed)",
- src, dest, tmp_name, compress_level > 0 ? "" : "un");
- } else {
- fd_out = open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
- if (fd_out == -1) {
- saved_errno = errno;
- close(fd_out);
- errno = saved_errno;
- return -1;
- }
- cc_log("Copying %s to %s (%scompressed)",
- src, dest, compress_level > 0 ? "" : "un");
- }
-
- gz_in = gzdopen(fd_in, "rb");
- if (!gz_in) {
- saved_errno = errno;
- cc_log("gzdopen(src) error: %s", strerror(saved_errno));
- close(fd_in);
- goto error;
- }
-
- if (compress_level > 0) {
- // A gzip file occupies at least 20 bytes, so it will always occupy an
- // entire filesystem block, even for empty files. Turn off compression for
- // empty files to save some space.
- struct stat st;
- if (x_fstat(fd_in, &st) != 0) {
- goto error;
- }
- if (file_size(&st) == 0) {
- compress_level = 0;
- }
- }
-
- if (compress_level > 0) {
- gz_out = gzdopen(dup(fd_out), "wb");
- if (!gz_out) {
- saved_errno = errno;
- cc_log("gzdopen(dest) error: %s", strerror(saved_errno));
- goto error;
- }
- gzsetparams(gz_out, compress_level, Z_DEFAULT_STRATEGY);
- }
-
- int n;
- char buf[READ_BUFFER_SIZE];
- while ((n = gzread(gz_in, buf, sizeof(buf))) > 0) {
- int written;
- if (compress_level > 0) {
- written = gzwrite(gz_out, buf, n);
- } else {
- written = 0;
- do {
- ssize_t count = write(fd_out, buf + written, n - written);
- if (count == -1 && errno != EINTR) {
- saved_errno = errno;
- break;
- }
- written += count;
- } while (written < n);
- }
- if (written != n) {
- if (compress_level > 0) {
- int errnum;
- cc_log("gzwrite error: %s (errno: %s)",
- gzerror(gz_in, &errnum),
- strerror(saved_errno));
- } else {
- cc_log("write error: %s", strerror(saved_errno));
- }
- goto error;
- }
- }
-
- // gzeof won't tell if there's an error in the trailing CRC, so we must check
- // gzerror before considering everything OK.
- int errnum;
- gzerror(gz_in, &errnum);
- if (!gzeof(gz_in) || (errnum != Z_OK && errnum != Z_STREAM_END)) {
- saved_errno = errno;
- cc_log("gzread error: %s (errno: %s)",
- gzerror(gz_in, &errnum), strerror(saved_errno));
- gzclose(gz_in);
- if (gz_out) {
- gzclose(gz_out);
- }
- close(fd_out);
- if (via_tmp_file) {
- tmp_unlink(tmp_name);
- free(tmp_name);
- }
- return -1;
- }
-
- gzclose(gz_in);
- gz_in = NULL;
- if (gz_out) {
- gzclose(gz_out);
- gz_out = NULL;
- }
-
-#ifndef _WIN32
- fchmod(fd_out, 0666 & ~get_umask());
-#endif
-
- // The close can fail on NFS if out of space.
- if (close(fd_out) == -1) {
- saved_errno = errno;
- cc_log("close error: %s", strerror(saved_errno));
- goto error;
- }
-
- if (via_tmp_file) {
- if (x_rename(tmp_name, dest) == -1) {
- saved_errno = errno;
- cc_log("rename error: %s", strerror(saved_errno));
- goto error;
- }
-
- free(tmp_name);
- }
-
- return 0;
-
-error:
- if (gz_in) {
- gzclose(gz_in);
- }
- if (gz_out) {
- gzclose(gz_out);
- }
- if (fd_out != -1) {
- close(fd_out);
- }
- if (via_tmp_file && tmp_name) {
- tmp_unlink(tmp_name);
- free(tmp_name);
- }
- errno = saved_errno;
- return -1;
-}
-
-// Run copy_file() and, if successful, delete the source file.
-int
-move_file(const char *src, const char *dest, int compress_level)
-{
- int ret = copy_file(src, dest, compress_level, true);
- if (ret != -1) {
- x_unlink(src);
- }
- return ret;
-}
-
-// Like move_file(), but assumes that src is uncompressed and that src and dest
-// are on the same file system.
-int
-move_uncompressed_file(const char *src, const char *dest, int compress_level)
-{
- if (compress_level > 0) {
- return move_file(src, dest, compress_level);
- } else {
- return x_rename(src, dest);
- }
-}
-
-// Test if a file is zlib compressed.
-bool
-file_is_compressed(const char *filename)
-{
- FILE *f = fopen(filename, "rb");
- if (!f) {
- return false;
- }
-
- // Test if file starts with 1F8B, which is zlib's magic number.
- if ((fgetc(f) != 0x1f) || (fgetc(f) != 0x8b)) {
- fclose(f);
- return false;
- }
-
- fclose(f);
- return true;
-}
-
-// Make sure a directory exists.
-int
-create_dir(const char *dir)
-{
- struct stat st;
- if (stat(dir, &st) == 0) {
- if (S_ISDIR(st.st_mode)) {
- return 0;
- }
- errno = ENOTDIR;
- return 1;
- }
- if (mkdir(dir, 0777) != 0 && errno != EEXIST) {
- return 1;
- }
- return 0;
-}
-
-// Create directories leading to path. Returns 0 on success, otherwise -1.
-int
-create_parent_dirs(const char *path)
-{
- int res;
- char *parent = dirname(path);
- struct stat st;
- if (stat(parent, &st) == 0) {
- if (S_ISDIR(st.st_mode)) {
- res = 0;
- } else {
- res = -1;
- errno = ENOTDIR;
- }
- } else {
- res = create_parent_dirs(parent);
- if (res == 0) {
- res = mkdir(parent, 0777);
- // Have to handle the condition of the directory already existing because
- // 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.
- if (res != 0 && errno == EEXIST) {
- res = 0;
- }
- } else {
- res = -1;
- }
- }
- free(parent);
- return res;
-}
-
-// Return a static string with the current hostname.
-const char *
-get_hostname(void)
-{
- static char hostname[260] = "";
-
- if (hostname[0]) {
- return hostname;
- }
-
- strcpy(hostname, "unknown");
-#if HAVE_GETHOSTNAME
- gethostname(hostname, sizeof(hostname) - 1);
-#elif defined(_WIN32)
- const char *computer_name = getenv("COMPUTERNAME");
- if (computer_name) {
- snprintf(hostname, sizeof(hostname), "%s", computer_name);
- return hostname;
- }
-
- WORD w_version_requested = MAKEWORD(2, 2);
- WSADATA wsa_data;
- int err = WSAStartup(w_version_requested, &wsa_data);
- if (err != 0) {
- // Tell the user that we could not find a usable Winsock DLL.
- cc_log("WSAStartup failed with error: %d", err);
- return hostname;
- }
-
- if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2) {
- // Tell the user that we could not find a usable WinSock DLL.
- cc_log("Could not find a usable version of Winsock.dll");
- WSACleanup();
- return hostname;
- }
-
- int result = gethostname(hostname, sizeof(hostname) - 1);
- if (result != 0) {
- LPVOID lp_msg_buf;
- DWORD dw = WSAGetLastError();
-
- FormatMessage(
- FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR) &lp_msg_buf, 0, NULL);
-
- LPVOID lp_display_buf = (LPVOID) LocalAlloc(
- LMEM_ZEROINIT,
- (lstrlen((LPCTSTR) lp_msg_buf) + lstrlen((LPCTSTR) __FILE__) + 200)
- * sizeof(TCHAR));
- _snprintf((LPTSTR) lp_display_buf,
- LocalSize(lp_display_buf) / sizeof(TCHAR),
- TEXT("%s failed with error %lu: %s"), __FILE__, dw,
- (const char *)lp_msg_buf);
-
- cc_log("can't get hostname OS returned error: %s", (char *)lp_display_buf);
-
- LocalFree(lp_msg_buf);
- LocalFree(lp_display_buf);
- }
- WSACleanup();
-#endif
-
- hostname[sizeof(hostname) - 1] = 0;
- return hostname;
-}
-
-// Return a string to be passed to mkstemp to create a temporary file. Also
-// tries to cope with NFS by adding the local hostname.
-const char *
-tmp_string(void)
-{
- static char *ret;
- if (!ret) {
- ret = format("%s.%u.XXXXXX", get_hostname(), (unsigned)getpid());
- }
- return ret;
-}
-
-// Return the hash result as a hex string. Size -1 means don't include size
-// suffix. Caller frees.
-char *
-format_hash_as_string(const unsigned char *hash, int size)
-{
- int i;
- char *ret = x_malloc(53);
- for (i = 0; i < 16; i++) {
- sprintf(&ret[i*2], "%02x", (unsigned) hash[i]);
- }
- if (size >= 0) {
- sprintf(&ret[i*2], "-%d", size);
- }
- return ret;
-}
-
-static char const 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";
-
-int
-create_cachedirtag(const char *dir)
-{
- char *filename = format("%s/CACHEDIR.TAG", dir);
- struct stat st;
- if (stat(filename, &st) == 0) {
- if (S_ISREG(st.st_mode)) {
- goto success;
- }
- errno = EEXIST;
- goto error;
- }
- FILE *f = fopen(filename, "w");
- if (!f) {
- goto error;
- }
- if (fwrite(CACHEDIR_TAG, sizeof(CACHEDIR_TAG)-1, 1, f) != 1) {
- fclose(f);
- goto error;
- }
- if (fclose(f)) {
- goto error;
- }
-success:
- free(filename);
- return 0;
-error:
- free(filename);
- return 1;
-}
-
-// Construct a string according to a format. Caller frees.
-char *
-format(const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
-
- char *ptr = NULL;
- if (vasprintf(&ptr, format, ap) == -1) {
- fatal("Out of memory in format");
- }
- va_end(ap);
-
- return ptr;
-}
-
-// This is like strdup() but dies if the malloc fails.
-char *
-x_strdup(const char *s)
-{
- char *ret = strdup(s);
- if (!ret) {
- fatal("Out of memory in x_strdup");
- }
- return ret;
-}
-
-// This is like strndup() but dies if the malloc fails.
-char *
-x_strndup(const char *s, size_t n)
-{
-#ifndef HAVE_STRNDUP
- if (!s) {
- return NULL;
- }
- size_t m = 0;
- while (m < n && s[m]) {
- m++;
- }
- char *ret = malloc(m + 1);
- if (ret) {
- memcpy(ret, s, m);
- ret[m] = '\0';
- }
-#else
- char *ret = strndup(s, n);
-#endif
- if (!ret) {
- fatal("x_strndup: Could not allocate %lu bytes", (unsigned long)n);
- }
- return ret;
-}
-
-// This is like malloc() but dies if the malloc fails.
-void *
-x_malloc(size_t size)
-{
- if (size == 0) {
- // malloc() may return NULL if size is zero, so always do this to make sure
- // that the code handles it regardless of platform.
- return NULL;
- }
- void *ret = malloc(size);
- if (!ret) {
- fatal("x_malloc: Could not allocate %lu bytes", (unsigned long)size);
- }
- return ret;
-}
-
-// This is like calloc() but dies if the allocation fails.
-void *
-x_calloc(size_t nmemb, size_t size)
-{
- if (nmemb * size == 0) {
- // calloc() may return NULL if nmemb or size is 0, so always do this to
- // make sure that the code handles it regardless of platform.
- return NULL;
- }
- void *ret = calloc(nmemb, size);
- if (!ret) {
- fatal("x_calloc: Could not allocate %lu bytes", (unsigned long)size);
- }
- return ret;
-}
-
-// This is like realloc() but dies if the malloc fails.
-void *
-x_realloc(void *ptr, size_t size)
-{
- if (!ptr) {
- return x_malloc(size);
- }
- void *p2 = realloc(ptr, size);
- if (!p2) {
- fatal("x_realloc: Could not allocate %lu bytes", (unsigned long)size);
- }
- return p2;
-}
-
-// This is like setenv.
-void x_setenv(const char *name, const char *value)
-{
-#ifdef HAVE_SETENV
- setenv(name, value, true);
-#else
- putenv(format("%s=%s", name, value)); // Leak to environment.
-#endif
-}
-
-// This is like unsetenv.
-void x_unsetenv(const char *name)
-{
-#ifdef HAVE_UNSETENV
- unsetenv(name);
-#else
- putenv(x_strdup(name)); // Leak to environment.
-#endif
-}
-
-// Like fstat() but also call cc_log on failure.
-int
-x_fstat(int fd, struct stat *buf)
-{
- int result = fstat(fd, buf);
- if (result != 0) {
- cc_log("Failed to fstat fd %d: %s", fd, strerror(errno));
- }
- return result;
-}
-
-// Like lstat() but also call cc_log on failure.
-int
-x_lstat(const char *pathname, struct stat *buf)
-{
- int result = lstat(pathname, buf);
- if (result != 0) {
- cc_log("Failed to lstat %s: %s", pathname, strerror(errno));
- }
- return result;
-}
-
-// Like stat() but also call cc_log on failure.
-int
-x_stat(const char *pathname, struct stat *buf)
-{
- int result = stat(pathname, buf);
- if (result != 0) {
- cc_log("Failed to stat %s: %s", pathname, strerror(errno));
- }
- return result;
-}
-
-// Construct a string according to the format and store it in *ptr. The
-// original *ptr is then freed.
-void
-reformat(char **ptr, const char *format, ...)
-{
- char *saved = *ptr;
- *ptr = NULL;
-
- va_list ap;
- va_start(ap, format);
- if (vasprintf(ptr, format, ap) == -1) {
- fatal("Out of memory in reformat");
- }
- va_end(ap);
-
- if (saved) {
- free(saved);
- }
-}
-
-// Recursive directory traversal. fn() is called on all entries in the tree.
-void
-traverse(const char *dir, void (*fn)(const char *, struct stat *))
-{
- DIR *d = opendir(dir);
- if (!d) {
- return;
- }
-
- struct dirent *de;
- while ((de = readdir(d))) {
- if (str_eq(de->d_name, ".")) {
- continue;
- }
- if (str_eq(de->d_name, "..")) {
- continue;
- }
-
- if (strlen(de->d_name) == 0) {
- continue;
- }
-
- char *fname = format("%s/%s", dir, de->d_name);
- struct stat st;
- if (lstat(fname, &st)) {
- if (errno != ENOENT && errno != ESTALE) {
- fatal("lstat %s failed: %s", fname, strerror(errno));
- }
- free(fname);
- continue;
- }
-
- if (S_ISDIR(st.st_mode)) {
- traverse(fname, fn);
- }
-
- fn(fname, &st);
- free(fname);
- }
-
- closedir(d);
-}
-
-
-// Return the base name of a file - caller frees.
-char *
-basename(const char *path)
-{
- char *p = strrchr(path, '/');
- if (p) {
- path = p + 1;
- }
-#ifdef _WIN32
- p = strrchr(path, '\\');
- if (p) {
- path = p + 1;
- }
-#endif
-
- return x_strdup(path);
-}
-
-// Return the dir name of a file - caller frees.
-char *
-dirname(const char *path)
-{
- char *s = x_strdup(path);
- char *p = strrchr(s, '/');
-#ifdef _WIN32
- char *p2 = strrchr(s, '\\');
- if (!p || (p2 && p < p2)) {
- p = p2;
- }
-#endif
- if (!p) {
- free(s);
- s = x_strdup(".");
- } else if (p == s) {
- *(p + 1) = 0;
- } else {
- *p = 0;
- }
- return s;
-}
-
-// Return the file extension (including the dot) of a path as a pointer into
-// path. If path has no file extension, the empty string and the end of path is
-// returned.
-const char *
-get_extension(const char *path)
-{
- size_t len = strlen(path);
- for (const char *p = &path[len - 1]; p >= path; --p) {
- if (*p == '.') {
- return p;
- }
- if (*p == '/') {
- break;
- }
- }
- return &path[len];
-}
-
-// Return a string containing the given path without the filename extension.
-// Caller frees.
-char *
-remove_extension(const char *path)
-{
- return x_strndup(path, strlen(path) - strlen(get_extension(path)));
-}
-
-// Return size on disk of a file.
-size_t
-file_size(struct stat *st)
-{
-#ifdef _WIN32
- return (st->st_size + 1023) & ~1023;
-#else
- return st->st_blocks * 512;
-#endif
-}
-
-// Format a size as a human-readable string. Caller frees.
-char *
-format_human_readable_size(uint64_t v)
-{
- char *s;
- if (v >= 1000*1000*1000) {
- s = format("%.1f GB", v/((double)(1000*1000*1000)));
- } else if (v >= 1000*1000) {
- s = format("%.1f MB", v/((double)(1000*1000)));
- } else {
- s = format("%.1f kB", v/((double)(1000)));
- }
- return s;
-}
-
-// Format a size as a parsable string. Caller frees.
-char *
-format_parsable_size_with_suffix(uint64_t size)
-{
- char *s;
- if (size >= 1000*1000*1000) {
- s = format("%.1fG", size / ((double)(1000*1000*1000)));
- } else if (size >= 1000*1000) {
- s = format("%.1fM", size / ((double)(1000*1000)));
- } else if (size >= 1000) {
- s = format("%.1fk", size / ((double)(1000)));
- } else {
- s = format("%u", (unsigned)size);
- }
- return s;
-}
-
-// 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.
-bool
-parse_size_with_suffix(const char *str, uint64_t *size)
-{
- errno = 0;
-
- char *p;
- double x = strtod(str, &p);
- if (errno != 0 || x < 0 || p == str || *str == '\0') {
- return false;
- }
-
- while (isspace(*p)) {
- ++p;
- }
-
- if (*p != '\0') {
- unsigned multiplier = *(p+1) == 'i' ? 1024 : 1000;
- switch (*p) {
- case 'T':
- x *= multiplier;
- // Fallthrough.
- case 'G':
- x *= multiplier;
- // Fallthrough.
- case 'M':
- x *= multiplier;
- // Fallthrough.
- case 'K':
- case 'k':
- x *= multiplier;
- break;
- default:
- return false;
- }
- } else {
- // Default suffix: G.
- x *= 1000 * 1000 * 1000;
- }
- *size = (uint64_t)x;
- return true;
-}
-
-// A sane realpath() function, trying to cope with stupid path limits and a
-// broken API. Caller frees.
-char *
-x_realpath(const char *path)
-{
- long maxlen = path_max(path);
- char *ret = x_malloc(maxlen);
- char *p;
-
-#if HAVE_REALPATH
- p = realpath(path, ret);
-#elif defined(_WIN32)
- if (path[0] == '/') {
- path++; // Skip leading slash.
- }
- HANDLE path_handle = CreateFile(
- path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL, NULL);
- if (INVALID_HANDLE_VALUE != path_handle) {
- bool ok = GetFinalPathNameByHandle(
- path_handle, ret, maxlen, FILE_NAME_NORMALIZED) != 0;
- CloseHandle(path_handle);
- if (!ok) {
- free(ret);
- return x_strdup(path);
- }
- p = ret + 4; // Strip \\?\ from the file name.
- } else {
- snprintf(ret, maxlen, "%s", path);
- p = ret;
- }
-#else
- // Yes, there are such systems. This replacement relies on the fact that when
- // we call x_realpath we only care about symlinks.
- {
- int len = readlink(path, ret, maxlen-1);
- if (len == -1) {
- free(ret);
- return NULL;
- }
- ret[len] = 0;
- p = ret;
- }
-#endif
- if (p) {
- p = x_strdup(p);
- free(ret);
- return p;
- }
- free(ret);
- return NULL;
-}
-
-// A getcwd that will returns an allocated buffer.
-char *
-gnu_getcwd(void)
-{
- unsigned size = 128;
-
- while (true) {
- char *buffer = (char *)x_malloc(size);
- if (getcwd(buffer, size) == buffer) {
- return buffer;
- }
- free(buffer);
- if (errno != ERANGE) {
- cc_log("getcwd error: %d (%s)", errno, strerror(errno));
- return NULL;
- }
- size *= 2;
- }
-}
-
-#ifndef HAVE_LOCALTIME_R
-// localtime_r replacement.
-struct tm *
-localtime_r(const time_t *timep, struct tm *result)
-{
- struct tm *tm = localtime(timep);
- if (tm) {
- *result = *tm;
- return result;
- } else {
- memset(result, 0, sizeof(*result));
- return NULL;
- }
-}
-#endif
-
-#ifndef HAVE_STRTOK_R
-// strtok_r replacement.
-char *
-strtok_r(char *str, const char *delim, char **saveptr)
-{
- if (!str) {
- str = *saveptr;
- }
- int len = strlen(str);
- char *ret = strtok(str, delim);
- if (ret) {
- char *save = ret;
- while (*save++) {
- // Do nothing.
- }
- if ((len + 1) == (intptr_t) (save - str)) {
- save--;
- }
- *saveptr = save;
- }
- return ret;
-}
-#endif
-
-// Create an empty temporary file. *fname will be reallocated and set to the
-// resulting filename. Returns an open file descriptor to the file.
-int
-create_tmp_fd(char **fname)
-{
- char *template = format("%s.%s", *fname, tmp_string());
- int fd = mkstemp(template);
- if (fd == -1 && errno == ENOENT) {
- if (create_parent_dirs(*fname) != 0) {
- fatal("Failed to create directory %s: %s",
- dirname(*fname), strerror(errno));
- }
- reformat(&template, "%s.%s", *fname, tmp_string());
- fd = mkstemp(template);
- }
- if (fd == -1) {
- fatal("Failed to create temporary file for %s: %s",
- *fname, strerror(errno));
- }
- set_cloexec_flag(fd);
-
-#ifndef _WIN32
- fchmod(fd, 0666 & ~get_umask());
-#endif
-
- free(*fname);
- *fname = template;
- return fd;
-}
-
-// Create an empty temporary file. *fname will be reallocated and set to the
-// resulting filename. Returns an open FILE*.
-FILE *
-create_tmp_file(char **fname, const char *mode)
-{
- FILE *file = fdopen(create_tmp_fd(fname), mode);
- if (!file) {
- fatal("Failed to create file %s: %s", *fname, strerror(errno));
- }
- return file;
-}
-
-// Return current user's home directory, or NULL if it can't be determined.
-const char *
-get_home_directory(void)
-{
- 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
- return NULL;
-}
-
-// Get the current directory by reading $PWD. If $PWD isn't sane, gnu_getcwd()
-// is used. Caller frees.
-char *
-get_cwd(void)
-{
- struct stat st_pwd;
- struct stat st_cwd;
-
- char *cwd = gnu_getcwd();
- if (!cwd) {
- return NULL;
- }
- char *pwd = getenv("PWD");
- if (!pwd) {
- return cwd;
- }
- if (stat(pwd, &st_pwd) != 0) {
- return cwd;
- }
- if (stat(cwd, &st_cwd) != 0) {
- return cwd;
- }
- if (st_pwd.st_dev == st_cwd.st_dev && st_pwd.st_ino == st_cwd.st_ino) {
- free(cwd);
- return x_strdup(pwd);
- } else {
- return cwd;
- }
-}
-
-// Check whether s1 and s2 have the same executable name.
-bool
-same_executable_name(const char *s1, const char *s2)
-{
-#ifdef _WIN32
- bool eq = strcasecmp(s1, s2) == 0;
- if (!eq) {
- char *tmp = format("%s.exe", s2);
- eq = strcasecmp(s1, tmp) == 0;
- free(tmp);
- }
- return eq;
-#else
- return str_eq(s1, s2);
-#endif
-}
-
-// Compute the length of the longest directory path that is common to two
-// paths. s1 is assumed to be the path to a directory.
-size_t
-common_dir_prefix_length(const char *s1, const char *s2)
-{
- const char *p1 = s1;
- const char *p2 = s2;
-
- while (*p1 && *p2 && *p1 == *p2) {
- ++p1;
- ++p2;
- }
- while ((*p1 && *p1 != '/') || (*p2 && *p2 != '/')) {
- p1--;
- p2--;
- }
- if (!*p1 && !*p2 && p2 == s2 + 1) {
- // Special case for s1 and s2 both being "/".
- return 0;
- }
- return p1 - s1;
-}
-
-// Compute a relative path from from (an absolute path to a directory) to to (a
-// path). Assumes that both from and to are well-formed and canonical. Caller
-// frees.
-char *
-get_relative_path(const char *from, const char *to)
-{
- size_t common_prefix_len;
- char *result;
-
- assert(from && is_absolute_path(from));
- assert(to);
-
- if (!*to || !is_absolute_path(to)) {
- return x_strdup(to);
- }
-
-#ifdef _WIN32
- // Paths can be escaped by a slash for use with -isystem.
- if (from[0] == '/') {
- from++;
- }
- if (to[0] == '/') {
- to++;
- }
- // Both paths are absolute, drop the drive letters.
- assert(from[0] == to[0]); // Assume the same drive letter.
- from += 2;
- to += 2;
-#endif
-
- result = x_strdup("");
- common_prefix_len = common_dir_prefix_length(from, to);
- if (common_prefix_len > 0 || !str_eq(from, "/")) {
- const char *p;
- for (p = from + common_prefix_len; *p; p++) {
- if (*p == '/') {
- reformat(&result, "../%s", result);
- }
- }
- }
- if (strlen(to) > common_prefix_len) {
- reformat(&result, "%s%s", result, to + common_prefix_len + 1);
- }
- for (int i = strlen(result) - 1; i >= 0 && result[i] == '/'; i--) {
- result[i] = '\0';
- }
- if (str_eq(result, "")) {
- free(result);
- result = x_strdup(".");
- }
- return result;
-}
-
-// Return whether path is absolute.
-bool
-is_absolute_path(const char *path)
-{
-#ifdef _WIN32
- return path[0] && path[1] == ':';
-#else
- return path[0] == '/';
-#endif
-}
-
-// Return whether the argument is a full path.
-bool
-is_full_path(const char *path)
-{
- if (strchr(path, '/')) {
- return true;
- }
-#ifdef _WIN32
- if (strchr(path, '\\')) {
- return true;
- }
-#endif
- return false;
-}
-
-bool is_symlink(const char *path)
-{
-#ifdef _WIN32
- (void)path;
- return false;
-#else
- struct stat st;
- return x_lstat(path, &st) == 0 && ((st.st_mode & S_IFMT) == S_IFLNK);
-#endif
-}
-
-// Update the modification time of a file in the cache to save it from LRU
-// cleanup.
-void
-update_mtime(const char *path)
-{
-#ifdef HAVE_UTIMES
- utimes(path, NULL);
-#else
- utime(path, NULL);
-#endif
-}
-
-// If exit() already has been called, call _exit(), otherwise exit(). This is
-// used to avoid calling exit() inside an atexit handler.
-void
-x_exit(int status)
-{
- static bool first_time = true;
- if (first_time) {
- first_time = false;
- exit(status);
- } else {
- _exit(status);
- }
-}
-
-// Rename oldpath to newpath (deleting newpath).
-int
-x_rename(const char *oldpath, const char *newpath)
-{
-#ifndef _WIN32
- return rename(oldpath, newpath);
-#else
- // Windows' rename() refuses to overwrite an existing file.
- // If the function succeeds, the return value is nonzero.
- if (MoveFileExA(oldpath, newpath, MOVEFILE_REPLACE_EXISTING) == 0) {
- LPVOID lp_msg_buf;
- DWORD dw = GetLastError();
- FormatMessage(
- FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL, dw,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lp_msg_buf,
- 0,
- NULL);
-
- LPVOID lp_display_buf = (LPVOID) LocalAlloc(
- LMEM_ZEROINIT,
- (lstrlen((LPCTSTR) lp_msg_buf) + lstrlen((LPCTSTR) __FILE__) + 40)
- * sizeof(TCHAR));
- _snprintf((LPTSTR) lp_display_buf,
- LocalSize(lp_display_buf) / sizeof(TCHAR),
- TEXT("%s failed with error %lu: %s"), __FILE__, dw,
- (const char *)lp_msg_buf);
-
- cc_log("can't rename file %s to %s OS returned error: %s",
- oldpath, newpath, (char *) lp_display_buf);
-
- LocalFree(lp_msg_buf);
- LocalFree(lp_display_buf);
- return -1;
- } else {
- return 0;
- }
-#endif
-}
-
-// Remove path, NFS hazardous. Use only for temporary files that will not exist
-// on other systems. That is, the path should include tmp_string().
-int
-tmp_unlink(const char *path)
-{
- cc_log("Unlink %s", path);
- int rc = unlink(path);
- if (rc) {
- cc_log("Unlink failed: %s", strerror(errno));
- }
- return rc;
-}
-
-static int
-do_x_unlink(const char *path, bool log_failure)
-{
- 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.
- char *tmp_name = format("%s.ccache.rm.tmp", path);
-
- int result = 0;
- if (x_rename(path, tmp_name) == -1) {
- result = -1;
- saved_errno = errno;
- goto out;
- }
- if (unlink(tmp_name) == -1) {
- // If it was released in a race, that's OK.
- if (errno != ENOENT && errno != ESTALE) {
- result = -1;
- saved_errno = errno;
- }
- }
-
-out:
- if (result == 0 || log_failure) {
- cc_log("Unlink %s via %s", path, tmp_name);
- if (result != 0 && log_failure) {
- cc_log("x_unlink failed: %s", strerror(saved_errno));
- }
- }
- free(tmp_name);
- errno = saved_errno;
- return result;
-}
-
-// Remove path, NFS safe, log both successes and failures.
-int
-x_unlink(const char *path)
-{
- return do_x_unlink(path, true);
-}
-
-// Remove path, NFS safe, only log successes.
-int
-x_try_unlink(const char *path)
-{
- return do_x_unlink(path, false);
-}
-
-#ifndef _WIN32
-// Like readlink() but returns the string or NULL on failure. Caller frees.
-char *
-x_readlink(const char *path)
-{
- long maxlen = path_max(path);
- char *buf = x_malloc(maxlen);
- ssize_t len = readlink(path, buf, maxlen-1);
- if (len == -1) {
- free(buf);
- return NULL;
- }
- buf[len] = 0;
- return buf;
-}
-#endif
-
-// Reads the content of a file. Size hint 0 means no hint. Returns true on
-// success, otherwise false.
-bool
-read_file(const char *path, size_t size_hint, char **data, size_t *size)
-{
- if (size_hint == 0) {
- struct stat st;
- if (x_stat(path, &st) == 0) {
- size_hint = st.st_size;
- }
- }
- size_hint = (size_hint < 1024) ? 1024 : size_hint;
-
- int fd = open(path, O_RDONLY | O_BINARY);
- if (fd == -1) {
- return false;
- }
- size_t allocated = size_hint;
- *data = x_malloc(allocated);
- int ret;
- size_t pos = 0;
- while (true) {
- if (pos > allocated / 2) {
- allocated *= 2;
- *data = x_realloc(*data, allocated);
- }
- ret = read(fd, *data + pos, allocated - pos);
- if (ret == 0 || (ret == -1 && errno != EINTR)) {
- break;
- }
- if (ret > 0) {
- pos += ret;
- }
- }
- close(fd);
- if (ret == -1) {
- cc_log("Failed reading %s", path);
- free(*data);
- *data = NULL;
- return false;
- }
-
- *size = pos;
- return true;
-}
-
-// Return the content (with NUL termination) of a text file, or NULL on error.
-// Caller frees. Size hint 0 means no hint.
-char *
-read_text_file(const char *path, size_t size_hint)
-{
- size_t size;
- char *data;
- if (read_file(path, size_hint, &data, &size)) {
- data = x_realloc(data, size + 1);
- data[size] = '\0';
- return data;
- } else {
- return NULL;
- }
-}
-
-static bool
-expand_variable(const char **str, char **result, char **errmsg)
-{
- assert(**str == '$');
-
- bool curly;
- const char *p = *str + 1;
- if (*p == '{') {
- curly = true;
- ++p;
- } else {
- curly = false;
- }
-
- const char *q = p;
- while (isalnum(*q) || *q == '_') {
- ++q;
- }
- if (curly) {
- if (*q != '}') {
- *errmsg = format("syntax error: missing '}' after \"%s\"", p);
- return false;
- }
- }
-
- if (q == p) {
- // Special case: don't consider a single $ the start of a variable.
- reformat(result, "%s$", *result);
- return true;
- }
-
- char *name = x_strndup(p, q - p);
- const char *value = getenv(name);
- if (!value) {
- *errmsg = format("environment variable \"%s\" not set", name);
- free(name);
- return false;
- }
- reformat(result, "%s%s", *result, value);
- if (!curly) {
- --q;
- }
- *str = q;
- free(name);
- return true;
-}
-
-// Substitute all instances of $VAR or ${VAR}, where VAR is an environment
-// variable, in a string. Caller frees. If one of the environment variables
-// doesn't exist, NULL will be returned and *errmsg will be an appropriate
-// error message (caller frees).
-char *
-subst_env_in_string(const char *str, char **errmsg)
-{
- assert(errmsg);
- *errmsg = NULL;
-
- char *result = x_strdup("");
- const char *p = str; // Interval start.
- const char *q = str; // Interval end.
- for (q = str; *q; ++q) {
- if (*q == '$') {
- reformat(&result, "%s%.*s", result, (int)(q - p), p);
- if (!expand_variable(&q, &result, errmsg)) {
- free(result);
- return NULL;
- }
- p = q + 1;
- }
- }
- reformat(&result, "%s%.*s", result, (int)(q - p), p);
- return result;
-}
-
-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
-}
-
-double time_seconds(void)
-{
-#ifdef HAVE_GETTIMEOFDAY
- struct timeval tv;
- gettimeofday(&tv, NULL);
- return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
-#else
- return (double)time(NULL);
-#endif
-}
+++ /dev/null
-extern const char CCACHE_VERSION[]; const char CCACHE_VERSION[] = "3.7.12";
+++ /dev/null
-/* adler32.c -- compute the Adler-32 checksum of a data stream
- * Copyright (C) 1995-2011, 2016 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-/* @(#) $Id$ */
-
-#include "zutil.h"
-
-local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2));
-
-#define BASE 65521U /* largest prime smaller than 65536 */
-#define NMAX 5552
-/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
-
-#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;}
-#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1);
-#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2);
-#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4);
-#define DO16(buf) DO8(buf,0); DO8(buf,8);
-
-/* use NO_DIVIDE if your processor does not do division in hardware --
- try it both ways to see which is faster */
-#ifdef NO_DIVIDE
-/* note that this assumes BASE is 65521, where 65536 % 65521 == 15
- (thank you to John Reiser for pointing this out) */
-# define CHOP(a) \
- do { \
- unsigned long tmp = a >> 16; \
- a &= 0xffffUL; \
- a += (tmp << 4) - tmp; \
- } while (0)
-# define MOD28(a) \
- do { \
- CHOP(a); \
- if (a >= BASE) a -= BASE; \
- } while (0)
-# define MOD(a) \
- do { \
- CHOP(a); \
- MOD28(a); \
- } while (0)
-# define MOD63(a) \
- do { /* this assumes a is not negative */ \
- z_off64_t tmp = a >> 32; \
- a &= 0xffffffffL; \
- a += (tmp << 8) - (tmp << 5) + tmp; \
- tmp = a >> 16; \
- a &= 0xffffL; \
- a += (tmp << 4) - tmp; \
- tmp = a >> 16; \
- a &= 0xffffL; \
- a += (tmp << 4) - tmp; \
- if (a >= BASE) a -= BASE; \
- } while (0)
-#else
-# define MOD(a) a %= BASE
-# define MOD28(a) a %= BASE
-# define MOD63(a) a %= BASE
-#endif
-
-/* ========================================================================= */
-uLong ZEXPORT adler32_z(adler, buf, len)
- uLong adler;
- const Bytef *buf;
- z_size_t len;
-{
- unsigned long sum2;
- unsigned n;
-
- /* split Adler-32 into component sums */
- sum2 = (adler >> 16) & 0xffff;
- adler &= 0xffff;
-
- /* in case user likes doing a byte at a time, keep it fast */
- if (len == 1) {
- adler += buf[0];
- if (adler >= BASE)
- adler -= BASE;
- sum2 += adler;
- if (sum2 >= BASE)
- sum2 -= BASE;
- return adler | (sum2 << 16);
- }
-
- /* initial Adler-32 value (deferred check for len == 1 speed) */
- if (buf == Z_NULL)
- return 1L;
-
- /* in case short lengths are provided, keep it somewhat fast */
- if (len < 16) {
- while (len--) {
- adler += *buf++;
- sum2 += adler;
- }
- if (adler >= BASE)
- adler -= BASE;
- MOD28(sum2); /* only added so many BASE's */
- return adler | (sum2 << 16);
- }
-
- /* do length NMAX blocks -- requires just one modulo operation */
- while (len >= NMAX) {
- len -= NMAX;
- n = NMAX / 16; /* NMAX is divisible by 16 */
- do {
- DO16(buf); /* 16 sums unrolled */
- buf += 16;
- } while (--n);
- MOD(adler);
- MOD(sum2);
- }
-
- /* do remaining bytes (less than NMAX, still just one modulo) */
- if (len) { /* avoid modulos if none remaining */
- while (len >= 16) {
- len -= 16;
- DO16(buf);
- buf += 16;
- }
- while (len--) {
- adler += *buf++;
- sum2 += adler;
- }
- MOD(adler);
- MOD(sum2);
- }
-
- /* return recombined sums */
- return adler | (sum2 << 16);
-}
-
-/* ========================================================================= */
-uLong ZEXPORT adler32(adler, buf, len)
- uLong adler;
- const Bytef *buf;
- uInt len;
-{
- return adler32_z(adler, buf, len);
-}
-
-/* ========================================================================= */
-local uLong adler32_combine_(adler1, adler2, len2)
- uLong adler1;
- uLong adler2;
- z_off64_t len2;
-{
- unsigned long sum1;
- unsigned long sum2;
- unsigned rem;
-
- /* for negative len, return invalid adler32 as a clue for debugging */
- if (len2 < 0)
- return 0xffffffffUL;
-
- /* the derivation of this formula is left as an exercise for the reader */
- MOD63(len2); /* assumes len2 >= 0 */
- rem = (unsigned)len2;
- sum1 = adler1 & 0xffff;
- sum2 = rem * sum1;
- MOD(sum2);
- sum1 += (adler2 & 0xffff) + BASE - 1;
- sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem;
- if (sum1 >= BASE) sum1 -= BASE;
- if (sum1 >= BASE) sum1 -= BASE;
- if (sum2 >= ((unsigned long)BASE << 1)) sum2 -= ((unsigned long)BASE << 1);
- if (sum2 >= BASE) sum2 -= BASE;
- return sum1 | (sum2 << 16);
-}
-
-/* ========================================================================= */
-uLong ZEXPORT adler32_combine(adler1, adler2, len2)
- uLong adler1;
- uLong adler2;
- z_off_t len2;
-{
- return adler32_combine_(adler1, adler2, len2);
-}
-
-uLong ZEXPORT adler32_combine64(adler1, adler2, len2)
- uLong adler1;
- uLong adler2;
- z_off64_t len2;
-{
- return adler32_combine_(adler1, adler2, len2);
-}
+++ /dev/null
-/* crc32.c -- compute the CRC-32 of a data stream
- * Copyright (C) 1995-2006, 2010, 2011, 2012, 2016 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- *
- * Thanks to Rodney Brown <rbrown64@csc.com.au> for his contribution of faster
- * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing
- * tables for updating the shift register in one step with three exclusive-ors
- * instead of four steps with four exclusive-ors. This results in about a
- * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3.
- */
-
-/* @(#) $Id$ */
-
-/*
- Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore
- protection on the static variables used to control the first-use generation
- of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should
- first call get_crc_table() to initialize the tables before allowing more than
- one thread to use crc32().
-
- DYNAMIC_CRC_TABLE and MAKECRCH can be #defined to write out crc32.h.
- */
-
-#ifdef MAKECRCH
-# include <stdio.h>
-# ifndef DYNAMIC_CRC_TABLE
-# define DYNAMIC_CRC_TABLE
-# endif /* !DYNAMIC_CRC_TABLE */
-#endif /* MAKECRCH */
-
-#include "zutil.h" /* for STDC and FAR definitions */
-
-/* Definitions for doing the crc four data bytes at a time. */
-#if !defined(NOBYFOUR) && defined(Z_U4)
-# define BYFOUR
-#endif
-#ifdef BYFOUR
- local unsigned long crc32_little OF((unsigned long,
- const unsigned char FAR *, z_size_t));
- local unsigned long crc32_big OF((unsigned long,
- const unsigned char FAR *, z_size_t));
-# define TBLS 8
-#else
-# define TBLS 1
-#endif /* BYFOUR */
-
-/* Local functions for crc concatenation */
-local unsigned long gf2_matrix_times OF((unsigned long *mat,
- unsigned long vec));
-local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat));
-local uLong crc32_combine_ OF((uLong crc1, uLong crc2, z_off64_t len2));
-
-
-#ifdef DYNAMIC_CRC_TABLE
-
-local volatile int crc_table_empty = 1;
-local z_crc_t FAR crc_table[TBLS][256];
-local void make_crc_table OF((void));
-#ifdef MAKECRCH
- local void write_table OF((FILE *, const z_crc_t FAR *));
-#endif /* MAKECRCH */
-/*
- Generate tables for a byte-wise 32-bit CRC calculation on the polynomial:
- x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.
-
- Polynomials over GF(2) are represented in binary, one bit per coefficient,
- with the lowest powers in the most significant bit. Then adding polynomials
- is just exclusive-or, and multiplying a polynomial by x is a right shift by
- one. If we call the above polynomial p, and represent a byte as the
- polynomial q, also with the lowest power in the most significant bit (so the
- byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,
- where a mod b means the remainder after dividing a by b.
-
- This calculation is done using the shift-register method of multiplying and
- taking the remainder. The register is initialized to zero, and for each
- incoming bit, x^32 is added mod p to the register if the bit is a one (where
- x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by
- x (which is shifting right by one and adding x^32 mod p if the bit shifted
- out is a one). We start with the highest power (least significant bit) of
- q and repeat for all eight bits of q.
-
- The first table is simply the CRC of all possible eight bit values. This is
- all the information needed to generate CRCs on data a byte at a time for all
- combinations of CRC register values and incoming bytes. The remaining tables
- allow for word-at-a-time CRC calculation for both big-endian and little-
- endian machines, where a word is four bytes.
-*/
-local void make_crc_table()
-{
- z_crc_t c;
- int n, k;
- z_crc_t poly; /* polynomial exclusive-or pattern */
- /* terms of polynomial defining this crc (except x^32): */
- static volatile int first = 1; /* flag to limit concurrent making */
- static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
-
- /* See if another task is already doing this (not thread-safe, but better
- than nothing -- significantly reduces duration of vulnerability in
- case the advice about DYNAMIC_CRC_TABLE is ignored) */
- if (first) {
- first = 0;
-
- /* make exclusive-or pattern from polynomial (0xedb88320UL) */
- poly = 0;
- for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++)
- poly |= (z_crc_t)1 << (31 - p[n]);
-
- /* generate a crc for every 8-bit value */
- for (n = 0; n < 256; n++) {
- c = (z_crc_t)n;
- for (k = 0; k < 8; k++)
- c = c & 1 ? poly ^ (c >> 1) : c >> 1;
- crc_table[0][n] = c;
- }
-
-#ifdef BYFOUR
- /* generate crc for each value followed by one, two, and three zeros,
- and then the byte reversal of those as well as the first table */
- for (n = 0; n < 256; n++) {
- c = crc_table[0][n];
- crc_table[4][n] = ZSWAP32(c);
- for (k = 1; k < 4; k++) {
- c = crc_table[0][c & 0xff] ^ (c >> 8);
- crc_table[k][n] = c;
- crc_table[k + 4][n] = ZSWAP32(c);
- }
- }
-#endif /* BYFOUR */
-
- crc_table_empty = 0;
- }
- else { /* not first */
- /* wait for the other guy to finish (not efficient, but rare) */
- while (crc_table_empty)
- ;
- }
-
-#ifdef MAKECRCH
- /* write out CRC tables to crc32.h */
- {
- FILE *out;
-
- out = fopen("crc32.h", "w");
- if (out == NULL) return;
- fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n");
- fprintf(out, " * Generated automatically by crc32.c\n */\n\n");
- fprintf(out, "local const z_crc_t FAR ");
- fprintf(out, "crc_table[TBLS][256] =\n{\n {\n");
- write_table(out, crc_table[0]);
-# ifdef BYFOUR
- fprintf(out, "#ifdef BYFOUR\n");
- for (k = 1; k < 8; k++) {
- fprintf(out, " },\n {\n");
- write_table(out, crc_table[k]);
- }
- fprintf(out, "#endif\n");
-# endif /* BYFOUR */
- fprintf(out, " }\n};\n");
- fclose(out);
- }
-#endif /* MAKECRCH */
-}
-
-#ifdef MAKECRCH
-local void write_table(out, table)
- FILE *out;
- const z_crc_t FAR *table;
-{
- int n;
-
- for (n = 0; n < 256; n++)
- fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ",
- (unsigned long)(table[n]),
- n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", "));
-}
-#endif /* MAKECRCH */
-
-#else /* !DYNAMIC_CRC_TABLE */
-/* ========================================================================
- * Tables of CRC-32s of all single-byte values, made by make_crc_table().
- */
-#include "crc32.h"
-#endif /* DYNAMIC_CRC_TABLE */
-
-/* =========================================================================
- * This function can be used by asm versions of crc32()
- */
-const z_crc_t FAR * ZEXPORT get_crc_table()
-{
-#ifdef DYNAMIC_CRC_TABLE
- if (crc_table_empty)
- make_crc_table();
-#endif /* DYNAMIC_CRC_TABLE */
- return (const z_crc_t FAR *)crc_table;
-}
-
-/* ========================================================================= */
-#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8)
-#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1
-
-/* ========================================================================= */
-unsigned long ZEXPORT crc32_z(crc, buf, len)
- unsigned long crc;
- const unsigned char FAR *buf;
- z_size_t len;
-{
- if (buf == Z_NULL) return 0UL;
-
-#ifdef DYNAMIC_CRC_TABLE
- if (crc_table_empty)
- make_crc_table();
-#endif /* DYNAMIC_CRC_TABLE */
-
-#ifdef BYFOUR
- if (sizeof(void *) == sizeof(ptrdiff_t)) {
- z_crc_t endian;
-
- endian = 1;
- if (*((unsigned char *)(&endian)))
- return crc32_little(crc, buf, len);
- else
- return crc32_big(crc, buf, len);
- }
-#endif /* BYFOUR */
- crc = crc ^ 0xffffffffUL;
- while (len >= 8) {
- DO8;
- len -= 8;
- }
- if (len) do {
- DO1;
- } while (--len);
- return crc ^ 0xffffffffUL;
-}
-
-/* ========================================================================= */
-unsigned long ZEXPORT crc32(crc, buf, len)
- unsigned long crc;
- const unsigned char FAR *buf;
- uInt len;
-{
- return crc32_z(crc, buf, len);
-}
-
-#ifdef BYFOUR
-
-/*
- This BYFOUR code accesses the passed unsigned char * buffer with a 32-bit
- integer pointer type. This violates the strict aliasing rule, where a
- compiler can assume, for optimization purposes, that two pointers to
- fundamentally different types won't ever point to the same memory. This can
- manifest as a problem only if one of the pointers is written to. This code
- only reads from those pointers. So long as this code remains isolated in
- this compilation unit, there won't be a problem. For this reason, this code
- should not be copied and pasted into a compilation unit in which other code
- writes to the buffer that is passed to these routines.
- */
-
-/* ========================================================================= */
-#define DOLIT4 c ^= *buf4++; \
- c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \
- crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24]
-#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4
-
-/* ========================================================================= */
-local unsigned long crc32_little(crc, buf, len)
- unsigned long crc;
- const unsigned char FAR *buf;
- z_size_t len;
-{
- register z_crc_t c;
- register const z_crc_t FAR *buf4;
-
- c = (z_crc_t)crc;
- c = ~c;
- while (len && ((ptrdiff_t)buf & 3)) {
- c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
- len--;
- }
-
- buf4 = (const z_crc_t FAR *)(const void FAR *)buf;
- while (len >= 32) {
- DOLIT32;
- len -= 32;
- }
- while (len >= 4) {
- DOLIT4;
- len -= 4;
- }
- buf = (const unsigned char FAR *)buf4;
-
- if (len) do {
- c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
- } while (--len);
- c = ~c;
- return (unsigned long)c;
-}
-
-/* ========================================================================= */
-#define DOBIG4 c ^= *buf4++; \
- c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \
- crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24]
-#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4
-
-/* ========================================================================= */
-local unsigned long crc32_big(crc, buf, len)
- unsigned long crc;
- const unsigned char FAR *buf;
- z_size_t len;
-{
- register z_crc_t c;
- register const z_crc_t FAR *buf4;
-
- c = ZSWAP32((z_crc_t)crc);
- c = ~c;
- while (len && ((ptrdiff_t)buf & 3)) {
- c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
- len--;
- }
-
- buf4 = (const z_crc_t FAR *)(const void FAR *)buf;
- while (len >= 32) {
- DOBIG32;
- len -= 32;
- }
- while (len >= 4) {
- DOBIG4;
- len -= 4;
- }
- buf = (const unsigned char FAR *)buf4;
-
- if (len) do {
- c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
- } while (--len);
- c = ~c;
- return (unsigned long)(ZSWAP32(c));
-}
-
-#endif /* BYFOUR */
-
-#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */
-
-/* ========================================================================= */
-local unsigned long gf2_matrix_times(mat, vec)
- unsigned long *mat;
- unsigned long vec;
-{
- unsigned long sum;
-
- sum = 0;
- while (vec) {
- if (vec & 1)
- sum ^= *mat;
- vec >>= 1;
- mat++;
- }
- return sum;
-}
-
-/* ========================================================================= */
-local void gf2_matrix_square(square, mat)
- unsigned long *square;
- unsigned long *mat;
-{
- int n;
-
- for (n = 0; n < GF2_DIM; n++)
- square[n] = gf2_matrix_times(mat, mat[n]);
-}
-
-/* ========================================================================= */
-local uLong crc32_combine_(crc1, crc2, len2)
- uLong crc1;
- uLong crc2;
- z_off64_t len2;
-{
- int n;
- unsigned long row;
- unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */
- unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */
-
- /* degenerate case (also disallow negative lengths) */
- if (len2 <= 0)
- return crc1;
-
- /* put operator for one zero bit in odd */
- odd[0] = 0xedb88320UL; /* CRC-32 polynomial */
- row = 1;
- for (n = 1; n < GF2_DIM; n++) {
- odd[n] = row;
- row <<= 1;
- }
-
- /* put operator for two zero bits in even */
- gf2_matrix_square(even, odd);
-
- /* put operator for four zero bits in odd */
- gf2_matrix_square(odd, even);
-
- /* apply len2 zeros to crc1 (first square will put the operator for one
- zero byte, eight zero bits, in even) */
- do {
- /* apply zeros operator for this bit of len2 */
- gf2_matrix_square(even, odd);
- if (len2 & 1)
- crc1 = gf2_matrix_times(even, crc1);
- len2 >>= 1;
-
- /* if no more bits set, then done */
- if (len2 == 0)
- break;
-
- /* another iteration of the loop with odd and even swapped */
- gf2_matrix_square(odd, even);
- if (len2 & 1)
- crc1 = gf2_matrix_times(odd, crc1);
- len2 >>= 1;
-
- /* if no more bits set, then done */
- } while (len2 != 0);
-
- /* return combined crc */
- crc1 ^= crc2;
- return crc1;
-}
-
-/* ========================================================================= */
-uLong ZEXPORT crc32_combine(crc1, crc2, len2)
- uLong crc1;
- uLong crc2;
- z_off_t len2;
-{
- return crc32_combine_(crc1, crc2, len2);
-}
-
-uLong ZEXPORT crc32_combine64(crc1, crc2, len2)
- uLong crc1;
- uLong crc2;
- z_off64_t len2;
-{
- return crc32_combine_(crc1, crc2, len2);
-}
+++ /dev/null
-/* crc32.h -- tables for rapid CRC calculation
- * Generated automatically by crc32.c
- */
-
-local const z_crc_t FAR crc_table[TBLS][256] =
-{
- {
- 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL,
- 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL,
- 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL,
- 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL,
- 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL,
- 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL,
- 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL,
- 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL,
- 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL,
- 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL,
- 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL,
- 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,
- 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL,
- 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL,
- 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL,
- 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL,
- 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL,
- 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL,
- 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL,
- 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL,
- 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL,
- 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL,
- 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL,
- 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,
- 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL,
- 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL,
- 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL,
- 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL,
- 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL,
- 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL,
- 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL,
- 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL,
- 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL,
- 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL,
- 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL,
- 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,
- 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL,
- 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL,
- 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL,
- 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL,
- 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL,
- 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL,
- 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL,
- 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL,
- 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL,
- 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL,
- 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL,
- 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,
- 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL,
- 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL,
- 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL,
- 0x2d02ef8dUL
-#ifdef BYFOUR
- },
- {
- 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL,
- 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL,
- 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL,
- 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL,
- 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL,
- 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL,
- 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL,
- 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL,
- 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL,
- 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL,
- 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL,
- 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL,
- 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL,
- 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL,
- 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL,
- 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL,
- 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL,
- 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL,
- 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL,
- 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL,
- 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL,
- 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL,
- 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL,
- 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL,
- 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL,
- 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL,
- 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL,
- 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL,
- 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL,
- 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL,
- 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL,
- 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL,
- 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL,
- 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL,
- 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL,
- 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL,
- 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL,
- 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL,
- 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL,
- 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL,
- 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL,
- 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL,
- 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL,
- 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL,
- 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL,
- 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL,
- 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL,
- 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL,
- 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL,
- 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL,
- 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL,
- 0x9324fd72UL
- },
- {
- 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL,
- 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL,
- 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL,
- 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL,
- 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL,
- 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL,
- 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL,
- 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL,
- 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL,
- 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL,
- 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL,
- 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL,
- 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL,
- 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL,
- 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL,
- 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL,
- 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL,
- 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL,
- 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL,
- 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL,
- 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL,
- 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL,
- 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL,
- 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL,
- 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL,
- 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL,
- 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL,
- 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL,
- 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL,
- 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL,
- 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL,
- 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL,
- 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL,
- 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL,
- 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL,
- 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL,
- 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL,
- 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL,
- 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL,
- 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL,
- 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL,
- 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL,
- 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL,
- 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL,
- 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL,
- 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL,
- 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL,
- 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL,
- 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL,
- 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL,
- 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL,
- 0xbe9834edUL
- },
- {
- 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL,
- 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL,
- 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL,
- 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL,
- 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL,
- 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL,
- 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL,
- 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL,
- 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL,
- 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL,
- 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL,
- 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL,
- 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL,
- 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL,
- 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL,
- 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL,
- 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL,
- 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL,
- 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL,
- 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL,
- 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL,
- 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL,
- 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL,
- 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL,
- 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL,
- 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL,
- 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL,
- 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL,
- 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL,
- 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL,
- 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL,
- 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL,
- 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL,
- 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL,
- 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL,
- 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL,
- 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL,
- 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL,
- 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL,
- 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL,
- 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL,
- 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL,
- 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL,
- 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL,
- 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL,
- 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL,
- 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL,
- 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL,
- 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL,
- 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL,
- 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL,
- 0xde0506f1UL
- },
- {
- 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL,
- 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL,
- 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL,
- 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL,
- 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL,
- 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL,
- 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL,
- 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL,
- 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL,
- 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL,
- 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL,
- 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL,
- 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL,
- 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL,
- 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL,
- 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL,
- 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL,
- 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL,
- 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL,
- 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL,
- 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL,
- 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL,
- 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL,
- 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL,
- 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL,
- 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL,
- 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL,
- 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL,
- 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL,
- 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL,
- 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL,
- 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL,
- 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL,
- 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL,
- 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL,
- 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL,
- 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL,
- 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL,
- 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL,
- 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL,
- 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL,
- 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL,
- 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL,
- 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL,
- 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL,
- 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL,
- 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL,
- 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL,
- 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL,
- 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL,
- 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL,
- 0x8def022dUL
- },
- {
- 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL,
- 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL,
- 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL,
- 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL,
- 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL,
- 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL,
- 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL,
- 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL,
- 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL,
- 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL,
- 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL,
- 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL,
- 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL,
- 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL,
- 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL,
- 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL,
- 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL,
- 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL,
- 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL,
- 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL,
- 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL,
- 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL,
- 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL,
- 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL,
- 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL,
- 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL,
- 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL,
- 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL,
- 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL,
- 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL,
- 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL,
- 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL,
- 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL,
- 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL,
- 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL,
- 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL,
- 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL,
- 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL,
- 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL,
- 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL,
- 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL,
- 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL,
- 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL,
- 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL,
- 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL,
- 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL,
- 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL,
- 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL,
- 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL,
- 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL,
- 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL,
- 0x72fd2493UL
- },
- {
- 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL,
- 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL,
- 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL,
- 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL,
- 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL,
- 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL,
- 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL,
- 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL,
- 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL,
- 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL,
- 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL,
- 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL,
- 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL,
- 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL,
- 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL,
- 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL,
- 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL,
- 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL,
- 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL,
- 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL,
- 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL,
- 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL,
- 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL,
- 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL,
- 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL,
- 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL,
- 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL,
- 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL,
- 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL,
- 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL,
- 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL,
- 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL,
- 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL,
- 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL,
- 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL,
- 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL,
- 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL,
- 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL,
- 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL,
- 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL,
- 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL,
- 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL,
- 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL,
- 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL,
- 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL,
- 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL,
- 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL,
- 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL,
- 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL,
- 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL,
- 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL,
- 0xed3498beUL
- },
- {
- 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL,
- 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL,
- 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL,
- 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL,
- 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL,
- 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL,
- 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL,
- 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL,
- 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL,
- 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL,
- 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL,
- 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL,
- 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL,
- 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL,
- 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL,
- 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL,
- 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL,
- 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL,
- 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL,
- 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL,
- 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL,
- 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL,
- 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL,
- 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL,
- 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL,
- 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL,
- 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL,
- 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL,
- 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL,
- 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL,
- 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL,
- 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL,
- 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL,
- 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL,
- 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL,
- 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL,
- 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL,
- 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL,
- 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL,
- 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL,
- 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL,
- 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL,
- 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL,
- 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL,
- 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL,
- 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL,
- 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL,
- 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL,
- 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL,
- 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL,
- 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL,
- 0xf10605deUL
-#endif
- }
-};
+++ /dev/null
-/* deflate.c -- compress data using the deflation algorithm
- * Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-/*
- * ALGORITHM
- *
- * The "deflation" process depends on being able to identify portions
- * of the input text which are identical to earlier input (within a
- * sliding window trailing behind the input currently being processed).
- *
- * The most straightforward technique turns out to be the fastest for
- * most input files: try all possible matches and select the longest.
- * The key feature of this algorithm is that insertions into the string
- * dictionary are very simple and thus fast, and deletions are avoided
- * completely. Insertions are performed at each input character, whereas
- * string matches are performed only when the previous match ends. So it
- * is preferable to spend more time in matches to allow very fast string
- * insertions and avoid deletions. The matching algorithm for small
- * strings is inspired from that of Rabin & Karp. A brute force approach
- * is used to find longer strings when a small match has been found.
- * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
- * (by Leonid Broukhis).
- * A previous version of this file used a more sophisticated algorithm
- * (by Fiala and Greene) which is guaranteed to run in linear amortized
- * time, but has a larger average cost, uses more memory and is patented.
- * However the F&G algorithm may be faster for some highly redundant
- * files if the parameter max_chain_length (described below) is too large.
- *
- * ACKNOWLEDGEMENTS
- *
- * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
- * I found it in 'freeze' written by Leonid Broukhis.
- * Thanks to many people for bug reports and testing.
- *
- * REFERENCES
- *
- * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification".
- * Available in http://tools.ietf.org/html/rfc1951
- *
- * A description of the Rabin and Karp algorithm is given in the book
- * "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
- *
- * Fiala,E.R., and Greene,D.H.
- * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
- *
- */
-
-/* @(#) $Id$ */
-
-#include "deflate.h"
-
-const char deflate_copyright[] =
- " deflate 1.2.11 Copyright 1995-2017 Jean-loup Gailly and Mark Adler ";
-/*
- If you use the zlib library in a product, an acknowledgment is welcome
- in the documentation of your product. If for some reason you cannot
- include such an acknowledgment, I would appreciate that you keep this
- copyright string in the executable of your product.
- */
-
-/* ===========================================================================
- * Function prototypes.
- */
-typedef enum {
- need_more, /* block not completed, need more input or more output */
- block_done, /* block flush performed */
- finish_started, /* finish started, need only more output at next deflate */
- finish_done /* finish done, accept no more input or output */
-} block_state;
-
-typedef block_state (*compress_func) OF((deflate_state *s, int flush));
-/* Compression function. Returns the block state after the call. */
-
-local int deflateStateCheck OF((z_streamp strm));
-local void slide_hash OF((deflate_state *s));
-local void fill_window OF((deflate_state *s));
-local block_state deflate_stored OF((deflate_state *s, int flush));
-local block_state deflate_fast OF((deflate_state *s, int flush));
-#ifndef FASTEST
-local block_state deflate_slow OF((deflate_state *s, int flush));
-#endif
-local block_state deflate_rle OF((deflate_state *s, int flush));
-local block_state deflate_huff OF((deflate_state *s, int flush));
-local void lm_init OF((deflate_state *s));
-local void putShortMSB OF((deflate_state *s, uInt b));
-local void flush_pending OF((z_streamp strm));
-local unsigned read_buf OF((z_streamp strm, Bytef *buf, unsigned size));
-#ifdef ASMV
-# pragma message("Assembler code may have bugs -- use at your own risk")
- void match_init OF((void)); /* asm code initialization */
- uInt longest_match OF((deflate_state *s, IPos cur_match));
-#else
-local uInt longest_match OF((deflate_state *s, IPos cur_match));
-#endif
-
-#ifdef ZLIB_DEBUG
-local void check_match OF((deflate_state *s, IPos start, IPos match,
- int length));
-#endif
-
-/* ===========================================================================
- * Local data
- */
-
-#define NIL 0
-/* Tail of hash chains */
-
-#ifndef TOO_FAR
-# define TOO_FAR 4096
-#endif
-/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
-
-/* Values for max_lazy_match, good_match and max_chain_length, depending on
- * the desired pack level (0..9). The values given below have been tuned to
- * exclude worst case performance for pathological files. Better values may be
- * found for specific files.
- */
-typedef struct config_s {
- ush good_length; /* reduce lazy search above this match length */
- ush max_lazy; /* do not perform lazy search above this match length */
- ush nice_length; /* quit search above this match length */
- ush max_chain;
- compress_func func;
-} config;
-
-#ifdef FASTEST
-local const config configuration_table[2] = {
-/* good lazy nice chain */
-/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */
-/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */
-#else
-local const config configuration_table[10] = {
-/* good lazy nice chain */
-/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */
-/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */
-/* 2 */ {4, 5, 16, 8, deflate_fast},
-/* 3 */ {4, 6, 32, 32, deflate_fast},
-
-/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */
-/* 5 */ {8, 16, 32, 32, deflate_slow},
-/* 6 */ {8, 16, 128, 128, deflate_slow},
-/* 7 */ {8, 32, 128, 256, deflate_slow},
-/* 8 */ {32, 128, 258, 1024, deflate_slow},
-/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */
-#endif
-
-/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
- * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
- * meaning.
- */
-
-/* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */
-#define RANK(f) (((f) * 2) - ((f) > 4 ? 9 : 0))
-
-/* ===========================================================================
- * Update a hash value with the given input byte
- * IN assertion: all calls to UPDATE_HASH are made with consecutive input
- * characters, so that a running hash key can be computed from the previous
- * key instead of complete recalculation each time.
- */
-#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask)
-
-
-/* ===========================================================================
- * Insert string str in the dictionary and set match_head to the previous head
- * of the hash chain (the most recent string with same hash key). Return
- * the previous length of the hash chain.
- * If this file is compiled with -DFASTEST, the compression level is forced
- * to 1, and no hash chains are maintained.
- * IN assertion: all calls to INSERT_STRING are made with consecutive input
- * characters and the first MIN_MATCH bytes of str are valid (except for
- * the last MIN_MATCH-1 bytes of the input file).
- */
-#ifdef FASTEST
-#define INSERT_STRING(s, str, match_head) \
- (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
- match_head = s->head[s->ins_h], \
- s->head[s->ins_h] = (Pos)(str))
-#else
-#define INSERT_STRING(s, str, match_head) \
- (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
- match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \
- s->head[s->ins_h] = (Pos)(str))
-#endif
-
-/* ===========================================================================
- * Initialize the hash table (avoiding 64K overflow for 16 bit systems).
- * prev[] will be initialized on the fly.
- */
-#define CLEAR_HASH(s) \
- s->head[s->hash_size-1] = NIL; \
- zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));
-
-/* ===========================================================================
- * Slide the hash table when sliding the window down (could be avoided with 32
- * bit values at the expense of memory usage). We slide even when level == 0 to
- * keep the hash table consistent if we switch back to level > 0 later.
- */
-local void slide_hash(s)
- deflate_state *s;
-{
- unsigned n, m;
- Posf *p;
- uInt wsize = s->w_size;
-
- n = s->hash_size;
- p = &s->head[n];
- do {
- m = *--p;
- *p = (Pos)(m >= wsize ? m - wsize : NIL);
- } while (--n);
- n = wsize;
-#ifndef FASTEST
- p = &s->prev[n];
- do {
- m = *--p;
- *p = (Pos)(m >= wsize ? m - wsize : NIL);
- /* If n is not on any hash chain, prev[n] is garbage but
- * its value will never be used.
- */
- } while (--n);
-#endif
-}
-
-/* ========================================================================= */
-int ZEXPORT deflateInit_(strm, level, version, stream_size)
- z_streamp strm;
- int level;
- const char *version;
- int stream_size;
-{
- return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
- Z_DEFAULT_STRATEGY, version, stream_size);
- /* To do: ignore strm->next_in if we use it as window */
-}
-
-/* ========================================================================= */
-int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
- version, stream_size)
- z_streamp strm;
- int level;
- int method;
- int windowBits;
- int memLevel;
- int strategy;
- const char *version;
- int stream_size;
-{
- deflate_state *s;
- int wrap = 1;
- static const char my_version[] = ZLIB_VERSION;
-
- ushf *overlay;
- /* We overlay pending_buf and d_buf+l_buf. This works since the average
- * output size for (length,distance) codes is <= 24 bits.
- */
-
- if (version == Z_NULL || version[0] != my_version[0] ||
- stream_size != sizeof(z_stream)) {
- return Z_VERSION_ERROR;
- }
- if (strm == Z_NULL) return Z_STREAM_ERROR;
-
- strm->msg = Z_NULL;
- if (strm->zalloc == (alloc_func)0) {
-#ifdef Z_SOLO
- return Z_STREAM_ERROR;
-#else
- strm->zalloc = zcalloc;
- strm->opaque = (voidpf)0;
-#endif
- }
- if (strm->zfree == (free_func)0)
-#ifdef Z_SOLO
- return Z_STREAM_ERROR;
-#else
- strm->zfree = zcfree;
-#endif
-
-#ifdef FASTEST
- if (level != 0) level = 1;
-#else
- if (level == Z_DEFAULT_COMPRESSION) level = 6;
-#endif
-
- if (windowBits < 0) { /* suppress zlib wrapper */
- wrap = 0;
- windowBits = -windowBits;
- }
-#ifdef GZIP
- else if (windowBits > 15) {
- wrap = 2; /* write gzip wrapper instead */
- windowBits -= 16;
- }
-#endif
- if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED ||
- windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||
- strategy < 0 || strategy > Z_FIXED || (windowBits == 8 && wrap != 1)) {
- return Z_STREAM_ERROR;
- }
- if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */
- s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
- if (s == Z_NULL) return Z_MEM_ERROR;
- strm->state = (struct internal_state FAR *)s;
- s->strm = strm;
- s->status = INIT_STATE; /* to pass state test in deflateReset() */
-
- s->wrap = wrap;
- s->gzhead = Z_NULL;
- s->w_bits = (uInt)windowBits;
- s->w_size = 1 << s->w_bits;
- s->w_mask = s->w_size - 1;
-
- s->hash_bits = (uInt)memLevel + 7;
- s->hash_size = 1 << s->hash_bits;
- s->hash_mask = s->hash_size - 1;
- s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
-
- s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));
- s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos));
- s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos));
-
- s->high_water = 0; /* nothing written to s->window yet */
-
- s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
-
- overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
- s->pending_buf = (uchf *) overlay;
- s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L);
-
- if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
- s->pending_buf == Z_NULL) {
- s->status = FINISH_STATE;
- strm->msg = ERR_MSG(Z_MEM_ERROR);
- deflateEnd (strm);
- return Z_MEM_ERROR;
- }
- s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
- s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
-
- s->level = level;
- s->strategy = strategy;
- s->method = (Byte)method;
-
- return deflateReset(strm);
-}
-
-/* =========================================================================
- * Check for a valid deflate stream state. Return 0 if ok, 1 if not.
- */
-local int deflateStateCheck (strm)
- z_streamp strm;
-{
- deflate_state *s;
- if (strm == Z_NULL ||
- strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0)
- return 1;
- s = strm->state;
- if (s == Z_NULL || s->strm != strm || (s->status != INIT_STATE &&
-#ifdef GZIP
- s->status != GZIP_STATE &&
-#endif
- s->status != EXTRA_STATE &&
- s->status != NAME_STATE &&
- s->status != COMMENT_STATE &&
- s->status != HCRC_STATE &&
- s->status != BUSY_STATE &&
- s->status != FINISH_STATE))
- return 1;
- return 0;
-}
-
-/* ========================================================================= */
-int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
- z_streamp strm;
- const Bytef *dictionary;
- uInt dictLength;
-{
- deflate_state *s;
- uInt str, n;
- int wrap;
- unsigned avail;
- z_const unsigned char *next;
-
- if (deflateStateCheck(strm) || dictionary == Z_NULL)
- return Z_STREAM_ERROR;
- s = strm->state;
- wrap = s->wrap;
- if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead)
- return Z_STREAM_ERROR;
-
- /* when using zlib wrappers, compute Adler-32 for provided dictionary */
- if (wrap == 1)
- strm->adler = adler32(strm->adler, dictionary, dictLength);
- s->wrap = 0; /* avoid computing Adler-32 in read_buf */
-
- /* if dictionary would fill window, just replace the history */
- if (dictLength >= s->w_size) {
- if (wrap == 0) { /* already empty otherwise */
- CLEAR_HASH(s);
- s->strstart = 0;
- s->block_start = 0L;
- s->insert = 0;
- }
- dictionary += dictLength - s->w_size; /* use the tail */
- dictLength = s->w_size;
- }
-
- /* insert dictionary into window and hash */
- avail = strm->avail_in;
- next = strm->next_in;
- strm->avail_in = dictLength;
- strm->next_in = (z_const Bytef *)dictionary;
- fill_window(s);
- while (s->lookahead >= MIN_MATCH) {
- str = s->strstart;
- n = s->lookahead - (MIN_MATCH-1);
- do {
- UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]);
-#ifndef FASTEST
- s->prev[str & s->w_mask] = s->head[s->ins_h];
-#endif
- s->head[s->ins_h] = (Pos)str;
- str++;
- } while (--n);
- s->strstart = str;
- s->lookahead = MIN_MATCH-1;
- fill_window(s);
- }
- s->strstart += s->lookahead;
- s->block_start = (long)s->strstart;
- s->insert = s->lookahead;
- s->lookahead = 0;
- s->match_length = s->prev_length = MIN_MATCH-1;
- s->match_available = 0;
- strm->next_in = next;
- strm->avail_in = avail;
- s->wrap = wrap;
- return Z_OK;
-}
-
-/* ========================================================================= */
-int ZEXPORT deflateGetDictionary (strm, dictionary, dictLength)
- z_streamp strm;
- Bytef *dictionary;
- uInt *dictLength;
-{
- deflate_state *s;
- uInt len;
-
- if (deflateStateCheck(strm))
- return Z_STREAM_ERROR;
- s = strm->state;
- len = s->strstart + s->lookahead;
- if (len > s->w_size)
- len = s->w_size;
- if (dictionary != Z_NULL && len)
- zmemcpy(dictionary, s->window + s->strstart + s->lookahead - len, len);
- if (dictLength != Z_NULL)
- *dictLength = len;
- return Z_OK;
-}
-
-/* ========================================================================= */
-int ZEXPORT deflateResetKeep (strm)
- z_streamp strm;
-{
- deflate_state *s;
-
- if (deflateStateCheck(strm)) {
- return Z_STREAM_ERROR;
- }
-
- strm->total_in = strm->total_out = 0;
- strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */
- strm->data_type = Z_UNKNOWN;
-
- s = (deflate_state *)strm->state;
- s->pending = 0;
- s->pending_out = s->pending_buf;
-
- if (s->wrap < 0) {
- s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */
- }
- s->status =
-#ifdef GZIP
- s->wrap == 2 ? GZIP_STATE :
-#endif
- s->wrap ? INIT_STATE : BUSY_STATE;
- strm->adler =
-#ifdef GZIP
- s->wrap == 2 ? crc32(0L, Z_NULL, 0) :
-#endif
- adler32(0L, Z_NULL, 0);
- s->last_flush = Z_NO_FLUSH;
-
- _tr_init(s);
-
- return Z_OK;
-}
-
-/* ========================================================================= */
-int ZEXPORT deflateReset (strm)
- z_streamp strm;
-{
- int ret;
-
- ret = deflateResetKeep(strm);
- if (ret == Z_OK)
- lm_init(strm->state);
- return ret;
-}
-
-/* ========================================================================= */
-int ZEXPORT deflateSetHeader (strm, head)
- z_streamp strm;
- gz_headerp head;
-{
- if (deflateStateCheck(strm) || strm->state->wrap != 2)
- return Z_STREAM_ERROR;
- strm->state->gzhead = head;
- return Z_OK;
-}
-
-/* ========================================================================= */
-int ZEXPORT deflatePending (strm, pending, bits)
- unsigned *pending;
- int *bits;
- z_streamp strm;
-{
- if (deflateStateCheck(strm)) return Z_STREAM_ERROR;
- if (pending != Z_NULL)
- *pending = strm->state->pending;
- if (bits != Z_NULL)
- *bits = strm->state->bi_valid;
- return Z_OK;
-}
-
-/* ========================================================================= */
-int ZEXPORT deflatePrime (strm, bits, value)
- z_streamp strm;
- int bits;
- int value;
-{
- deflate_state *s;
- int put;
-
- if (deflateStateCheck(strm)) return Z_STREAM_ERROR;
- s = strm->state;
- if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3))
- return Z_BUF_ERROR;
- do {
- put = Buf_size - s->bi_valid;
- if (put > bits)
- put = bits;
- s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid);
- s->bi_valid += put;
- _tr_flush_bits(s);
- value >>= put;
- bits -= put;
- } while (bits);
- return Z_OK;
-}
-
-/* ========================================================================= */
-int ZEXPORT deflateParams(strm, level, strategy)
- z_streamp strm;
- int level;
- int strategy;
-{
- deflate_state *s;
- compress_func func;
-
- if (deflateStateCheck(strm)) return Z_STREAM_ERROR;
- s = strm->state;
-
-#ifdef FASTEST
- if (level != 0) level = 1;
-#else
- if (level == Z_DEFAULT_COMPRESSION) level = 6;
-#endif
- if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) {
- return Z_STREAM_ERROR;
- }
- func = configuration_table[s->level].func;
-
- if ((strategy != s->strategy || func != configuration_table[level].func) &&
- s->high_water) {
- /* Flush the last buffer: */
- int err = deflate(strm, Z_BLOCK);
- if (err == Z_STREAM_ERROR)
- return err;
- if (strm->avail_out == 0)
- return Z_BUF_ERROR;
- }
- if (s->level != level) {
- if (s->level == 0 && s->matches != 0) {
- if (s->matches == 1)
- slide_hash(s);
- else
- CLEAR_HASH(s);
- s->matches = 0;
- }
- s->level = level;
- s->max_lazy_match = configuration_table[level].max_lazy;
- s->good_match = configuration_table[level].good_length;
- s->nice_match = configuration_table[level].nice_length;
- s->max_chain_length = configuration_table[level].max_chain;
- }
- s->strategy = strategy;
- return Z_OK;
-}
-
-/* ========================================================================= */
-int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain)
- z_streamp strm;
- int good_length;
- int max_lazy;
- int nice_length;
- int max_chain;
-{
- deflate_state *s;
-
- if (deflateStateCheck(strm)) return Z_STREAM_ERROR;
- s = strm->state;
- s->good_match = (uInt)good_length;
- s->max_lazy_match = (uInt)max_lazy;
- s->nice_match = nice_length;
- s->max_chain_length = (uInt)max_chain;
- return Z_OK;
-}
-
-/* =========================================================================
- * For the default windowBits of 15 and memLevel of 8, this function returns
- * a close to exact, as well as small, upper bound on the compressed size.
- * They are coded as constants here for a reason--if the #define's are
- * changed, then this function needs to be changed as well. The return
- * value for 15 and 8 only works for those exact settings.
- *
- * For any setting other than those defaults for windowBits and memLevel,
- * the value returned is a conservative worst case for the maximum expansion
- * resulting from using fixed blocks instead of stored blocks, which deflate
- * can emit on compressed data for some combinations of the parameters.
- *
- * This function could be more sophisticated to provide closer upper bounds for
- * every combination of windowBits and memLevel. But even the conservative
- * upper bound of about 14% expansion does not seem onerous for output buffer
- * allocation.
- */
-uLong ZEXPORT deflateBound(strm, sourceLen)
- z_streamp strm;
- uLong sourceLen;
-{
- deflate_state *s;
- uLong complen, wraplen;
-
- /* conservative upper bound for compressed data */
- complen = sourceLen +
- ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5;
-
- /* if can't get parameters, return conservative bound plus zlib wrapper */
- if (deflateStateCheck(strm))
- return complen + 6;
-
- /* compute wrapper length */
- s = strm->state;
- switch (s->wrap) {
- case 0: /* raw deflate */
- wraplen = 0;
- break;
- case 1: /* zlib wrapper */
- wraplen = 6 + (s->strstart ? 4 : 0);
- break;
-#ifdef GZIP
- case 2: /* gzip wrapper */
- wraplen = 18;
- if (s->gzhead != Z_NULL) { /* user-supplied gzip header */
- Bytef *str;
- if (s->gzhead->extra != Z_NULL)
- wraplen += 2 + s->gzhead->extra_len;
- str = s->gzhead->name;
- if (str != Z_NULL)
- do {
- wraplen++;
- } while (*str++);
- str = s->gzhead->comment;
- if (str != Z_NULL)
- do {
- wraplen++;
- } while (*str++);
- if (s->gzhead->hcrc)
- wraplen += 2;
- }
- break;
-#endif
- default: /* for compiler happiness */
- wraplen = 6;
- }
-
- /* if not default parameters, return conservative bound */
- if (s->w_bits != 15 || s->hash_bits != 8 + 7)
- return complen + wraplen;
-
- /* default settings: return tight bound for that case */
- return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
- (sourceLen >> 25) + 13 - 6 + wraplen;
-}
-
-/* =========================================================================
- * Put a short in the pending buffer. The 16-bit value is put in MSB order.
- * IN assertion: the stream state is correct and there is enough room in
- * pending_buf.
- */
-local void putShortMSB (s, b)
- deflate_state *s;
- uInt b;
-{
- put_byte(s, (Byte)(b >> 8));
- put_byte(s, (Byte)(b & 0xff));
-}
-
-/* =========================================================================
- * Flush as much pending output as possible. All deflate() output, except for
- * some deflate_stored() output, goes through this function so some
- * applications may wish to modify it to avoid allocating a large
- * strm->next_out buffer and copying into it. (See also read_buf()).
- */
-local void flush_pending(strm)
- z_streamp strm;
-{
- unsigned len;
- deflate_state *s = strm->state;
-
- _tr_flush_bits(s);
- len = s->pending;
- if (len > strm->avail_out) len = strm->avail_out;
- if (len == 0) return;
-
- zmemcpy(strm->next_out, s->pending_out, len);
- strm->next_out += len;
- s->pending_out += len;
- strm->total_out += len;
- strm->avail_out -= len;
- s->pending -= len;
- if (s->pending == 0) {
- s->pending_out = s->pending_buf;
- }
-}
-
-/* ===========================================================================
- * Update the header CRC with the bytes s->pending_buf[beg..s->pending - 1].
- */
-#define HCRC_UPDATE(beg) \
- do { \
- if (s->gzhead->hcrc && s->pending > (beg)) \
- strm->adler = crc32(strm->adler, s->pending_buf + (beg), \
- s->pending - (beg)); \
- } while (0)
-
-/* ========================================================================= */
-int ZEXPORT deflate (strm, flush)
- z_streamp strm;
- int flush;
-{
- int old_flush; /* value of flush param for previous deflate call */
- deflate_state *s;
-
- if (deflateStateCheck(strm) || flush > Z_BLOCK || flush < 0) {
- return Z_STREAM_ERROR;
- }
- s = strm->state;
-
- if (strm->next_out == Z_NULL ||
- (strm->avail_in != 0 && strm->next_in == Z_NULL) ||
- (s->status == FINISH_STATE && flush != Z_FINISH)) {
- ERR_RETURN(strm, Z_STREAM_ERROR);
- }
- if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR);
-
- old_flush = s->last_flush;
- s->last_flush = flush;
-
- /* Flush as much pending output as possible */
- if (s->pending != 0) {
- flush_pending(strm);
- if (strm->avail_out == 0) {
- /* Since avail_out is 0, deflate will be called again with
- * more output space, but possibly with both pending and
- * avail_in equal to zero. There won't be anything to do,
- * but this is not an error situation so make sure we
- * return OK instead of BUF_ERROR at next call of deflate:
- */
- s->last_flush = -1;
- return Z_OK;
- }
-
- /* Make sure there is something to do and avoid duplicate consecutive
- * flushes. For repeated and useless calls with Z_FINISH, we keep
- * returning Z_STREAM_END instead of Z_BUF_ERROR.
- */
- } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) &&
- flush != Z_FINISH) {
- ERR_RETURN(strm, Z_BUF_ERROR);
- }
-
- /* User must not provide more input after the first FINISH: */
- if (s->status == FINISH_STATE && strm->avail_in != 0) {
- ERR_RETURN(strm, Z_BUF_ERROR);
- }
-
- /* Write the header */
- if (s->status == INIT_STATE) {
- /* zlib header */
- uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
- uInt level_flags;
-
- if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2)
- level_flags = 0;
- else if (s->level < 6)
- level_flags = 1;
- else if (s->level == 6)
- level_flags = 2;
- else
- level_flags = 3;
- header |= (level_flags << 6);
- if (s->strstart != 0) header |= PRESET_DICT;
- header += 31 - (header % 31);
-
- putShortMSB(s, header);
-
- /* Save the adler32 of the preset dictionary: */
- if (s->strstart != 0) {
- putShortMSB(s, (uInt)(strm->adler >> 16));
- putShortMSB(s, (uInt)(strm->adler & 0xffff));
- }
- strm->adler = adler32(0L, Z_NULL, 0);
- s->status = BUSY_STATE;
-
- /* Compression must start with an empty pending buffer */
- flush_pending(strm);
- if (s->pending != 0) {
- s->last_flush = -1;
- return Z_OK;
- }
- }
-#ifdef GZIP
- if (s->status == GZIP_STATE) {
- /* gzip header */
- strm->adler = crc32(0L, Z_NULL, 0);
- put_byte(s, 31);
- put_byte(s, 139);
- put_byte(s, 8);
- if (s->gzhead == Z_NULL) {
- put_byte(s, 0);
- put_byte(s, 0);
- put_byte(s, 0);
- put_byte(s, 0);
- put_byte(s, 0);
- put_byte(s, s->level == 9 ? 2 :
- (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?
- 4 : 0));
- put_byte(s, OS_CODE);
- s->status = BUSY_STATE;
-
- /* Compression must start with an empty pending buffer */
- flush_pending(strm);
- if (s->pending != 0) {
- s->last_flush = -1;
- return Z_OK;
- }
- }
- else {
- put_byte(s, (s->gzhead->text ? 1 : 0) +
- (s->gzhead->hcrc ? 2 : 0) +
- (s->gzhead->extra == Z_NULL ? 0 : 4) +
- (s->gzhead->name == Z_NULL ? 0 : 8) +
- (s->gzhead->comment == Z_NULL ? 0 : 16)
- );
- put_byte(s, (Byte)(s->gzhead->time & 0xff));
- put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff));
- put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff));
- put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff));
- put_byte(s, s->level == 9 ? 2 :
- (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?
- 4 : 0));
- put_byte(s, s->gzhead->os & 0xff);
- if (s->gzhead->extra != Z_NULL) {
- put_byte(s, s->gzhead->extra_len & 0xff);
- put_byte(s, (s->gzhead->extra_len >> 8) & 0xff);
- }
- if (s->gzhead->hcrc)
- strm->adler = crc32(strm->adler, s->pending_buf,
- s->pending);
- s->gzindex = 0;
- s->status = EXTRA_STATE;
- }
- }
- if (s->status == EXTRA_STATE) {
- if (s->gzhead->extra != Z_NULL) {
- ulg beg = s->pending; /* start of bytes to update crc */
- uInt left = (s->gzhead->extra_len & 0xffff) - s->gzindex;
- while (s->pending + left > s->pending_buf_size) {
- uInt copy = s->pending_buf_size - s->pending;
- zmemcpy(s->pending_buf + s->pending,
- s->gzhead->extra + s->gzindex, copy);
- s->pending = s->pending_buf_size;
- HCRC_UPDATE(beg);
- s->gzindex += copy;
- flush_pending(strm);
- if (s->pending != 0) {
- s->last_flush = -1;
- return Z_OK;
- }
- beg = 0;
- left -= copy;
- }
- zmemcpy(s->pending_buf + s->pending,
- s->gzhead->extra + s->gzindex, left);
- s->pending += left;
- HCRC_UPDATE(beg);
- s->gzindex = 0;
- }
- s->status = NAME_STATE;
- }
- if (s->status == NAME_STATE) {
- if (s->gzhead->name != Z_NULL) {
- ulg beg = s->pending; /* start of bytes to update crc */
- int val;
- do {
- if (s->pending == s->pending_buf_size) {
- HCRC_UPDATE(beg);
- flush_pending(strm);
- if (s->pending != 0) {
- s->last_flush = -1;
- return Z_OK;
- }
- beg = 0;
- }
- val = s->gzhead->name[s->gzindex++];
- put_byte(s, val);
- } while (val != 0);
- HCRC_UPDATE(beg);
- s->gzindex = 0;
- }
- s->status = COMMENT_STATE;
- }
- if (s->status == COMMENT_STATE) {
- if (s->gzhead->comment != Z_NULL) {
- ulg beg = s->pending; /* start of bytes to update crc */
- int val;
- do {
- if (s->pending == s->pending_buf_size) {
- HCRC_UPDATE(beg);
- flush_pending(strm);
- if (s->pending != 0) {
- s->last_flush = -1;
- return Z_OK;
- }
- beg = 0;
- }
- val = s->gzhead->comment[s->gzindex++];
- put_byte(s, val);
- } while (val != 0);
- HCRC_UPDATE(beg);
- }
- s->status = HCRC_STATE;
- }
- if (s->status == HCRC_STATE) {
- if (s->gzhead->hcrc) {
- if (s->pending + 2 > s->pending_buf_size) {
- flush_pending(strm);
- if (s->pending != 0) {
- s->last_flush = -1;
- return Z_OK;
- }
- }
- put_byte(s, (Byte)(strm->adler & 0xff));
- put_byte(s, (Byte)((strm->adler >> 8) & 0xff));
- strm->adler = crc32(0L, Z_NULL, 0);
- }
- s->status = BUSY_STATE;
-
- /* Compression must start with an empty pending buffer */
- flush_pending(strm);
- if (s->pending != 0) {
- s->last_flush = -1;
- return Z_OK;
- }
- }
-#endif
-
- /* Start a new block or continue the current one.
- */
- if (strm->avail_in != 0 || s->lookahead != 0 ||
- (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) {
- block_state bstate;
-
- bstate = s->level == 0 ? deflate_stored(s, flush) :
- s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) :
- s->strategy == Z_RLE ? deflate_rle(s, flush) :
- (*(configuration_table[s->level].func))(s, flush);
-
- if (bstate == finish_started || bstate == finish_done) {
- s->status = FINISH_STATE;
- }
- if (bstate == need_more || bstate == finish_started) {
- if (strm->avail_out == 0) {
- s->last_flush = -1; /* avoid BUF_ERROR next call, see above */
- }
- return Z_OK;
- /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
- * of deflate should use the same flush parameter to make sure
- * that the flush is complete. So we don't have to output an
- * empty block here, this will be done at next call. This also
- * ensures that for a very small output buffer, we emit at most
- * one empty block.
- */
- }
- if (bstate == block_done) {
- if (flush == Z_PARTIAL_FLUSH) {
- _tr_align(s);
- } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */
- _tr_stored_block(s, (char*)0, 0L, 0);
- /* For a full flush, this empty block will be recognized
- * as a special marker by inflate_sync().
- */
- if (flush == Z_FULL_FLUSH) {
- CLEAR_HASH(s); /* forget history */
- if (s->lookahead == 0) {
- s->strstart = 0;
- s->block_start = 0L;
- s->insert = 0;
- }
- }
- }
- flush_pending(strm);
- if (strm->avail_out == 0) {
- s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */
- return Z_OK;
- }
- }
- }
-
- if (flush != Z_FINISH) return Z_OK;
- if (s->wrap <= 0) return Z_STREAM_END;
-
- /* Write the trailer */
-#ifdef GZIP
- if (s->wrap == 2) {
- put_byte(s, (Byte)(strm->adler & 0xff));
- put_byte(s, (Byte)((strm->adler >> 8) & 0xff));
- put_byte(s, (Byte)((strm->adler >> 16) & 0xff));
- put_byte(s, (Byte)((strm->adler >> 24) & 0xff));
- put_byte(s, (Byte)(strm->total_in & 0xff));
- put_byte(s, (Byte)((strm->total_in >> 8) & 0xff));
- put_byte(s, (Byte)((strm->total_in >> 16) & 0xff));
- put_byte(s, (Byte)((strm->total_in >> 24) & 0xff));
- }
- else
-#endif
- {
- putShortMSB(s, (uInt)(strm->adler >> 16));
- putShortMSB(s, (uInt)(strm->adler & 0xffff));
- }
- flush_pending(strm);
- /* If avail_out is zero, the application will call deflate again
- * to flush the rest.
- */
- if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */
- return s->pending != 0 ? Z_OK : Z_STREAM_END;
-}
-
-/* ========================================================================= */
-int ZEXPORT deflateEnd (strm)
- z_streamp strm;
-{
- int status;
-
- if (deflateStateCheck(strm)) return Z_STREAM_ERROR;
-
- status = strm->state->status;
-
- /* Deallocate in reverse order of allocations: */
- TRY_FREE(strm, strm->state->pending_buf);
- TRY_FREE(strm, strm->state->head);
- TRY_FREE(strm, strm->state->prev);
- TRY_FREE(strm, strm->state->window);
-
- ZFREE(strm, strm->state);
- strm->state = Z_NULL;
-
- return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK;
-}
-
-/* =========================================================================
- * Copy the source state to the destination state.
- * To simplify the source, this is not supported for 16-bit MSDOS (which
- * doesn't have enough memory anyway to duplicate compression states).
- */
-int ZEXPORT deflateCopy (dest, source)
- z_streamp dest;
- z_streamp source;
-{
-#ifdef MAXSEG_64K
- return Z_STREAM_ERROR;
-#else
- deflate_state *ds;
- deflate_state *ss;
- ushf *overlay;
-
-
- if (deflateStateCheck(source) || dest == Z_NULL) {
- return Z_STREAM_ERROR;
- }
-
- ss = source->state;
-
- zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream));
-
- ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state));
- if (ds == Z_NULL) return Z_MEM_ERROR;
- dest->state = (struct internal_state FAR *) ds;
- zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state));
- ds->strm = dest;
-
- ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
- ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos));
- ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos));
- overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2);
- ds->pending_buf = (uchf *) overlay;
-
- if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL ||
- ds->pending_buf == Z_NULL) {
- deflateEnd (dest);
- return Z_MEM_ERROR;
- }
- /* following zmemcpy do not work for 16-bit MSDOS */
- zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte));
- zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos));
- zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos));
- zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);
-
- ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
- ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush);
- ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize;
-
- ds->l_desc.dyn_tree = ds->dyn_ltree;
- ds->d_desc.dyn_tree = ds->dyn_dtree;
- ds->bl_desc.dyn_tree = ds->bl_tree;
-
- return Z_OK;
-#endif /* MAXSEG_64K */
-}
-
-/* ===========================================================================
- * Read a new buffer from the current input stream, update the adler32
- * and total number of bytes read. All deflate() input goes through
- * this function so some applications may wish to modify it to avoid
- * allocating a large strm->next_in buffer and copying from it.
- * (See also flush_pending()).
- */
-local unsigned read_buf(strm, buf, size)
- z_streamp strm;
- Bytef *buf;
- unsigned size;
-{
- unsigned len = strm->avail_in;
-
- if (len > size) len = size;
- if (len == 0) return 0;
-
- strm->avail_in -= len;
-
- zmemcpy(buf, strm->next_in, len);
- if (strm->state->wrap == 1) {
- strm->adler = adler32(strm->adler, buf, len);
- }
-#ifdef GZIP
- else if (strm->state->wrap == 2) {
- strm->adler = crc32(strm->adler, buf, len);
- }
-#endif
- strm->next_in += len;
- strm->total_in += len;
-
- return len;
-}
-
-/* ===========================================================================
- * Initialize the "longest match" routines for a new zlib stream
- */
-local void lm_init (s)
- deflate_state *s;
-{
- s->window_size = (ulg)2L*s->w_size;
-
- CLEAR_HASH(s);
-
- /* Set the default configuration parameters:
- */
- s->max_lazy_match = configuration_table[s->level].max_lazy;
- s->good_match = configuration_table[s->level].good_length;
- s->nice_match = configuration_table[s->level].nice_length;
- s->max_chain_length = configuration_table[s->level].max_chain;
-
- s->strstart = 0;
- s->block_start = 0L;
- s->lookahead = 0;
- s->insert = 0;
- s->match_length = s->prev_length = MIN_MATCH-1;
- s->match_available = 0;
- s->ins_h = 0;
-#ifndef FASTEST
-#ifdef ASMV
- match_init(); /* initialize the asm code */
-#endif
-#endif
-}
-
-#ifndef FASTEST
-/* ===========================================================================
- * Set match_start to the longest match starting at the given string and
- * return its length. Matches shorter or equal to prev_length are discarded,
- * in which case the result is equal to prev_length and match_start is
- * garbage.
- * IN assertions: cur_match is the head of the hash chain for the current
- * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
- * OUT assertion: the match length is not greater than s->lookahead.
- */
-#ifndef ASMV
-/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
- * match.S. The code will be functionally equivalent.
- */
-local uInt longest_match(s, cur_match)
- deflate_state *s;
- IPos cur_match; /* current match */
-{
- unsigned chain_length = s->max_chain_length;/* max hash chain length */
- register Bytef *scan = s->window + s->strstart; /* current string */
- register Bytef *match; /* matched string */
- register int len; /* length of current match */
- int best_len = (int)s->prev_length; /* best match length so far */
- int nice_match = s->nice_match; /* stop if match long enough */
- IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
- s->strstart - (IPos)MAX_DIST(s) : NIL;
- /* Stop when cur_match becomes <= limit. To simplify the code,
- * we prevent matches with the string of window index 0.
- */
- Posf *prev = s->prev;
- uInt wmask = s->w_mask;
-
-#ifdef UNALIGNED_OK
- /* Compare two bytes at a time. Note: this is not always beneficial.
- * Try with and without -DUNALIGNED_OK to check.
- */
- register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
- register ush scan_start = *(ushf*)scan;
- register ush scan_end = *(ushf*)(scan+best_len-1);
-#else
- register Bytef *strend = s->window + s->strstart + MAX_MATCH;
- register Byte scan_end1 = scan[best_len-1];
- register Byte scan_end = scan[best_len];
-#endif
-
- /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
- * It is easy to get rid of this optimization if necessary.
- */
- Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
-
- /* Do not waste too much time if we already have a good match: */
- if (s->prev_length >= s->good_match) {
- chain_length >>= 2;
- }
- /* Do not look for matches beyond the end of the input. This is necessary
- * to make deflate deterministic.
- */
- if ((uInt)nice_match > s->lookahead) nice_match = (int)s->lookahead;
-
- Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
-
- do {
- Assert(cur_match < s->strstart, "no future");
- match = s->window + cur_match;
-
- /* Skip to next match if the match length cannot increase
- * or if the match length is less than 2. Note that the checks below
- * for insufficient lookahead only occur occasionally for performance
- * reasons. Therefore uninitialized memory will be accessed, and
- * conditional jumps will be made that depend on those values.
- * However the length of the match is limited to the lookahead, so
- * the output of deflate is not affected by the uninitialized values.
- */
-#if (defined(UNALIGNED_OK) && MAX_MATCH == 258)
- /* This code assumes sizeof(unsigned short) == 2. Do not use
- * UNALIGNED_OK if your compiler uses a different size.
- */
- if (*(ushf*)(match+best_len-1) != scan_end ||
- *(ushf*)match != scan_start) continue;
-
- /* It is not necessary to compare scan[2] and match[2] since they are
- * always equal when the other bytes match, given that the hash keys
- * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
- * strstart+3, +5, ... up to strstart+257. We check for insufficient
- * lookahead only every 4th comparison; the 128th check will be made
- * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
- * necessary to put more guard bytes at the end of the window, or
- * to check more often for insufficient lookahead.
- */
- Assert(scan[2] == match[2], "scan[2]?");
- scan++, match++;
- do {
- } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
- *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
- *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
- *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
- scan < strend);
- /* The funny "do {}" generates better code on most compilers */
-
- /* Here, scan <= window+strstart+257 */
- Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
- if (*scan == *match) scan++;
-
- len = (MAX_MATCH - 1) - (int)(strend-scan);
- scan = strend - (MAX_MATCH-1);
-
-#else /* UNALIGNED_OK */
-
- if (match[best_len] != scan_end ||
- match[best_len-1] != scan_end1 ||
- *match != *scan ||
- *++match != scan[1]) continue;
-
- /* The check at best_len-1 can be removed because it will be made
- * again later. (This heuristic is not always a win.)
- * It is not necessary to compare scan[2] and match[2] since they
- * are always equal when the other bytes match, given that
- * the hash keys are equal and that HASH_BITS >= 8.
- */
- scan += 2, match++;
- Assert(*scan == *match, "match[2]?");
-
- /* We check for insufficient lookahead only every 8th comparison;
- * the 256th check will be made at strstart+258.
- */
- do {
- } while (*++scan == *++match && *++scan == *++match &&
- *++scan == *++match && *++scan == *++match &&
- *++scan == *++match && *++scan == *++match &&
- *++scan == *++match && *++scan == *++match &&
- scan < strend);
-
- Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
-
- len = MAX_MATCH - (int)(strend - scan);
- scan = strend - MAX_MATCH;
-
-#endif /* UNALIGNED_OK */
-
- if (len > best_len) {
- s->match_start = cur_match;
- best_len = len;
- if (len >= nice_match) break;
-#ifdef UNALIGNED_OK
- scan_end = *(ushf*)(scan+best_len-1);
-#else
- scan_end1 = scan[best_len-1];
- scan_end = scan[best_len];
-#endif
- }
- } while ((cur_match = prev[cur_match & wmask]) > limit
- && --chain_length != 0);
-
- if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
- return s->lookahead;
-}
-#endif /* ASMV */
-
-#else /* FASTEST */
-
-/* ---------------------------------------------------------------------------
- * Optimized version for FASTEST only
- */
-local uInt longest_match(s, cur_match)
- deflate_state *s;
- IPos cur_match; /* current match */
-{
- register Bytef *scan = s->window + s->strstart; /* current string */
- register Bytef *match; /* matched string */
- register int len; /* length of current match */
- register Bytef *strend = s->window + s->strstart + MAX_MATCH;
-
- /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
- * It is easy to get rid of this optimization if necessary.
- */
- Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
-
- Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
-
- Assert(cur_match < s->strstart, "no future");
-
- match = s->window + cur_match;
-
- /* Return failure if the match length is less than 2:
- */
- if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1;
-
- /* The check at best_len-1 can be removed because it will be made
- * again later. (This heuristic is not always a win.)
- * It is not necessary to compare scan[2] and match[2] since they
- * are always equal when the other bytes match, given that
- * the hash keys are equal and that HASH_BITS >= 8.
- */
- scan += 2, match += 2;
- Assert(*scan == *match, "match[2]?");
-
- /* We check for insufficient lookahead only every 8th comparison;
- * the 256th check will be made at strstart+258.
- */
- do {
- } while (*++scan == *++match && *++scan == *++match &&
- *++scan == *++match && *++scan == *++match &&
- *++scan == *++match && *++scan == *++match &&
- *++scan == *++match && *++scan == *++match &&
- scan < strend);
-
- Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
-
- len = MAX_MATCH - (int)(strend - scan);
-
- if (len < MIN_MATCH) return MIN_MATCH - 1;
-
- s->match_start = cur_match;
- return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead;
-}
-
-#endif /* FASTEST */
-
-#ifdef ZLIB_DEBUG
-
-#define EQUAL 0
-/* result of memcmp for equal strings */
-
-/* ===========================================================================
- * Check that the match at match_start is indeed a match.
- */
-local void check_match(s, start, match, length)
- deflate_state *s;
- IPos start, match;
- int length;
-{
- /* check that the match is indeed a match */
- if (zmemcmp(s->window + match,
- s->window + start, length) != EQUAL) {
- fprintf(stderr, " start %u, match %u, length %d\n",
- start, match, length);
- do {
- fprintf(stderr, "%c%c", s->window[match++], s->window[start++]);
- } while (--length != 0);
- z_error("invalid match");
- }
- if (z_verbose > 1) {
- fprintf(stderr,"\\[%d,%d]", start-match, length);
- do { putc(s->window[start++], stderr); } while (--length != 0);
- }
-}
-#else
-# define check_match(s, start, match, length)
-#endif /* ZLIB_DEBUG */
-
-/* ===========================================================================
- * Fill the window when the lookahead becomes insufficient.
- * Updates strstart and lookahead.
- *
- * IN assertion: lookahead < MIN_LOOKAHEAD
- * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
- * At least one byte has been read, or avail_in == 0; reads are
- * performed for at least two bytes (required for the zip translate_eol
- * option -- not supported here).
- */
-local void fill_window(s)
- deflate_state *s;
-{
- unsigned n;
- unsigned more; /* Amount of free space at the end of the window. */
- uInt wsize = s->w_size;
-
- Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead");
-
- do {
- more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
-
- /* Deal with !@#$% 64K limit: */
- if (sizeof(int) <= 2) {
- if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
- more = wsize;
-
- } else if (more == (unsigned)(-1)) {
- /* Very unlikely, but possible on 16 bit machine if
- * strstart == 0 && lookahead == 1 (input done a byte at time)
- */
- more--;
- }
- }
-
- /* If the window is almost full and there is insufficient lookahead,
- * move the upper half to the lower one to make room in the upper half.
- */
- if (s->strstart >= wsize+MAX_DIST(s)) {
-
- zmemcpy(s->window, s->window+wsize, (unsigned)wsize - more);
- s->match_start -= wsize;
- s->strstart -= wsize; /* we now have strstart >= MAX_DIST */
- s->block_start -= (long) wsize;
- slide_hash(s);
- more += wsize;
- }
- if (s->strm->avail_in == 0) break;
-
- /* If there was no sliding:
- * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
- * more == window_size - lookahead - strstart
- * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
- * => more >= window_size - 2*WSIZE + 2
- * In the BIG_MEM or MMAP case (not yet supported),
- * window_size == input_size + MIN_LOOKAHEAD &&
- * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
- * Otherwise, window_size == 2*WSIZE so more >= 2.
- * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
- */
- Assert(more >= 2, "more < 2");
-
- n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more);
- s->lookahead += n;
-
- /* Initialize the hash value now that we have some input: */
- if (s->lookahead + s->insert >= MIN_MATCH) {
- uInt str = s->strstart - s->insert;
- s->ins_h = s->window[str];
- UPDATE_HASH(s, s->ins_h, s->window[str + 1]);
-#if MIN_MATCH != 3
- Call UPDATE_HASH() MIN_MATCH-3 more times
-#endif
- while (s->insert) {
- UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]);
-#ifndef FASTEST
- s->prev[str & s->w_mask] = s->head[s->ins_h];
-#endif
- s->head[s->ins_h] = (Pos)str;
- str++;
- s->insert--;
- if (s->lookahead + s->insert < MIN_MATCH)
- break;
- }
- }
- /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
- * but this is not important since only literal bytes will be emitted.
- */
-
- } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
-
- /* If the WIN_INIT bytes after the end of the current data have never been
- * written, then zero those bytes in order to avoid memory check reports of
- * the use of uninitialized (or uninitialised as Julian writes) bytes by
- * the longest match routines. Update the high water mark for the next
- * time through here. WIN_INIT is set to MAX_MATCH since the longest match
- * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.
- */
- if (s->high_water < s->window_size) {
- ulg curr = s->strstart + (ulg)(s->lookahead);
- ulg init;
-
- if (s->high_water < curr) {
- /* Previous high water mark below current data -- zero WIN_INIT
- * bytes or up to end of window, whichever is less.
- */
- init = s->window_size - curr;
- if (init > WIN_INIT)
- init = WIN_INIT;
- zmemzero(s->window + curr, (unsigned)init);
- s->high_water = curr + init;
- }
- else if (s->high_water < (ulg)curr + WIN_INIT) {
- /* High water mark at or above current data, but below current data
- * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up
- * to end of window, whichever is less.
- */
- init = (ulg)curr + WIN_INIT - s->high_water;
- if (init > s->window_size - s->high_water)
- init = s->window_size - s->high_water;
- zmemzero(s->window + s->high_water, (unsigned)init);
- s->high_water += init;
- }
- }
-
- Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
- "not enough room for search");
-}
-
-/* ===========================================================================
- * Flush the current block, with given end-of-file flag.
- * IN assertion: strstart is set to the end of the current match.
- */
-#define FLUSH_BLOCK_ONLY(s, last) { \
- _tr_flush_block(s, (s->block_start >= 0L ? \
- (charf *)&s->window[(unsigned)s->block_start] : \
- (charf *)Z_NULL), \
- (ulg)((long)s->strstart - s->block_start), \
- (last)); \
- s->block_start = s->strstart; \
- flush_pending(s->strm); \
- Tracev((stderr,"[FLUSH]")); \
-}
-
-/* Same but force premature exit if necessary. */
-#define FLUSH_BLOCK(s, last) { \
- FLUSH_BLOCK_ONLY(s, last); \
- if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \
-}
-
-/* Maximum stored block length in deflate format (not including header). */
-#define MAX_STORED 65535
-
-/* Minimum of a and b. */
-#define MIN(a, b) ((a) > (b) ? (b) : (a))
-
-/* ===========================================================================
- * Copy without compression as much as possible from the input stream, return
- * the current block state.
- *
- * In case deflateParams() is used to later switch to a non-zero compression
- * level, s->matches (otherwise unused when storing) keeps track of the number
- * of hash table slides to perform. If s->matches is 1, then one hash table
- * slide will be done when switching. If s->matches is 2, the maximum value
- * allowed here, then the hash table will be cleared, since two or more slides
- * is the same as a clear.
- *
- * deflate_stored() is written to minimize the number of times an input byte is
- * copied. It is most efficient with large input and output buffers, which
- * maximizes the opportunites to have a single copy from next_in to next_out.
- */
-local block_state deflate_stored(s, flush)
- deflate_state *s;
- int flush;
-{
- /* Smallest worthy block size when not flushing or finishing. By default
- * this is 32K. This can be as small as 507 bytes for memLevel == 1. For
- * large input and output buffers, the stored block size will be larger.
- */
- unsigned min_block = MIN(s->pending_buf_size - 5, s->w_size);
-
- /* Copy as many min_block or larger stored blocks directly to next_out as
- * possible. If flushing, copy the remaining available input to next_out as
- * stored blocks, if there is enough space.
- */
- unsigned len, left, have, last = 0;
- unsigned used = s->strm->avail_in;
- do {
- /* Set len to the maximum size block that we can copy directly with the
- * available input data and output space. Set left to how much of that
- * would be copied from what's left in the window.
- */
- len = MAX_STORED; /* maximum deflate stored block length */
- have = (s->bi_valid + 42) >> 3; /* number of header bytes */
- if (s->strm->avail_out < have) /* need room for header */
- break;
- /* maximum stored block length that will fit in avail_out: */
- have = s->strm->avail_out - have;
- left = s->strstart - s->block_start; /* bytes left in window */
- if (len > (ulg)left + s->strm->avail_in)
- len = left + s->strm->avail_in; /* limit len to the input */
- if (len > have)
- len = have; /* limit len to the output */
-
- /* If the stored block would be less than min_block in length, or if
- * unable to copy all of the available input when flushing, then try
- * copying to the window and the pending buffer instead. Also don't
- * write an empty block when flushing -- deflate() does that.
- */
- if (len < min_block && ((len == 0 && flush != Z_FINISH) ||
- flush == Z_NO_FLUSH ||
- len != left + s->strm->avail_in))
- break;
-
- /* Make a dummy stored block in pending to get the header bytes,
- * including any pending bits. This also updates the debugging counts.
- */
- last = flush == Z_FINISH && len == left + s->strm->avail_in ? 1 : 0;
- _tr_stored_block(s, (char *)0, 0L, last);
-
- /* Replace the lengths in the dummy stored block with len. */
- s->pending_buf[s->pending - 4] = len;
- s->pending_buf[s->pending - 3] = len >> 8;
- s->pending_buf[s->pending - 2] = ~len;
- s->pending_buf[s->pending - 1] = ~len >> 8;
-
- /* Write the stored block header bytes. */
- flush_pending(s->strm);
-
-#ifdef ZLIB_DEBUG
- /* Update debugging counts for the data about to be copied. */
- s->compressed_len += len << 3;
- s->bits_sent += len << 3;
-#endif
-
- /* Copy uncompressed bytes from the window to next_out. */
- if (left) {
- if (left > len)
- left = len;
- zmemcpy(s->strm->next_out, s->window + s->block_start, left);
- s->strm->next_out += left;
- s->strm->avail_out -= left;
- s->strm->total_out += left;
- s->block_start += left;
- len -= left;
- }
-
- /* Copy uncompressed bytes directly from next_in to next_out, updating
- * the check value.
- */
- if (len) {
- read_buf(s->strm, s->strm->next_out, len);
- s->strm->next_out += len;
- s->strm->avail_out -= len;
- s->strm->total_out += len;
- }
- } while (last == 0);
-
- /* Update the sliding window with the last s->w_size bytes of the copied
- * data, or append all of the copied data to the existing window if less
- * than s->w_size bytes were copied. Also update the number of bytes to
- * insert in the hash tables, in the event that deflateParams() switches to
- * a non-zero compression level.
- */
- used -= s->strm->avail_in; /* number of input bytes directly copied */
- if (used) {
- /* If any input was used, then no unused input remains in the window,
- * therefore s->block_start == s->strstart.
- */
- if (used >= s->w_size) { /* supplant the previous history */
- s->matches = 2; /* clear hash */
- zmemcpy(s->window, s->strm->next_in - s->w_size, s->w_size);
- s->strstart = s->w_size;
- }
- else {
- if (s->window_size - s->strstart <= used) {
- /* Slide the window down. */
- s->strstart -= s->w_size;
- zmemcpy(s->window, s->window + s->w_size, s->strstart);
- if (s->matches < 2)
- s->matches++; /* add a pending slide_hash() */
- }
- zmemcpy(s->window + s->strstart, s->strm->next_in - used, used);
- s->strstart += used;
- }
- s->block_start = s->strstart;
- s->insert += MIN(used, s->w_size - s->insert);
- }
- if (s->high_water < s->strstart)
- s->high_water = s->strstart;
-
- /* If the last block was written to next_out, then done. */
- if (last)
- return finish_done;
-
- /* If flushing and all input has been consumed, then done. */
- if (flush != Z_NO_FLUSH && flush != Z_FINISH &&
- s->strm->avail_in == 0 && (long)s->strstart == s->block_start)
- return block_done;
-
- /* Fill the window with any remaining input. */
- have = s->window_size - s->strstart - 1;
- if (s->strm->avail_in > have && s->block_start >= (long)s->w_size) {
- /* Slide the window down. */
- s->block_start -= s->w_size;
- s->strstart -= s->w_size;
- zmemcpy(s->window, s->window + s->w_size, s->strstart);
- if (s->matches < 2)
- s->matches++; /* add a pending slide_hash() */
- have += s->w_size; /* more space now */
- }
- if (have > s->strm->avail_in)
- have = s->strm->avail_in;
- if (have) {
- read_buf(s->strm, s->window + s->strstart, have);
- s->strstart += have;
- }
- if (s->high_water < s->strstart)
- s->high_water = s->strstart;
-
- /* There was not enough avail_out to write a complete worthy or flushed
- * stored block to next_out. Write a stored block to pending instead, if we
- * have enough input for a worthy block, or if flushing and there is enough
- * room for the remaining input as a stored block in the pending buffer.
- */
- have = (s->bi_valid + 42) >> 3; /* number of header bytes */
- /* maximum stored block length that will fit in pending: */
- have = MIN(s->pending_buf_size - have, MAX_STORED);
- min_block = MIN(have, s->w_size);
- left = s->strstart - s->block_start;
- if (left >= min_block ||
- ((left || flush == Z_FINISH) && flush != Z_NO_FLUSH &&
- s->strm->avail_in == 0 && left <= have)) {
- len = MIN(left, have);
- last = flush == Z_FINISH && s->strm->avail_in == 0 &&
- len == left ? 1 : 0;
- _tr_stored_block(s, (charf *)s->window + s->block_start, len, last);
- s->block_start += len;
- flush_pending(s->strm);
- }
-
- /* We've done all we can with the available input and output. */
- return last ? finish_started : need_more;
-}
-
-/* ===========================================================================
- * Compress as much as possible from the input stream, return the current
- * block state.
- * This function does not perform lazy evaluation of matches and inserts
- * new strings in the dictionary only for unmatched strings or for short
- * matches. It is used only for the fast compression options.
- */
-local block_state deflate_fast(s, flush)
- deflate_state *s;
- int flush;
-{
- IPos hash_head; /* head of the hash chain */
- int bflush; /* set if current block must be flushed */
-
- for (;;) {
- /* Make sure that we always have enough lookahead, except
- * at the end of the input file. We need MAX_MATCH bytes
- * for the next match, plus MIN_MATCH bytes to insert the
- * string following the next match.
- */
- if (s->lookahead < MIN_LOOKAHEAD) {
- fill_window(s);
- if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
- return need_more;
- }
- if (s->lookahead == 0) break; /* flush the current block */
- }
-
- /* Insert the string window[strstart .. strstart+2] in the
- * dictionary, and set hash_head to the head of the hash chain:
- */
- hash_head = NIL;
- if (s->lookahead >= MIN_MATCH) {
- INSERT_STRING(s, s->strstart, hash_head);
- }
-
- /* Find the longest match, discarding those <= prev_length.
- * At this point we have always match_length < MIN_MATCH
- */
- if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) {
- /* To simplify the code, we prevent matches with the string
- * of window index 0 (in particular we have to avoid a match
- * of the string with itself at the start of the input file).
- */
- s->match_length = longest_match (s, hash_head);
- /* longest_match() sets match_start */
- }
- if (s->match_length >= MIN_MATCH) {
- check_match(s, s->strstart, s->match_start, s->match_length);
-
- _tr_tally_dist(s, s->strstart - s->match_start,
- s->match_length - MIN_MATCH, bflush);
-
- s->lookahead -= s->match_length;
-
- /* Insert new strings in the hash table only if the match length
- * is not too large. This saves time but degrades compression.
- */
-#ifndef FASTEST
- if (s->match_length <= s->max_insert_length &&
- s->lookahead >= MIN_MATCH) {
- s->match_length--; /* string at strstart already in table */
- do {
- s->strstart++;
- INSERT_STRING(s, s->strstart, hash_head);
- /* strstart never exceeds WSIZE-MAX_MATCH, so there are
- * always MIN_MATCH bytes ahead.
- */
- } while (--s->match_length != 0);
- s->strstart++;
- } else
-#endif
- {
- s->strstart += s->match_length;
- s->match_length = 0;
- s->ins_h = s->window[s->strstart];
- UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
-#if MIN_MATCH != 3
- Call UPDATE_HASH() MIN_MATCH-3 more times
-#endif
- /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
- * matter since it will be recomputed at next deflate call.
- */
- }
- } else {
- /* No match, output a literal byte */
- Tracevv((stderr,"%c", s->window[s->strstart]));
- _tr_tally_lit (s, s->window[s->strstart], bflush);
- s->lookahead--;
- s->strstart++;
- }
- if (bflush) FLUSH_BLOCK(s, 0);
- }
- s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1;
- if (flush == Z_FINISH) {
- FLUSH_BLOCK(s, 1);
- return finish_done;
- }
- if (s->last_lit)
- FLUSH_BLOCK(s, 0);
- return block_done;
-}
-
-#ifndef FASTEST
-/* ===========================================================================
- * Same as above, but achieves better compression. We use a lazy
- * evaluation for matches: a match is finally adopted only if there is
- * no better match at the next window position.
- */
-local block_state deflate_slow(s, flush)
- deflate_state *s;
- int flush;
-{
- IPos hash_head; /* head of hash chain */
- int bflush; /* set if current block must be flushed */
-
- /* Process the input block. */
- for (;;) {
- /* Make sure that we always have enough lookahead, except
- * at the end of the input file. We need MAX_MATCH bytes
- * for the next match, plus MIN_MATCH bytes to insert the
- * string following the next match.
- */
- if (s->lookahead < MIN_LOOKAHEAD) {
- fill_window(s);
- if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
- return need_more;
- }
- if (s->lookahead == 0) break; /* flush the current block */
- }
-
- /* Insert the string window[strstart .. strstart+2] in the
- * dictionary, and set hash_head to the head of the hash chain:
- */
- hash_head = NIL;
- if (s->lookahead >= MIN_MATCH) {
- INSERT_STRING(s, s->strstart, hash_head);
- }
-
- /* Find the longest match, discarding those <= prev_length.
- */
- s->prev_length = s->match_length, s->prev_match = s->match_start;
- s->match_length = MIN_MATCH-1;
-
- if (hash_head != NIL && s->prev_length < s->max_lazy_match &&
- s->strstart - hash_head <= MAX_DIST(s)) {
- /* To simplify the code, we prevent matches with the string
- * of window index 0 (in particular we have to avoid a match
- * of the string with itself at the start of the input file).
- */
- s->match_length = longest_match (s, hash_head);
- /* longest_match() sets match_start */
-
- if (s->match_length <= 5 && (s->strategy == Z_FILTERED
-#if TOO_FAR <= 32767
- || (s->match_length == MIN_MATCH &&
- s->strstart - s->match_start > TOO_FAR)
-#endif
- )) {
-
- /* If prev_match is also MIN_MATCH, match_start is garbage
- * but we will ignore the current match anyway.
- */
- s->match_length = MIN_MATCH-1;
- }
- }
- /* If there was a match at the previous step and the current
- * match is not better, output the previous match:
- */
- if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) {
- uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;
- /* Do not insert strings in hash table beyond this. */
-
- check_match(s, s->strstart-1, s->prev_match, s->prev_length);
-
- _tr_tally_dist(s, s->strstart -1 - s->prev_match,
- s->prev_length - MIN_MATCH, bflush);
-
- /* Insert in hash table all strings up to the end of the match.
- * strstart-1 and strstart are already inserted. If there is not
- * enough lookahead, the last two strings are not inserted in
- * the hash table.
- */
- s->lookahead -= s->prev_length-1;
- s->prev_length -= 2;
- do {
- if (++s->strstart <= max_insert) {
- INSERT_STRING(s, s->strstart, hash_head);
- }
- } while (--s->prev_length != 0);
- s->match_available = 0;
- s->match_length = MIN_MATCH-1;
- s->strstart++;
-
- if (bflush) FLUSH_BLOCK(s, 0);
-
- } else if (s->match_available) {
- /* If there was no match at the previous position, output a
- * single literal. If there was a match but the current match
- * is longer, truncate the previous match to a single literal.
- */
- Tracevv((stderr,"%c", s->window[s->strstart-1]));
- _tr_tally_lit(s, s->window[s->strstart-1], bflush);
- if (bflush) {
- FLUSH_BLOCK_ONLY(s, 0);
- }
- s->strstart++;
- s->lookahead--;
- if (s->strm->avail_out == 0) return need_more;
- } else {
- /* There is no previous match to compare with, wait for
- * the next step to decide.
- */
- s->match_available = 1;
- s->strstart++;
- s->lookahead--;
- }
- }
- Assert (flush != Z_NO_FLUSH, "no flush?");
- if (s->match_available) {
- Tracevv((stderr,"%c", s->window[s->strstart-1]));
- _tr_tally_lit(s, s->window[s->strstart-1], bflush);
- s->match_available = 0;
- }
- s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1;
- if (flush == Z_FINISH) {
- FLUSH_BLOCK(s, 1);
- return finish_done;
- }
- if (s->last_lit)
- FLUSH_BLOCK(s, 0);
- return block_done;
-}
-#endif /* FASTEST */
-
-/* ===========================================================================
- * For Z_RLE, simply look for runs of bytes, generate matches only of distance
- * one. Do not maintain a hash table. (It will be regenerated if this run of
- * deflate switches away from Z_RLE.)
- */
-local block_state deflate_rle(s, flush)
- deflate_state *s;
- int flush;
-{
- int bflush; /* set if current block must be flushed */
- uInt prev; /* byte at distance one to match */
- Bytef *scan, *strend; /* scan goes up to strend for length of run */
-
- for (;;) {
- /* Make sure that we always have enough lookahead, except
- * at the end of the input file. We need MAX_MATCH bytes
- * for the longest run, plus one for the unrolled loop.
- */
- if (s->lookahead <= MAX_MATCH) {
- fill_window(s);
- if (s->lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) {
- return need_more;
- }
- if (s->lookahead == 0) break; /* flush the current block */
- }
-
- /* See how many times the previous byte repeats */
- s->match_length = 0;
- if (s->lookahead >= MIN_MATCH && s->strstart > 0) {
- scan = s->window + s->strstart - 1;
- prev = *scan;
- if (prev == *++scan && prev == *++scan && prev == *++scan) {
- strend = s->window + s->strstart + MAX_MATCH;
- do {
- } while (prev == *++scan && prev == *++scan &&
- prev == *++scan && prev == *++scan &&
- prev == *++scan && prev == *++scan &&
- prev == *++scan && prev == *++scan &&
- scan < strend);
- s->match_length = MAX_MATCH - (uInt)(strend - scan);
- if (s->match_length > s->lookahead)
- s->match_length = s->lookahead;
- }
- Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan");
- }
-
- /* Emit match if have run of MIN_MATCH or longer, else emit literal */
- if (s->match_length >= MIN_MATCH) {
- check_match(s, s->strstart, s->strstart - 1, s->match_length);
-
- _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush);
-
- s->lookahead -= s->match_length;
- s->strstart += s->match_length;
- s->match_length = 0;
- } else {
- /* No match, output a literal byte */
- Tracevv((stderr,"%c", s->window[s->strstart]));
- _tr_tally_lit (s, s->window[s->strstart], bflush);
- s->lookahead--;
- s->strstart++;
- }
- if (bflush) FLUSH_BLOCK(s, 0);
- }
- s->insert = 0;
- if (flush == Z_FINISH) {
- FLUSH_BLOCK(s, 1);
- return finish_done;
- }
- if (s->last_lit)
- FLUSH_BLOCK(s, 0);
- return block_done;
-}
-
-/* ===========================================================================
- * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table.
- * (It will be regenerated if this run of deflate switches away from Huffman.)
- */
-local block_state deflate_huff(s, flush)
- deflate_state *s;
- int flush;
-{
- int bflush; /* set if current block must be flushed */
-
- for (;;) {
- /* Make sure that we have a literal to write. */
- if (s->lookahead == 0) {
- fill_window(s);
- if (s->lookahead == 0) {
- if (flush == Z_NO_FLUSH)
- return need_more;
- break; /* flush the current block */
- }
- }
-
- /* Output a literal byte */
- s->match_length = 0;
- Tracevv((stderr,"%c", s->window[s->strstart]));
- _tr_tally_lit (s, s->window[s->strstart], bflush);
- s->lookahead--;
- s->strstart++;
- if (bflush) FLUSH_BLOCK(s, 0);
- }
- s->insert = 0;
- if (flush == Z_FINISH) {
- FLUSH_BLOCK(s, 1);
- return finish_done;
- }
- if (s->last_lit)
- FLUSH_BLOCK(s, 0);
- return block_done;
-}
+++ /dev/null
-/* deflate.h -- internal compression state
- * Copyright (C) 1995-2016 Jean-loup Gailly
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-/* WARNING: this file should *not* be used by applications. It is
- part of the implementation of the compression library and is
- subject to change. Applications should only use zlib.h.
- */
-
-/* @(#) $Id$ */
-
-#ifndef DEFLATE_H
-#define DEFLATE_H
-
-#include "zutil.h"
-
-/* define NO_GZIP when compiling if you want to disable gzip header and
- trailer creation by deflate(). NO_GZIP would be used to avoid linking in
- the crc code when it is not needed. For shared libraries, gzip encoding
- should be left enabled. */
-#ifndef NO_GZIP
-# define GZIP
-#endif
-
-/* ===========================================================================
- * Internal compression state.
- */
-
-#define LENGTH_CODES 29
-/* number of length codes, not counting the special END_BLOCK code */
-
-#define LITERALS 256
-/* number of literal bytes 0..255 */
-
-#define L_CODES (LITERALS+1+LENGTH_CODES)
-/* number of Literal or Length codes, including the END_BLOCK code */
-
-#define D_CODES 30
-/* number of distance codes */
-
-#define BL_CODES 19
-/* number of codes used to transfer the bit lengths */
-
-#define HEAP_SIZE (2*L_CODES+1)
-/* maximum heap size */
-
-#define MAX_BITS 15
-/* All codes must not exceed MAX_BITS bits */
-
-#define Buf_size 16
-/* size of bit buffer in bi_buf */
-
-#define INIT_STATE 42 /* zlib header -> BUSY_STATE */
-#ifdef GZIP
-# define GZIP_STATE 57 /* gzip header -> BUSY_STATE | EXTRA_STATE */
-#endif
-#define EXTRA_STATE 69 /* gzip extra block -> NAME_STATE */
-#define NAME_STATE 73 /* gzip file name -> COMMENT_STATE */
-#define COMMENT_STATE 91 /* gzip comment -> HCRC_STATE */
-#define HCRC_STATE 103 /* gzip header CRC -> BUSY_STATE */
-#define BUSY_STATE 113 /* deflate -> FINISH_STATE */
-#define FINISH_STATE 666 /* stream complete */
-/* Stream status */
-
-
-/* Data structure describing a single value and its code string. */
-typedef struct ct_data_s {
- union {
- ush freq; /* frequency count */
- ush code; /* bit string */
- } fc;
- union {
- ush dad; /* father node in Huffman tree */
- ush len; /* length of bit string */
- } dl;
-} FAR ct_data;
-
-#define Freq fc.freq
-#define Code fc.code
-#define Dad dl.dad
-#define Len dl.len
-
-typedef struct static_tree_desc_s static_tree_desc;
-
-typedef struct tree_desc_s {
- ct_data *dyn_tree; /* the dynamic tree */
- int max_code; /* largest code with non zero frequency */
- const static_tree_desc *stat_desc; /* the corresponding static tree */
-} FAR tree_desc;
-
-typedef ush Pos;
-typedef Pos FAR Posf;
-typedef unsigned IPos;
-
-/* A Pos is an index in the character window. We use short instead of int to
- * save space in the various tables. IPos is used only for parameter passing.
- */
-
-typedef struct internal_state {
- z_streamp strm; /* pointer back to this zlib stream */
- int status; /* as the name implies */
- Bytef *pending_buf; /* output still pending */
- ulg pending_buf_size; /* size of pending_buf */
- Bytef *pending_out; /* next pending byte to output to the stream */
- ulg pending; /* nb of bytes in the pending buffer */
- int wrap; /* bit 0 true for zlib, bit 1 true for gzip */
- gz_headerp gzhead; /* gzip header information to write */
- ulg gzindex; /* where in extra, name, or comment */
- Byte method; /* can only be DEFLATED */
- int last_flush; /* value of flush param for previous deflate call */
-
- /* used by deflate.c: */
-
- uInt w_size; /* LZ77 window size (32K by default) */
- uInt w_bits; /* log2(w_size) (8..16) */
- uInt w_mask; /* w_size - 1 */
-
- Bytef *window;
- /* Sliding window. Input bytes are read into the second half of the window,
- * and move to the first half later to keep a dictionary of at least wSize
- * bytes. With this organization, matches are limited to a distance of
- * wSize-MAX_MATCH bytes, but this ensures that IO is always
- * performed with a length multiple of the block size. Also, it limits
- * the window size to 64K, which is quite useful on MSDOS.
- * To do: use the user input buffer as sliding window.
- */
-
- ulg window_size;
- /* Actual size of window: 2*wSize, except when the user input buffer
- * is directly used as sliding window.
- */
-
- Posf *prev;
- /* Link to older string with same hash index. To limit the size of this
- * array to 64K, this link is maintained only for the last 32K strings.
- * An index in this array is thus a window index modulo 32K.
- */
-
- Posf *head; /* Heads of the hash chains or NIL. */
-
- uInt ins_h; /* hash index of string to be inserted */
- uInt hash_size; /* number of elements in hash table */
- uInt hash_bits; /* log2(hash_size) */
- uInt hash_mask; /* hash_size-1 */
-
- uInt hash_shift;
- /* Number of bits by which ins_h must be shifted at each input
- * step. It must be such that after MIN_MATCH steps, the oldest
- * byte no longer takes part in the hash key, that is:
- * hash_shift * MIN_MATCH >= hash_bits
- */
-
- long block_start;
- /* Window position at the beginning of the current output block. Gets
- * negative when the window is moved backwards.
- */
-
- uInt match_length; /* length of best match */
- IPos prev_match; /* previous match */
- int match_available; /* set if previous match exists */
- uInt strstart; /* start of string to insert */
- uInt match_start; /* start of matching string */
- uInt lookahead; /* number of valid bytes ahead in window */
-
- uInt prev_length;
- /* Length of the best match at previous step. Matches not greater than this
- * are discarded. This is used in the lazy match evaluation.
- */
-
- uInt max_chain_length;
- /* To speed up deflation, hash chains are never searched beyond this
- * length. A higher limit improves compression ratio but degrades the
- * speed.
- */
-
- uInt max_lazy_match;
- /* Attempt to find a better match only when the current match is strictly
- * smaller than this value. This mechanism is used only for compression
- * levels >= 4.
- */
-# define max_insert_length max_lazy_match
- /* Insert new strings in the hash table only if the match length is not
- * greater than this length. This saves time but degrades compression.
- * max_insert_length is used only for compression levels <= 3.
- */
-
- int level; /* compression level (1..9) */
- int strategy; /* favor or force Huffman coding*/
-
- uInt good_match;
- /* Use a faster search when the previous match is longer than this */
-
- int nice_match; /* Stop searching when current match exceeds this */
-
- /* used by trees.c: */
- /* Didn't use ct_data typedef below to suppress compiler warning */
- struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */
- struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
- struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */
-
- struct tree_desc_s l_desc; /* desc. for literal tree */
- struct tree_desc_s d_desc; /* desc. for distance tree */
- struct tree_desc_s bl_desc; /* desc. for bit length tree */
-
- ush bl_count[MAX_BITS+1];
- /* number of codes at each bit length for an optimal tree */
-
- int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */
- int heap_len; /* number of elements in the heap */
- int heap_max; /* element of largest frequency */
- /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
- * The same heap array is used to build all trees.
- */
-
- uch depth[2*L_CODES+1];
- /* Depth of each subtree used as tie breaker for trees of equal frequency
- */
-
- uchf *l_buf; /* buffer for literals or lengths */
-
- uInt lit_bufsize;
- /* Size of match buffer for literals/lengths. There are 4 reasons for
- * limiting lit_bufsize to 64K:
- * - frequencies can be kept in 16 bit counters
- * - if compression is not successful for the first block, all input
- * data is still in the window so we can still emit a stored block even
- * when input comes from standard input. (This can also be done for
- * all blocks if lit_bufsize is not greater than 32K.)
- * - if compression is not successful for a file smaller than 64K, we can
- * even emit a stored file instead of a stored block (saving 5 bytes).
- * This is applicable only for zip (not gzip or zlib).
- * - creating new Huffman trees less frequently may not provide fast
- * adaptation to changes in the input data statistics. (Take for
- * example a binary file with poorly compressible code followed by
- * a highly compressible string table.) Smaller buffer sizes give
- * fast adaptation but have of course the overhead of transmitting
- * trees more frequently.
- * - I can't count above 4
- */
-
- uInt last_lit; /* running index in l_buf */
-
- ushf *d_buf;
- /* Buffer for distances. To simplify the code, d_buf and l_buf have
- * the same number of elements. To use different lengths, an extra flag
- * array would be necessary.
- */
-
- ulg opt_len; /* bit length of current block with optimal trees */
- ulg static_len; /* bit length of current block with static trees */
- uInt matches; /* number of string matches in current block */
- uInt insert; /* bytes at end of window left to insert */
-
-#ifdef ZLIB_DEBUG
- ulg compressed_len; /* total bit length of compressed file mod 2^32 */
- ulg bits_sent; /* bit length of compressed data sent mod 2^32 */
-#endif
-
- ush bi_buf;
- /* Output buffer. bits are inserted starting at the bottom (least
- * significant bits).
- */
- int bi_valid;
- /* Number of valid bits in bi_buf. All bits above the last valid bit
- * are always zero.
- */
-
- ulg high_water;
- /* High water mark offset in window for initialized bytes -- bytes above
- * this are set to zero in order to avoid memory check warnings when
- * longest match routines access bytes past the input. This is then
- * updated to the new high water mark.
- */
-
-} FAR deflate_state;
-
-/* Output a byte on the stream.
- * IN assertion: there is enough room in pending_buf.
- */
-#define put_byte(s, c) {s->pending_buf[s->pending++] = (Bytef)(c);}
-
-
-#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
-/* Minimum amount of lookahead, except at the end of the input file.
- * See deflate.c for comments about the MIN_MATCH+1.
- */
-
-#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD)
-/* In order to simplify the code, particularly on 16 bit machines, match
- * distances are limited to MAX_DIST instead of WSIZE.
- */
-
-#define WIN_INIT MAX_MATCH
-/* Number of bytes after end of data in window to initialize in order to avoid
- memory checker errors from longest match routines */
-
- /* in trees.c */
-void ZLIB_INTERNAL _tr_init OF((deflate_state *s));
-int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));
-void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf,
- ulg stored_len, int last));
-void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s));
-void ZLIB_INTERNAL _tr_align OF((deflate_state *s));
-void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf,
- ulg stored_len, int last));
-
-#define d_code(dist) \
- ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)])
-/* Mapping from a distance to a distance code. dist is the distance - 1 and
- * must not have side effects. _dist_code[256] and _dist_code[257] are never
- * used.
- */
-
-#ifndef ZLIB_DEBUG
-/* Inline versions of _tr_tally for speed: */
-
-#if defined(GEN_TREES_H) || !defined(STDC)
- extern uch ZLIB_INTERNAL _length_code[];
- extern uch ZLIB_INTERNAL _dist_code[];
-#else
- extern const uch ZLIB_INTERNAL _length_code[];
- extern const uch ZLIB_INTERNAL _dist_code[];
-#endif
-
-# define _tr_tally_lit(s, c, flush) \
- { uch cc = (c); \
- s->d_buf[s->last_lit] = 0; \
- s->l_buf[s->last_lit++] = cc; \
- s->dyn_ltree[cc].Freq++; \
- flush = (s->last_lit == s->lit_bufsize-1); \
- }
-# define _tr_tally_dist(s, distance, length, flush) \
- { uch len = (uch)(length); \
- ush dist = (ush)(distance); \
- s->d_buf[s->last_lit] = dist; \
- s->l_buf[s->last_lit++] = len; \
- dist--; \
- s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
- s->dyn_dtree[d_code(dist)].Freq++; \
- flush = (s->last_lit == s->lit_bufsize-1); \
- }
-#else
-# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
-# define _tr_tally_dist(s, distance, length, flush) \
- flush = _tr_tally(s, distance, length)
-#endif
-
-#endif /* DEFLATE_H */
+++ /dev/null
-/* gzclose.c -- zlib gzclose() function
- * Copyright (C) 2004, 2010 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-#include "gzguts.h"
-
-/* gzclose() is in a separate file so that it is linked in only if it is used.
- That way the other gzclose functions can be used instead to avoid linking in
- unneeded compression or decompression routines. */
-int ZEXPORT gzclose(file)
- gzFile file;
-{
-#ifndef NO_GZCOMPRESS
- gz_statep state;
-
- if (file == NULL)
- return Z_STREAM_ERROR;
- state = (gz_statep)file;
-
- return state->mode == GZ_READ ? gzclose_r(file) : gzclose_w(file);
-#else
- return gzclose_r(file);
-#endif
-}
+++ /dev/null
-/* gzguts.h -- zlib internal header definitions for gz* operations
- * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-#ifdef _LARGEFILE64_SOURCE
-# ifndef _LARGEFILE_SOURCE
-# define _LARGEFILE_SOURCE 1
-# endif
-# ifdef _FILE_OFFSET_BITS
-# undef _FILE_OFFSET_BITS
-# endif
-#endif
-
-#ifdef HAVE_HIDDEN
-# define ZLIB_INTERNAL __attribute__((visibility ("hidden")))
-#else
-# define ZLIB_INTERNAL
-#endif
-
-#include <stdio.h>
-#include "zlib.h"
-#ifdef STDC
-# include <string.h>
-# include <stdlib.h>
-# include <limits.h>
-#endif
-
-#ifndef _POSIX_SOURCE
-# define _POSIX_SOURCE
-#endif
-#include <fcntl.h>
-
-#ifdef _WIN32
-# include <stddef.h>
-#endif
-
-#if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32)
-# include <io.h>
-#endif
-
-#if defined(_WIN32)
-# define WIDECHAR
-#endif
-
-#ifdef WINAPI_FAMILY
-# define open _open
-# define read _read
-# define write _write
-# define close _close
-#endif
-
-#ifdef NO_DEFLATE /* for compatibility with old definition */
-# define NO_GZCOMPRESS
-#endif
-
-#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550)
-# ifndef HAVE_VSNPRINTF
-# define HAVE_VSNPRINTF
-# endif
-#endif
-
-#if defined(__CYGWIN__)
-# ifndef HAVE_VSNPRINTF
-# define HAVE_VSNPRINTF
-# endif
-#endif
-
-#if defined(MSDOS) && defined(__BORLANDC__) && (BORLANDC > 0x410)
-# ifndef HAVE_VSNPRINTF
-# define HAVE_VSNPRINTF
-# endif
-#endif
-
-#ifndef HAVE_VSNPRINTF
-# ifdef MSDOS
-/* vsnprintf may exist on some MS-DOS compilers (DJGPP?),
- but for now we just assume it doesn't. */
-# define NO_vsnprintf
-# endif
-# ifdef __TURBOC__
-# define NO_vsnprintf
-# endif
-# ifdef WIN32
-/* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */
-# if !defined(vsnprintf) && !defined(NO_vsnprintf)
-# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 )
-# define vsnprintf _vsnprintf
-# endif
-# endif
-# endif
-# ifdef __SASC
-# define NO_vsnprintf
-# endif
-# ifdef VMS
-# define NO_vsnprintf
-# endif
-# ifdef __OS400__
-# define NO_vsnprintf
-# endif
-# ifdef __MVS__
-# define NO_vsnprintf
-# endif
-#endif
-
-/* unlike snprintf (which is required in C99), _snprintf does not guarantee
- null termination of the result -- however this is only used in gzlib.c where
- the result is assured to fit in the space provided */
-#if defined(_MSC_VER) && _MSC_VER < 1900
-# define snprintf _snprintf
-#endif
-
-#ifndef local
-# define local static
-#endif
-/* since "static" is used to mean two completely different things in C, we
- define "local" for the non-static meaning of "static", for readability
- (compile with -Dlocal if your debugger can't find static symbols) */
-
-/* gz* functions always use library allocation functions */
-#ifndef STDC
- extern voidp malloc OF((uInt size));
- extern void free OF((voidpf ptr));
-#endif
-
-/* get errno and strerror definition */
-#if defined UNDER_CE
-# include <windows.h>
-# define zstrerror() gz_strwinerror((DWORD)GetLastError())
-#else
-# ifndef NO_STRERROR
-# include <errno.h>
-# define zstrerror() strerror(errno)
-# else
-# define zstrerror() "stdio error (consult errno)"
-# endif
-#endif
-
-/* provide prototypes for these when building zlib without LFS */
-#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0
- ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
- ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int));
- ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile));
- ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile));
-#endif
-
-/* default memLevel */
-#if MAX_MEM_LEVEL >= 8
-# define DEF_MEM_LEVEL 8
-#else
-# define DEF_MEM_LEVEL MAX_MEM_LEVEL
-#endif
-
-/* default i/o buffer size -- double this for output when reading (this and
- twice this must be able to fit in an unsigned type) */
-#define GZBUFSIZE 8192
-
-/* gzip modes, also provide a little integrity check on the passed structure */
-#define GZ_NONE 0
-#define GZ_READ 7247
-#define GZ_WRITE 31153
-#define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */
-
-/* values for gz_state how */
-#define LOOK 0 /* look for a gzip header */
-#define COPY 1 /* copy input directly */
-#define GZIP 2 /* decompress a gzip stream */
-
-/* internal gzip file state data structure */
-typedef struct {
- /* exposed contents for gzgetc() macro */
- struct gzFile_s x; /* "x" for exposed */
- /* x.have: number of bytes available at x.next */
- /* x.next: next output data to deliver or write */
- /* x.pos: current position in uncompressed data */
- /* used for both reading and writing */
- int mode; /* see gzip modes above */
- int fd; /* file descriptor */
- char *path; /* path or fd for error messages */
- unsigned size; /* buffer size, zero if not allocated yet */
- unsigned want; /* requested buffer size, default is GZBUFSIZE */
- unsigned char *in; /* input buffer (double-sized when writing) */
- unsigned char *out; /* output buffer (double-sized when reading) */
- int direct; /* 0 if processing gzip, 1 if transparent */
- /* just for reading */
- int how; /* 0: get header, 1: copy, 2: decompress */
- z_off64_t start; /* where the gzip data started, for rewinding */
- int eof; /* true if end of input file reached */
- int past; /* true if read requested past end */
- /* just for writing */
- int level; /* compression level */
- int strategy; /* compression strategy */
- /* seek request */
- z_off64_t skip; /* amount to skip (already rewound if backwards) */
- int seek; /* true if seek request pending */
- /* error information */
- int err; /* error code */
- char *msg; /* error message */
- /* zlib inflate or deflate stream */
- z_stream strm; /* stream structure in-place (not a pointer) */
-} gz_state;
-typedef gz_state FAR *gz_statep;
-
-/* shared functions */
-void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *));
-#if defined UNDER_CE
-char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error));
-#endif
-
-/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t
- value -- needed when comparing unsigned to z_off64_t, which is signed
- (possible z_off64_t types off_t, off64_t, and long are all signed) */
-#ifdef INT_MAX
-# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX)
-#else
-unsigned ZLIB_INTERNAL gz_intmax OF((void));
-# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax())
-#endif
+++ /dev/null
-/* gzlib.c -- zlib functions common to reading and writing gzip files
- * Copyright (C) 2004-2017 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-#include "gzguts.h"
-
-#if defined(_WIN32) && !defined(__BORLANDC__) && !defined(__MINGW32__)
-# define LSEEK _lseeki64
-#else
-#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0
-# define LSEEK lseek64
-#else
-# define LSEEK lseek
-#endif
-#endif
-
-/* Local functions */
-local void gz_reset OF((gz_statep));
-local gzFile gz_open OF((const void *, int, const char *));
-
-#if defined UNDER_CE
-
-/* Map the Windows error number in ERROR to a locale-dependent error message
- string and return a pointer to it. Typically, the values for ERROR come
- from GetLastError.
-
- The string pointed to shall not be modified by the application, but may be
- overwritten by a subsequent call to gz_strwinerror
-
- The gz_strwinerror function does not change the current setting of
- GetLastError. */
-char ZLIB_INTERNAL *gz_strwinerror (error)
- DWORD error;
-{
- static char buf[1024];
-
- wchar_t *msgbuf;
- DWORD lasterr = GetLastError();
- DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
- | FORMAT_MESSAGE_ALLOCATE_BUFFER,
- NULL,
- error,
- 0, /* Default language */
- (LPVOID)&msgbuf,
- 0,
- NULL);
- if (chars != 0) {
- /* If there is an \r\n appended, zap it. */
- if (chars >= 2
- && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') {
- chars -= 2;
- msgbuf[chars] = 0;
- }
-
- if (chars > sizeof (buf) - 1) {
- chars = sizeof (buf) - 1;
- msgbuf[chars] = 0;
- }
-
- wcstombs(buf, msgbuf, chars + 1);
- LocalFree(msgbuf);
- }
- else {
- sprintf(buf, "unknown win32 error (%ld)", error);
- }
-
- SetLastError(lasterr);
- return buf;
-}
-
-#endif /* UNDER_CE */
-
-/* Reset gzip file state */
-local void gz_reset(state)
- gz_statep state;
-{
- state->x.have = 0; /* no output data available */
- if (state->mode == GZ_READ) { /* for reading ... */
- state->eof = 0; /* not at end of file */
- state->past = 0; /* have not read past end yet */
- state->how = LOOK; /* look for gzip header */
- }
- state->seek = 0; /* no seek request pending */
- gz_error(state, Z_OK, NULL); /* clear error */
- state->x.pos = 0; /* no uncompressed data yet */
- state->strm.avail_in = 0; /* no input data yet */
-}
-
-/* Open a gzip file either by name or file descriptor. */
-local gzFile gz_open(path, fd, mode)
- const void *path;
- int fd;
- const char *mode;
-{
- gz_statep state;
- z_size_t len;
- int oflag;
-#ifdef O_CLOEXEC
- int cloexec = 0;
-#endif
-#ifdef O_EXCL
- int exclusive = 0;
-#endif
-
- /* check input */
- if (path == NULL)
- return NULL;
-
- /* allocate gzFile structure to return */
- state = (gz_statep)malloc(sizeof(gz_state));
- if (state == NULL)
- return NULL;
- state->size = 0; /* no buffers allocated yet */
- state->want = GZBUFSIZE; /* requested buffer size */
- state->msg = NULL; /* no error message yet */
-
- /* interpret mode */
- state->mode = GZ_NONE;
- state->level = Z_DEFAULT_COMPRESSION;
- state->strategy = Z_DEFAULT_STRATEGY;
- state->direct = 0;
- while (*mode) {
- if (*mode >= '0' && *mode <= '9')
- state->level = *mode - '0';
- else
- switch (*mode) {
- case 'r':
- state->mode = GZ_READ;
- break;
-#ifndef NO_GZCOMPRESS
- case 'w':
- state->mode = GZ_WRITE;
- break;
- case 'a':
- state->mode = GZ_APPEND;
- break;
-#endif
- case '+': /* can't read and write at the same time */
- free(state);
- return NULL;
- case 'b': /* ignore -- will request binary anyway */
- break;
-#ifdef O_CLOEXEC
- case 'e':
- cloexec = 1;
- break;
-#endif
-#ifdef O_EXCL
- case 'x':
- exclusive = 1;
- break;
-#endif
- case 'f':
- state->strategy = Z_FILTERED;
- break;
- case 'h':
- state->strategy = Z_HUFFMAN_ONLY;
- break;
- case 'R':
- state->strategy = Z_RLE;
- break;
- case 'F':
- state->strategy = Z_FIXED;
- break;
- case 'T':
- state->direct = 1;
- break;
- default: /* could consider as an error, but just ignore */
- ;
- }
- mode++;
- }
-
- /* must provide an "r", "w", or "a" */
- if (state->mode == GZ_NONE) {
- free(state);
- return NULL;
- }
-
- /* can't force transparent read */
- if (state->mode == GZ_READ) {
- if (state->direct) {
- free(state);
- return NULL;
- }
- state->direct = 1; /* for empty file */
- }
-
- /* save the path name for error messages */
-#ifdef WIDECHAR
- if (fd == -2) {
- len = wcstombs(NULL, path, 0);
- if (len == (z_size_t)-1)
- len = 0;
- }
- else
-#endif
- len = strlen((const char *)path);
- state->path = (char *)malloc(len + 1);
- if (state->path == NULL) {
- free(state);
- return NULL;
- }
-#ifdef WIDECHAR
- if (fd == -2)
- if (len)
- wcstombs(state->path, path, len + 1);
- else
- *(state->path) = 0;
- else
-#endif
-#if !defined(NO_snprintf) && !defined(NO_vsnprintf)
- (void)snprintf(state->path, len + 1, "%s", (const char *)path);
-#else
- strcpy(state->path, path);
-#endif
-
- /* compute the flags for open() */
- oflag =
-#ifdef O_LARGEFILE
- O_LARGEFILE |
-#endif
-#ifdef O_BINARY
- O_BINARY |
-#endif
-#ifdef O_CLOEXEC
- (cloexec ? O_CLOEXEC : 0) |
-#endif
- (state->mode == GZ_READ ?
- O_RDONLY :
- (O_WRONLY | O_CREAT |
-#ifdef O_EXCL
- (exclusive ? O_EXCL : 0) |
-#endif
- (state->mode == GZ_WRITE ?
- O_TRUNC :
- O_APPEND)));
-
- /* open the file with the appropriate flags (or just use fd) */
- state->fd = fd > -1 ? fd : (
-#ifdef WIDECHAR
- fd == -2 ? _wopen(path, oflag, 0666) :
-#endif
- open((const char *)path, oflag, 0666));
- if (state->fd == -1) {
- free(state->path);
- free(state);
- return NULL;
- }
- if (state->mode == GZ_APPEND) {
- LSEEK(state->fd, 0, SEEK_END); /* so gzoffset() is correct */
- state->mode = GZ_WRITE; /* simplify later checks */
- }
-
- /* save the current position for rewinding (only if reading) */
- if (state->mode == GZ_READ) {
- state->start = LSEEK(state->fd, 0, SEEK_CUR);
- if (state->start == -1) state->start = 0;
- }
-
- /* initialize stream */
- gz_reset(state);
-
- /* return stream */
- return (gzFile)state;
-}
-
-/* -- see zlib.h -- */
-gzFile ZEXPORT gzopen(path, mode)
- const char *path;
- const char *mode;
-{
- return gz_open(path, -1, mode);
-}
-
-/* -- see zlib.h -- */
-gzFile ZEXPORT gzopen64(path, mode)
- const char *path;
- const char *mode;
-{
- return gz_open(path, -1, mode);
-}
-
-/* -- see zlib.h -- */
-gzFile ZEXPORT gzdopen(fd, mode)
- int fd;
- const char *mode;
-{
- char *path; /* identifier for error messages */
- gzFile gz;
-
- if (fd == -1 || (path = (char *)malloc(7 + 3 * sizeof(int))) == NULL)
- return NULL;
-#if !defined(NO_snprintf) && !defined(NO_vsnprintf)
- (void)snprintf(path, 7 + 3 * sizeof(int), "<fd:%d>", fd);
-#else
- sprintf(path, "<fd:%d>", fd); /* for debugging */
-#endif
- gz = gz_open(path, fd, mode);
- free(path);
- return gz;
-}
-
-/* -- see zlib.h -- */
-#ifdef WIDECHAR
-gzFile ZEXPORT gzopen_w(path, mode)
- const wchar_t *path;
- const char *mode;
-{
- return gz_open(path, -2, mode);
-}
-#endif
-
-/* -- see zlib.h -- */
-int ZEXPORT gzbuffer(file, size)
- gzFile file;
- unsigned size;
-{
- gz_statep state;
-
- /* get internal structure and check integrity */
- if (file == NULL)
- return -1;
- state = (gz_statep)file;
- if (state->mode != GZ_READ && state->mode != GZ_WRITE)
- return -1;
-
- /* make sure we haven't already allocated memory */
- if (state->size != 0)
- return -1;
-
- /* check and set requested size */
- if ((size << 1) < size)
- return -1; /* need to be able to double it */
- if (size < 2)
- size = 2; /* need two bytes to check magic header */
- state->want = size;
- return 0;
-}
-
-/* -- see zlib.h -- */
-int ZEXPORT gzrewind(file)
- gzFile file;
-{
- gz_statep state;
-
- /* get internal structure */
- if (file == NULL)
- return -1;
- state = (gz_statep)file;
-
- /* check that we're reading and that there's no error */
- if (state->mode != GZ_READ ||
- (state->err != Z_OK && state->err != Z_BUF_ERROR))
- return -1;
-
- /* back up and start over */
- if (LSEEK(state->fd, state->start, SEEK_SET) == -1)
- return -1;
- gz_reset(state);
- return 0;
-}
-
-/* -- see zlib.h -- */
-z_off64_t ZEXPORT gzseek64(file, offset, whence)
- gzFile file;
- z_off64_t offset;
- int whence;
-{
- unsigned n;
- z_off64_t ret;
- gz_statep state;
-
- /* get internal structure and check integrity */
- if (file == NULL)
- return -1;
- state = (gz_statep)file;
- if (state->mode != GZ_READ && state->mode != GZ_WRITE)
- return -1;
-
- /* check that there's no error */
- if (state->err != Z_OK && state->err != Z_BUF_ERROR)
- return -1;
-
- /* can only seek from start or relative to current position */
- if (whence != SEEK_SET && whence != SEEK_CUR)
- return -1;
-
- /* normalize offset to a SEEK_CUR specification */
- if (whence == SEEK_SET)
- offset -= state->x.pos;
- else if (state->seek)
- offset += state->skip;
- state->seek = 0;
-
- /* if within raw area while reading, just go there */
- if (state->mode == GZ_READ && state->how == COPY &&
- state->x.pos + offset >= 0) {
- ret = LSEEK(state->fd, offset - state->x.have, SEEK_CUR);
- if (ret == -1)
- return -1;
- state->x.have = 0;
- state->eof = 0;
- state->past = 0;
- state->seek = 0;
- gz_error(state, Z_OK, NULL);
- state->strm.avail_in = 0;
- state->x.pos += offset;
- return state->x.pos;
- }
-
- /* calculate skip amount, rewinding if needed for back seek when reading */
- if (offset < 0) {
- if (state->mode != GZ_READ) /* writing -- can't go backwards */
- return -1;
- offset += state->x.pos;
- if (offset < 0) /* before start of file! */
- return -1;
- if (gzrewind(file) == -1) /* rewind, then skip to offset */
- return -1;
- }
-
- /* if reading, skip what's in output buffer (one less gzgetc() check) */
- if (state->mode == GZ_READ) {
- n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > offset ?
- (unsigned)offset : state->x.have;
- state->x.have -= n;
- state->x.next += n;
- state->x.pos += n;
- offset -= n;
- }
-
- /* request skip (if not zero) */
- if (offset) {
- state->seek = 1;
- state->skip = offset;
- }
- return state->x.pos + offset;
-}
-
-/* -- see zlib.h -- */
-z_off_t ZEXPORT gzseek(file, offset, whence)
- gzFile file;
- z_off_t offset;
- int whence;
-{
- z_off64_t ret;
-
- ret = gzseek64(file, (z_off64_t)offset, whence);
- return ret == (z_off_t)ret ? (z_off_t)ret : -1;
-}
-
-/* -- see zlib.h -- */
-z_off64_t ZEXPORT gztell64(file)
- gzFile file;
-{
- gz_statep state;
-
- /* get internal structure and check integrity */
- if (file == NULL)
- return -1;
- state = (gz_statep)file;
- if (state->mode != GZ_READ && state->mode != GZ_WRITE)
- return -1;
-
- /* return position */
- return state->x.pos + (state->seek ? state->skip : 0);
-}
-
-/* -- see zlib.h -- */
-z_off_t ZEXPORT gztell(file)
- gzFile file;
-{
- z_off64_t ret;
-
- ret = gztell64(file);
- return ret == (z_off_t)ret ? (z_off_t)ret : -1;
-}
-
-/* -- see zlib.h -- */
-z_off64_t ZEXPORT gzoffset64(file)
- gzFile file;
-{
- z_off64_t offset;
- gz_statep state;
-
- /* get internal structure and check integrity */
- if (file == NULL)
- return -1;
- state = (gz_statep)file;
- if (state->mode != GZ_READ && state->mode != GZ_WRITE)
- return -1;
-
- /* compute and return effective offset in file */
- offset = LSEEK(state->fd, 0, SEEK_CUR);
- if (offset == -1)
- return -1;
- if (state->mode == GZ_READ) /* reading */
- offset -= state->strm.avail_in; /* don't count buffered input */
- return offset;
-}
-
-/* -- see zlib.h -- */
-z_off_t ZEXPORT gzoffset(file)
- gzFile file;
-{
- z_off64_t ret;
-
- ret = gzoffset64(file);
- return ret == (z_off_t)ret ? (z_off_t)ret : -1;
-}
-
-/* -- see zlib.h -- */
-int ZEXPORT gzeof(file)
- gzFile file;
-{
- gz_statep state;
-
- /* get internal structure and check integrity */
- if (file == NULL)
- return 0;
- state = (gz_statep)file;
- if (state->mode != GZ_READ && state->mode != GZ_WRITE)
- return 0;
-
- /* return end-of-file state */
- return state->mode == GZ_READ ? state->past : 0;
-}
-
-/* -- see zlib.h -- */
-const char * ZEXPORT gzerror(file, errnum)
- gzFile file;
- int *errnum;
-{
- gz_statep state;
-
- /* get internal structure and check integrity */
- if (file == NULL)
- return NULL;
- state = (gz_statep)file;
- if (state->mode != GZ_READ && state->mode != GZ_WRITE)
- return NULL;
-
- /* return error information */
- if (errnum != NULL)
- *errnum = state->err;
- return state->err == Z_MEM_ERROR ? "out of memory" :
- (state->msg == NULL ? "" : state->msg);
-}
-
-/* -- see zlib.h -- */
-void ZEXPORT gzclearerr(file)
- gzFile file;
-{
- gz_statep state;
-
- /* get internal structure and check integrity */
- if (file == NULL)
- return;
- state = (gz_statep)file;
- if (state->mode != GZ_READ && state->mode != GZ_WRITE)
- return;
-
- /* clear error and end-of-file */
- if (state->mode == GZ_READ) {
- state->eof = 0;
- state->past = 0;
- }
- gz_error(state, Z_OK, NULL);
-}
-
-/* Create an error message in allocated memory and set state->err and
- state->msg accordingly. Free any previous error message already there. Do
- not try to free or allocate space if the error is Z_MEM_ERROR (out of
- memory). Simply save the error message as a static string. If there is an
- allocation failure constructing the error message, then convert the error to
- out of memory. */
-void ZLIB_INTERNAL gz_error(state, err, msg)
- gz_statep state;
- int err;
- const char *msg;
-{
- /* free previously allocated message and clear */
- if (state->msg != NULL) {
- if (state->err != Z_MEM_ERROR)
- free(state->msg);
- state->msg = NULL;
- }
-
- /* if fatal, set state->x.have to 0 so that the gzgetc() macro fails */
- if (err != Z_OK && err != Z_BUF_ERROR)
- state->x.have = 0;
-
- /* set error code, and if no message, then done */
- state->err = err;
- if (msg == NULL)
- return;
-
- /* for an out of memory error, return literal string when requested */
- if (err == Z_MEM_ERROR)
- return;
-
- /* construct error message with path */
- if ((state->msg = (char *)malloc(strlen(state->path) + strlen(msg) + 3)) ==
- NULL) {
- state->err = Z_MEM_ERROR;
- return;
- }
-#if !defined(NO_snprintf) && !defined(NO_vsnprintf)
- (void)snprintf(state->msg, strlen(state->path) + strlen(msg) + 3,
- "%s%s%s", state->path, ": ", msg);
-#else
- strcpy(state->msg, state->path);
- strcat(state->msg, ": ");
- strcat(state->msg, msg);
-#endif
-}
-
-#ifndef INT_MAX
-/* portably return maximum value for an int (when limits.h presumed not
- available) -- we need to do this to cover cases where 2's complement not
- used, since C standard permits 1's complement and sign-bit representations,
- otherwise we could just use ((unsigned)-1) >> 1 */
-unsigned ZLIB_INTERNAL gz_intmax()
-{
- unsigned p, q;
-
- p = 1;
- do {
- q = p;
- p <<= 1;
- p++;
- } while (p > q);
- return q >> 1;
-}
-#endif
+++ /dev/null
-/* gzread.c -- zlib functions for reading gzip files
- * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-#include "gzguts.h"
-
-/* Local functions */
-local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *));
-local int gz_avail OF((gz_statep));
-local int gz_look OF((gz_statep));
-local int gz_decomp OF((gz_statep));
-local int gz_fetch OF((gz_statep));
-local int gz_skip OF((gz_statep, z_off64_t));
-local z_size_t gz_read OF((gz_statep, voidp, z_size_t));
-
-/* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from
- state->fd, and update state->eof, state->err, and state->msg as appropriate.
- This function needs to loop on read(), since read() is not guaranteed to
- read the number of bytes requested, depending on the type of descriptor. */
-local int gz_load(state, buf, len, have)
- gz_statep state;
- unsigned char *buf;
- unsigned len;
- unsigned *have;
-{
- int ret;
- unsigned get, max = ((unsigned)-1 >> 2) + 1;
-
- *have = 0;
- do {
- get = len - *have;
- if (get > max)
- get = max;
- ret = read(state->fd, buf + *have, get);
- if (ret <= 0)
- break;
- *have += (unsigned)ret;
- } while (*have < len);
- if (ret < 0) {
- gz_error(state, Z_ERRNO, zstrerror());
- return -1;
- }
- if (ret == 0)
- state->eof = 1;
- return 0;
-}
-
-/* Load up input buffer and set eof flag if last data loaded -- return -1 on
- error, 0 otherwise. Note that the eof flag is set when the end of the input
- file is reached, even though there may be unused data in the buffer. Once
- that data has been used, no more attempts will be made to read the file.
- If strm->avail_in != 0, then the current data is moved to the beginning of
- the input buffer, and then the remainder of the buffer is loaded with the
- available data from the input file. */
-local int gz_avail(state)
- gz_statep state;
-{
- unsigned got;
- z_streamp strm = &(state->strm);
-
- if (state->err != Z_OK && state->err != Z_BUF_ERROR)
- return -1;
- if (state->eof == 0) {
- if (strm->avail_in) { /* copy what's there to the start */
- unsigned char *p = state->in;
- unsigned const char *q = strm->next_in;
- unsigned n = strm->avail_in;
- do {
- *p++ = *q++;
- } while (--n);
- }
- if (gz_load(state, state->in + strm->avail_in,
- state->size - strm->avail_in, &got) == -1)
- return -1;
- strm->avail_in += got;
- strm->next_in = state->in;
- }
- return 0;
-}
-
-/* Look for gzip header, set up for inflate or copy. state->x.have must be 0.
- If this is the first time in, allocate required memory. state->how will be
- left unchanged if there is no more input data available, will be set to COPY
- if there is no gzip header and direct copying will be performed, or it will
- be set to GZIP for decompression. If direct copying, then leftover input
- data from the input buffer will be copied to the output buffer. In that
- case, all further file reads will be directly to either the output buffer or
- a user buffer. If decompressing, the inflate state will be initialized.
- gz_look() will return 0 on success or -1 on failure. */
-local int gz_look(state)
- gz_statep state;
-{
- z_streamp strm = &(state->strm);
-
- /* allocate read buffers and inflate memory */
- if (state->size == 0) {
- /* allocate buffers */
- state->in = (unsigned char *)malloc(state->want);
- state->out = (unsigned char *)malloc(state->want << 1);
- if (state->in == NULL || state->out == NULL) {
- free(state->out);
- free(state->in);
- gz_error(state, Z_MEM_ERROR, "out of memory");
- return -1;
- }
- state->size = state->want;
-
- /* allocate inflate memory */
- state->strm.zalloc = Z_NULL;
- state->strm.zfree = Z_NULL;
- state->strm.opaque = Z_NULL;
- state->strm.avail_in = 0;
- state->strm.next_in = Z_NULL;
- if (inflateInit2(&(state->strm), 15 + 16) != Z_OK) { /* gunzip */
- free(state->out);
- free(state->in);
- state->size = 0;
- gz_error(state, Z_MEM_ERROR, "out of memory");
- return -1;
- }
- }
-
- /* get at least the magic bytes in the input buffer */
- if (strm->avail_in < 2) {
- if (gz_avail(state) == -1)
- return -1;
- if (strm->avail_in == 0)
- return 0;
- }
-
- /* look for gzip magic bytes -- if there, do gzip decoding (note: there is
- a logical dilemma here when considering the case of a partially written
- gzip file, to wit, if a single 31 byte is written, then we cannot tell
- whether this is a single-byte file, or just a partially written gzip
- file -- for here we assume that if a gzip file is being written, then
- the header will be written in a single operation, so that reading a
- single byte is sufficient indication that it is not a gzip file) */
- if (strm->avail_in > 1 &&
- strm->next_in[0] == 31 && strm->next_in[1] == 139) {
- inflateReset(strm);
- state->how = GZIP;
- state->direct = 0;
- return 0;
- }
-
- /* no gzip header -- if we were decoding gzip before, then this is trailing
- garbage. Ignore the trailing garbage and finish. */
- if (state->direct == 0) {
- strm->avail_in = 0;
- state->eof = 1;
- state->x.have = 0;
- return 0;
- }
-
- /* doing raw i/o, copy any leftover input to output -- this assumes that
- the output buffer is larger than the input buffer, which also assures
- space for gzungetc() */
- state->x.next = state->out;
- if (strm->avail_in) {
- memcpy(state->x.next, strm->next_in, strm->avail_in);
- state->x.have = strm->avail_in;
- strm->avail_in = 0;
- }
- state->how = COPY;
- state->direct = 1;
- return 0;
-}
-
-/* Decompress from input to the provided next_out and avail_out in the state.
- On return, state->x.have and state->x.next point to the just decompressed
- data. If the gzip stream completes, state->how is reset to LOOK to look for
- the next gzip stream or raw data, once state->x.have is depleted. Returns 0
- on success, -1 on failure. */
-local int gz_decomp(state)
- gz_statep state;
-{
- int ret = Z_OK;
- unsigned had;
- z_streamp strm = &(state->strm);
-
- /* fill output buffer up to end of deflate stream */
- had = strm->avail_out;
- do {
- /* get more input for inflate() */
- if (strm->avail_in == 0 && gz_avail(state) == -1)
- return -1;
- if (strm->avail_in == 0) {
- gz_error(state, Z_BUF_ERROR, "unexpected end of file");
- break;
- }
-
- /* decompress and handle errors */
- ret = inflate(strm, Z_NO_FLUSH);
- if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) {
- gz_error(state, Z_STREAM_ERROR,
- "internal error: inflate stream corrupt");
- return -1;
- }
- if (ret == Z_MEM_ERROR) {
- gz_error(state, Z_MEM_ERROR, "out of memory");
- return -1;
- }
- if (ret == Z_DATA_ERROR) { /* deflate stream invalid */
- gz_error(state, Z_DATA_ERROR,
- strm->msg == NULL ? "compressed data error" : strm->msg);
- return -1;
- }
- } while (strm->avail_out && ret != Z_STREAM_END);
-
- /* update available output */
- state->x.have = had - strm->avail_out;
- state->x.next = strm->next_out - state->x.have;
-
- /* if the gzip stream completed successfully, look for another */
- if (ret == Z_STREAM_END)
- state->how = LOOK;
-
- /* good decompression */
- return 0;
-}
-
-/* Fetch data and put it in the output buffer. Assumes state->x.have is 0.
- Data is either copied from the input file or decompressed from the input
- file depending on state->how. If state->how is LOOK, then a gzip header is
- looked for to determine whether to copy or decompress. Returns -1 on error,
- otherwise 0. gz_fetch() will leave state->how as COPY or GZIP unless the
- end of the input file has been reached and all data has been processed. */
-local int gz_fetch(state)
- gz_statep state;
-{
- z_streamp strm = &(state->strm);
-
- do {
- switch(state->how) {
- case LOOK: /* -> LOOK, COPY (only if never GZIP), or GZIP */
- if (gz_look(state) == -1)
- return -1;
- if (state->how == LOOK)
- return 0;
- break;
- case COPY: /* -> COPY */
- if (gz_load(state, state->out, state->size << 1, &(state->x.have))
- == -1)
- return -1;
- state->x.next = state->out;
- return 0;
- case GZIP: /* -> GZIP or LOOK (if end of gzip stream) */
- strm->avail_out = state->size << 1;
- strm->next_out = state->out;
- if (gz_decomp(state) == -1)
- return -1;
- }
- } while (state->x.have == 0 && (!state->eof || strm->avail_in));
- return 0;
-}
-
-/* Skip len uncompressed bytes of output. Return -1 on error, 0 on success. */
-local int gz_skip(state, len)
- gz_statep state;
- z_off64_t len;
-{
- unsigned n;
-
- /* skip over len bytes or reach end-of-file, whichever comes first */
- while (len)
- /* skip over whatever is in output buffer */
- if (state->x.have) {
- n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > len ?
- (unsigned)len : state->x.have;
- state->x.have -= n;
- state->x.next += n;
- state->x.pos += n;
- len -= n;
- }
-
- /* output buffer empty -- return if we're at the end of the input */
- else if (state->eof && state->strm.avail_in == 0)
- break;
-
- /* need more data to skip -- load up output buffer */
- else {
- /* get more output, looking for header if required */
- if (gz_fetch(state) == -1)
- return -1;
- }
- return 0;
-}
-
-/* Read len bytes into buf from file, or less than len up to the end of the
- input. Return the number of bytes read. If zero is returned, either the
- end of file was reached, or there was an error. state->err must be
- consulted in that case to determine which. */
-local z_size_t gz_read(state, buf, len)
- gz_statep state;
- voidp buf;
- z_size_t len;
-{
- z_size_t got;
- unsigned n;
-
- /* if len is zero, avoid unnecessary operations */
- if (len == 0)
- return 0;
-
- /* process a skip request */
- if (state->seek) {
- state->seek = 0;
- if (gz_skip(state, state->skip) == -1)
- return 0;
- }
-
- /* get len bytes to buf, or less than len if at the end */
- got = 0;
- do {
- /* set n to the maximum amount of len that fits in an unsigned int */
- n = -1;
- if (n > len)
- n = len;
-
- /* first just try copying data from the output buffer */
- if (state->x.have) {
- if (state->x.have < n)
- n = state->x.have;
- memcpy(buf, state->x.next, n);
- state->x.next += n;
- state->x.have -= n;
- }
-
- /* output buffer empty -- return if we're at the end of the input */
- else if (state->eof && state->strm.avail_in == 0) {
- state->past = 1; /* tried to read past end */
- break;
- }
-
- /* need output data -- for small len or new stream load up our output
- buffer */
- else if (state->how == LOOK || n < (state->size << 1)) {
- /* get more output, looking for header if required */
- if (gz_fetch(state) == -1)
- return 0;
- continue; /* no progress yet -- go back to copy above */
- /* the copy above assures that we will leave with space in the
- output buffer, allowing at least one gzungetc() to succeed */
- }
-
- /* large len -- read directly into user buffer */
- else if (state->how == COPY) { /* read directly */
- if (gz_load(state, (unsigned char *)buf, n, &n) == -1)
- return 0;
- }
-
- /* large len -- decompress directly into user buffer */
- else { /* state->how == GZIP */
- state->strm.avail_out = n;
- state->strm.next_out = (unsigned char *)buf;
- if (gz_decomp(state) == -1)
- return 0;
- n = state->x.have;
- state->x.have = 0;
- }
-
- /* update progress */
- len -= n;
- buf = (char *)buf + n;
- got += n;
- state->x.pos += n;
- } while (len);
-
- /* return number of bytes read into user buffer */
- return got;
-}
-
-/* -- see zlib.h -- */
-int ZEXPORT gzread(file, buf, len)
- gzFile file;
- voidp buf;
- unsigned len;
-{
- gz_statep state;
-
- /* get internal structure */
- if (file == NULL)
- return -1;
- state = (gz_statep)file;
-
- /* check that we're reading and that there's no (serious) error */
- if (state->mode != GZ_READ ||
- (state->err != Z_OK && state->err != Z_BUF_ERROR))
- return -1;
-
- /* since an int is returned, make sure len fits in one, otherwise return
- with an error (this avoids a flaw in the interface) */
- if ((int)len < 0) {
- gz_error(state, Z_STREAM_ERROR, "request does not fit in an int");
- return -1;
- }
-
- /* read len or fewer bytes to buf */
- len = gz_read(state, buf, len);
-
- /* check for an error */
- if (len == 0 && state->err != Z_OK && state->err != Z_BUF_ERROR)
- return -1;
-
- /* return the number of bytes read (this is assured to fit in an int) */
- return (int)len;
-}
-
-/* -- see zlib.h -- */
-z_size_t ZEXPORT gzfread(buf, size, nitems, file)
- voidp buf;
- z_size_t size;
- z_size_t nitems;
- gzFile file;
-{
- z_size_t len;
- gz_statep state;
-
- /* get internal structure */
- if (file == NULL)
- return 0;
- state = (gz_statep)file;
-
- /* check that we're reading and that there's no (serious) error */
- if (state->mode != GZ_READ ||
- (state->err != Z_OK && state->err != Z_BUF_ERROR))
- return 0;
-
- /* compute bytes to read -- error on overflow */
- len = nitems * size;
- if (size && len / size != nitems) {
- gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t");
- return 0;
- }
-
- /* read len or fewer bytes to buf, return the number of full items read */
- return len ? gz_read(state, buf, len) / size : 0;
-}
-
-/* -- see zlib.h -- */
-#ifdef Z_PREFIX_SET
-# undef z_gzgetc
-#else
-# undef gzgetc
-#endif
-int ZEXPORT gzgetc(file)
- gzFile file;
-{
- int ret;
- unsigned char buf[1];
- gz_statep state;
-
- /* get internal structure */
- if (file == NULL)
- return -1;
- state = (gz_statep)file;
-
- /* check that we're reading and that there's no (serious) error */
- if (state->mode != GZ_READ ||
- (state->err != Z_OK && state->err != Z_BUF_ERROR))
- return -1;
-
- /* try output buffer (no need to check for skip request) */
- if (state->x.have) {
- state->x.have--;
- state->x.pos++;
- return *(state->x.next)++;
- }
-
- /* nothing there -- try gz_read() */
- ret = gz_read(state, buf, 1);
- return ret < 1 ? -1 : buf[0];
-}
-
-int ZEXPORT gzgetc_(file)
-gzFile file;
-{
- return gzgetc(file);
-}
-
-/* -- see zlib.h -- */
-int ZEXPORT gzungetc(c, file)
- int c;
- gzFile file;
-{
- gz_statep state;
-
- /* get internal structure */
- if (file == NULL)
- return -1;
- state = (gz_statep)file;
-
- /* check that we're reading and that there's no (serious) error */
- if (state->mode != GZ_READ ||
- (state->err != Z_OK && state->err != Z_BUF_ERROR))
- return -1;
-
- /* process a skip request */
- if (state->seek) {
- state->seek = 0;
- if (gz_skip(state, state->skip) == -1)
- return -1;
- }
-
- /* can't push EOF */
- if (c < 0)
- return -1;
-
- /* if output buffer empty, put byte at end (allows more pushing) */
- if (state->x.have == 0) {
- state->x.have = 1;
- state->x.next = state->out + (state->size << 1) - 1;
- state->x.next[0] = (unsigned char)c;
- state->x.pos--;
- state->past = 0;
- return c;
- }
-
- /* if no room, give up (must have already done a gzungetc()) */
- if (state->x.have == (state->size << 1)) {
- gz_error(state, Z_DATA_ERROR, "out of room to push characters");
- return -1;
- }
-
- /* slide output data if needed and insert byte before existing data */
- if (state->x.next == state->out) {
- unsigned char *src = state->out + state->x.have;
- unsigned char *dest = state->out + (state->size << 1);
- while (src > state->out)
- *--dest = *--src;
- state->x.next = dest;
- }
- state->x.have++;
- state->x.next--;
- state->x.next[0] = (unsigned char)c;
- state->x.pos--;
- state->past = 0;
- return c;
-}
-
-/* -- see zlib.h -- */
-char * ZEXPORT gzgets(file, buf, len)
- gzFile file;
- char *buf;
- int len;
-{
- unsigned left, n;
- char *str;
- unsigned char *eol;
- gz_statep state;
-
- /* check parameters and get internal structure */
- if (file == NULL || buf == NULL || len < 1)
- return NULL;
- state = (gz_statep)file;
-
- /* check that we're reading and that there's no (serious) error */
- if (state->mode != GZ_READ ||
- (state->err != Z_OK && state->err != Z_BUF_ERROR))
- return NULL;
-
- /* process a skip request */
- if (state->seek) {
- state->seek = 0;
- if (gz_skip(state, state->skip) == -1)
- return NULL;
- }
-
- /* copy output bytes up to new line or len - 1, whichever comes first --
- append a terminating zero to the string (we don't check for a zero in
- the contents, let the user worry about that) */
- str = buf;
- left = (unsigned)len - 1;
- if (left) do {
- /* assure that something is in the output buffer */
- if (state->x.have == 0 && gz_fetch(state) == -1)
- return NULL; /* error */
- if (state->x.have == 0) { /* end of file */
- state->past = 1; /* read past end */
- break; /* return what we have */
- }
-
- /* look for end-of-line in current output buffer */
- n = state->x.have > left ? left : state->x.have;
- eol = (unsigned char *)memchr(state->x.next, '\n', n);
- if (eol != NULL)
- n = (unsigned)(eol - state->x.next) + 1;
-
- /* copy through end-of-line, or remainder if not found */
- memcpy(buf, state->x.next, n);
- state->x.have -= n;
- state->x.next += n;
- state->x.pos += n;
- left -= n;
- buf += n;
- } while (left && eol == NULL);
-
- /* return terminated string, or if nothing, end of file */
- if (buf == str)
- return NULL;
- buf[0] = 0;
- return str;
-}
-
-/* -- see zlib.h -- */
-int ZEXPORT gzdirect(file)
- gzFile file;
-{
- gz_statep state;
-
- /* get internal structure */
- if (file == NULL)
- return 0;
- state = (gz_statep)file;
-
- /* if the state is not known, but we can find out, then do so (this is
- mainly for right after a gzopen() or gzdopen()) */
- if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0)
- (void)gz_look(state);
-
- /* return 1 if transparent, 0 if processing a gzip stream */
- return state->direct;
-}
-
-/* -- see zlib.h -- */
-int ZEXPORT gzclose_r(file)
- gzFile file;
-{
- int ret, err;
- gz_statep state;
-
- /* get internal structure */
- if (file == NULL)
- return Z_STREAM_ERROR;
- state = (gz_statep)file;
-
- /* check that we're reading */
- if (state->mode != GZ_READ)
- return Z_STREAM_ERROR;
-
- /* free memory and close file */
- if (state->size) {
- inflateEnd(&(state->strm));
- free(state->out);
- free(state->in);
- }
- err = state->err == Z_BUF_ERROR ? Z_BUF_ERROR : Z_OK;
- gz_error(state, Z_OK, NULL);
- free(state->path);
- ret = close(state->fd);
- free(state);
- return ret ? Z_ERRNO : err;
-}
+++ /dev/null
-/* gzwrite.c -- zlib functions for writing gzip files
- * Copyright (C) 2004-2017 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-#include "gzguts.h"
-
-/* Local functions */
-local int gz_init OF((gz_statep));
-local int gz_comp OF((gz_statep, int));
-local int gz_zero OF((gz_statep, z_off64_t));
-local z_size_t gz_write OF((gz_statep, voidpc, z_size_t));
-
-/* Initialize state for writing a gzip file. Mark initialization by setting
- state->size to non-zero. Return -1 on a memory allocation failure, or 0 on
- success. */
-local int gz_init(state)
- gz_statep state;
-{
- int ret;
- z_streamp strm = &(state->strm);
-
- /* allocate input buffer (double size for gzprintf) */
- state->in = (unsigned char *)malloc(state->want << 1);
- if (state->in == NULL) {
- gz_error(state, Z_MEM_ERROR, "out of memory");
- return -1;
- }
-
- /* only need output buffer and deflate state if compressing */
- if (!state->direct) {
- /* allocate output buffer */
- state->out = (unsigned char *)malloc(state->want);
- if (state->out == NULL) {
- free(state->in);
- gz_error(state, Z_MEM_ERROR, "out of memory");
- return -1;
- }
-
- /* allocate deflate memory, set up for gzip compression */
- strm->zalloc = Z_NULL;
- strm->zfree = Z_NULL;
- strm->opaque = Z_NULL;
- ret = deflateInit2(strm, state->level, Z_DEFLATED,
- MAX_WBITS + 16, DEF_MEM_LEVEL, state->strategy);
- if (ret != Z_OK) {
- free(state->out);
- free(state->in);
- gz_error(state, Z_MEM_ERROR, "out of memory");
- return -1;
- }
- strm->next_in = NULL;
- }
-
- /* mark state as initialized */
- state->size = state->want;
-
- /* initialize write buffer if compressing */
- if (!state->direct) {
- strm->avail_out = state->size;
- strm->next_out = state->out;
- state->x.next = strm->next_out;
- }
- return 0;
-}
-
-/* Compress whatever is at avail_in and next_in and write to the output file.
- Return -1 if there is an error writing to the output file or if gz_init()
- fails to allocate memory, otherwise 0. flush is assumed to be a valid
- deflate() flush value. If flush is Z_FINISH, then the deflate() state is
- reset to start a new gzip stream. If gz->direct is true, then simply write
- to the output file without compressing, and ignore flush. */
-local int gz_comp(state, flush)
- gz_statep state;
- int flush;
-{
- int ret, writ;
- unsigned have, put, max = ((unsigned)-1 >> 2) + 1;
- z_streamp strm = &(state->strm);
-
- /* allocate memory if this is the first time through */
- if (state->size == 0 && gz_init(state) == -1)
- return -1;
-
- /* write directly if requested */
- if (state->direct) {
- while (strm->avail_in) {
- put = strm->avail_in > max ? max : strm->avail_in;
- writ = write(state->fd, strm->next_in, put);
- if (writ < 0) {
- gz_error(state, Z_ERRNO, zstrerror());
- return -1;
- }
- strm->avail_in -= (unsigned)writ;
- strm->next_in += writ;
- }
- return 0;
- }
-
- /* run deflate() on provided input until it produces no more output */
- ret = Z_OK;
- do {
- /* write out current buffer contents if full, or if flushing, but if
- doing Z_FINISH then don't write until we get to Z_STREAM_END */
- if (strm->avail_out == 0 || (flush != Z_NO_FLUSH &&
- (flush != Z_FINISH || ret == Z_STREAM_END))) {
- while (strm->next_out > state->x.next) {
- put = strm->next_out - state->x.next > (int)max ? max :
- (unsigned)(strm->next_out - state->x.next);
- writ = write(state->fd, state->x.next, put);
- if (writ < 0) {
- gz_error(state, Z_ERRNO, zstrerror());
- return -1;
- }
- state->x.next += writ;
- }
- if (strm->avail_out == 0) {
- strm->avail_out = state->size;
- strm->next_out = state->out;
- state->x.next = state->out;
- }
- }
-
- /* compress */
- have = strm->avail_out;
- ret = deflate(strm, flush);
- if (ret == Z_STREAM_ERROR) {
- gz_error(state, Z_STREAM_ERROR,
- "internal error: deflate stream corrupt");
- return -1;
- }
- have -= strm->avail_out;
- } while (have);
-
- /* if that completed a deflate stream, allow another to start */
- if (flush == Z_FINISH)
- deflateReset(strm);
-
- /* all done, no errors */
- return 0;
-}
-
-/* Compress len zeros to output. Return -1 on a write error or memory
- allocation failure by gz_comp(), or 0 on success. */
-local int gz_zero(state, len)
- gz_statep state;
- z_off64_t len;
-{
- int first;
- unsigned n;
- z_streamp strm = &(state->strm);
-
- /* consume whatever's left in the input buffer */
- if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
- return -1;
-
- /* compress len zeros (len guaranteed > 0) */
- first = 1;
- while (len) {
- n = GT_OFF(state->size) || (z_off64_t)state->size > len ?
- (unsigned)len : state->size;
- if (first) {
- memset(state->in, 0, n);
- first = 0;
- }
- strm->avail_in = n;
- strm->next_in = state->in;
- state->x.pos += n;
- if (gz_comp(state, Z_NO_FLUSH) == -1)
- return -1;
- len -= n;
- }
- return 0;
-}
-
-/* Write len bytes from buf to file. Return the number of bytes written. If
- the returned value is less than len, then there was an error. */
-local z_size_t gz_write(state, buf, len)
- gz_statep state;
- voidpc buf;
- z_size_t len;
-{
- z_size_t put = len;
-
- /* if len is zero, avoid unnecessary operations */
- if (len == 0)
- return 0;
-
- /* allocate memory if this is the first time through */
- if (state->size == 0 && gz_init(state) == -1)
- return 0;
-
- /* check for seek request */
- if (state->seek) {
- state->seek = 0;
- if (gz_zero(state, state->skip) == -1)
- return 0;
- }
-
- /* for small len, copy to input buffer, otherwise compress directly */
- if (len < state->size) {
- /* copy to input buffer, compress when full */
- do {
- unsigned have, copy;
-
- if (state->strm.avail_in == 0)
- state->strm.next_in = state->in;
- have = (unsigned)((state->strm.next_in + state->strm.avail_in) -
- state->in);
- copy = state->size - have;
- if (copy > len)
- copy = len;
- memcpy(state->in + have, buf, copy);
- state->strm.avail_in += copy;
- state->x.pos += copy;
- buf = (const char *)buf + copy;
- len -= copy;
- if (len && gz_comp(state, Z_NO_FLUSH) == -1)
- return 0;
- } while (len);
- }
- else {
- /* consume whatever's left in the input buffer */
- if (state->strm.avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
- return 0;
-
- /* directly compress user buffer to file */
- state->strm.next_in = (z_const Bytef *)buf;
- do {
- unsigned n = (unsigned)-1;
- if (n > len)
- n = len;
- state->strm.avail_in = n;
- state->x.pos += n;
- if (gz_comp(state, Z_NO_FLUSH) == -1)
- return 0;
- len -= n;
- } while (len);
- }
-
- /* input was all buffered or compressed */
- return put;
-}
-
-/* -- see zlib.h -- */
-int ZEXPORT gzwrite(file, buf, len)
- gzFile file;
- voidpc buf;
- unsigned len;
-{
- gz_statep state;
-
- /* get internal structure */
- if (file == NULL)
- return 0;
- state = (gz_statep)file;
-
- /* check that we're writing and that there's no error */
- if (state->mode != GZ_WRITE || state->err != Z_OK)
- return 0;
-
- /* since an int is returned, make sure len fits in one, otherwise return
- with an error (this avoids a flaw in the interface) */
- if ((int)len < 0) {
- gz_error(state, Z_DATA_ERROR, "requested length does not fit in int");
- return 0;
- }
-
- /* write len bytes from buf (the return value will fit in an int) */
- return (int)gz_write(state, buf, len);
-}
-
-/* -- see zlib.h -- */
-z_size_t ZEXPORT gzfwrite(buf, size, nitems, file)
- voidpc buf;
- z_size_t size;
- z_size_t nitems;
- gzFile file;
-{
- z_size_t len;
- gz_statep state;
-
- /* get internal structure */
- if (file == NULL)
- return 0;
- state = (gz_statep)file;
-
- /* check that we're writing and that there's no error */
- if (state->mode != GZ_WRITE || state->err != Z_OK)
- return 0;
-
- /* compute bytes to read -- error on overflow */
- len = nitems * size;
- if (size && len / size != nitems) {
- gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t");
- return 0;
- }
-
- /* write len bytes to buf, return the number of full items written */
- return len ? gz_write(state, buf, len) / size : 0;
-}
-
-/* -- see zlib.h -- */
-int ZEXPORT gzputc(file, c)
- gzFile file;
- int c;
-{
- unsigned have;
- unsigned char buf[1];
- gz_statep state;
- z_streamp strm;
-
- /* get internal structure */
- if (file == NULL)
- return -1;
- state = (gz_statep)file;
- strm = &(state->strm);
-
- /* check that we're writing and that there's no error */
- if (state->mode != GZ_WRITE || state->err != Z_OK)
- return -1;
-
- /* check for seek request */
- if (state->seek) {
- state->seek = 0;
- if (gz_zero(state, state->skip) == -1)
- return -1;
- }
-
- /* try writing to input buffer for speed (state->size == 0 if buffer not
- initialized) */
- if (state->size) {
- if (strm->avail_in == 0)
- strm->next_in = state->in;
- have = (unsigned)((strm->next_in + strm->avail_in) - state->in);
- if (have < state->size) {
- state->in[have] = (unsigned char)c;
- strm->avail_in++;
- state->x.pos++;
- return c & 0xff;
- }
- }
-
- /* no room in buffer or not initialized, use gz_write() */
- buf[0] = (unsigned char)c;
- if (gz_write(state, buf, 1) != 1)
- return -1;
- return c & 0xff;
-}
-
-/* -- see zlib.h -- */
-int ZEXPORT gzputs(file, str)
- gzFile file;
- const char *str;
-{
- int ret;
- z_size_t len;
- gz_statep state;
-
- /* get internal structure */
- if (file == NULL)
- return -1;
- state = (gz_statep)file;
-
- /* check that we're writing and that there's no error */
- if (state->mode != GZ_WRITE || state->err != Z_OK)
- return -1;
-
- /* write string */
- len = strlen(str);
- ret = gz_write(state, str, len);
- return ret == 0 && len != 0 ? -1 : ret;
-}
-
-#if defined(STDC) || defined(Z_HAVE_STDARG_H)
-#include <stdarg.h>
-
-/* -- see zlib.h -- */
-int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va)
-{
- int len;
- unsigned left;
- char *next;
- gz_statep state;
- z_streamp strm;
-
- /* get internal structure */
- if (file == NULL)
- return Z_STREAM_ERROR;
- state = (gz_statep)file;
- strm = &(state->strm);
-
- /* check that we're writing and that there's no error */
- if (state->mode != GZ_WRITE || state->err != Z_OK)
- return Z_STREAM_ERROR;
-
- /* make sure we have some buffer space */
- if (state->size == 0 && gz_init(state) == -1)
- return state->err;
-
- /* check for seek request */
- if (state->seek) {
- state->seek = 0;
- if (gz_zero(state, state->skip) == -1)
- return state->err;
- }
-
- /* do the printf() into the input buffer, put length in len -- the input
- buffer is double-sized just for this function, so there is guaranteed to
- be state->size bytes available after the current contents */
- if (strm->avail_in == 0)
- strm->next_in = state->in;
- next = (char *)(state->in + (strm->next_in - state->in) + strm->avail_in);
- next[state->size - 1] = 0;
-#ifdef NO_vsnprintf
-# ifdef HAS_vsprintf_void
- (void)vsprintf(next, format, va);
- for (len = 0; len < state->size; len++)
- if (next[len] == 0) break;
-# else
- len = vsprintf(next, format, va);
-# endif
-#else
-# ifdef HAS_vsnprintf_void
- (void)vsnprintf(next, state->size, format, va);
- len = strlen(next);
-# else
- len = vsnprintf(next, state->size, format, va);
-# endif
-#endif
-
- /* check that printf() results fit in buffer */
- if (len == 0 || (unsigned)len >= state->size || next[state->size - 1] != 0)
- return 0;
-
- /* update buffer and position, compress first half if past that */
- strm->avail_in += (unsigned)len;
- state->x.pos += len;
- if (strm->avail_in >= state->size) {
- left = strm->avail_in - state->size;
- strm->avail_in = state->size;
- if (gz_comp(state, Z_NO_FLUSH) == -1)
- return state->err;
- memcpy(state->in, state->in + state->size, left);
- strm->next_in = state->in;
- strm->avail_in = left;
- }
- return len;
-}
-
-int ZEXPORTVA gzprintf(gzFile file, const char *format, ...)
-{
- va_list va;
- int ret;
-
- va_start(va, format);
- ret = gzvprintf(file, format, va);
- va_end(va);
- return ret;
-}
-
-#else /* !STDC && !Z_HAVE_STDARG_H */
-
-/* -- see zlib.h -- */
-int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
- a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
- gzFile file;
- const char *format;
- int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
- a11, a12, a13, a14, a15, a16, a17, a18, a19, a20;
-{
- unsigned len, left;
- char *next;
- gz_statep state;
- z_streamp strm;
-
- /* get internal structure */
- if (file == NULL)
- return Z_STREAM_ERROR;
- state = (gz_statep)file;
- strm = &(state->strm);
-
- /* check that can really pass pointer in ints */
- if (sizeof(int) != sizeof(void *))
- return Z_STREAM_ERROR;
-
- /* check that we're writing and that there's no error */
- if (state->mode != GZ_WRITE || state->err != Z_OK)
- return Z_STREAM_ERROR;
-
- /* make sure we have some buffer space */
- if (state->size == 0 && gz_init(state) == -1)
- return state->error;
-
- /* check for seek request */
- if (state->seek) {
- state->seek = 0;
- if (gz_zero(state, state->skip) == -1)
- return state->error;
- }
-
- /* do the printf() into the input buffer, put length in len -- the input
- buffer is double-sized just for this function, so there is guaranteed to
- be state->size bytes available after the current contents */
- if (strm->avail_in == 0)
- strm->next_in = state->in;
- next = (char *)(strm->next_in + strm->avail_in);
- next[state->size - 1] = 0;
-#ifdef NO_snprintf
-# ifdef HAS_sprintf_void
- sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
- a13, a14, a15, a16, a17, a18, a19, a20);
- for (len = 0; len < size; len++)
- if (next[len] == 0)
- break;
-# else
- len = sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11,
- a12, a13, a14, a15, a16, a17, a18, a19, a20);
-# endif
-#else
-# ifdef HAS_snprintf_void
- snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8, a9,
- a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
- len = strlen(next);
-# else
- len = snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8,
- a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
-# endif
-#endif
-
- /* check that printf() results fit in buffer */
- if (len == 0 || len >= state->size || next[state->size - 1] != 0)
- return 0;
-
- /* update buffer and position, compress first half if past that */
- strm->avail_in += len;
- state->x.pos += len;
- if (strm->avail_in >= state->size) {
- left = strm->avail_in - state->size;
- strm->avail_in = state->size;
- if (gz_comp(state, Z_NO_FLUSH) == -1)
- return state->err;
- memcpy(state->in, state->in + state->size, left);
- strm->next_in = state->in;
- strm->avail_in = left;
- }
- return (int)len;
-}
-
-#endif
-
-/* -- see zlib.h -- */
-int ZEXPORT gzflush(file, flush)
- gzFile file;
- int flush;
-{
- gz_statep state;
-
- /* get internal structure */
- if (file == NULL)
- return Z_STREAM_ERROR;
- state = (gz_statep)file;
-
- /* check that we're writing and that there's no error */
- if (state->mode != GZ_WRITE || state->err != Z_OK)
- return Z_STREAM_ERROR;
-
- /* check flush parameter */
- if (flush < 0 || flush > Z_FINISH)
- return Z_STREAM_ERROR;
-
- /* check for seek request */
- if (state->seek) {
- state->seek = 0;
- if (gz_zero(state, state->skip) == -1)
- return state->err;
- }
-
- /* compress remaining data with requested flush */
- (void)gz_comp(state, flush);
- return state->err;
-}
-
-/* -- see zlib.h -- */
-int ZEXPORT gzsetparams(file, level, strategy)
- gzFile file;
- int level;
- int strategy;
-{
- gz_statep state;
- z_streamp strm;
-
- /* get internal structure */
- if (file == NULL)
- return Z_STREAM_ERROR;
- state = (gz_statep)file;
- strm = &(state->strm);
-
- /* check that we're writing and that there's no error */
- if (state->mode != GZ_WRITE || state->err != Z_OK)
- return Z_STREAM_ERROR;
-
- /* if no change is requested, then do nothing */
- if (level == state->level && strategy == state->strategy)
- return Z_OK;
-
- /* check for seek request */
- if (state->seek) {
- state->seek = 0;
- if (gz_zero(state, state->skip) == -1)
- return state->err;
- }
-
- /* change compression parameters for subsequent input */
- if (state->size) {
- /* flush previous input with previous parameters before changing */
- if (strm->avail_in && gz_comp(state, Z_BLOCK) == -1)
- return state->err;
- deflateParams(strm, level, strategy);
- }
- state->level = level;
- state->strategy = strategy;
- return Z_OK;
-}
-
-/* -- see zlib.h -- */
-int ZEXPORT gzclose_w(file)
- gzFile file;
-{
- int ret = Z_OK;
- gz_statep state;
-
- /* get internal structure */
- if (file == NULL)
- return Z_STREAM_ERROR;
- state = (gz_statep)file;
-
- /* check that we're writing */
- if (state->mode != GZ_WRITE)
- return Z_STREAM_ERROR;
-
- /* check for seek request */
- if (state->seek) {
- state->seek = 0;
- if (gz_zero(state, state->skip) == -1)
- ret = state->err;
- }
-
- /* flush, free memory, and close file */
- if (gz_comp(state, Z_FINISH) == -1)
- ret = state->err;
- if (state->size) {
- if (!state->direct) {
- (void)deflateEnd(&(state->strm));
- free(state->out);
- }
- free(state->in);
- }
- gz_error(state, Z_OK, NULL);
- free(state->path);
- if (close(state->fd) == -1)
- ret = Z_ERRNO;
- free(state);
- return ret;
-}
+++ /dev/null
-/* inffast.c -- fast decoding
- * Copyright (C) 1995-2017 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-#include "zutil.h"
-#include "inftrees.h"
-#include "inflate.h"
-#include "inffast.h"
-
-#ifdef ASMINF
-# pragma message("Assembler code may have bugs -- use at your own risk")
-#else
-
-/*
- Decode literal, length, and distance codes and write out the resulting
- literal and match bytes until either not enough input or output is
- available, an end-of-block is encountered, or a data error is encountered.
- When large enough input and output buffers are supplied to inflate(), for
- example, a 16K input buffer and a 64K output buffer, more than 95% of the
- inflate execution time is spent in this routine.
-
- Entry assumptions:
-
- state->mode == LEN
- strm->avail_in >= 6
- strm->avail_out >= 258
- start >= strm->avail_out
- state->bits < 8
-
- On return, state->mode is one of:
-
- LEN -- ran out of enough output space or enough available input
- TYPE -- reached end of block code, inflate() to interpret next block
- BAD -- error in block data
-
- Notes:
-
- - The maximum input bits used by a length/distance pair is 15 bits for the
- length code, 5 bits for the length extra, 15 bits for the distance code,
- and 13 bits for the distance extra. This totals 48 bits, or six bytes.
- Therefore if strm->avail_in >= 6, then there is enough input to avoid
- checking for available input while decoding.
-
- - The maximum bytes that a single length/distance pair can output is 258
- bytes, which is the maximum length that can be coded. inflate_fast()
- requires strm->avail_out >= 258 for each loop to avoid checking for
- output space.
- */
-void ZLIB_INTERNAL inflate_fast(strm, start)
-z_streamp strm;
-unsigned start; /* inflate()'s starting value for strm->avail_out */
-{
- struct inflate_state FAR *state;
- z_const unsigned char FAR *in; /* local strm->next_in */
- z_const unsigned char FAR *last; /* have enough input while in < last */
- unsigned char FAR *out; /* local strm->next_out */
- unsigned char FAR *beg; /* inflate()'s initial strm->next_out */
- unsigned char FAR *end; /* while out < end, enough space available */
-#ifdef INFLATE_STRICT
- unsigned dmax; /* maximum distance from zlib header */
-#endif
- unsigned wsize; /* window size or zero if not using window */
- unsigned whave; /* valid bytes in the window */
- unsigned wnext; /* window write index */
- unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */
- unsigned long hold; /* local strm->hold */
- unsigned bits; /* local strm->bits */
- code const FAR *lcode; /* local strm->lencode */
- code const FAR *dcode; /* local strm->distcode */
- unsigned lmask; /* mask for first level of length codes */
- unsigned dmask; /* mask for first level of distance codes */
- code here; /* retrieved table entry */
- unsigned op; /* code bits, operation, extra bits, or */
- /* window position, window bytes to copy */
- unsigned len; /* match length, unused bytes */
- unsigned dist; /* match distance */
- unsigned char FAR *from; /* where to copy match from */
-
- /* copy state to local variables */
- state = (struct inflate_state FAR *)strm->state;
- in = strm->next_in;
- last = in + (strm->avail_in - 5);
- out = strm->next_out;
- beg = out - (start - strm->avail_out);
- end = out + (strm->avail_out - 257);
-#ifdef INFLATE_STRICT
- dmax = state->dmax;
-#endif
- wsize = state->wsize;
- whave = state->whave;
- wnext = state->wnext;
- window = state->window;
- hold = state->hold;
- bits = state->bits;
- lcode = state->lencode;
- dcode = state->distcode;
- lmask = (1U << state->lenbits) - 1;
- dmask = (1U << state->distbits) - 1;
-
- /* decode literals and length/distances until end-of-block or not enough
- input data or output space */
- do {
- if (bits < 15) {
- hold += (unsigned long)(*in++) << bits;
- bits += 8;
- hold += (unsigned long)(*in++) << bits;
- bits += 8;
- }
- here = lcode[hold & lmask];
- dolen:
- op = (unsigned)(here.bits);
- hold >>= op;
- bits -= op;
- op = (unsigned)(here.op);
- if (op == 0) { /* literal */
- Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
- "inflate: literal '%c'\n" :
- "inflate: literal 0x%02x\n", here.val));
- *out++ = (unsigned char)(here.val);
- }
- else if (op & 16) { /* length base */
- len = (unsigned)(here.val);
- op &= 15; /* number of extra bits */
- if (op) {
- if (bits < op) {
- hold += (unsigned long)(*in++) << bits;
- bits += 8;
- }
- len += (unsigned)hold & ((1U << op) - 1);
- hold >>= op;
- bits -= op;
- }
- Tracevv((stderr, "inflate: length %u\n", len));
- if (bits < 15) {
- hold += (unsigned long)(*in++) << bits;
- bits += 8;
- hold += (unsigned long)(*in++) << bits;
- bits += 8;
- }
- here = dcode[hold & dmask];
- dodist:
- op = (unsigned)(here.bits);
- hold >>= op;
- bits -= op;
- op = (unsigned)(here.op);
- if (op & 16) { /* distance base */
- dist = (unsigned)(here.val);
- op &= 15; /* number of extra bits */
- if (bits < op) {
- hold += (unsigned long)(*in++) << bits;
- bits += 8;
- if (bits < op) {
- hold += (unsigned long)(*in++) << bits;
- bits += 8;
- }
- }
- dist += (unsigned)hold & ((1U << op) - 1);
-#ifdef INFLATE_STRICT
- if (dist > dmax) {
- strm->msg = (char *)"invalid distance too far back";
- state->mode = BAD;
- break;
- }
-#endif
- hold >>= op;
- bits -= op;
- Tracevv((stderr, "inflate: distance %u\n", dist));
- op = (unsigned)(out - beg); /* max distance in output */
- if (dist > op) { /* see if copy from window */
- op = dist - op; /* distance back in window */
- if (op > whave) {
- if (state->sane) {
- strm->msg =
- (char *)"invalid distance too far back";
- state->mode = BAD;
- break;
- }
-#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
- if (len <= op - whave) {
- do {
- *out++ = 0;
- } while (--len);
- continue;
- }
- len -= op - whave;
- do {
- *out++ = 0;
- } while (--op > whave);
- if (op == 0) {
- from = out - dist;
- do {
- *out++ = *from++;
- } while (--len);
- continue;
- }
-#endif
- }
- from = window;
- if (wnext == 0) { /* very common case */
- from += wsize - op;
- if (op < len) { /* some from window */
- len -= op;
- do {
- *out++ = *from++;
- } while (--op);
- from = out - dist; /* rest from output */
- }
- }
- else if (wnext < op) { /* wrap around window */
- from += wsize + wnext - op;
- op -= wnext;
- if (op < len) { /* some from end of window */
- len -= op;
- do {
- *out++ = *from++;
- } while (--op);
- from = window;
- if (wnext < len) { /* some from start of window */
- op = wnext;
- len -= op;
- do {
- *out++ = *from++;
- } while (--op);
- from = out - dist; /* rest from output */
- }
- }
- }
- else { /* contiguous in window */
- from += wnext - op;
- if (op < len) { /* some from window */
- len -= op;
- do {
- *out++ = *from++;
- } while (--op);
- from = out - dist; /* rest from output */
- }
- }
- while (len > 2) {
- *out++ = *from++;
- *out++ = *from++;
- *out++ = *from++;
- len -= 3;
- }
- if (len) {
- *out++ = *from++;
- if (len > 1)
- *out++ = *from++;
- }
- }
- else {
- from = out - dist; /* copy direct from output */
- do { /* minimum length is three */
- *out++ = *from++;
- *out++ = *from++;
- *out++ = *from++;
- len -= 3;
- } while (len > 2);
- if (len) {
- *out++ = *from++;
- if (len > 1)
- *out++ = *from++;
- }
- }
- }
- else if ((op & 64) == 0) { /* 2nd level distance code */
- here = dcode[here.val + (hold & ((1U << op) - 1))];
- goto dodist;
- }
- else {
- strm->msg = (char *)"invalid distance code";
- state->mode = BAD;
- break;
- }
- }
- else if ((op & 64) == 0) { /* 2nd level length code */
- here = lcode[here.val + (hold & ((1U << op) - 1))];
- goto dolen;
- }
- else if (op & 32) { /* end-of-block */
- Tracevv((stderr, "inflate: end of block\n"));
- state->mode = TYPE;
- break;
- }
- else {
- strm->msg = (char *)"invalid literal/length code";
- state->mode = BAD;
- break;
- }
- } while (in < last && out < end);
-
- /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
- len = bits >> 3;
- in -= len;
- bits -= len << 3;
- hold &= (1U << bits) - 1;
-
- /* update state and return */
- strm->next_in = in;
- strm->next_out = out;
- strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last));
- strm->avail_out = (unsigned)(out < end ?
- 257 + (end - out) : 257 - (out - end));
- state->hold = hold;
- state->bits = bits;
- return;
-}
-
-/*
- inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe):
- - Using bit fields for code structure
- - Different op definition to avoid & for extra bits (do & for table bits)
- - Three separate decoding do-loops for direct, window, and wnext == 0
- - Special case for distance > 1 copies to do overlapped load and store copy
- - Explicit branch predictions (based on measured branch probabilities)
- - Deferring match copy and interspersed it with decoding subsequent codes
- - Swapping literal/length else
- - Swapping window/direct else
- - Larger unrolled copy loops (three is about right)
- - Moving len -= 3 statement into middle of loop
- */
-
-#endif /* !ASMINF */
+++ /dev/null
-/* inffast.h -- header to use inffast.c
- * Copyright (C) 1995-2003, 2010 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-/* WARNING: this file should *not* be used by applications. It is
- part of the implementation of the compression library and is
- subject to change. Applications should only use zlib.h.
- */
-
-void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start));
+++ /dev/null
- /* inffixed.h -- table for decoding fixed codes
- * Generated automatically by makefixed().
- */
-
- /* WARNING: this file should *not* be used by applications.
- It is part of the implementation of this library and is
- subject to change. Applications should only use zlib.h.
- */
-
- static const code lenfix[512] = {
- {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48},
- {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128},
- {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59},
- {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176},
- {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20},
- {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100},
- {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8},
- {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216},
- {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76},
- {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114},
- {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2},
- {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148},
- {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42},
- {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86},
- {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15},
- {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236},
- {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62},
- {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142},
- {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31},
- {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162},
- {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25},
- {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105},
- {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4},
- {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202},
- {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69},
- {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125},
- {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13},
- {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195},
- {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35},
- {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91},
- {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19},
- {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246},
- {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55},
- {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135},
- {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99},
- {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190},
- {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16},
- {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96},
- {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6},
- {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209},
- {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72},
- {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116},
- {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4},
- {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153},
- {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44},
- {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82},
- {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11},
- {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229},
- {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58},
- {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138},
- {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51},
- {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173},
- {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30},
- {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110},
- {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0},
- {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195},
- {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65},
- {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121},
- {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9},
- {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258},
- {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37},
- {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93},
- {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23},
- {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251},
- {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51},
- {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131},
- {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67},
- {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183},
- {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23},
- {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103},
- {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9},
- {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223},
- {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79},
- {0,9,255}
- };
-
- static const code distfix[32] = {
- {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025},
- {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193},
- {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385},
- {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577},
- {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073},
- {22,5,193},{64,5,0}
- };
+++ /dev/null
-/* inflate.c -- zlib decompression
- * Copyright (C) 1995-2016 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-/*
- * Change history:
- *
- * 1.2.beta0 24 Nov 2002
- * - First version -- complete rewrite of inflate to simplify code, avoid
- * creation of window when not needed, minimize use of window when it is
- * needed, make inffast.c even faster, implement gzip decoding, and to
- * improve code readability and style over the previous zlib inflate code
- *
- * 1.2.beta1 25 Nov 2002
- * - Use pointers for available input and output checking in inffast.c
- * - Remove input and output counters in inffast.c
- * - Change inffast.c entry and loop from avail_in >= 7 to >= 6
- * - Remove unnecessary second byte pull from length extra in inffast.c
- * - Unroll direct copy to three copies per loop in inffast.c
- *
- * 1.2.beta2 4 Dec 2002
- * - Change external routine names to reduce potential conflicts
- * - Correct filename to inffixed.h for fixed tables in inflate.c
- * - Make hbuf[] unsigned char to match parameter type in inflate.c
- * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset)
- * to avoid negation problem on Alphas (64 bit) in inflate.c
- *
- * 1.2.beta3 22 Dec 2002
- * - Add comments on state->bits assertion in inffast.c
- * - Add comments on op field in inftrees.h
- * - Fix bug in reuse of allocated window after inflateReset()
- * - Remove bit fields--back to byte structure for speed
- * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths
- * - Change post-increments to pre-increments in inflate_fast(), PPC biased?
- * - Add compile time option, POSTINC, to use post-increments instead (Intel?)
- * - Make MATCH copy in inflate() much faster for when inflate_fast() not used
- * - Use local copies of stream next and avail values, as well as local bit
- * buffer and bit count in inflate()--for speed when inflate_fast() not used
- *
- * 1.2.beta4 1 Jan 2003
- * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings
- * - Move a comment on output buffer sizes from inffast.c to inflate.c
- * - Add comments in inffast.c to introduce the inflate_fast() routine
- * - Rearrange window copies in inflate_fast() for speed and simplification
- * - Unroll last copy for window match in inflate_fast()
- * - Use local copies of window variables in inflate_fast() for speed
- * - Pull out common wnext == 0 case for speed in inflate_fast()
- * - Make op and len in inflate_fast() unsigned for consistency
- * - Add FAR to lcode and dcode declarations in inflate_fast()
- * - Simplified bad distance check in inflate_fast()
- * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new
- * source file infback.c to provide a call-back interface to inflate for
- * programs like gzip and unzip -- uses window as output buffer to avoid
- * window copying
- *
- * 1.2.beta5 1 Jan 2003
- * - Improved inflateBack() interface to allow the caller to provide initial
- * input in strm.
- * - Fixed stored blocks bug in inflateBack()
- *
- * 1.2.beta6 4 Jan 2003
- * - Added comments in inffast.c on effectiveness of POSTINC
- * - Typecasting all around to reduce compiler warnings
- * - Changed loops from while (1) or do {} while (1) to for (;;), again to
- * make compilers happy
- * - Changed type of window in inflateBackInit() to unsigned char *
- *
- * 1.2.beta7 27 Jan 2003
- * - Changed many types to unsigned or unsigned short to avoid warnings
- * - Added inflateCopy() function
- *
- * 1.2.0 9 Mar 2003
- * - Changed inflateBack() interface to provide separate opaque descriptors
- * for the in() and out() functions
- * - Changed inflateBack() argument and in_func typedef to swap the length
- * and buffer address return values for the input function
- * - Check next_in and next_out for Z_NULL on entry to inflate()
- *
- * The history for versions after 1.2.0 are in ChangeLog in zlib distribution.
- */
-
-#include "zutil.h"
-#include "inftrees.h"
-#include "inflate.h"
-#include "inffast.h"
-
-#ifdef MAKEFIXED
-# ifndef BUILDFIXED
-# define BUILDFIXED
-# endif
-#endif
-
-/* function prototypes */
-local int inflateStateCheck OF((z_streamp strm));
-local void fixedtables OF((struct inflate_state FAR *state));
-local int updatewindow OF((z_streamp strm, const unsigned char FAR *end,
- unsigned copy));
-#ifdef BUILDFIXED
- void makefixed OF((void));
-#endif
-local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf,
- unsigned len));
-
-local int inflateStateCheck(strm)
-z_streamp strm;
-{
- struct inflate_state FAR *state;
- if (strm == Z_NULL ||
- strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0)
- return 1;
- state = (struct inflate_state FAR *)strm->state;
- if (state == Z_NULL || state->strm != strm ||
- state->mode < HEAD || state->mode > SYNC)
- return 1;
- return 0;
-}
-
-int ZEXPORT inflateResetKeep(strm)
-z_streamp strm;
-{
- struct inflate_state FAR *state;
-
- if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
- state = (struct inflate_state FAR *)strm->state;
- strm->total_in = strm->total_out = state->total = 0;
- strm->msg = Z_NULL;
- if (state->wrap) /* to support ill-conceived Java test suite */
- strm->adler = state->wrap & 1;
- state->mode = HEAD;
- state->last = 0;
- state->havedict = 0;
- state->dmax = 32768U;
- state->head = Z_NULL;
- state->hold = 0;
- state->bits = 0;
- state->lencode = state->distcode = state->next = state->codes;
- state->sane = 1;
- state->back = -1;
- Tracev((stderr, "inflate: reset\n"));
- return Z_OK;
-}
-
-int ZEXPORT inflateReset(strm)
-z_streamp strm;
-{
- struct inflate_state FAR *state;
-
- if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
- state = (struct inflate_state FAR *)strm->state;
- state->wsize = 0;
- state->whave = 0;
- state->wnext = 0;
- return inflateResetKeep(strm);
-}
-
-int ZEXPORT inflateReset2(strm, windowBits)
-z_streamp strm;
-int windowBits;
-{
- int wrap;
- struct inflate_state FAR *state;
-
- /* get the state */
- if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
- state = (struct inflate_state FAR *)strm->state;
-
- /* extract wrap request from windowBits parameter */
- if (windowBits < 0) {
- wrap = 0;
- windowBits = -windowBits;
- }
- else {
- wrap = (windowBits >> 4) + 5;
-#ifdef GUNZIP
- if (windowBits < 48)
- windowBits &= 15;
-#endif
- }
-
- /* set number of window bits, free window if different */
- if (windowBits && (windowBits < 8 || windowBits > 15))
- return Z_STREAM_ERROR;
- if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) {
- ZFREE(strm, state->window);
- state->window = Z_NULL;
- }
-
- /* update state and reset the rest of it */
- state->wrap = wrap;
- state->wbits = (unsigned)windowBits;
- return inflateReset(strm);
-}
-
-int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size)
-z_streamp strm;
-int windowBits;
-const char *version;
-int stream_size;
-{
- int ret;
- struct inflate_state FAR *state;
-
- if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
- stream_size != (int)(sizeof(z_stream)))
- return Z_VERSION_ERROR;
- if (strm == Z_NULL) return Z_STREAM_ERROR;
- strm->msg = Z_NULL; /* in case we return an error */
- if (strm->zalloc == (alloc_func)0) {
-#ifdef Z_SOLO
- return Z_STREAM_ERROR;
-#else
- strm->zalloc = zcalloc;
- strm->opaque = (voidpf)0;
-#endif
- }
- if (strm->zfree == (free_func)0)
-#ifdef Z_SOLO
- return Z_STREAM_ERROR;
-#else
- strm->zfree = zcfree;
-#endif
- state = (struct inflate_state FAR *)
- ZALLOC(strm, 1, sizeof(struct inflate_state));
- if (state == Z_NULL) return Z_MEM_ERROR;
- Tracev((stderr, "inflate: allocated\n"));
- strm->state = (struct internal_state FAR *)state;
- state->strm = strm;
- state->window = Z_NULL;
- state->mode = HEAD; /* to pass state test in inflateReset2() */
- ret = inflateReset2(strm, windowBits);
- if (ret != Z_OK) {
- ZFREE(strm, state);
- strm->state = Z_NULL;
- }
- return ret;
-}
-
-int ZEXPORT inflateInit_(strm, version, stream_size)
-z_streamp strm;
-const char *version;
-int stream_size;
-{
- return inflateInit2_(strm, DEF_WBITS, version, stream_size);
-}
-
-int ZEXPORT inflatePrime(strm, bits, value)
-z_streamp strm;
-int bits;
-int value;
-{
- struct inflate_state FAR *state;
-
- if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
- state = (struct inflate_state FAR *)strm->state;
- if (bits < 0) {
- state->hold = 0;
- state->bits = 0;
- return Z_OK;
- }
- if (bits > 16 || state->bits + (uInt)bits > 32) return Z_STREAM_ERROR;
- value &= (1L << bits) - 1;
- state->hold += (unsigned)value << state->bits;
- state->bits += (uInt)bits;
- return Z_OK;
-}
-
-/*
- Return state with length and distance decoding tables and index sizes set to
- fixed code decoding. Normally this returns fixed tables from inffixed.h.
- If BUILDFIXED is defined, then instead this routine builds the tables the
- first time it's called, and returns those tables the first time and
- thereafter. This reduces the size of the code by about 2K bytes, in
- exchange for a little execution time. However, BUILDFIXED should not be
- used for threaded applications, since the rewriting of the tables and virgin
- may not be thread-safe.
- */
-local void fixedtables(state)
-struct inflate_state FAR *state;
-{
-#ifdef BUILDFIXED
- static int virgin = 1;
- static code *lenfix, *distfix;
- static code fixed[544];
-
- /* build fixed huffman tables if first call (may not be thread safe) */
- if (virgin) {
- unsigned sym, bits;
- static code *next;
-
- /* literal/length table */
- sym = 0;
- while (sym < 144) state->lens[sym++] = 8;
- while (sym < 256) state->lens[sym++] = 9;
- while (sym < 280) state->lens[sym++] = 7;
- while (sym < 288) state->lens[sym++] = 8;
- next = fixed;
- lenfix = next;
- bits = 9;
- inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
-
- /* distance table */
- sym = 0;
- while (sym < 32) state->lens[sym++] = 5;
- distfix = next;
- bits = 5;
- inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
-
- /* do this just once */
- virgin = 0;
- }
-#else /* !BUILDFIXED */
-# include "inffixed.h"
-#endif /* BUILDFIXED */
- state->lencode = lenfix;
- state->lenbits = 9;
- state->distcode = distfix;
- state->distbits = 5;
-}
-
-#ifdef MAKEFIXED
-#include <stdio.h>
-
-/*
- Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also
- defines BUILDFIXED, so the tables are built on the fly. makefixed() writes
- those tables to stdout, which would be piped to inffixed.h. A small program
- can simply call makefixed to do this:
-
- void makefixed(void);
-
- int main(void)
- {
- makefixed();
- return 0;
- }
-
- Then that can be linked with zlib built with MAKEFIXED defined and run:
-
- a.out > inffixed.h
- */
-void makefixed()
-{
- unsigned low, size;
- struct inflate_state state;
-
- fixedtables(&state);
- puts(" /* inffixed.h -- table for decoding fixed codes");
- puts(" * Generated automatically by makefixed().");
- puts(" */");
- puts("");
- puts(" /* WARNING: this file should *not* be used by applications.");
- puts(" It is part of the implementation of this library and is");
- puts(" subject to change. Applications should only use zlib.h.");
- puts(" */");
- puts("");
- size = 1U << 9;
- printf(" static const code lenfix[%u] = {", size);
- low = 0;
- for (;;) {
- if ((low % 7) == 0) printf("\n ");
- printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op,
- state.lencode[low].bits, state.lencode[low].val);
- if (++low == size) break;
- putchar(',');
- }
- puts("\n };");
- size = 1U << 5;
- printf("\n static const code distfix[%u] = {", size);
- low = 0;
- for (;;) {
- if ((low % 6) == 0) printf("\n ");
- printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits,
- state.distcode[low].val);
- if (++low == size) break;
- putchar(',');
- }
- puts("\n };");
-}
-#endif /* MAKEFIXED */
-
-/*
- Update the window with the last wsize (normally 32K) bytes written before
- returning. If window does not exist yet, create it. This is only called
- when a window is already in use, or when output has been written during this
- inflate call, but the end of the deflate stream has not been reached yet.
- It is also called to create a window for dictionary data when a dictionary
- is loaded.
-
- Providing output buffers larger than 32K to inflate() should provide a speed
- advantage, since only the last 32K of output is copied to the sliding window
- upon return from inflate(), and since all distances after the first 32K of
- output will fall in the output data, making match copies simpler and faster.
- The advantage may be dependent on the size of the processor's data caches.
- */
-local int updatewindow(strm, end, copy)
-z_streamp strm;
-const Bytef *end;
-unsigned copy;
-{
- struct inflate_state FAR *state;
- unsigned dist;
-
- state = (struct inflate_state FAR *)strm->state;
-
- /* if it hasn't been done already, allocate space for the window */
- if (state->window == Z_NULL) {
- state->window = (unsigned char FAR *)
- ZALLOC(strm, 1U << state->wbits,
- sizeof(unsigned char));
- if (state->window == Z_NULL) return 1;
- }
-
- /* if window not in use yet, initialize */
- if (state->wsize == 0) {
- state->wsize = 1U << state->wbits;
- state->wnext = 0;
- state->whave = 0;
- }
-
- /* copy state->wsize or less output bytes into the circular window */
- if (copy >= state->wsize) {
- zmemcpy(state->window, end - state->wsize, state->wsize);
- state->wnext = 0;
- state->whave = state->wsize;
- }
- else {
- dist = state->wsize - state->wnext;
- if (dist > copy) dist = copy;
- zmemcpy(state->window + state->wnext, end - copy, dist);
- copy -= dist;
- if (copy) {
- zmemcpy(state->window, end - copy, copy);
- state->wnext = copy;
- state->whave = state->wsize;
- }
- else {
- state->wnext += dist;
- if (state->wnext == state->wsize) state->wnext = 0;
- if (state->whave < state->wsize) state->whave += dist;
- }
- }
- return 0;
-}
-
-/* Macros for inflate(): */
-
-/* check function to use adler32() for zlib or crc32() for gzip */
-#ifdef GUNZIP
-# define UPDATE(check, buf, len) \
- (state->flags ? crc32(check, buf, len) : adler32(check, buf, len))
-#else
-# define UPDATE(check, buf, len) adler32(check, buf, len)
-#endif
-
-/* check macros for header crc */
-#ifdef GUNZIP
-# define CRC2(check, word) \
- do { \
- hbuf[0] = (unsigned char)(word); \
- hbuf[1] = (unsigned char)((word) >> 8); \
- check = crc32(check, hbuf, 2); \
- } while (0)
-
-# define CRC4(check, word) \
- do { \
- hbuf[0] = (unsigned char)(word); \
- hbuf[1] = (unsigned char)((word) >> 8); \
- hbuf[2] = (unsigned char)((word) >> 16); \
- hbuf[3] = (unsigned char)((word) >> 24); \
- check = crc32(check, hbuf, 4); \
- } while (0)
-#endif
-
-/* Load registers with state in inflate() for speed */
-#define LOAD() \
- do { \
- put = strm->next_out; \
- left = strm->avail_out; \
- next = strm->next_in; \
- have = strm->avail_in; \
- hold = state->hold; \
- bits = state->bits; \
- } while (0)
-
-/* Restore state from registers in inflate() */
-#define RESTORE() \
- do { \
- strm->next_out = put; \
- strm->avail_out = left; \
- strm->next_in = next; \
- strm->avail_in = have; \
- state->hold = hold; \
- state->bits = bits; \
- } while (0)
-
-/* Clear the input bit accumulator */
-#define INITBITS() \
- do { \
- hold = 0; \
- bits = 0; \
- } while (0)
-
-/* Get a byte of input into the bit accumulator, or return from inflate()
- if there is no input available. */
-#define PULLBYTE() \
- do { \
- if (have == 0) goto inf_leave; \
- have--; \
- hold += (unsigned long)(*next++) << bits; \
- bits += 8; \
- } while (0)
-
-/* Assure that there are at least n bits in the bit accumulator. If there is
- not enough available input to do that, then return from inflate(). */
-#define NEEDBITS(n) \
- do { \
- while (bits < (unsigned)(n)) \
- PULLBYTE(); \
- } while (0)
-
-/* Return the low n bits of the bit accumulator (n < 16) */
-#define BITS(n) \
- ((unsigned)hold & ((1U << (n)) - 1))
-
-/* Remove n bits from the bit accumulator */
-#define DROPBITS(n) \
- do { \
- hold >>= (n); \
- bits -= (unsigned)(n); \
- } while (0)
-
-/* Remove zero to seven bits as needed to go to a byte boundary */
-#define BYTEBITS() \
- do { \
- hold >>= bits & 7; \
- bits -= bits & 7; \
- } while (0)
-
-/*
- inflate() uses a state machine to process as much input data and generate as
- much output data as possible before returning. The state machine is
- structured roughly as follows:
-
- for (;;) switch (state) {
- ...
- case STATEn:
- if (not enough input data or output space to make progress)
- return;
- ... make progress ...
- state = STATEm;
- break;
- ...
- }
-
- so when inflate() is called again, the same case is attempted again, and
- if the appropriate resources are provided, the machine proceeds to the
- next state. The NEEDBITS() macro is usually the way the state evaluates
- whether it can proceed or should return. NEEDBITS() does the return if
- the requested bits are not available. The typical use of the BITS macros
- is:
-
- NEEDBITS(n);
- ... do something with BITS(n) ...
- DROPBITS(n);
-
- where NEEDBITS(n) either returns from inflate() if there isn't enough
- input left to load n bits into the accumulator, or it continues. BITS(n)
- gives the low n bits in the accumulator. When done, DROPBITS(n) drops
- the low n bits off the accumulator. INITBITS() clears the accumulator
- and sets the number of available bits to zero. BYTEBITS() discards just
- enough bits to put the accumulator on a byte boundary. After BYTEBITS()
- and a NEEDBITS(8), then BITS(8) would return the next byte in the stream.
-
- NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return
- if there is no input available. The decoding of variable length codes uses
- PULLBYTE() directly in order to pull just enough bytes to decode the next
- code, and no more.
-
- Some states loop until they get enough input, making sure that enough
- state information is maintained to continue the loop where it left off
- if NEEDBITS() returns in the loop. For example, want, need, and keep
- would all have to actually be part of the saved state in case NEEDBITS()
- returns:
-
- case STATEw:
- while (want < need) {
- NEEDBITS(n);
- keep[want++] = BITS(n);
- DROPBITS(n);
- }
- state = STATEx;
- case STATEx:
-
- As shown above, if the next state is also the next case, then the break
- is omitted.
-
- A state may also return if there is not enough output space available to
- complete that state. Those states are copying stored data, writing a
- literal byte, and copying a matching string.
-
- When returning, a "goto inf_leave" is used to update the total counters,
- update the check value, and determine whether any progress has been made
- during that inflate() call in order to return the proper return code.
- Progress is defined as a change in either strm->avail_in or strm->avail_out.
- When there is a window, goto inf_leave will update the window with the last
- output written. If a goto inf_leave occurs in the middle of decompression
- and there is no window currently, goto inf_leave will create one and copy
- output to the window for the next call of inflate().
-
- In this implementation, the flush parameter of inflate() only affects the
- return code (per zlib.h). inflate() always writes as much as possible to
- strm->next_out, given the space available and the provided input--the effect
- documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers
- the allocation of and copying into a sliding window until necessary, which
- provides the effect documented in zlib.h for Z_FINISH when the entire input
- stream available. So the only thing the flush parameter actually does is:
- when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it
- will return Z_BUF_ERROR if it has not reached the end of the stream.
- */
-
-int ZEXPORT inflate(strm, flush)
-z_streamp strm;
-int flush;
-{
- struct inflate_state FAR *state;
- z_const unsigned char FAR *next; /* next input */
- unsigned char FAR *put; /* next output */
- unsigned have, left; /* available input and output */
- unsigned long hold; /* bit buffer */
- unsigned bits; /* bits in bit buffer */
- unsigned in, out; /* save starting available input and output */
- unsigned copy; /* number of stored or match bytes to copy */
- unsigned char FAR *from; /* where to copy match bytes from */
- code here; /* current decoding table entry */
- code last; /* parent table entry */
- unsigned len; /* length to copy for repeats, bits to drop */
- int ret; /* return code */
-#ifdef GUNZIP
- unsigned char hbuf[4]; /* buffer for gzip header crc calculation */
-#endif
- static const unsigned short order[19] = /* permutation of code lengths */
- {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
-
- if (inflateStateCheck(strm) || strm->next_out == Z_NULL ||
- (strm->next_in == Z_NULL && strm->avail_in != 0))
- return Z_STREAM_ERROR;
-
- state = (struct inflate_state FAR *)strm->state;
- if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */
- LOAD();
- in = have;
- out = left;
- ret = Z_OK;
- for (;;)
- switch (state->mode) {
- case HEAD:
- if (state->wrap == 0) {
- state->mode = TYPEDO;
- break;
- }
- NEEDBITS(16);
-#ifdef GUNZIP
- if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */
- if (state->wbits == 0)
- state->wbits = 15;
- state->check = crc32(0L, Z_NULL, 0);
- CRC2(state->check, hold);
- INITBITS();
- state->mode = FLAGS;
- break;
- }
- state->flags = 0; /* expect zlib header */
- if (state->head != Z_NULL)
- state->head->done = -1;
- if (!(state->wrap & 1) || /* check if zlib header allowed */
-#else
- if (
-#endif
- ((BITS(8) << 8) + (hold >> 8)) % 31) {
- strm->msg = (char *)"incorrect header check";
- state->mode = BAD;
- break;
- }
- if (BITS(4) != Z_DEFLATED) {
- strm->msg = (char *)"unknown compression method";
- state->mode = BAD;
- break;
- }
- DROPBITS(4);
- len = BITS(4) + 8;
- if (state->wbits == 0)
- state->wbits = len;
- if (len > 15 || len > state->wbits) {
- strm->msg = (char *)"invalid window size";
- state->mode = BAD;
- break;
- }
- state->dmax = 1U << len;
- Tracev((stderr, "inflate: zlib header ok\n"));
- strm->adler = state->check = adler32(0L, Z_NULL, 0);
- state->mode = hold & 0x200 ? DICTID : TYPE;
- INITBITS();
- break;
-#ifdef GUNZIP
- case FLAGS:
- NEEDBITS(16);
- state->flags = (int)(hold);
- if ((state->flags & 0xff) != Z_DEFLATED) {
- strm->msg = (char *)"unknown compression method";
- state->mode = BAD;
- break;
- }
- if (state->flags & 0xe000) {
- strm->msg = (char *)"unknown header flags set";
- state->mode = BAD;
- break;
- }
- if (state->head != Z_NULL)
- state->head->text = (int)((hold >> 8) & 1);
- if ((state->flags & 0x0200) && (state->wrap & 4))
- CRC2(state->check, hold);
- INITBITS();
- state->mode = TIME;
- case TIME:
- NEEDBITS(32);
- if (state->head != Z_NULL)
- state->head->time = hold;
- if ((state->flags & 0x0200) && (state->wrap & 4))
- CRC4(state->check, hold);
- INITBITS();
- state->mode = OS;
- case OS:
- NEEDBITS(16);
- if (state->head != Z_NULL) {
- state->head->xflags = (int)(hold & 0xff);
- state->head->os = (int)(hold >> 8);
- }
- if ((state->flags & 0x0200) && (state->wrap & 4))
- CRC2(state->check, hold);
- INITBITS();
- state->mode = EXLEN;
- case EXLEN:
- if (state->flags & 0x0400) {
- NEEDBITS(16);
- state->length = (unsigned)(hold);
- if (state->head != Z_NULL)
- state->head->extra_len = (unsigned)hold;
- if ((state->flags & 0x0200) && (state->wrap & 4))
- CRC2(state->check, hold);
- INITBITS();
- }
- else if (state->head != Z_NULL)
- state->head->extra = Z_NULL;
- state->mode = EXTRA;
- case EXTRA:
- if (state->flags & 0x0400) {
- copy = state->length;
- if (copy > have) copy = have;
- if (copy) {
- if (state->head != Z_NULL &&
- state->head->extra != Z_NULL) {
- len = state->head->extra_len - state->length;
- zmemcpy(state->head->extra + len, next,
- len + copy > state->head->extra_max ?
- state->head->extra_max - len : copy);
- }
- if ((state->flags & 0x0200) && (state->wrap & 4))
- state->check = crc32(state->check, next, copy);
- have -= copy;
- next += copy;
- state->length -= copy;
- }
- if (state->length) goto inf_leave;
- }
- state->length = 0;
- state->mode = NAME;
- case NAME:
- if (state->flags & 0x0800) {
- if (have == 0) goto inf_leave;
- copy = 0;
- do {
- len = (unsigned)(next[copy++]);
- if (state->head != Z_NULL &&
- state->head->name != Z_NULL &&
- state->length < state->head->name_max)
- state->head->name[state->length++] = (Bytef)len;
- } while (len && copy < have);
- if ((state->flags & 0x0200) && (state->wrap & 4))
- state->check = crc32(state->check, next, copy);
- have -= copy;
- next += copy;
- if (len) goto inf_leave;
- }
- else if (state->head != Z_NULL)
- state->head->name = Z_NULL;
- state->length = 0;
- state->mode = COMMENT;
- case COMMENT:
- if (state->flags & 0x1000) {
- if (have == 0) goto inf_leave;
- copy = 0;
- do {
- len = (unsigned)(next[copy++]);
- if (state->head != Z_NULL &&
- state->head->comment != Z_NULL &&
- state->length < state->head->comm_max)
- state->head->comment[state->length++] = (Bytef)len;
- } while (len && copy < have);
- if ((state->flags & 0x0200) && (state->wrap & 4))
- state->check = crc32(state->check, next, copy);
- have -= copy;
- next += copy;
- if (len) goto inf_leave;
- }
- else if (state->head != Z_NULL)
- state->head->comment = Z_NULL;
- state->mode = HCRC;
- case HCRC:
- if (state->flags & 0x0200) {
- NEEDBITS(16);
- if ((state->wrap & 4) && hold != (state->check & 0xffff)) {
- strm->msg = (char *)"header crc mismatch";
- state->mode = BAD;
- break;
- }
- INITBITS();
- }
- if (state->head != Z_NULL) {
- state->head->hcrc = (int)((state->flags >> 9) & 1);
- state->head->done = 1;
- }
- strm->adler = state->check = crc32(0L, Z_NULL, 0);
- state->mode = TYPE;
- break;
-#endif
- case DICTID:
- NEEDBITS(32);
- strm->adler = state->check = ZSWAP32(hold);
- INITBITS();
- state->mode = DICT;
- case DICT:
- if (state->havedict == 0) {
- RESTORE();
- return Z_NEED_DICT;
- }
- strm->adler = state->check = adler32(0L, Z_NULL, 0);
- state->mode = TYPE;
- case TYPE:
- if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave;
- case TYPEDO:
- if (state->last) {
- BYTEBITS();
- state->mode = CHECK;
- break;
- }
- NEEDBITS(3);
- state->last = BITS(1);
- DROPBITS(1);
- switch (BITS(2)) {
- case 0: /* stored block */
- Tracev((stderr, "inflate: stored block%s\n",
- state->last ? " (last)" : ""));
- state->mode = STORED;
- break;
- case 1: /* fixed block */
- fixedtables(state);
- Tracev((stderr, "inflate: fixed codes block%s\n",
- state->last ? " (last)" : ""));
- state->mode = LEN_; /* decode codes */
- if (flush == Z_TREES) {
- DROPBITS(2);
- goto inf_leave;
- }
- break;
- case 2: /* dynamic block */
- Tracev((stderr, "inflate: dynamic codes block%s\n",
- state->last ? " (last)" : ""));
- state->mode = TABLE;
- break;
- case 3:
- strm->msg = (char *)"invalid block type";
- state->mode = BAD;
- }
- DROPBITS(2);
- break;
- case STORED:
- BYTEBITS(); /* go to byte boundary */
- NEEDBITS(32);
- if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
- strm->msg = (char *)"invalid stored block lengths";
- state->mode = BAD;
- break;
- }
- state->length = (unsigned)hold & 0xffff;
- Tracev((stderr, "inflate: stored length %u\n",
- state->length));
- INITBITS();
- state->mode = COPY_;
- if (flush == Z_TREES) goto inf_leave;
- case COPY_:
- state->mode = COPY;
- case COPY:
- copy = state->length;
- if (copy) {
- if (copy > have) copy = have;
- if (copy > left) copy = left;
- if (copy == 0) goto inf_leave;
- zmemcpy(put, next, copy);
- have -= copy;
- next += copy;
- left -= copy;
- put += copy;
- state->length -= copy;
- break;
- }
- Tracev((stderr, "inflate: stored end\n"));
- state->mode = TYPE;
- break;
- case TABLE:
- NEEDBITS(14);
- state->nlen = BITS(5) + 257;
- DROPBITS(5);
- state->ndist = BITS(5) + 1;
- DROPBITS(5);
- state->ncode = BITS(4) + 4;
- DROPBITS(4);
-#ifndef PKZIP_BUG_WORKAROUND
- if (state->nlen > 286 || state->ndist > 30) {
- strm->msg = (char *)"too many length or distance symbols";
- state->mode = BAD;
- break;
- }
-#endif
- Tracev((stderr, "inflate: table sizes ok\n"));
- state->have = 0;
- state->mode = LENLENS;
- case LENLENS:
- while (state->have < state->ncode) {
- NEEDBITS(3);
- state->lens[order[state->have++]] = (unsigned short)BITS(3);
- DROPBITS(3);
- }
- while (state->have < 19)
- state->lens[order[state->have++]] = 0;
- state->next = state->codes;
- state->lencode = (const code FAR *)(state->next);
- state->lenbits = 7;
- ret = inflate_table(CODES, state->lens, 19, &(state->next),
- &(state->lenbits), state->work);
- if (ret) {
- strm->msg = (char *)"invalid code lengths set";
- state->mode = BAD;
- break;
- }
- Tracev((stderr, "inflate: code lengths ok\n"));
- state->have = 0;
- state->mode = CODELENS;
- case CODELENS:
- while (state->have < state->nlen + state->ndist) {
- for (;;) {
- here = state->lencode[BITS(state->lenbits)];
- if ((unsigned)(here.bits) <= bits) break;
- PULLBYTE();
- }
- if (here.val < 16) {
- DROPBITS(here.bits);
- state->lens[state->have++] = here.val;
- }
- else {
- if (here.val == 16) {
- NEEDBITS(here.bits + 2);
- DROPBITS(here.bits);
- if (state->have == 0) {
- strm->msg = (char *)"invalid bit length repeat";
- state->mode = BAD;
- break;
- }
- len = state->lens[state->have - 1];
- copy = 3 + BITS(2);
- DROPBITS(2);
- }
- else if (here.val == 17) {
- NEEDBITS(here.bits + 3);
- DROPBITS(here.bits);
- len = 0;
- copy = 3 + BITS(3);
- DROPBITS(3);
- }
- else {
- NEEDBITS(here.bits + 7);
- DROPBITS(here.bits);
- len = 0;
- copy = 11 + BITS(7);
- DROPBITS(7);
- }
- if (state->have + copy > state->nlen + state->ndist) {
- strm->msg = (char *)"invalid bit length repeat";
- state->mode = BAD;
- break;
- }
- while (copy--)
- state->lens[state->have++] = (unsigned short)len;
- }
- }
-
- /* handle error breaks in while */
- if (state->mode == BAD) break;
-
- /* check for end-of-block code (better have one) */
- if (state->lens[256] == 0) {
- strm->msg = (char *)"invalid code -- missing end-of-block";
- state->mode = BAD;
- break;
- }
-
- /* build code tables -- note: do not change the lenbits or distbits
- values here (9 and 6) without reading the comments in inftrees.h
- concerning the ENOUGH constants, which depend on those values */
- state->next = state->codes;
- state->lencode = (const code FAR *)(state->next);
- state->lenbits = 9;
- ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
- &(state->lenbits), state->work);
- if (ret) {
- strm->msg = (char *)"invalid literal/lengths set";
- state->mode = BAD;
- break;
- }
- state->distcode = (const code FAR *)(state->next);
- state->distbits = 6;
- ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
- &(state->next), &(state->distbits), state->work);
- if (ret) {
- strm->msg = (char *)"invalid distances set";
- state->mode = BAD;
- break;
- }
- Tracev((stderr, "inflate: codes ok\n"));
- state->mode = LEN_;
- if (flush == Z_TREES) goto inf_leave;
- case LEN_:
- state->mode = LEN;
- case LEN:
- if (have >= 6 && left >= 258) {
- RESTORE();
- inflate_fast(strm, out);
- LOAD();
- if (state->mode == TYPE)
- state->back = -1;
- break;
- }
- state->back = 0;
- for (;;) {
- here = state->lencode[BITS(state->lenbits)];
- if ((unsigned)(here.bits) <= bits) break;
- PULLBYTE();
- }
- if (here.op && (here.op & 0xf0) == 0) {
- last = here;
- for (;;) {
- here = state->lencode[last.val +
- (BITS(last.bits + last.op) >> last.bits)];
- if ((unsigned)(last.bits + here.bits) <= bits) break;
- PULLBYTE();
- }
- DROPBITS(last.bits);
- state->back += last.bits;
- }
- DROPBITS(here.bits);
- state->back += here.bits;
- state->length = (unsigned)here.val;
- if ((int)(here.op) == 0) {
- Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
- "inflate: literal '%c'\n" :
- "inflate: literal 0x%02x\n", here.val));
- state->mode = LIT;
- break;
- }
- if (here.op & 32) {
- Tracevv((stderr, "inflate: end of block\n"));
- state->back = -1;
- state->mode = TYPE;
- break;
- }
- if (here.op & 64) {
- strm->msg = (char *)"invalid literal/length code";
- state->mode = BAD;
- break;
- }
- state->extra = (unsigned)(here.op) & 15;
- state->mode = LENEXT;
- case LENEXT:
- if (state->extra) {
- NEEDBITS(state->extra);
- state->length += BITS(state->extra);
- DROPBITS(state->extra);
- state->back += state->extra;
- }
- Tracevv((stderr, "inflate: length %u\n", state->length));
- state->was = state->length;
- state->mode = DIST;
- case DIST:
- for (;;) {
- here = state->distcode[BITS(state->distbits)];
- if ((unsigned)(here.bits) <= bits) break;
- PULLBYTE();
- }
- if ((here.op & 0xf0) == 0) {
- last = here;
- for (;;) {
- here = state->distcode[last.val +
- (BITS(last.bits + last.op) >> last.bits)];
- if ((unsigned)(last.bits + here.bits) <= bits) break;
- PULLBYTE();
- }
- DROPBITS(last.bits);
- state->back += last.bits;
- }
- DROPBITS(here.bits);
- state->back += here.bits;
- if (here.op & 64) {
- strm->msg = (char *)"invalid distance code";
- state->mode = BAD;
- break;
- }
- state->offset = (unsigned)here.val;
- state->extra = (unsigned)(here.op) & 15;
- state->mode = DISTEXT;
- case DISTEXT:
- if (state->extra) {
- NEEDBITS(state->extra);
- state->offset += BITS(state->extra);
- DROPBITS(state->extra);
- state->back += state->extra;
- }
-#ifdef INFLATE_STRICT
- if (state->offset > state->dmax) {
- strm->msg = (char *)"invalid distance too far back";
- state->mode = BAD;
- break;
- }
-#endif
- Tracevv((stderr, "inflate: distance %u\n", state->offset));
- state->mode = MATCH;
- case MATCH:
- if (left == 0) goto inf_leave;
- copy = out - left;
- if (state->offset > copy) { /* copy from window */
- copy = state->offset - copy;
- if (copy > state->whave) {
- if (state->sane) {
- strm->msg = (char *)"invalid distance too far back";
- state->mode = BAD;
- break;
- }
-#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
- Trace((stderr, "inflate.c too far\n"));
- copy -= state->whave;
- if (copy > state->length) copy = state->length;
- if (copy > left) copy = left;
- left -= copy;
- state->length -= copy;
- do {
- *put++ = 0;
- } while (--copy);
- if (state->length == 0) state->mode = LEN;
- break;
-#endif
- }
- if (copy > state->wnext) {
- copy -= state->wnext;
- from = state->window + (state->wsize - copy);
- }
- else
- from = state->window + (state->wnext - copy);
- if (copy > state->length) copy = state->length;
- }
- else { /* copy from output */
- from = put - state->offset;
- copy = state->length;
- }
- if (copy > left) copy = left;
- left -= copy;
- state->length -= copy;
- do {
- *put++ = *from++;
- } while (--copy);
- if (state->length == 0) state->mode = LEN;
- break;
- case LIT:
- if (left == 0) goto inf_leave;
- *put++ = (unsigned char)(state->length);
- left--;
- state->mode = LEN;
- break;
- case CHECK:
- if (state->wrap) {
- NEEDBITS(32);
- out -= left;
- strm->total_out += out;
- state->total += out;
- if ((state->wrap & 4) && out)
- strm->adler = state->check =
- UPDATE(state->check, put - out, out);
- out = left;
- if ((state->wrap & 4) && (
-#ifdef GUNZIP
- state->flags ? hold :
-#endif
- ZSWAP32(hold)) != state->check) {
- strm->msg = (char *)"incorrect data check";
- state->mode = BAD;
- break;
- }
- INITBITS();
- Tracev((stderr, "inflate: check matches trailer\n"));
- }
-#ifdef GUNZIP
- state->mode = LENGTH;
- case LENGTH:
- if (state->wrap && state->flags) {
- NEEDBITS(32);
- if (hold != (state->total & 0xffffffffUL)) {
- strm->msg = (char *)"incorrect length check";
- state->mode = BAD;
- break;
- }
- INITBITS();
- Tracev((stderr, "inflate: length matches trailer\n"));
- }
-#endif
- state->mode = DONE;
- case DONE:
- ret = Z_STREAM_END;
- goto inf_leave;
- case BAD:
- ret = Z_DATA_ERROR;
- goto inf_leave;
- case MEM:
- return Z_MEM_ERROR;
- case SYNC:
- default:
- return Z_STREAM_ERROR;
- }
-
- /*
- Return from inflate(), updating the total counts and the check value.
- If there was no progress during the inflate() call, return a buffer
- error. Call updatewindow() to create and/or update the window state.
- Note: a memory error from inflate() is non-recoverable.
- */
- inf_leave:
- RESTORE();
- if (state->wsize || (out != strm->avail_out && state->mode < BAD &&
- (state->mode < CHECK || flush != Z_FINISH)))
- if (updatewindow(strm, strm->next_out, out - strm->avail_out)) {
- state->mode = MEM;
- return Z_MEM_ERROR;
- }
- in -= strm->avail_in;
- out -= strm->avail_out;
- strm->total_in += in;
- strm->total_out += out;
- state->total += out;
- if ((state->wrap & 4) && out)
- strm->adler = state->check =
- UPDATE(state->check, strm->next_out - out, out);
- strm->data_type = (int)state->bits + (state->last ? 64 : 0) +
- (state->mode == TYPE ? 128 : 0) +
- (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0);
- if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK)
- ret = Z_BUF_ERROR;
- return ret;
-}
-
-int ZEXPORT inflateEnd(strm)
-z_streamp strm;
-{
- struct inflate_state FAR *state;
- if (inflateStateCheck(strm))
- return Z_STREAM_ERROR;
- state = (struct inflate_state FAR *)strm->state;
- if (state->window != Z_NULL) ZFREE(strm, state->window);
- ZFREE(strm, strm->state);
- strm->state = Z_NULL;
- Tracev((stderr, "inflate: end\n"));
- return Z_OK;
-}
-
-int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength)
-z_streamp strm;
-Bytef *dictionary;
-uInt *dictLength;
-{
- struct inflate_state FAR *state;
-
- /* check state */
- if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
- state = (struct inflate_state FAR *)strm->state;
-
- /* copy dictionary */
- if (state->whave && dictionary != Z_NULL) {
- zmemcpy(dictionary, state->window + state->wnext,
- state->whave - state->wnext);
- zmemcpy(dictionary + state->whave - state->wnext,
- state->window, state->wnext);
- }
- if (dictLength != Z_NULL)
- *dictLength = state->whave;
- return Z_OK;
-}
-
-int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength)
-z_streamp strm;
-const Bytef *dictionary;
-uInt dictLength;
-{
- struct inflate_state FAR *state;
- unsigned long dictid;
- int ret;
-
- /* check state */
- if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
- state = (struct inflate_state FAR *)strm->state;
- if (state->wrap != 0 && state->mode != DICT)
- return Z_STREAM_ERROR;
-
- /* check for correct dictionary identifier */
- if (state->mode == DICT) {
- dictid = adler32(0L, Z_NULL, 0);
- dictid = adler32(dictid, dictionary, dictLength);
- if (dictid != state->check)
- return Z_DATA_ERROR;
- }
-
- /* copy dictionary to window using updatewindow(), which will amend the
- existing dictionary if appropriate */
- ret = updatewindow(strm, dictionary + dictLength, dictLength);
- if (ret) {
- state->mode = MEM;
- return Z_MEM_ERROR;
- }
- state->havedict = 1;
- Tracev((stderr, "inflate: dictionary set\n"));
- return Z_OK;
-}
-
-int ZEXPORT inflateGetHeader(strm, head)
-z_streamp strm;
-gz_headerp head;
-{
- struct inflate_state FAR *state;
-
- /* check state */
- if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
- state = (struct inflate_state FAR *)strm->state;
- if ((state->wrap & 2) == 0) return Z_STREAM_ERROR;
-
- /* save header structure */
- state->head = head;
- head->done = 0;
- return Z_OK;
-}
-
-/*
- Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found
- or when out of input. When called, *have is the number of pattern bytes
- found in order so far, in 0..3. On return *have is updated to the new
- state. If on return *have equals four, then the pattern was found and the
- return value is how many bytes were read including the last byte of the
- pattern. If *have is less than four, then the pattern has not been found
- yet and the return value is len. In the latter case, syncsearch() can be
- called again with more data and the *have state. *have is initialized to
- zero for the first call.
- */
-local unsigned syncsearch(have, buf, len)
-unsigned FAR *have;
-const unsigned char FAR *buf;
-unsigned len;
-{
- unsigned got;
- unsigned next;
-
- got = *have;
- next = 0;
- while (next < len && got < 4) {
- if ((int)(buf[next]) == (got < 2 ? 0 : 0xff))
- got++;
- else if (buf[next])
- got = 0;
- else
- got = 4 - got;
- next++;
- }
- *have = got;
- return next;
-}
-
-int ZEXPORT inflateSync(strm)
-z_streamp strm;
-{
- unsigned len; /* number of bytes to look at or looked at */
- unsigned long in, out; /* temporary to save total_in and total_out */
- unsigned char buf[4]; /* to restore bit buffer to byte string */
- struct inflate_state FAR *state;
-
- /* check parameters */
- if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
- state = (struct inflate_state FAR *)strm->state;
- if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR;
-
- /* if first time, start search in bit buffer */
- if (state->mode != SYNC) {
- state->mode = SYNC;
- state->hold <<= state->bits & 7;
- state->bits -= state->bits & 7;
- len = 0;
- while (state->bits >= 8) {
- buf[len++] = (unsigned char)(state->hold);
- state->hold >>= 8;
- state->bits -= 8;
- }
- state->have = 0;
- syncsearch(&(state->have), buf, len);
- }
-
- /* search available input */
- len = syncsearch(&(state->have), strm->next_in, strm->avail_in);
- strm->avail_in -= len;
- strm->next_in += len;
- strm->total_in += len;
-
- /* return no joy or set up to restart inflate() on a new block */
- if (state->have != 4) return Z_DATA_ERROR;
- in = strm->total_in; out = strm->total_out;
- inflateReset(strm);
- strm->total_in = in; strm->total_out = out;
- state->mode = TYPE;
- return Z_OK;
-}
-
-/*
- Returns true if inflate is currently at the end of a block generated by
- Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
- implementation to provide an additional safety check. PPP uses
- Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored
- block. When decompressing, PPP checks that at the end of input packet,
- inflate is waiting for these length bytes.
- */
-int ZEXPORT inflateSyncPoint(strm)
-z_streamp strm;
-{
- struct inflate_state FAR *state;
-
- if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
- state = (struct inflate_state FAR *)strm->state;
- return state->mode == STORED && state->bits == 0;
-}
-
-int ZEXPORT inflateCopy(dest, source)
-z_streamp dest;
-z_streamp source;
-{
- struct inflate_state FAR *state;
- struct inflate_state FAR *copy;
- unsigned char FAR *window;
- unsigned wsize;
-
- /* check input */
- if (inflateStateCheck(source) || dest == Z_NULL)
- return Z_STREAM_ERROR;
- state = (struct inflate_state FAR *)source->state;
-
- /* allocate space */
- copy = (struct inflate_state FAR *)
- ZALLOC(source, 1, sizeof(struct inflate_state));
- if (copy == Z_NULL) return Z_MEM_ERROR;
- window = Z_NULL;
- if (state->window != Z_NULL) {
- window = (unsigned char FAR *)
- ZALLOC(source, 1U << state->wbits, sizeof(unsigned char));
- if (window == Z_NULL) {
- ZFREE(source, copy);
- return Z_MEM_ERROR;
- }
- }
-
- /* copy state */
- zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream));
- zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state));
- copy->strm = dest;
- if (state->lencode >= state->codes &&
- state->lencode <= state->codes + ENOUGH - 1) {
- copy->lencode = copy->codes + (state->lencode - state->codes);
- copy->distcode = copy->codes + (state->distcode - state->codes);
- }
- copy->next = copy->codes + (state->next - state->codes);
- if (window != Z_NULL) {
- wsize = 1U << state->wbits;
- zmemcpy(window, state->window, wsize);
- }
- copy->window = window;
- dest->state = (struct internal_state FAR *)copy;
- return Z_OK;
-}
-
-int ZEXPORT inflateUndermine(strm, subvert)
-z_streamp strm;
-int subvert;
-{
- struct inflate_state FAR *state;
-
- if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
- state = (struct inflate_state FAR *)strm->state;
-#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
- state->sane = !subvert;
- return Z_OK;
-#else
- (void)subvert;
- state->sane = 1;
- return Z_DATA_ERROR;
-#endif
-}
-
-int ZEXPORT inflateValidate(strm, check)
-z_streamp strm;
-int check;
-{
- struct inflate_state FAR *state;
-
- if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
- state = (struct inflate_state FAR *)strm->state;
- if (check)
- state->wrap |= 4;
- else
- state->wrap &= ~4;
- return Z_OK;
-}
-
-long ZEXPORT inflateMark(strm)
-z_streamp strm;
-{
- struct inflate_state FAR *state;
-
- if (inflateStateCheck(strm))
- return -(1L << 16);
- state = (struct inflate_state FAR *)strm->state;
- return (long)(((unsigned long)((long)state->back)) << 16) +
- (state->mode == COPY ? state->length :
- (state->mode == MATCH ? state->was - state->length : 0));
-}
-
-unsigned long ZEXPORT inflateCodesUsed(strm)
-z_streamp strm;
-{
- struct inflate_state FAR *state;
- if (inflateStateCheck(strm)) return (unsigned long)-1;
- state = (struct inflate_state FAR *)strm->state;
- return (unsigned long)(state->next - state->codes);
-}
+++ /dev/null
-/* inflate.h -- internal inflate state definition
- * Copyright (C) 1995-2016 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-/* WARNING: this file should *not* be used by applications. It is
- part of the implementation of the compression library and is
- subject to change. Applications should only use zlib.h.
- */
-
-/* define NO_GZIP when compiling if you want to disable gzip header and
- trailer decoding by inflate(). NO_GZIP would be used to avoid linking in
- the crc code when it is not needed. For shared libraries, gzip decoding
- should be left enabled. */
-#ifndef NO_GZIP
-# define GUNZIP
-#endif
-
-/* Possible inflate modes between inflate() calls */
-typedef enum {
- HEAD = 16180, /* i: waiting for magic header */
- FLAGS, /* i: waiting for method and flags (gzip) */
- TIME, /* i: waiting for modification time (gzip) */
- OS, /* i: waiting for extra flags and operating system (gzip) */
- EXLEN, /* i: waiting for extra length (gzip) */
- EXTRA, /* i: waiting for extra bytes (gzip) */
- NAME, /* i: waiting for end of file name (gzip) */
- COMMENT, /* i: waiting for end of comment (gzip) */
- HCRC, /* i: waiting for header crc (gzip) */
- DICTID, /* i: waiting for dictionary check value */
- DICT, /* waiting for inflateSetDictionary() call */
- TYPE, /* i: waiting for type bits, including last-flag bit */
- TYPEDO, /* i: same, but skip check to exit inflate on new block */
- STORED, /* i: waiting for stored size (length and complement) */
- COPY_, /* i/o: same as COPY below, but only first time in */
- COPY, /* i/o: waiting for input or output to copy stored block */
- TABLE, /* i: waiting for dynamic block table lengths */
- LENLENS, /* i: waiting for code length code lengths */
- CODELENS, /* i: waiting for length/lit and distance code lengths */
- LEN_, /* i: same as LEN below, but only first time in */
- LEN, /* i: waiting for length/lit/eob code */
- LENEXT, /* i: waiting for length extra bits */
- DIST, /* i: waiting for distance code */
- DISTEXT, /* i: waiting for distance extra bits */
- MATCH, /* o: waiting for output space to copy string */
- LIT, /* o: waiting for output space to write literal */
- CHECK, /* i: waiting for 32-bit check value */
- LENGTH, /* i: waiting for 32-bit length (gzip) */
- DONE, /* finished check, done -- remain here until reset */
- BAD, /* got a data error -- remain here until reset */
- MEM, /* got an inflate() memory error -- remain here until reset */
- SYNC /* looking for synchronization bytes to restart inflate() */
-} inflate_mode;
-
-/*
- State transitions between above modes -
-
- (most modes can go to BAD or MEM on error -- not shown for clarity)
-
- Process header:
- HEAD -> (gzip) or (zlib) or (raw)
- (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT ->
- HCRC -> TYPE
- (zlib) -> DICTID or TYPE
- DICTID -> DICT -> TYPE
- (raw) -> TYPEDO
- Read deflate blocks:
- TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK
- STORED -> COPY_ -> COPY -> TYPE
- TABLE -> LENLENS -> CODELENS -> LEN_
- LEN_ -> LEN
- Read deflate codes in fixed or dynamic block:
- LEN -> LENEXT or LIT or TYPE
- LENEXT -> DIST -> DISTEXT -> MATCH -> LEN
- LIT -> LEN
- Process trailer:
- CHECK -> LENGTH -> DONE
- */
-
-/* State maintained between inflate() calls -- approximately 7K bytes, not
- including the allocated sliding window, which is up to 32K bytes. */
-struct inflate_state {
- z_streamp strm; /* pointer back to this zlib stream */
- inflate_mode mode; /* current inflate mode */
- int last; /* true if processing last block */
- int wrap; /* bit 0 true for zlib, bit 1 true for gzip,
- bit 2 true to validate check value */
- int havedict; /* true if dictionary provided */
- int flags; /* gzip header method and flags (0 if zlib) */
- unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */
- unsigned long check; /* protected copy of check value */
- unsigned long total; /* protected copy of output count */
- gz_headerp head; /* where to save gzip header information */
- /* sliding window */
- unsigned wbits; /* log base 2 of requested window size */
- unsigned wsize; /* window size or zero if not using window */
- unsigned whave; /* valid bytes in the window */
- unsigned wnext; /* window write index */
- unsigned char FAR *window; /* allocated sliding window, if needed */
- /* bit accumulator */
- unsigned long hold; /* input bit accumulator */
- unsigned bits; /* number of bits in "in" */
- /* for string and stored block copying */
- unsigned length; /* literal or length of data to copy */
- unsigned offset; /* distance back to copy string from */
- /* for table and code decoding */
- unsigned extra; /* extra bits needed */
- /* fixed and dynamic code tables */
- code const FAR *lencode; /* starting table for length/literal codes */
- code const FAR *distcode; /* starting table for distance codes */
- unsigned lenbits; /* index bits for lencode */
- unsigned distbits; /* index bits for distcode */
- /* dynamic table building */
- unsigned ncode; /* number of code length code lengths */
- unsigned nlen; /* number of length code lengths */
- unsigned ndist; /* number of distance code lengths */
- unsigned have; /* number of code lengths in lens[] */
- code FAR *next; /* next available space in codes[] */
- unsigned short lens[320]; /* temporary storage for code lengths */
- unsigned short work[288]; /* work area for code table building */
- code codes[ENOUGH]; /* space for code tables */
- int sane; /* if false, allow invalid distance too far */
- int back; /* bits back of last unprocessed length/lit */
- unsigned was; /* initial length of match */
-};
+++ /dev/null
-/* inftrees.c -- generate Huffman trees for efficient decoding
- * Copyright (C) 1995-2017 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-#include "zutil.h"
-#include "inftrees.h"
-
-#define MAXBITS 15
-
-const char inflate_copyright[] =
- " inflate 1.2.11 Copyright 1995-2017 Mark Adler ";
-/*
- If you use the zlib library in a product, an acknowledgment is welcome
- in the documentation of your product. If for some reason you cannot
- include such an acknowledgment, I would appreciate that you keep this
- copyright string in the executable of your product.
- */
-
-/*
- Build a set of tables to decode the provided canonical Huffman code.
- The code lengths are lens[0..codes-1]. The result starts at *table,
- whose indices are 0..2^bits-1. work is a writable array of at least
- lens shorts, which is used as a work area. type is the type of code
- to be generated, CODES, LENS, or DISTS. On return, zero is success,
- -1 is an invalid code, and +1 means that ENOUGH isn't enough. table
- on return points to the next available entry's address. bits is the
- requested root table index bits, and on return it is the actual root
- table index bits. It will differ if the request is greater than the
- longest code or if it is less than the shortest code.
- */
-int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work)
-codetype type;
-unsigned short FAR *lens;
-unsigned codes;
-code FAR * FAR *table;
-unsigned FAR *bits;
-unsigned short FAR *work;
-{
- unsigned len; /* a code's length in bits */
- unsigned sym; /* index of code symbols */
- unsigned min, max; /* minimum and maximum code lengths */
- unsigned root; /* number of index bits for root table */
- unsigned curr; /* number of index bits for current table */
- unsigned drop; /* code bits to drop for sub-table */
- int left; /* number of prefix codes available */
- unsigned used; /* code entries in table used */
- unsigned huff; /* Huffman code */
- unsigned incr; /* for incrementing code, index */
- unsigned fill; /* index for replicating entries */
- unsigned low; /* low bits for current root entry */
- unsigned mask; /* mask for low root bits */
- code here; /* table entry for duplication */
- code FAR *next; /* next available space in table */
- const unsigned short FAR *base; /* base value table to use */
- const unsigned short FAR *extra; /* extra bits table to use */
- unsigned match; /* use base and extra for symbol >= match */
- unsigned short count[MAXBITS+1]; /* number of codes of each length */
- unsigned short offs[MAXBITS+1]; /* offsets in table for each length */
- static const unsigned short lbase[31] = { /* Length codes 257..285 base */
- 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
- 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
- static const unsigned short lext[31] = { /* Length codes 257..285 extra */
- 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
- 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 77, 202};
- static const unsigned short dbase[32] = { /* Distance codes 0..29 base */
- 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
- 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
- 8193, 12289, 16385, 24577, 0, 0};
- static const unsigned short dext[32] = { /* Distance codes 0..29 extra */
- 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
- 23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
- 28, 28, 29, 29, 64, 64};
-
- /*
- Process a set of code lengths to create a canonical Huffman code. The
- code lengths are lens[0..codes-1]. Each length corresponds to the
- symbols 0..codes-1. The Huffman code is generated by first sorting the
- symbols by length from short to long, and retaining the symbol order
- for codes with equal lengths. Then the code starts with all zero bits
- for the first code of the shortest length, and the codes are integer
- increments for the same length, and zeros are appended as the length
- increases. For the deflate format, these bits are stored backwards
- from their more natural integer increment ordering, and so when the
- decoding tables are built in the large loop below, the integer codes
- are incremented backwards.
-
- This routine assumes, but does not check, that all of the entries in
- lens[] are in the range 0..MAXBITS. The caller must assure this.
- 1..MAXBITS is interpreted as that code length. zero means that that
- symbol does not occur in this code.
-
- The codes are sorted by computing a count of codes for each length,
- creating from that a table of starting indices for each length in the
- sorted table, and then entering the symbols in order in the sorted
- table. The sorted table is work[], with that space being provided by
- the caller.
-
- The length counts are used for other purposes as well, i.e. finding
- the minimum and maximum length codes, determining if there are any
- codes at all, checking for a valid set of lengths, and looking ahead
- at length counts to determine sub-table sizes when building the
- decoding tables.
- */
-
- /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
- for (len = 0; len <= MAXBITS; len++)
- count[len] = 0;
- for (sym = 0; sym < codes; sym++)
- count[lens[sym]]++;
-
- /* bound code lengths, force root to be within code lengths */
- root = *bits;
- for (max = MAXBITS; max >= 1; max--)
- if (count[max] != 0) break;
- if (root > max) root = max;
- if (max == 0) { /* no symbols to code at all */
- here.op = (unsigned char)64; /* invalid code marker */
- here.bits = (unsigned char)1;
- here.val = (unsigned short)0;
- *(*table)++ = here; /* make a table to force an error */
- *(*table)++ = here;
- *bits = 1;
- return 0; /* no symbols, but wait for decoding to report error */
- }
- for (min = 1; min < max; min++)
- if (count[min] != 0) break;
- if (root < min) root = min;
-
- /* check for an over-subscribed or incomplete set of lengths */
- left = 1;
- for (len = 1; len <= MAXBITS; len++) {
- left <<= 1;
- left -= count[len];
- if (left < 0) return -1; /* over-subscribed */
- }
- if (left > 0 && (type == CODES || max != 1))
- return -1; /* incomplete set */
-
- /* generate offsets into symbol table for each length for sorting */
- offs[1] = 0;
- for (len = 1; len < MAXBITS; len++)
- offs[len + 1] = offs[len] + count[len];
-
- /* sort symbols by length, by symbol order within each length */
- for (sym = 0; sym < codes; sym++)
- if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym;
-
- /*
- Create and fill in decoding tables. In this loop, the table being
- filled is at next and has curr index bits. The code being used is huff
- with length len. That code is converted to an index by dropping drop
- bits off of the bottom. For codes where len is less than drop + curr,
- those top drop + curr - len bits are incremented through all values to
- fill the table with replicated entries.
-
- root is the number of index bits for the root table. When len exceeds
- root, sub-tables are created pointed to by the root entry with an index
- of the low root bits of huff. This is saved in low to check for when a
- new sub-table should be started. drop is zero when the root table is
- being filled, and drop is root when sub-tables are being filled.
-
- When a new sub-table is needed, it is necessary to look ahead in the
- code lengths to determine what size sub-table is needed. The length
- counts are used for this, and so count[] is decremented as codes are
- entered in the tables.
-
- used keeps track of how many table entries have been allocated from the
- provided *table space. It is checked for LENS and DIST tables against
- the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in
- the initial root table size constants. See the comments in inftrees.h
- for more information.
-
- sym increments through all symbols, and the loop terminates when
- all codes of length max, i.e. all codes, have been processed. This
- routine permits incomplete codes, so another loop after this one fills
- in the rest of the decoding tables with invalid code markers.
- */
-
- /* set up for code type */
- switch (type) {
- case CODES:
- base = extra = work; /* dummy value--not used */
- match = 20;
- break;
- case LENS:
- base = lbase;
- extra = lext;
- match = 257;
- break;
- default: /* DISTS */
- base = dbase;
- extra = dext;
- match = 0;
- }
-
- /* initialize state for loop */
- huff = 0; /* starting code */
- sym = 0; /* starting code symbol */
- len = min; /* starting code length */
- next = *table; /* current table to fill in */
- curr = root; /* current table index bits */
- drop = 0; /* current bits to drop from code for index */
- low = (unsigned)(-1); /* trigger new sub-table when len > root */
- used = 1U << root; /* use root table entries */
- mask = used - 1; /* mask for comparing low */
-
- /* check available table space */
- if ((type == LENS && used > ENOUGH_LENS) ||
- (type == DISTS && used > ENOUGH_DISTS))
- return 1;
-
- /* process all codes and make table entries */
- for (;;) {
- /* create table entry */
- here.bits = (unsigned char)(len - drop);
- if (work[sym] + 1U < match) {
- here.op = (unsigned char)0;
- here.val = work[sym];
- }
- else if (work[sym] >= match) {
- here.op = (unsigned char)(extra[work[sym] - match]);
- here.val = base[work[sym] - match];
- }
- else {
- here.op = (unsigned char)(32 + 64); /* end of block */
- here.val = 0;
- }
-
- /* replicate for those indices with low len bits equal to huff */
- incr = 1U << (len - drop);
- fill = 1U << curr;
- min = fill; /* save offset to next table */
- do {
- fill -= incr;
- next[(huff >> drop) + fill] = here;
- } while (fill != 0);
-
- /* backwards increment the len-bit code huff */
- incr = 1U << (len - 1);
- while (huff & incr)
- incr >>= 1;
- if (incr != 0) {
- huff &= incr - 1;
- huff += incr;
- }
- else
- huff = 0;
-
- /* go to next symbol, update count, len */
- sym++;
- if (--(count[len]) == 0) {
- if (len == max) break;
- len = lens[work[sym]];
- }
-
- /* create new sub-table if needed */
- if (len > root && (huff & mask) != low) {
- /* if first time, transition to sub-tables */
- if (drop == 0)
- drop = root;
-
- /* increment past last table */
- next += min; /* here min is 1 << curr */
-
- /* determine length of next table */
- curr = len - drop;
- left = (int)(1 << curr);
- while (curr + drop < max) {
- left -= count[curr + drop];
- if (left <= 0) break;
- curr++;
- left <<= 1;
- }
-
- /* check for enough space */
- used += 1U << curr;
- if ((type == LENS && used > ENOUGH_LENS) ||
- (type == DISTS && used > ENOUGH_DISTS))
- return 1;
-
- /* point entry in root table to sub-table */
- low = huff & mask;
- (*table)[low].op = (unsigned char)curr;
- (*table)[low].bits = (unsigned char)root;
- (*table)[low].val = (unsigned short)(next - *table);
- }
- }
-
- /* fill in remaining table entry if code is incomplete (guaranteed to have
- at most one remaining entry, since if the code is incomplete, the
- maximum code length that was allowed to get this far is one bit) */
- if (huff != 0) {
- here.op = (unsigned char)64; /* invalid code marker */
- here.bits = (unsigned char)(len - drop);
- here.val = (unsigned short)0;
- next[huff] = here;
- }
-
- /* set return parameters */
- *table += used;
- *bits = root;
- return 0;
-}
+++ /dev/null
-/* inftrees.h -- header to use inftrees.c
- * Copyright (C) 1995-2005, 2010 Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-/* WARNING: this file should *not* be used by applications. It is
- part of the implementation of the compression library and is
- subject to change. Applications should only use zlib.h.
- */
-
-/* Structure for decoding tables. Each entry provides either the
- information needed to do the operation requested by the code that
- indexed that table entry, or it provides a pointer to another
- table that indexes more bits of the code. op indicates whether
- the entry is a pointer to another table, a literal, a length or
- distance, an end-of-block, or an invalid code. For a table
- pointer, the low four bits of op is the number of index bits of
- that table. For a length or distance, the low four bits of op
- is the number of extra bits to get after the code. bits is
- the number of bits in this code or part of the code to drop off
- of the bit buffer. val is the actual byte to output in the case
- of a literal, the base length or distance, or the offset from
- the current table to the next table. Each entry is four bytes. */
-typedef struct {
- unsigned char op; /* operation, extra bits, table bits */
- unsigned char bits; /* bits in this part of the code */
- unsigned short val; /* offset in table or code value */
-} code;
-
-/* op values as set by inflate_table():
- 00000000 - literal
- 0000tttt - table link, tttt != 0 is the number of table index bits
- 0001eeee - length or distance, eeee is the number of extra bits
- 01100000 - end of block
- 01000000 - invalid code
- */
-
-/* Maximum size of the dynamic table. The maximum number of code structures is
- 1444, which is the sum of 852 for literal/length codes and 592 for distance
- codes. These values were found by exhaustive searches using the program
- examples/enough.c found in the zlib distribtution. The arguments to that
- program are the number of symbols, the initial root table size, and the
- maximum bit length of a code. "enough 286 9 15" for literal/length codes
- returns returns 852, and "enough 30 6 15" for distance codes returns 592.
- The initial root table size (9 or 6) is found in the fifth argument of the
- inflate_table() calls in inflate.c and infback.c. If the root table size is
- changed, then these maximum sizes would be need to be recalculated and
- updated. */
-#define ENOUGH_LENS 852
-#define ENOUGH_DISTS 592
-#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS)
-
-/* Type of code to build for inflate_table() */
-typedef enum {
- CODES,
- LENS,
- DISTS
-} codetype;
-
-int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens,
- unsigned codes, code FAR * FAR *table,
- unsigned FAR *bits, unsigned short FAR *work));
+++ /dev/null
-/* trees.c -- output deflated data using Huffman coding
- * Copyright (C) 1995-2017 Jean-loup Gailly
- * detect_data_type() function provided freely by Cosmin Truta, 2006
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-/*
- * ALGORITHM
- *
- * The "deflation" process uses several Huffman trees. The more
- * common source values are represented by shorter bit sequences.
- *
- * Each code tree is stored in a compressed form which is itself
- * a Huffman encoding of the lengths of all the code strings (in
- * ascending order by source values). The actual code strings are
- * reconstructed from the lengths in the inflate process, as described
- * in the deflate specification.
- *
- * REFERENCES
- *
- * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification".
- * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc
- *
- * Storer, James A.
- * Data Compression: Methods and Theory, pp. 49-50.
- * Computer Science Press, 1988. ISBN 0-7167-8156-5.
- *
- * Sedgewick, R.
- * Algorithms, p290.
- * Addison-Wesley, 1983. ISBN 0-201-06672-6.
- */
-
-/* @(#) $Id$ */
-
-/* #define GEN_TREES_H */
-
-#include "deflate.h"
-
-#ifdef ZLIB_DEBUG
-# include <ctype.h>
-#endif
-
-/* ===========================================================================
- * Constants
- */
-
-#define MAX_BL_BITS 7
-/* Bit length codes must not exceed MAX_BL_BITS bits */
-
-#define END_BLOCK 256
-/* end of block literal code */
-
-#define REP_3_6 16
-/* repeat previous bit length 3-6 times (2 bits of repeat count) */
-
-#define REPZ_3_10 17
-/* repeat a zero length 3-10 times (3 bits of repeat count) */
-
-#define REPZ_11_138 18
-/* repeat a zero length 11-138 times (7 bits of repeat count) */
-
-local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */
- = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};
-
-local const int extra_dbits[D_CODES] /* extra bits for each distance code */
- = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
-
-local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */
- = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
-
-local const uch bl_order[BL_CODES]
- = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
-/* The lengths of the bit length codes are sent in order of decreasing
- * probability, to avoid transmitting the lengths for unused bit length codes.
- */
-
-/* ===========================================================================
- * Local data. These are initialized only once.
- */
-
-#define DIST_CODE_LEN 512 /* see definition of array dist_code below */
-
-#if defined(GEN_TREES_H) || !defined(STDC)
-/* non ANSI compilers may not accept trees.h */
-
-local ct_data static_ltree[L_CODES+2];
-/* The static literal tree. Since the bit lengths are imposed, there is no
- * need for the L_CODES extra codes used during heap construction. However
- * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
- * below).
- */
-
-local ct_data static_dtree[D_CODES];
-/* The static distance tree. (Actually a trivial tree since all codes use
- * 5 bits.)
- */
-
-uch _dist_code[DIST_CODE_LEN];
-/* Distance codes. The first 256 values correspond to the distances
- * 3 .. 258, the last 256 values correspond to the top 8 bits of
- * the 15 bit distances.
- */
-
-uch _length_code[MAX_MATCH-MIN_MATCH+1];
-/* length code for each normalized match length (0 == MIN_MATCH) */
-
-local int base_length[LENGTH_CODES];
-/* First normalized length for each code (0 = MIN_MATCH) */
-
-local int base_dist[D_CODES];
-/* First normalized distance for each code (0 = distance of 1) */
-
-#else
-# include "trees.h"
-#endif /* GEN_TREES_H */
-
-struct static_tree_desc_s {
- const ct_data *static_tree; /* static tree or NULL */
- const intf *extra_bits; /* extra bits for each code or NULL */
- int extra_base; /* base index for extra_bits */
- int elems; /* max number of elements in the tree */
- int max_length; /* max bit length for the codes */
-};
-
-local const static_tree_desc static_l_desc =
-{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};
-
-local const static_tree_desc static_d_desc =
-{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS};
-
-local const static_tree_desc static_bl_desc =
-{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS};
-
-/* ===========================================================================
- * Local (static) routines in this file.
- */
-
-local void tr_static_init OF((void));
-local void init_block OF((deflate_state *s));
-local void pqdownheap OF((deflate_state *s, ct_data *tree, int k));
-local void gen_bitlen OF((deflate_state *s, tree_desc *desc));
-local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count));
-local void build_tree OF((deflate_state *s, tree_desc *desc));
-local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code));
-local void send_tree OF((deflate_state *s, ct_data *tree, int max_code));
-local int build_bl_tree OF((deflate_state *s));
-local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
- int blcodes));
-local void compress_block OF((deflate_state *s, const ct_data *ltree,
- const ct_data *dtree));
-local int detect_data_type OF((deflate_state *s));
-local unsigned bi_reverse OF((unsigned value, int length));
-local void bi_windup OF((deflate_state *s));
-local void bi_flush OF((deflate_state *s));
-
-#ifdef GEN_TREES_H
-local void gen_trees_header OF((void));
-#endif
-
-#ifndef ZLIB_DEBUG
-# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len)
- /* Send a code of the given tree. c and tree must not have side effects */
-
-#else /* !ZLIB_DEBUG */
-# define send_code(s, c, tree) \
- { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \
- send_bits(s, tree[c].Code, tree[c].Len); }
-#endif
-
-/* ===========================================================================
- * Output a short LSB first on the stream.
- * IN assertion: there is enough room in pendingBuf.
- */
-#define put_short(s, w) { \
- put_byte(s, (uch)((w) & 0xff)); \
- put_byte(s, (uch)((ush)(w) >> 8)); \
-}
-
-/* ===========================================================================
- * Send a value on a given number of bits.
- * IN assertion: length <= 16 and value fits in length bits.
- */
-#ifdef ZLIB_DEBUG
-local void send_bits OF((deflate_state *s, int value, int length));
-
-local void send_bits(s, value, length)
- deflate_state *s;
- int value; /* value to send */
- int length; /* number of bits */
-{
- Tracevv((stderr," l %2d v %4x ", length, value));
- Assert(length > 0 && length <= 15, "invalid length");
- s->bits_sent += (ulg)length;
-
- /* If not enough room in bi_buf, use (valid) bits from bi_buf and
- * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
- * unused bits in value.
- */
- if (s->bi_valid > (int)Buf_size - length) {
- s->bi_buf |= (ush)value << s->bi_valid;
- put_short(s, s->bi_buf);
- s->bi_buf = (ush)value >> (Buf_size - s->bi_valid);
- s->bi_valid += length - Buf_size;
- } else {
- s->bi_buf |= (ush)value << s->bi_valid;
- s->bi_valid += length;
- }
-}
-#else /* !ZLIB_DEBUG */
-
-#define send_bits(s, value, length) \
-{ int len = length;\
- if (s->bi_valid > (int)Buf_size - len) {\
- int val = (int)value;\
- s->bi_buf |= (ush)val << s->bi_valid;\
- put_short(s, s->bi_buf);\
- s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\
- s->bi_valid += len - Buf_size;\
- } else {\
- s->bi_buf |= (ush)(value) << s->bi_valid;\
- s->bi_valid += len;\
- }\
-}
-#endif /* ZLIB_DEBUG */
-
-
-/* the arguments must not have side effects */
-
-/* ===========================================================================
- * Initialize the various 'constant' tables.
- */
-local void tr_static_init()
-{
-#if defined(GEN_TREES_H) || !defined(STDC)
- static int static_init_done = 0;
- int n; /* iterates over tree elements */
- int bits; /* bit counter */
- int length; /* length value */
- int code; /* code value */
- int dist; /* distance index */
- ush bl_count[MAX_BITS+1];
- /* number of codes at each bit length for an optimal tree */
-
- if (static_init_done) return;
-
- /* For some embedded targets, global variables are not initialized: */
-#ifdef NO_INIT_GLOBAL_POINTERS
- static_l_desc.static_tree = static_ltree;
- static_l_desc.extra_bits = extra_lbits;
- static_d_desc.static_tree = static_dtree;
- static_d_desc.extra_bits = extra_dbits;
- static_bl_desc.extra_bits = extra_blbits;
-#endif
-
- /* Initialize the mapping length (0..255) -> length code (0..28) */
- length = 0;
- for (code = 0; code < LENGTH_CODES-1; code++) {
- base_length[code] = length;
- for (n = 0; n < (1<<extra_lbits[code]); n++) {
- _length_code[length++] = (uch)code;
- }
- }
- Assert (length == 256, "tr_static_init: length != 256");
- /* Note that the length 255 (match length 258) can be represented
- * in two different ways: code 284 + 5 bits or code 285, so we
- * overwrite length_code[255] to use the best encoding:
- */
- _length_code[length-1] = (uch)code;
-
- /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
- dist = 0;
- for (code = 0 ; code < 16; code++) {
- base_dist[code] = dist;
- for (n = 0; n < (1<<extra_dbits[code]); n++) {
- _dist_code[dist++] = (uch)code;
- }
- }
- Assert (dist == 256, "tr_static_init: dist != 256");
- dist >>= 7; /* from now on, all distances are divided by 128 */
- for ( ; code < D_CODES; code++) {
- base_dist[code] = dist << 7;
- for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
- _dist_code[256 + dist++] = (uch)code;
- }
- }
- Assert (dist == 256, "tr_static_init: 256+dist != 512");
-
- /* Construct the codes of the static literal tree */
- for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;
- n = 0;
- while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++;
- while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++;
- while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++;
- while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++;
- /* Codes 286 and 287 do not exist, but we must include them in the
- * tree construction to get a canonical Huffman tree (longest code
- * all ones)
- */
- gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count);
-
- /* The static distance tree is trivial: */
- for (n = 0; n < D_CODES; n++) {
- static_dtree[n].Len = 5;
- static_dtree[n].Code = bi_reverse((unsigned)n, 5);
- }
- static_init_done = 1;
-
-# ifdef GEN_TREES_H
- gen_trees_header();
-# endif
-#endif /* defined(GEN_TREES_H) || !defined(STDC) */
-}
-
-/* ===========================================================================
- * Genererate the file trees.h describing the static trees.
- */
-#ifdef GEN_TREES_H
-# ifndef ZLIB_DEBUG
-# include <stdio.h>
-# endif
-
-# define SEPARATOR(i, last, width) \
- ((i) == (last)? "\n};\n\n" : \
- ((i) % (width) == (width)-1 ? ",\n" : ", "))
-
-void gen_trees_header()
-{
- FILE *header = fopen("trees.h", "w");
- int i;
-
- Assert (header != NULL, "Can't open trees.h");
- fprintf(header,
- "/* header created automatically with -DGEN_TREES_H */\n\n");
-
- fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n");
- for (i = 0; i < L_CODES+2; i++) {
- fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code,
- static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5));
- }
-
- fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n");
- for (i = 0; i < D_CODES; i++) {
- fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code,
- static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5));
- }
-
- fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n");
- for (i = 0; i < DIST_CODE_LEN; i++) {
- fprintf(header, "%2u%s", _dist_code[i],
- SEPARATOR(i, DIST_CODE_LEN-1, 20));
- }
-
- fprintf(header,
- "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n");
- for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) {
- fprintf(header, "%2u%s", _length_code[i],
- SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20));
- }
-
- fprintf(header, "local const int base_length[LENGTH_CODES] = {\n");
- for (i = 0; i < LENGTH_CODES; i++) {
- fprintf(header, "%1u%s", base_length[i],
- SEPARATOR(i, LENGTH_CODES-1, 20));
- }
-
- fprintf(header, "local const int base_dist[D_CODES] = {\n");
- for (i = 0; i < D_CODES; i++) {
- fprintf(header, "%5u%s", base_dist[i],
- SEPARATOR(i, D_CODES-1, 10));
- }
-
- fclose(header);
-}
-#endif /* GEN_TREES_H */
-
-/* ===========================================================================
- * Initialize the tree data structures for a new zlib stream.
- */
-void ZLIB_INTERNAL _tr_init(s)
- deflate_state *s;
-{
- tr_static_init();
-
- s->l_desc.dyn_tree = s->dyn_ltree;
- s->l_desc.stat_desc = &static_l_desc;
-
- s->d_desc.dyn_tree = s->dyn_dtree;
- s->d_desc.stat_desc = &static_d_desc;
-
- s->bl_desc.dyn_tree = s->bl_tree;
- s->bl_desc.stat_desc = &static_bl_desc;
-
- s->bi_buf = 0;
- s->bi_valid = 0;
-#ifdef ZLIB_DEBUG
- s->compressed_len = 0L;
- s->bits_sent = 0L;
-#endif
-
- /* Initialize the first block of the first file: */
- init_block(s);
-}
-
-/* ===========================================================================
- * Initialize a new block.
- */
-local void init_block(s)
- deflate_state *s;
-{
- int n; /* iterates over tree elements */
-
- /* Initialize the trees. */
- for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0;
- for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0;
- for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
-
- s->dyn_ltree[END_BLOCK].Freq = 1;
- s->opt_len = s->static_len = 0L;
- s->last_lit = s->matches = 0;
-}
-
-#define SMALLEST 1
-/* Index within the heap array of least frequent node in the Huffman tree */
-
-
-/* ===========================================================================
- * Remove the smallest element from the heap and recreate the heap with
- * one less element. Updates heap and heap_len.
- */
-#define pqremove(s, tree, top) \
-{\
- top = s->heap[SMALLEST]; \
- s->heap[SMALLEST] = s->heap[s->heap_len--]; \
- pqdownheap(s, tree, SMALLEST); \
-}
-
-/* ===========================================================================
- * Compares to subtrees, using the tree depth as tie breaker when
- * the subtrees have equal frequency. This minimizes the worst case length.
- */
-#define smaller(tree, n, m, depth) \
- (tree[n].Freq < tree[m].Freq || \
- (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
-
-/* ===========================================================================
- * Restore the heap property by moving down the tree starting at node k,
- * exchanging a node with the smallest of its two sons if necessary, stopping
- * when the heap property is re-established (each father smaller than its
- * two sons).
- */
-local void pqdownheap(s, tree, k)
- deflate_state *s;
- ct_data *tree; /* the tree to restore */
- int k; /* node to move down */
-{
- int v = s->heap[k];
- int j = k << 1; /* left son of k */
- while (j <= s->heap_len) {
- /* Set j to the smallest of the two sons: */
- if (j < s->heap_len &&
- smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {
- j++;
- }
- /* Exit if v is smaller than both sons */
- if (smaller(tree, v, s->heap[j], s->depth)) break;
-
- /* Exchange v with the smallest son */
- s->heap[k] = s->heap[j]; k = j;
-
- /* And continue down the tree, setting j to the left son of k */
- j <<= 1;
- }
- s->heap[k] = v;
-}
-
-/* ===========================================================================
- * Compute the optimal bit lengths for a tree and update the total bit length
- * for the current block.
- * IN assertion: the fields freq and dad are set, heap[heap_max] and
- * above are the tree nodes sorted by increasing frequency.
- * OUT assertions: the field len is set to the optimal bit length, the
- * array bl_count contains the frequencies for each bit length.
- * The length opt_len is updated; static_len is also updated if stree is
- * not null.
- */
-local void gen_bitlen(s, desc)
- deflate_state *s;
- tree_desc *desc; /* the tree descriptor */
-{
- ct_data *tree = desc->dyn_tree;
- int max_code = desc->max_code;
- const ct_data *stree = desc->stat_desc->static_tree;
- const intf *extra = desc->stat_desc->extra_bits;
- int base = desc->stat_desc->extra_base;
- int max_length = desc->stat_desc->max_length;
- int h; /* heap index */
- int n, m; /* iterate over the tree elements */
- int bits; /* bit length */
- int xbits; /* extra bits */
- ush f; /* frequency */
- int overflow = 0; /* number of elements with bit length too large */
-
- for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0;
-
- /* In a first pass, compute the optimal bit lengths (which may
- * overflow in the case of the bit length tree).
- */
- tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */
-
- for (h = s->heap_max+1; h < HEAP_SIZE; h++) {
- n = s->heap[h];
- bits = tree[tree[n].Dad].Len + 1;
- if (bits > max_length) bits = max_length, overflow++;
- tree[n].Len = (ush)bits;
- /* We overwrite tree[n].Dad which is no longer needed */
-
- if (n > max_code) continue; /* not a leaf node */
-
- s->bl_count[bits]++;
- xbits = 0;
- if (n >= base) xbits = extra[n-base];
- f = tree[n].Freq;
- s->opt_len += (ulg)f * (unsigned)(bits + xbits);
- if (stree) s->static_len += (ulg)f * (unsigned)(stree[n].Len + xbits);
- }
- if (overflow == 0) return;
-
- Tracev((stderr,"\nbit length overflow\n"));
- /* This happens for example on obj2 and pic of the Calgary corpus */
-
- /* Find the first bit length which could increase: */
- do {
- bits = max_length-1;
- while (s->bl_count[bits] == 0) bits--;
- s->bl_count[bits]--; /* move one leaf down the tree */
- s->bl_count[bits+1] += 2; /* move one overflow item as its brother */
- s->bl_count[max_length]--;
- /* The brother of the overflow item also moves one step up,
- * but this does not affect bl_count[max_length]
- */
- overflow -= 2;
- } while (overflow > 0);
-
- /* Now recompute all bit lengths, scanning in increasing frequency.
- * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
- * lengths instead of fixing only the wrong ones. This idea is taken
- * from 'ar' written by Haruhiko Okumura.)
- */
- for (bits = max_length; bits != 0; bits--) {
- n = s->bl_count[bits];
- while (n != 0) {
- m = s->heap[--h];
- if (m > max_code) continue;
- if ((unsigned) tree[m].Len != (unsigned) bits) {
- Tracev((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
- s->opt_len += ((ulg)bits - tree[m].Len) * tree[m].Freq;
- tree[m].Len = (ush)bits;
- }
- n--;
- }
- }
-}
-
-/* ===========================================================================
- * Generate the codes for a given tree and bit counts (which need not be
- * optimal).
- * IN assertion: the array bl_count contains the bit length statistics for
- * the given tree and the field len is set for all tree elements.
- * OUT assertion: the field code is set for all tree elements of non
- * zero code length.
- */
-local void gen_codes (tree, max_code, bl_count)
- ct_data *tree; /* the tree to decorate */
- int max_code; /* largest code with non zero frequency */
- ushf *bl_count; /* number of codes at each bit length */
-{
- ush next_code[MAX_BITS+1]; /* next code value for each bit length */
- unsigned code = 0; /* running code value */
- int bits; /* bit index */
- int n; /* code index */
-
- /* The distribution counts are first used to generate the code values
- * without bit reversal.
- */
- for (bits = 1; bits <= MAX_BITS; bits++) {
- code = (code + bl_count[bits-1]) << 1;
- next_code[bits] = (ush)code;
- }
- /* Check that the bit counts in bl_count are consistent. The last code
- * must be all ones.
- */
- Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
- "inconsistent bit counts");
- Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
-
- for (n = 0; n <= max_code; n++) {
- int len = tree[n].Len;
- if (len == 0) continue;
- /* Now reverse the bits */
- tree[n].Code = (ush)bi_reverse(next_code[len]++, len);
-
- Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
- n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
- }
-}
-
-/* ===========================================================================
- * Construct one Huffman tree and assigns the code bit strings and lengths.
- * Update the total bit length for the current block.
- * IN assertion: the field freq is set for all tree elements.
- * OUT assertions: the fields len and code are set to the optimal bit length
- * and corresponding code. The length opt_len is updated; static_len is
- * also updated if stree is not null. The field max_code is set.
- */
-local void build_tree(s, desc)
- deflate_state *s;
- tree_desc *desc; /* the tree descriptor */
-{
- ct_data *tree = desc->dyn_tree;
- const ct_data *stree = desc->stat_desc->static_tree;
- int elems = desc->stat_desc->elems;
- int n, m; /* iterate over heap elements */
- int max_code = -1; /* largest code with non zero frequency */
- int node; /* new node being created */
-
- /* Construct the initial heap, with least frequent element in
- * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
- * heap[0] is not used.
- */
- s->heap_len = 0, s->heap_max = HEAP_SIZE;
-
- for (n = 0; n < elems; n++) {
- if (tree[n].Freq != 0) {
- s->heap[++(s->heap_len)] = max_code = n;
- s->depth[n] = 0;
- } else {
- tree[n].Len = 0;
- }
- }
-
- /* The pkzip format requires that at least one distance code exists,
- * and that at least one bit should be sent even if there is only one
- * possible code. So to avoid special checks later on we force at least
- * two codes of non zero frequency.
- */
- while (s->heap_len < 2) {
- node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);
- tree[node].Freq = 1;
- s->depth[node] = 0;
- s->opt_len--; if (stree) s->static_len -= stree[node].Len;
- /* node is 0 or 1 so it does not have extra bits */
- }
- desc->max_code = max_code;
-
- /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
- * establish sub-heaps of increasing lengths:
- */
- for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);
-
- /* Construct the Huffman tree by repeatedly combining the least two
- * frequent nodes.
- */
- node = elems; /* next internal node of the tree */
- do {
- pqremove(s, tree, n); /* n = node of least frequency */
- m = s->heap[SMALLEST]; /* m = node of next least frequency */
-
- s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */
- s->heap[--(s->heap_max)] = m;
-
- /* Create a new node father of n and m */
- tree[node].Freq = tree[n].Freq + tree[m].Freq;
- s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ?
- s->depth[n] : s->depth[m]) + 1);
- tree[n].Dad = tree[m].Dad = (ush)node;
-#ifdef DUMP_BL_TREE
- if (tree == s->bl_tree) {
- fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)",
- node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
- }
-#endif
- /* and insert the new node in the heap */
- s->heap[SMALLEST] = node++;
- pqdownheap(s, tree, SMALLEST);
-
- } while (s->heap_len >= 2);
-
- s->heap[--(s->heap_max)] = s->heap[SMALLEST];
-
- /* At this point, the fields freq and dad are set. We can now
- * generate the bit lengths.
- */
- gen_bitlen(s, (tree_desc *)desc);
-
- /* The field len is now set, we can generate the bit codes */
- gen_codes ((ct_data *)tree, max_code, s->bl_count);
-}
-
-/* ===========================================================================
- * Scan a literal or distance tree to determine the frequencies of the codes
- * in the bit length tree.
- */
-local void scan_tree (s, tree, max_code)
- deflate_state *s;
- ct_data *tree; /* the tree to be scanned */
- int max_code; /* and its largest code of non zero frequency */
-{
- int n; /* iterates over all tree elements */
- int prevlen = -1; /* last emitted length */
- int curlen; /* length of current code */
- int nextlen = tree[0].Len; /* length of next code */
- int count = 0; /* repeat count of the current code */
- int max_count = 7; /* max repeat count */
- int min_count = 4; /* min repeat count */
-
- if (nextlen == 0) max_count = 138, min_count = 3;
- tree[max_code+1].Len = (ush)0xffff; /* guard */
-
- for (n = 0; n <= max_code; n++) {
- curlen = nextlen; nextlen = tree[n+1].Len;
- if (++count < max_count && curlen == nextlen) {
- continue;
- } else if (count < min_count) {
- s->bl_tree[curlen].Freq += count;
- } else if (curlen != 0) {
- if (curlen != prevlen) s->bl_tree[curlen].Freq++;
- s->bl_tree[REP_3_6].Freq++;
- } else if (count <= 10) {
- s->bl_tree[REPZ_3_10].Freq++;
- } else {
- s->bl_tree[REPZ_11_138].Freq++;
- }
- count = 0; prevlen = curlen;
- if (nextlen == 0) {
- max_count = 138, min_count = 3;
- } else if (curlen == nextlen) {
- max_count = 6, min_count = 3;
- } else {
- max_count = 7, min_count = 4;
- }
- }
-}
-
-/* ===========================================================================
- * Send a literal or distance tree in compressed form, using the codes in
- * bl_tree.
- */
-local void send_tree (s, tree, max_code)
- deflate_state *s;
- ct_data *tree; /* the tree to be scanned */
- int max_code; /* and its largest code of non zero frequency */
-{
- int n; /* iterates over all tree elements */
- int prevlen = -1; /* last emitted length */
- int curlen; /* length of current code */
- int nextlen = tree[0].Len; /* length of next code */
- int count = 0; /* repeat count of the current code */
- int max_count = 7; /* max repeat count */
- int min_count = 4; /* min repeat count */
-
- /* tree[max_code+1].Len = -1; */ /* guard already set */
- if (nextlen == 0) max_count = 138, min_count = 3;
-
- for (n = 0; n <= max_code; n++) {
- curlen = nextlen; nextlen = tree[n+1].Len;
- if (++count < max_count && curlen == nextlen) {
- continue;
- } else if (count < min_count) {
- do { send_code(s, curlen, s->bl_tree); } while (--count != 0);
-
- } else if (curlen != 0) {
- if (curlen != prevlen) {
- send_code(s, curlen, s->bl_tree); count--;
- }
- Assert(count >= 3 && count <= 6, " 3_6?");
- send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);
-
- } else if (count <= 10) {
- send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);
-
- } else {
- send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);
- }
- count = 0; prevlen = curlen;
- if (nextlen == 0) {
- max_count = 138, min_count = 3;
- } else if (curlen == nextlen) {
- max_count = 6, min_count = 3;
- } else {
- max_count = 7, min_count = 4;
- }
- }
-}
-
-/* ===========================================================================
- * Construct the Huffman tree for the bit lengths and return the index in
- * bl_order of the last bit length code to send.
- */
-local int build_bl_tree(s)
- deflate_state *s;
-{
- int max_blindex; /* index of last bit length code of non zero freq */
-
- /* Determine the bit length frequencies for literal and distance trees */
- scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code);
- scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code);
-
- /* Build the bit length tree: */
- build_tree(s, (tree_desc *)(&(s->bl_desc)));
- /* opt_len now includes the length of the tree representations, except
- * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
- */
-
- /* Determine the number of bit length codes to send. The pkzip format
- * requires that at least 4 bit length codes be sent. (appnote.txt says
- * 3 but the actual value used is 4.)
- */
- for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
- if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;
- }
- /* Update opt_len to include the bit length tree and counts */
- s->opt_len += 3*((ulg)max_blindex+1) + 5+5+4;
- Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
- s->opt_len, s->static_len));
-
- return max_blindex;
-}
-
-/* ===========================================================================
- * Send the header for a block using dynamic Huffman trees: the counts, the
- * lengths of the bit length codes, the literal tree and the distance tree.
- * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
- */
-local void send_all_trees(s, lcodes, dcodes, blcodes)
- deflate_state *s;
- int lcodes, dcodes, blcodes; /* number of codes for each tree */
-{
- int rank; /* index in bl_order */
-
- Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
- Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
- "too many codes");
- Tracev((stderr, "\nbl counts: "));
- send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
- send_bits(s, dcodes-1, 5);
- send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */
- for (rank = 0; rank < blcodes; rank++) {
- Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
- send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
- }
- Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
-
- send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */
- Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
-
- send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */
- Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
-}
-
-/* ===========================================================================
- * Send a stored block
- */
-void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last)
- deflate_state *s;
- charf *buf; /* input block */
- ulg stored_len; /* length of input block */
- int last; /* one if this is the last block for a file */
-{
- send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */
- bi_windup(s); /* align on byte boundary */
- put_short(s, (ush)stored_len);
- put_short(s, (ush)~stored_len);
- zmemcpy(s->pending_buf + s->pending, (Bytef *)buf, stored_len);
- s->pending += stored_len;
-#ifdef ZLIB_DEBUG
- s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L;
- s->compressed_len += (stored_len + 4) << 3;
- s->bits_sent += 2*16;
- s->bits_sent += stored_len<<3;
-#endif
-}
-
-/* ===========================================================================
- * Flush the bits in the bit buffer to pending output (leaves at most 7 bits)
- */
-void ZLIB_INTERNAL _tr_flush_bits(s)
- deflate_state *s;
-{
- bi_flush(s);
-}
-
-/* ===========================================================================
- * Send one empty static block to give enough lookahead for inflate.
- * This takes 10 bits, of which 7 may remain in the bit buffer.
- */
-void ZLIB_INTERNAL _tr_align(s)
- deflate_state *s;
-{
- send_bits(s, STATIC_TREES<<1, 3);
- send_code(s, END_BLOCK, static_ltree);
-#ifdef ZLIB_DEBUG
- s->compressed_len += 10L; /* 3 for block type, 7 for EOB */
-#endif
- bi_flush(s);
-}
-
-/* ===========================================================================
- * Determine the best encoding for the current block: dynamic trees, static
- * trees or store, and write out the encoded block.
- */
-void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last)
- deflate_state *s;
- charf *buf; /* input block, or NULL if too old */
- ulg stored_len; /* length of input block */
- int last; /* one if this is the last block for a file */
-{
- ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
- int max_blindex = 0; /* index of last bit length code of non zero freq */
-
- /* Build the Huffman trees unless a stored block is forced */
- if (s->level > 0) {
-
- /* Check if the file is binary or text */
- if (s->strm->data_type == Z_UNKNOWN)
- s->strm->data_type = detect_data_type(s);
-
- /* Construct the literal and distance trees */
- build_tree(s, (tree_desc *)(&(s->l_desc)));
- Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
- s->static_len));
-
- build_tree(s, (tree_desc *)(&(s->d_desc)));
- Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
- s->static_len));
- /* At this point, opt_len and static_len are the total bit lengths of
- * the compressed block data, excluding the tree representations.
- */
-
- /* Build the bit length tree for the above two trees, and get the index
- * in bl_order of the last bit length code to send.
- */
- max_blindex = build_bl_tree(s);
-
- /* Determine the best encoding. Compute the block lengths in bytes. */
- opt_lenb = (s->opt_len+3+7)>>3;
- static_lenb = (s->static_len+3+7)>>3;
-
- Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
- opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
- s->last_lit));
-
- if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
-
- } else {
- Assert(buf != (char*)0, "lost buf");
- opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
- }
-
-#ifdef FORCE_STORED
- if (buf != (char*)0) { /* force stored block */
-#else
- if (stored_len+4 <= opt_lenb && buf != (char*)0) {
- /* 4: two words for the lengths */
-#endif
- /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
- * Otherwise we can't have processed more than WSIZE input bytes since
- * the last block flush, because compression would have been
- * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
- * transform a block into a stored block.
- */
- _tr_stored_block(s, buf, stored_len, last);
-
-#ifdef FORCE_STATIC
- } else if (static_lenb >= 0) { /* force static trees */
-#else
- } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) {
-#endif
- send_bits(s, (STATIC_TREES<<1)+last, 3);
- compress_block(s, (const ct_data *)static_ltree,
- (const ct_data *)static_dtree);
-#ifdef ZLIB_DEBUG
- s->compressed_len += 3 + s->static_len;
-#endif
- } else {
- send_bits(s, (DYN_TREES<<1)+last, 3);
- send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
- max_blindex+1);
- compress_block(s, (const ct_data *)s->dyn_ltree,
- (const ct_data *)s->dyn_dtree);
-#ifdef ZLIB_DEBUG
- s->compressed_len += 3 + s->opt_len;
-#endif
- }
- Assert (s->compressed_len == s->bits_sent, "bad compressed size");
- /* The above check is made mod 2^32, for files larger than 512 MB
- * and uLong implemented on 32 bits.
- */
- init_block(s);
-
- if (last) {
- bi_windup(s);
-#ifdef ZLIB_DEBUG
- s->compressed_len += 7; /* align on byte boundary */
-#endif
- }
- Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
- s->compressed_len-7*last));
-}
-
-/* ===========================================================================
- * Save the match info and tally the frequency counts. Return true if
- * the current block must be flushed.
- */
-int ZLIB_INTERNAL _tr_tally (s, dist, lc)
- deflate_state *s;
- unsigned dist; /* distance of matched string */
- unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
-{
- s->d_buf[s->last_lit] = (ush)dist;
- s->l_buf[s->last_lit++] = (uch)lc;
- if (dist == 0) {
- /* lc is the unmatched char */
- s->dyn_ltree[lc].Freq++;
- } else {
- s->matches++;
- /* Here, lc is the match length - MIN_MATCH */
- dist--; /* dist = match distance - 1 */
- Assert((ush)dist < (ush)MAX_DIST(s) &&
- (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
- (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match");
-
- s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++;
- s->dyn_dtree[d_code(dist)].Freq++;
- }
-
-#ifdef TRUNCATE_BLOCK
- /* Try to guess if it is profitable to stop the current block here */
- if ((s->last_lit & 0x1fff) == 0 && s->level > 2) {
- /* Compute an upper bound for the compressed length */
- ulg out_length = (ulg)s->last_lit*8L;
- ulg in_length = (ulg)((long)s->strstart - s->block_start);
- int dcode;
- for (dcode = 0; dcode < D_CODES; dcode++) {
- out_length += (ulg)s->dyn_dtree[dcode].Freq *
- (5L+extra_dbits[dcode]);
- }
- out_length >>= 3;
- Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
- s->last_lit, in_length, out_length,
- 100L - out_length*100L/in_length));
- if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
- }
-#endif
- return (s->last_lit == s->lit_bufsize-1);
- /* We avoid equality with lit_bufsize because of wraparound at 64K
- * on 16 bit machines and because stored blocks are restricted to
- * 64K-1 bytes.
- */
-}
-
-/* ===========================================================================
- * Send the block data compressed using the given Huffman trees
- */
-local void compress_block(s, ltree, dtree)
- deflate_state *s;
- const ct_data *ltree; /* literal tree */
- const ct_data *dtree; /* distance tree */
-{
- unsigned dist; /* distance of matched string */
- int lc; /* match length or unmatched char (if dist == 0) */
- unsigned lx = 0; /* running index in l_buf */
- unsigned code; /* the code to send */
- int extra; /* number of extra bits to send */
-
- if (s->last_lit != 0) do {
- dist = s->d_buf[lx];
- lc = s->l_buf[lx++];
- if (dist == 0) {
- send_code(s, lc, ltree); /* send a literal byte */
- Tracecv(isgraph(lc), (stderr," '%c' ", lc));
- } else {
- /* Here, lc is the match length - MIN_MATCH */
- code = _length_code[lc];
- send_code(s, code+LITERALS+1, ltree); /* send the length code */
- extra = extra_lbits[code];
- if (extra != 0) {
- lc -= base_length[code];
- send_bits(s, lc, extra); /* send the extra length bits */
- }
- dist--; /* dist is now the match distance - 1 */
- code = d_code(dist);
- Assert (code < D_CODES, "bad d_code");
-
- send_code(s, code, dtree); /* send the distance code */
- extra = extra_dbits[code];
- if (extra != 0) {
- dist -= (unsigned)base_dist[code];
- send_bits(s, dist, extra); /* send the extra distance bits */
- }
- } /* literal or match pair ? */
-
- /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
- Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,
- "pendingBuf overflow");
-
- } while (lx < s->last_lit);
-
- send_code(s, END_BLOCK, ltree);
-}
-
-/* ===========================================================================
- * Check if the data type is TEXT or BINARY, using the following algorithm:
- * - TEXT if the two conditions below are satisfied:
- * a) There are no non-portable control characters belonging to the
- * "black list" (0..6, 14..25, 28..31).
- * b) There is at least one printable character belonging to the
- * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).
- * - BINARY otherwise.
- * - The following partially-portable control characters form a
- * "gray list" that is ignored in this detection algorithm:
- * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).
- * IN assertion: the fields Freq of dyn_ltree are set.
- */
-local int detect_data_type(s)
- deflate_state *s;
-{
- /* black_mask is the bit mask of black-listed bytes
- * set bits 0..6, 14..25, and 28..31
- * 0xf3ffc07f = binary 11110011111111111100000001111111
- */
- unsigned long black_mask = 0xf3ffc07fUL;
- int n;
-
- /* Check for non-textual ("black-listed") bytes. */
- for (n = 0; n <= 31; n++, black_mask >>= 1)
- if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0))
- return Z_BINARY;
-
- /* Check for textual ("white-listed") bytes. */
- if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0
- || s->dyn_ltree[13].Freq != 0)
- return Z_TEXT;
- for (n = 32; n < LITERALS; n++)
- if (s->dyn_ltree[n].Freq != 0)
- return Z_TEXT;
-
- /* There are no "black-listed" or "white-listed" bytes:
- * this stream either is empty or has tolerated ("gray-listed") bytes only.
- */
- return Z_BINARY;
-}
-
-/* ===========================================================================
- * Reverse the first len bits of a code, using straightforward code (a faster
- * method would use a table)
- * IN assertion: 1 <= len <= 15
- */
-local unsigned bi_reverse(code, len)
- unsigned code; /* the value to invert */
- int len; /* its bit length */
-{
- register unsigned res = 0;
- do {
- res |= code & 1;
- code >>= 1, res <<= 1;
- } while (--len > 0);
- return res >> 1;
-}
-
-/* ===========================================================================
- * Flush the bit buffer, keeping at most 7 bits in it.
- */
-local void bi_flush(s)
- deflate_state *s;
-{
- if (s->bi_valid == 16) {
- put_short(s, s->bi_buf);
- s->bi_buf = 0;
- s->bi_valid = 0;
- } else if (s->bi_valid >= 8) {
- put_byte(s, (Byte)s->bi_buf);
- s->bi_buf >>= 8;
- s->bi_valid -= 8;
- }
-}
-
-/* ===========================================================================
- * Flush the bit buffer and align the output on a byte boundary
- */
-local void bi_windup(s)
- deflate_state *s;
-{
- if (s->bi_valid > 8) {
- put_short(s, s->bi_buf);
- } else if (s->bi_valid > 0) {
- put_byte(s, (Byte)s->bi_buf);
- }
- s->bi_buf = 0;
- s->bi_valid = 0;
-#ifdef ZLIB_DEBUG
- s->bits_sent = (s->bits_sent+7) & ~7;
-#endif
-}
+++ /dev/null
-/* header created automatically with -DGEN_TREES_H */
-
-local const ct_data static_ltree[L_CODES+2] = {
-{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}},
-{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}},
-{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}},
-{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}},
-{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}},
-{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}},
-{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}},
-{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}},
-{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}},
-{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}},
-{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}},
-{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}},
-{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}},
-{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}},
-{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}},
-{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}},
-{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}},
-{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}},
-{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}},
-{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}},
-{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}},
-{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}},
-{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}},
-{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}},
-{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}},
-{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}},
-{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}},
-{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}},
-{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}},
-{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}},
-{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}},
-{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}},
-{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}},
-{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}},
-{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}},
-{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}},
-{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}},
-{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}},
-{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}},
-{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}},
-{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}},
-{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}},
-{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}},
-{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}},
-{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}},
-{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}},
-{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}},
-{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}},
-{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}},
-{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}},
-{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}},
-{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}},
-{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}},
-{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}},
-{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}},
-{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}},
-{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}},
-{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}}
-};
-
-local const ct_data static_dtree[D_CODES] = {
-{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}},
-{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}},
-{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}},
-{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}},
-{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}},
-{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}}
-};
-
-const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {
- 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8,
- 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10,
-10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
-11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
-12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
-13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
-13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
-14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
-14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
-14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,
-15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
-15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
-15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17,
-18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,
-23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
-24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
-26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
-26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,
-27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
-27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
-28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
-28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
-28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
-29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
-29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
-29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
-};
-
-const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12,
-13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
-17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,
-19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
-21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
-22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
-23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
-24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
-25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
-25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
-26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
-26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
-27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
-};
-
-local const int base_length[LENGTH_CODES] = {
-0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
-64, 80, 96, 112, 128, 160, 192, 224, 0
-};
-
-local const int base_dist[D_CODES] = {
- 0, 1, 2, 3, 4, 6, 8, 12, 16, 24,
- 32, 48, 64, 96, 128, 192, 256, 384, 512, 768,
- 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576
-};
-
+++ /dev/null
-/* zconf.h -- configuration of the zlib compression library
- * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-/* @(#) $Id$ */
-
-#ifndef ZCONF_H
-#define ZCONF_H
-
-/*
- * If you *really* need a unique prefix for all types and library functions,
- * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
- * Even better than compiling with -DZ_PREFIX would be to use configure to set
- * this permanently in zconf.h using "./configure --zprefix".
- */
-#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */
-# define Z_PREFIX_SET
-
-/* all linked symbols and init macros */
-# define _dist_code z__dist_code
-# define _length_code z__length_code
-# define _tr_align z__tr_align
-# define _tr_flush_bits z__tr_flush_bits
-# define _tr_flush_block z__tr_flush_block
-# define _tr_init z__tr_init
-# define _tr_stored_block z__tr_stored_block
-# define _tr_tally z__tr_tally
-# define adler32 z_adler32
-# define adler32_combine z_adler32_combine
-# define adler32_combine64 z_adler32_combine64
-# define adler32_z z_adler32_z
-# ifndef Z_SOLO
-# define compress z_compress
-# define compress2 z_compress2
-# define compressBound z_compressBound
-# endif
-# define crc32 z_crc32
-# define crc32_combine z_crc32_combine
-# define crc32_combine64 z_crc32_combine64
-# define crc32_z z_crc32_z
-# define deflate z_deflate
-# define deflateBound z_deflateBound
-# define deflateCopy z_deflateCopy
-# define deflateEnd z_deflateEnd
-# define deflateGetDictionary z_deflateGetDictionary
-# define deflateInit z_deflateInit
-# define deflateInit2 z_deflateInit2
-# define deflateInit2_ z_deflateInit2_
-# define deflateInit_ z_deflateInit_
-# define deflateParams z_deflateParams
-# define deflatePending z_deflatePending
-# define deflatePrime z_deflatePrime
-# define deflateReset z_deflateReset
-# define deflateResetKeep z_deflateResetKeep
-# define deflateSetDictionary z_deflateSetDictionary
-# define deflateSetHeader z_deflateSetHeader
-# define deflateTune z_deflateTune
-# define deflate_copyright z_deflate_copyright
-# define get_crc_table z_get_crc_table
-# ifndef Z_SOLO
-# define gz_error z_gz_error
-# define gz_intmax z_gz_intmax
-# define gz_strwinerror z_gz_strwinerror
-# define gzbuffer z_gzbuffer
-# define gzclearerr z_gzclearerr
-# define gzclose z_gzclose
-# define gzclose_r z_gzclose_r
-# define gzclose_w z_gzclose_w
-# define gzdirect z_gzdirect
-# define gzdopen z_gzdopen
-# define gzeof z_gzeof
-# define gzerror z_gzerror
-# define gzflush z_gzflush
-# define gzfread z_gzfread
-# define gzfwrite z_gzfwrite
-# define gzgetc z_gzgetc
-# define gzgetc_ z_gzgetc_
-# define gzgets z_gzgets
-# define gzoffset z_gzoffset
-# define gzoffset64 z_gzoffset64
-# define gzopen z_gzopen
-# define gzopen64 z_gzopen64
-# ifdef _WIN32
-# define gzopen_w z_gzopen_w
-# endif
-# define gzprintf z_gzprintf
-# define gzputc z_gzputc
-# define gzputs z_gzputs
-# define gzread z_gzread
-# define gzrewind z_gzrewind
-# define gzseek z_gzseek
-# define gzseek64 z_gzseek64
-# define gzsetparams z_gzsetparams
-# define gztell z_gztell
-# define gztell64 z_gztell64
-# define gzungetc z_gzungetc
-# define gzvprintf z_gzvprintf
-# define gzwrite z_gzwrite
-# endif
-# define inflate z_inflate
-# define inflateBack z_inflateBack
-# define inflateBackEnd z_inflateBackEnd
-# define inflateBackInit z_inflateBackInit
-# define inflateBackInit_ z_inflateBackInit_
-# define inflateCodesUsed z_inflateCodesUsed
-# define inflateCopy z_inflateCopy
-# define inflateEnd z_inflateEnd
-# define inflateGetDictionary z_inflateGetDictionary
-# define inflateGetHeader z_inflateGetHeader
-# define inflateInit z_inflateInit
-# define inflateInit2 z_inflateInit2
-# define inflateInit2_ z_inflateInit2_
-# define inflateInit_ z_inflateInit_
-# define inflateMark z_inflateMark
-# define inflatePrime z_inflatePrime
-# define inflateReset z_inflateReset
-# define inflateReset2 z_inflateReset2
-# define inflateResetKeep z_inflateResetKeep
-# define inflateSetDictionary z_inflateSetDictionary
-# define inflateSync z_inflateSync
-# define inflateSyncPoint z_inflateSyncPoint
-# define inflateUndermine z_inflateUndermine
-# define inflateValidate z_inflateValidate
-# define inflate_copyright z_inflate_copyright
-# define inflate_fast z_inflate_fast
-# define inflate_table z_inflate_table
-# ifndef Z_SOLO
-# define uncompress z_uncompress
-# define uncompress2 z_uncompress2
-# endif
-# define zError z_zError
-# ifndef Z_SOLO
-# define zcalloc z_zcalloc
-# define zcfree z_zcfree
-# endif
-# define zlibCompileFlags z_zlibCompileFlags
-# define zlibVersion z_zlibVersion
-
-/* all zlib typedefs in zlib.h and zconf.h */
-# define Byte z_Byte
-# define Bytef z_Bytef
-# define alloc_func z_alloc_func
-# define charf z_charf
-# define free_func z_free_func
-# ifndef Z_SOLO
-# define gzFile z_gzFile
-# endif
-# define gz_header z_gz_header
-# define gz_headerp z_gz_headerp
-# define in_func z_in_func
-# define intf z_intf
-# define out_func z_out_func
-# define uInt z_uInt
-# define uIntf z_uIntf
-# define uLong z_uLong
-# define uLongf z_uLongf
-# define voidp z_voidp
-# define voidpc z_voidpc
-# define voidpf z_voidpf
-
-/* all zlib structs in zlib.h and zconf.h */
-# define gz_header_s z_gz_header_s
-# define internal_state z_internal_state
-
-#endif
-
-#if defined(__MSDOS__) && !defined(MSDOS)
-# define MSDOS
-#endif
-#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
-# define OS2
-#endif
-#if defined(_WINDOWS) && !defined(WINDOWS)
-# define WINDOWS
-#endif
-#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)
-# ifndef WIN32
-# define WIN32
-# endif
-#endif
-#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
-# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
-# ifndef SYS16BIT
-# define SYS16BIT
-# endif
-# endif
-#endif
-
-/*
- * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
- * than 64k bytes at a time (needed on systems with 16-bit int).
- */
-#ifdef SYS16BIT
-# define MAXSEG_64K
-#endif
-#ifdef MSDOS
-# define UNALIGNED_OK
-#endif
-
-#ifdef __STDC_VERSION__
-# ifndef STDC
-# define STDC
-# endif
-# if __STDC_VERSION__ >= 199901L
-# ifndef STDC99
-# define STDC99
-# endif
-# endif
-#endif
-#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
-# define STDC
-#endif
-#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
-# define STDC
-#endif
-#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
-# define STDC
-#endif
-#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
-# define STDC
-#endif
-
-#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */
-# define STDC
-#endif
-
-#ifndef STDC
-# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
-# define const /* note: need a more gentle solution here */
-# endif
-#endif
-
-#if defined(ZLIB_CONST) && !defined(z_const)
-# define z_const const
-#else
-# define z_const
-#endif
-
-#ifdef Z_SOLO
- typedef unsigned long z_size_t;
-#else
-# define z_longlong long long
-# if defined(NO_SIZE_T)
- typedef unsigned NO_SIZE_T z_size_t;
-# elif defined(STDC)
-# include <stddef.h>
- typedef size_t z_size_t;
-# else
- typedef unsigned long z_size_t;
-# endif
-# undef z_longlong
-#endif
-
-/* Maximum value for memLevel in deflateInit2 */
-#ifndef MAX_MEM_LEVEL
-# ifdef MAXSEG_64K
-# define MAX_MEM_LEVEL 8
-# else
-# define MAX_MEM_LEVEL 9
-# endif
-#endif
-
-/* Maximum value for windowBits in deflateInit2 and inflateInit2.
- * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
- * created by gzip. (Files created by minigzip can still be extracted by
- * gzip.)
- */
-#ifndef MAX_WBITS
-# define MAX_WBITS 15 /* 32K LZ77 window */
-#endif
-
-/* The memory requirements for deflate are (in bytes):
- (1 << (windowBits+2)) + (1 << (memLevel+9))
- that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
- plus a few kilobytes for small objects. For example, if you want to reduce
- the default memory requirements from 256K to 128K, compile with
- make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
- Of course this will generally degrade compression (there's no free lunch).
-
- The memory requirements for inflate are (in bytes) 1 << windowBits
- that is, 32K for windowBits=15 (default value) plus about 7 kilobytes
- for small objects.
-*/
-
- /* Type declarations */
-
-#ifndef OF /* function prototypes */
-# ifdef STDC
-# define OF(args) args
-# else
-# define OF(args) ()
-# endif
-#endif
-
-#ifndef Z_ARG /* function prototypes for stdarg */
-# if defined(STDC) || defined(Z_HAVE_STDARG_H)
-# define Z_ARG(args) args
-# else
-# define Z_ARG(args) ()
-# endif
-#endif
-
-/* The following definitions for FAR are needed only for MSDOS mixed
- * model programming (small or medium model with some far allocations).
- * This was tested only with MSC; for other MSDOS compilers you may have
- * to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
- * just define FAR to be empty.
- */
-#ifdef SYS16BIT
-# if defined(M_I86SM) || defined(M_I86MM)
- /* MSC small or medium model */
-# define SMALL_MEDIUM
-# ifdef _MSC_VER
-# define FAR _far
-# else
-# define FAR far
-# endif
-# endif
-# if (defined(__SMALL__) || defined(__MEDIUM__))
- /* Turbo C small or medium model */
-# define SMALL_MEDIUM
-# ifdef __BORLANDC__
-# define FAR _far
-# else
-# define FAR far
-# endif
-# endif
-#endif
-
-#if defined(WINDOWS) || defined(WIN32)
- /* If building or using zlib as a DLL, define ZLIB_DLL.
- * This is not mandatory, but it offers a little performance increase.
- */
-# ifdef ZLIB_DLL
-# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
-# ifdef ZLIB_INTERNAL
-# define ZEXTERN extern __declspec(dllexport)
-# else
-# define ZEXTERN extern __declspec(dllimport)
-# endif
-# endif
-# endif /* ZLIB_DLL */
- /* If building or using zlib with the WINAPI/WINAPIV calling convention,
- * define ZLIB_WINAPI.
- * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
- */
-# ifdef ZLIB_WINAPI
-# ifdef FAR
-# undef FAR
-# endif
-# include <windows.h>
- /* No need for _export, use ZLIB.DEF instead. */
- /* For complete Windows compatibility, use WINAPI, not __stdcall. */
-# define ZEXPORT WINAPI
-# ifdef WIN32
-# define ZEXPORTVA WINAPIV
-# else
-# define ZEXPORTVA FAR CDECL
-# endif
-# endif
-#endif
-
-#if defined (__BEOS__)
-# ifdef ZLIB_DLL
-# ifdef ZLIB_INTERNAL
-# define ZEXPORT __declspec(dllexport)
-# define ZEXPORTVA __declspec(dllexport)
-# else
-# define ZEXPORT __declspec(dllimport)
-# define ZEXPORTVA __declspec(dllimport)
-# endif
-# endif
-#endif
-
-#ifndef ZEXTERN
-# define ZEXTERN extern
-#endif
-#ifndef ZEXPORT
-# define ZEXPORT
-#endif
-#ifndef ZEXPORTVA
-# define ZEXPORTVA
-#endif
-
-#ifndef FAR
-# define FAR
-#endif
-
-#if !defined(__MACTYPES__)
-typedef unsigned char Byte; /* 8 bits */
-#endif
-typedef unsigned int uInt; /* 16 bits or more */
-typedef unsigned long uLong; /* 32 bits or more */
-
-#ifdef SMALL_MEDIUM
- /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
-# define Bytef Byte FAR
-#else
- typedef Byte FAR Bytef;
-#endif
-typedef char FAR charf;
-typedef int FAR intf;
-typedef uInt FAR uIntf;
-typedef uLong FAR uLongf;
-
-#ifdef STDC
- typedef void const *voidpc;
- typedef void FAR *voidpf;
- typedef void *voidp;
-#else
- typedef Byte const *voidpc;
- typedef Byte FAR *voidpf;
- typedef Byte *voidp;
-#endif
-
-#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC)
-# include <limits.h>
-# if (UINT_MAX == 0xffffffffUL)
-# define Z_U4 unsigned
-# elif (ULONG_MAX == 0xffffffffUL)
-# define Z_U4 unsigned long
-# elif (USHRT_MAX == 0xffffffffUL)
-# define Z_U4 unsigned short
-# endif
-#endif
-
-#ifdef Z_U4
- typedef Z_U4 z_crc_t;
-#else
- typedef unsigned long z_crc_t;
-#endif
-
-#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */
-# define Z_HAVE_UNISTD_H
-#endif
-
-#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */
-# define Z_HAVE_STDARG_H
-#endif
-
-#ifdef STDC
-# ifndef Z_SOLO
-# include <sys/types.h> /* for off_t */
-# endif
-#endif
-
-#if defined(STDC) || defined(Z_HAVE_STDARG_H)
-# ifndef Z_SOLO
-# include <stdarg.h> /* for va_list */
-# endif
-#endif
-
-#ifdef _WIN32
-# ifndef Z_SOLO
-# include <stddef.h> /* for wchar_t */
-# endif
-#endif
-
-/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and
- * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even
- * though the former does not conform to the LFS document), but considering
- * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as
- * equivalently requesting no 64-bit operations
- */
-#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1
-# undef _LARGEFILE64_SOURCE
-#endif
-
-#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H)
-# define Z_HAVE_UNISTD_H
-#endif
-#ifndef Z_SOLO
-# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE)
-# include <unistd.h> /* for SEEK_*, off_t, and _LFS64_LARGEFILE */
-# ifdef VMS
-# include <unixio.h> /* for off_t */
-# endif
-# ifndef z_off_t
-# define z_off_t off_t
-# endif
-# endif
-#endif
-
-#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0
-# define Z_LFS64
-#endif
-
-#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64)
-# define Z_LARGE64
-#endif
-
-#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64)
-# define Z_WANT64
-#endif
-
-#if !defined(SEEK_SET) && !defined(Z_SOLO)
-# define SEEK_SET 0 /* Seek from beginning of file. */
-# define SEEK_CUR 1 /* Seek from current position. */
-# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
-#endif
-
-#ifndef z_off_t
-# define z_off_t long
-#endif
-
-#if !defined(_WIN32) && defined(Z_LARGE64)
-# define z_off64_t off64_t
-#else
-# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO)
-# define z_off64_t __int64
-# else
-# define z_off64_t z_off_t
-# endif
-#endif
-
-/* MVS linker does not support external names larger than 8 bytes */
-#if defined(__MVS__)
- #pragma map(deflateInit_,"DEIN")
- #pragma map(deflateInit2_,"DEIN2")
- #pragma map(deflateEnd,"DEEND")
- #pragma map(deflateBound,"DEBND")
- #pragma map(inflateInit_,"ININ")
- #pragma map(inflateInit2_,"ININ2")
- #pragma map(inflateEnd,"INEND")
- #pragma map(inflateSync,"INSY")
- #pragma map(inflateSetDictionary,"INSEDI")
- #pragma map(compressBound,"CMBND")
- #pragma map(inflate_table,"INTABL")
- #pragma map(inflate_fast,"INFA")
- #pragma map(inflate_copyright,"INCOPY")
-#endif
-
-#endif /* ZCONF_H */
+++ /dev/null
-/* zlib.h -- interface of the 'zlib' general purpose compression library
- version 1.2.11, January 15th, 2017
-
- 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
-
-
- The data format used by the zlib library is described by RFCs (Request for
- Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
- (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
-*/
-
-#ifndef ZLIB_H
-#define ZLIB_H
-
-#include "zconf.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define ZLIB_VERSION "1.2.11"
-#define ZLIB_VERNUM 0x12b0
-#define ZLIB_VER_MAJOR 1
-#define ZLIB_VER_MINOR 2
-#define ZLIB_VER_REVISION 11
-#define ZLIB_VER_SUBREVISION 0
-
-/*
- The 'zlib' compression library provides in-memory compression and
- decompression functions, including integrity checks of the uncompressed data.
- This version of the library supports only one compression method (deflation)
- but other algorithms will be added later and will have the same stream
- interface.
-
- Compression can be done in a single step if the buffers are large enough,
- or can be done by repeated calls of the compression function. In the latter
- case, the application must provide more input and/or consume the output
- (providing more output space) before each call.
-
- The compressed data format used by default by the in-memory functions is
- the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped
- around a deflate stream, which is itself documented in RFC 1951.
-
- The library also supports reading and writing files in gzip (.gz) format
- with an interface similar to that of stdio using the functions that start
- with "gz". The gzip format is different from the zlib format. gzip is a
- gzip wrapper, documented in RFC 1952, wrapped around a deflate stream.
-
- This library can optionally read and write gzip and raw deflate streams in
- memory as well.
-
- The zlib format was designed to be compact and fast for use in memory
- and on communications channels. The gzip format was designed for single-
- file compression on file systems, has a larger header than zlib to maintain
- directory information, and uses a different, slower check method than zlib.
-
- The library does not install any signal handler. The decoder checks
- the consistency of the compressed data, so the library should never crash
- even in the case of corrupted input.
-*/
-
-typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
-typedef void (*free_func) OF((voidpf opaque, voidpf address));
-
-struct internal_state;
-
-typedef struct z_stream_s {
- z_const Bytef *next_in; /* next input byte */
- uInt avail_in; /* number of bytes available at next_in */
- uLong total_in; /* total number of input bytes read so far */
-
- Bytef *next_out; /* next output byte will go here */
- uInt avail_out; /* remaining free space at next_out */
- uLong total_out; /* total number of bytes output so far */
-
- z_const char *msg; /* last error message, NULL if no error */
- struct internal_state FAR *state; /* not visible by applications */
-
- alloc_func zalloc; /* used to allocate the internal state */
- free_func zfree; /* used to free the internal state */
- voidpf opaque; /* private data object passed to zalloc and zfree */
-
- int data_type; /* best guess about the data type: binary or text
- for deflate, or the decoding state for inflate */
- uLong adler; /* Adler-32 or CRC-32 value of the uncompressed data */
- uLong reserved; /* reserved for future use */
-} z_stream;
-
-typedef z_stream FAR *z_streamp;
-
-/*
- gzip header information passed to and from zlib routines. See RFC 1952
- for more details on the meanings of these fields.
-*/
-typedef struct gz_header_s {
- int text; /* true if compressed data believed to be text */
- uLong time; /* modification time */
- int xflags; /* extra flags (not used when writing a gzip file) */
- int os; /* operating system */
- Bytef *extra; /* pointer to extra field or Z_NULL if none */
- uInt extra_len; /* extra field length (valid if extra != Z_NULL) */
- uInt extra_max; /* space at extra (only when reading header) */
- Bytef *name; /* pointer to zero-terminated file name or Z_NULL */
- uInt name_max; /* space at name (only when reading header) */
- Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */
- uInt comm_max; /* space at comment (only when reading header) */
- int hcrc; /* true if there was or will be a header crc */
- int done; /* true when done reading gzip header (not used
- when writing a gzip file) */
-} gz_header;
-
-typedef gz_header FAR *gz_headerp;
-
-/*
- The application must update next_in and avail_in when avail_in has dropped
- to zero. It must update next_out and avail_out when avail_out has dropped
- to zero. The application must initialize zalloc, zfree and opaque before
- calling the init function. All other fields are set by the compression
- library and must not be updated by the application.
-
- The opaque value provided by the application will be passed as the first
- parameter for calls of zalloc and zfree. This can be useful for custom
- memory management. The compression library attaches no meaning to the
- opaque value.
-
- zalloc must return Z_NULL if there is not enough memory for the object.
- If zlib is used in a multi-threaded application, zalloc and zfree must be
- thread safe. In that case, zlib is thread-safe. When zalloc and zfree are
- Z_NULL on entry to the initialization function, they are set to internal
- routines that use the standard library functions malloc() and free().
-
- On 16-bit systems, the functions zalloc and zfree must be able to allocate
- exactly 65536 bytes, but will not be required to allocate more than this if
- the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers
- returned by zalloc for objects of exactly 65536 bytes *must* have their
- offset normalized to zero. The default allocation function provided by this
- library ensures this (see zutil.c). To reduce memory requirements and avoid
- any allocation of 64K objects, at the expense of compression ratio, compile
- the library with -DMAX_WBITS=14 (see zconf.h).
-
- The fields total_in and total_out can be used for statistics or progress
- reports. After compression, total_in holds the total size of the
- uncompressed data and may be saved for use by the decompressor (particularly
- if the decompressor wants to decompress everything in a single step).
-*/
-
- /* constants */
-
-#define Z_NO_FLUSH 0
-#define Z_PARTIAL_FLUSH 1
-#define Z_SYNC_FLUSH 2
-#define Z_FULL_FLUSH 3
-#define Z_FINISH 4
-#define Z_BLOCK 5
-#define Z_TREES 6
-/* Allowed flush values; see deflate() and inflate() below for details */
-
-#define Z_OK 0
-#define Z_STREAM_END 1
-#define Z_NEED_DICT 2
-#define Z_ERRNO (-1)
-#define Z_STREAM_ERROR (-2)
-#define Z_DATA_ERROR (-3)
-#define Z_MEM_ERROR (-4)
-#define Z_BUF_ERROR (-5)
-#define Z_VERSION_ERROR (-6)
-/* Return codes for the compression/decompression functions. Negative values
- * are errors, positive values are used for special but normal events.
- */
-
-#define Z_NO_COMPRESSION 0
-#define Z_BEST_SPEED 1
-#define Z_BEST_COMPRESSION 9
-#define Z_DEFAULT_COMPRESSION (-1)
-/* compression levels */
-
-#define Z_FILTERED 1
-#define Z_HUFFMAN_ONLY 2
-#define Z_RLE 3
-#define Z_FIXED 4
-#define Z_DEFAULT_STRATEGY 0
-/* compression strategy; see deflateInit2() below for details */
-
-#define Z_BINARY 0
-#define Z_TEXT 1
-#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */
-#define Z_UNKNOWN 2
-/* Possible values of the data_type field for deflate() */
-
-#define Z_DEFLATED 8
-/* The deflate compression method (the only one supported in this version) */
-
-#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
-
-#define zlib_version zlibVersion()
-/* for compatibility with versions < 1.0.2 */
-
-
- /* basic functions */
-
-ZEXTERN const char * ZEXPORT zlibVersion OF((void));
-/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
- If the first character differs, the library code actually used is not
- compatible with the zlib.h header file used by the application. This check
- is automatically made by deflateInit and inflateInit.
- */
-
-/*
-ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
-
- Initializes the internal stream state for compression. The fields
- zalloc, zfree and opaque must be initialized before by the caller. If
- zalloc and zfree are set to Z_NULL, deflateInit updates them to use default
- allocation functions.
-
- The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
- 1 gives best speed, 9 gives best compression, 0 gives no compression at all
- (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION
- requests a default compromise between speed and compression (currently
- equivalent to level 6).
-
- deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
- memory, Z_STREAM_ERROR if level is not a valid compression level, or
- Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
- with the version assumed by the caller (ZLIB_VERSION). msg is set to null
- if there is no error message. deflateInit does not perform any compression:
- this will be done by deflate().
-*/
-
-
-ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
-/*
- deflate compresses as much data as possible, and stops when the input
- buffer becomes empty or the output buffer becomes full. It may introduce
- some output latency (reading input without producing any output) except when
- forced to flush.
-
- The detailed semantics are as follows. deflate performs one or both of the
- following actions:
-
- - Compress more input starting at next_in and update next_in and avail_in
- accordingly. If not all input can be processed (because there is not
- enough room in the output buffer), next_in and avail_in are updated and
- processing will resume at this point for the next call of deflate().
-
- - Generate more output starting at next_out and update next_out and avail_out
- accordingly. This action is forced if the parameter flush is non zero.
- Forcing flush frequently degrades the compression ratio, so this parameter
- should be set only when necessary. Some output may be provided even if
- flush is zero.
-
- Before the call of deflate(), the application should ensure that at least
- one of the actions is possible, by providing more input and/or consuming more
- output, and updating avail_in or avail_out accordingly; avail_out should
- never be zero before the call. The application can consume the compressed
- output when it wants, for example when the output buffer is full (avail_out
- == 0), or after each call of deflate(). If deflate returns Z_OK and with
- zero avail_out, it must be called again after making room in the output
- buffer because there might be more output pending. See deflatePending(),
- which can be used if desired to determine whether or not there is more ouput
- in that case.
-
- Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to
- decide how much data to accumulate before producing output, in order to
- maximize compression.
-
- If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
- flushed to the output buffer and the output is aligned on a byte boundary, so
- that the decompressor can get all input data available so far. (In
- particular avail_in is zero after the call if enough output space has been
- provided before the call.) Flushing may degrade compression for some
- compression algorithms and so it should be used only when necessary. This
- completes the current deflate block and follows it with an empty stored block
- that is three bits plus filler bits to the next byte, followed by four bytes
- (00 00 ff ff).
-
- If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the
- output buffer, but the output is not aligned to a byte boundary. All of the
- input data so far will be available to the decompressor, as for Z_SYNC_FLUSH.
- This completes the current deflate block and follows it with an empty fixed
- codes block that is 10 bits long. This assures that enough bytes are output
- in order for the decompressor to finish the block before the empty fixed
- codes block.
-
- If flush is set to Z_BLOCK, a deflate block is completed and emitted, as
- for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to
- seven bits of the current block are held to be written as the next byte after
- the next deflate block is completed. In this case, the decompressor may not
- be provided enough bits at this point in order to complete decompression of
- the data provided so far to the compressor. It may need to wait for the next
- block to be emitted. This is for advanced applications that need to control
- the emission of deflate blocks.
-
- If flush is set to Z_FULL_FLUSH, all output is flushed as with
- Z_SYNC_FLUSH, and the compression state is reset so that decompression can
- restart from this point if previous compressed data has been damaged or if
- random access is desired. Using Z_FULL_FLUSH too often can seriously degrade
- compression.
-
- If deflate returns with avail_out == 0, this function must be called again
- with the same value of the flush parameter and more output space (updated
- avail_out), until the flush is complete (deflate returns with non-zero
- avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that
- avail_out is greater than six to avoid repeated flush markers due to
- avail_out == 0 on return.
-
- If the parameter flush is set to Z_FINISH, pending input is processed,
- pending output is flushed and deflate returns with Z_STREAM_END if there was
- enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this
- function must be called again with Z_FINISH and more output space (updated
- avail_out) but no more input data, until it returns with Z_STREAM_END or an
- error. After deflate has returned Z_STREAM_END, the only possible operations
- on the stream are deflateReset or deflateEnd.
-
- Z_FINISH can be used in the first deflate call after deflateInit if all the
- compression is to be done in a single step. In order to complete in one
- call, avail_out must be at least the value returned by deflateBound (see
- below). Then deflate is guaranteed to return Z_STREAM_END. If not enough
- output space is provided, deflate will not return Z_STREAM_END, and it must
- be called again as described above.
-
- deflate() sets strm->adler to the Adler-32 checksum of all input read
- so far (that is, total_in bytes). If a gzip stream is being generated, then
- strm->adler will be the CRC-32 checksum of the input read so far. (See
- deflateInit2 below.)
-
- deflate() may update strm->data_type if it can make a good guess about
- the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is
- considered binary. This field is only for information purposes and does not
- affect the compression algorithm in any manner.
-
- deflate() returns Z_OK if some progress has been made (more input
- processed or more output produced), Z_STREAM_END if all input has been
- consumed and all output has been produced (only when flush is set to
- Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
- if next_in or next_out was Z_NULL or the state was inadvertently written over
- by the application), or Z_BUF_ERROR if no progress is possible (for example
- avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and
- deflate() can be called again with more input and more output space to
- continue compressing.
-*/
-
-
-ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
-/*
- All dynamically allocated data structures for this stream are freed.
- This function discards any unprocessed input and does not flush any pending
- output.
-
- deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
- stream state was inconsistent, Z_DATA_ERROR if the stream was freed
- prematurely (some input or output was discarded). In the error case, msg
- may be set but then points to a static string (which must not be
- deallocated).
-*/
-
-
-/*
-ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
-
- Initializes the internal stream state for decompression. The fields
- next_in, avail_in, zalloc, zfree and opaque must be initialized before by
- the caller. In the current version of inflate, the provided input is not
- read or consumed. The allocation of a sliding window will be deferred to
- the first call of inflate (if the decompression does not complete on the
- first call). If zalloc and zfree are set to Z_NULL, inflateInit updates
- them to use default allocation functions.
-
- inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
- memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
- version assumed by the caller, or Z_STREAM_ERROR if the parameters are
- invalid, such as a null pointer to the structure. msg is set to null if
- there is no error message. inflateInit does not perform any decompression.
- Actual decompression will be done by inflate(). So next_in, and avail_in,
- next_out, and avail_out are unused and unchanged. The current
- implementation of inflateInit() does not process any header information --
- that is deferred until inflate() is called.
-*/
-
-
-ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
-/*
- inflate decompresses as much data as possible, and stops when the input
- buffer becomes empty or the output buffer becomes full. It may introduce
- some output latency (reading input without producing any output) except when
- forced to flush.
-
- The detailed semantics are as follows. inflate performs one or both of the
- following actions:
-
- - Decompress more input starting at next_in and update next_in and avail_in
- accordingly. If not all input can be processed (because there is not
- enough room in the output buffer), then next_in and avail_in are updated
- accordingly, and processing will resume at this point for the next call of
- inflate().
-
- - Generate more output starting at next_out and update next_out and avail_out
- accordingly. inflate() provides as much output as possible, until there is
- no more input data or no more space in the output buffer (see below about
- the flush parameter).
-
- Before the call of inflate(), the application should ensure that at least
- one of the actions is possible, by providing more input and/or consuming more
- output, and updating the next_* and avail_* values accordingly. If the
- caller of inflate() does not provide both available input and available
- output space, it is possible that there will be no progress made. The
- application can consume the uncompressed output when it wants, for example
- when the output buffer is full (avail_out == 0), or after each call of
- inflate(). If inflate returns Z_OK and with zero avail_out, it must be
- called again after making room in the output buffer because there might be
- more output pending.
-
- The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH,
- Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much
- output as possible to the output buffer. Z_BLOCK requests that inflate()
- stop if and when it gets to the next deflate block boundary. When decoding
- the zlib or gzip format, this will cause inflate() to return immediately
- after the header and before the first block. When doing a raw inflate,
- inflate() will go ahead and process the first block, and will return when it
- gets to the end of that block, or when it runs out of data.
-
- The Z_BLOCK option assists in appending to or combining deflate streams.
- To assist in this, on return inflate() always sets strm->data_type to the
- number of unused bits in the last byte taken from strm->next_in, plus 64 if
- inflate() is currently decoding the last block in the deflate stream, plus
- 128 if inflate() returned immediately after decoding an end-of-block code or
- decoding the complete header up to just before the first byte of the deflate
- stream. The end-of-block will not be indicated until all of the uncompressed
- data from that block has been written to strm->next_out. The number of
- unused bits may in general be greater than seven, except when bit 7 of
- data_type is set, in which case the number of unused bits will be less than
- eight. data_type is set as noted here every time inflate() returns for all
- flush options, and so can be used to determine the amount of currently
- consumed input in bits.
-
- The Z_TREES option behaves as Z_BLOCK does, but it also returns when the
- end of each deflate block header is reached, before any actual data in that
- block is decoded. This allows the caller to determine the length of the
- deflate block header for later use in random access within a deflate block.
- 256 is added to the value of strm->data_type when inflate() returns
- immediately after reaching the end of the deflate block header.
-
- inflate() should normally be called until it returns Z_STREAM_END or an
- error. However if all decompression is to be performed in a single step (a
- single call of inflate), the parameter flush should be set to Z_FINISH. In
- this case all pending input is processed and all pending output is flushed;
- avail_out must be large enough to hold all of the uncompressed data for the
- operation to complete. (The size of the uncompressed data may have been
- saved by the compressor for this purpose.) The use of Z_FINISH is not
- required to perform an inflation in one step. However it may be used to
- inform inflate that a faster approach can be used for the single inflate()
- call. Z_FINISH also informs inflate to not maintain a sliding window if the
- stream completes, which reduces inflate's memory footprint. If the stream
- does not complete, either because not all of the stream is provided or not
- enough output space is provided, then a sliding window will be allocated and
- inflate() can be called again to continue the operation as if Z_NO_FLUSH had
- been used.
-
- In this implementation, inflate() always flushes as much output as
- possible to the output buffer, and always uses the faster approach on the
- first call. So the effects of the flush parameter in this implementation are
- on the return value of inflate() as noted below, when inflate() returns early
- when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of
- memory for a sliding window when Z_FINISH is used.
-
- If a preset dictionary is needed after this call (see inflateSetDictionary
- below), inflate sets strm->adler to the Adler-32 checksum of the dictionary
- chosen by the compressor and returns Z_NEED_DICT; otherwise it sets
- strm->adler to the Adler-32 checksum of all output produced so far (that is,
- total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described
- below. At the end of the stream, inflate() checks that its computed Adler-32
- checksum is equal to that saved by the compressor and returns Z_STREAM_END
- only if the checksum is correct.
-
- inflate() can decompress and check either zlib-wrapped or gzip-wrapped
- deflate data. The header type is detected automatically, if requested when
- initializing with inflateInit2(). Any information contained in the gzip
- header is not retained unless inflateGetHeader() is used. When processing
- gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output
- produced so far. The CRC-32 is checked against the gzip trailer, as is the
- uncompressed length, modulo 2^32.
-
- inflate() returns Z_OK if some progress has been made (more input processed
- or more output produced), Z_STREAM_END if the end of the compressed data has
- been reached and all uncompressed output has been produced, Z_NEED_DICT if a
- preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
- corrupted (input stream not conforming to the zlib format or incorrect check
- value, in which case strm->msg points to a string with a more specific
- error), Z_STREAM_ERROR if the stream structure was inconsistent (for example
- next_in or next_out was Z_NULL, or the state was inadvertently written over
- by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR
- if no progress was possible or if there was not enough room in the output
- buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and
- inflate() can be called again with more input and more output space to
- continue decompressing. If Z_DATA_ERROR is returned, the application may
- then call inflateSync() to look for a good compression block if a partial
- recovery of the data is to be attempted.
-*/
-
-
-ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
-/*
- All dynamically allocated data structures for this stream are freed.
- This function discards any unprocessed input and does not flush any pending
- output.
-
- inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state
- was inconsistent.
-*/
-
-
- /* Advanced functions */
-
-/*
- The following functions are needed only in some special applications.
-*/
-
-/*
-ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
- int level,
- int method,
- int windowBits,
- int memLevel,
- int strategy));
-
- This is another version of deflateInit with more compression options. The
- fields next_in, zalloc, zfree and opaque must be initialized before by the
- caller.
-
- The method parameter is the compression method. It must be Z_DEFLATED in
- this version of the library.
-
- The windowBits parameter is the base two logarithm of the window size
- (the size of the history buffer). It should be in the range 8..15 for this
- version of the library. Larger values of this parameter result in better
- compression at the expense of memory usage. The default value is 15 if
- deflateInit is used instead.
-
- For the current implementation of deflate(), a windowBits value of 8 (a
- window size of 256 bytes) is not supported. As a result, a request for 8
- will result in 9 (a 512-byte window). In that case, providing 8 to
- inflateInit2() will result in an error when the zlib header with 9 is
- checked against the initialization of inflate(). The remedy is to not use 8
- with deflateInit2() with this initialization, or at least in that case use 9
- with inflateInit2().
-
- windowBits can also be -8..-15 for raw deflate. In this case, -windowBits
- determines the window size. deflate() will then generate raw deflate data
- with no zlib header or trailer, and will not compute a check value.
-
- windowBits can also be greater than 15 for optional gzip encoding. Add
- 16 to windowBits to write a simple gzip header and trailer around the
- compressed data instead of a zlib wrapper. The gzip header will have no
- file name, no extra data, no comment, no modification time (set to zero), no
- header crc, and the operating system will be set to the appropriate value,
- if the operating system was determined at compile time. If a gzip stream is
- being written, strm->adler is a CRC-32 instead of an Adler-32.
-
- For raw deflate or gzip encoding, a request for a 256-byte window is
- rejected as invalid, since only the zlib header provides a means of
- transmitting the window size to the decompressor.
-
- The memLevel parameter specifies how much memory should be allocated
- for the internal compression state. memLevel=1 uses minimum memory but is
- slow and reduces compression ratio; memLevel=9 uses maximum memory for
- optimal speed. The default value is 8. See zconf.h for total memory usage
- as a function of windowBits and memLevel.
-
- The strategy parameter is used to tune the compression algorithm. Use the
- value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
- filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no
- string match), or Z_RLE to limit match distances to one (run-length
- encoding). Filtered data consists mostly of small values with a somewhat
- random distribution. In this case, the compression algorithm is tuned to
- compress them better. The effect of Z_FILTERED is to force more Huffman
- coding and less string matching; it is somewhat intermediate between
- Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as
- fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The
- strategy parameter only affects the compression ratio but not the
- correctness of the compressed output even if it is not set appropriately.
- Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler
- decoder for special applications.
-
- deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
- memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid
- method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is
- incompatible with the version assumed by the caller (ZLIB_VERSION). msg is
- set to null if there is no error message. deflateInit2 does not perform any
- compression: this will be done by deflate().
-*/
-
-ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
- const Bytef *dictionary,
- uInt dictLength));
-/*
- Initializes the compression dictionary from the given byte sequence
- without producing any compressed output. When using the zlib format, this
- function must be called immediately after deflateInit, deflateInit2 or
- deflateReset, and before any call of deflate. When doing raw deflate, this
- function must be called either before any call of deflate, or immediately
- after the completion of a deflate block, i.e. after all input has been
- consumed and all output has been delivered when using any of the flush
- options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The
- compressor and decompressor must use exactly the same dictionary (see
- inflateSetDictionary).
-
- The dictionary should consist of strings (byte sequences) that are likely
- to be encountered later in the data to be compressed, with the most commonly
- used strings preferably put towards the end of the dictionary. Using a
- dictionary is most useful when the data to be compressed is short and can be
- predicted with good accuracy; the data can then be compressed better than
- with the default empty dictionary.
-
- Depending on the size of the compression data structures selected by
- deflateInit or deflateInit2, a part of the dictionary may in effect be
- discarded, for example if the dictionary is larger than the window size
- provided in deflateInit or deflateInit2. Thus the strings most likely to be
- useful should be put at the end of the dictionary, not at the front. In
- addition, the current implementation of deflate will use at most the window
- size minus 262 bytes of the provided dictionary.
-
- Upon return of this function, strm->adler is set to the Adler-32 value
- of the dictionary; the decompressor may later use this value to determine
- which dictionary has been used by the compressor. (The Adler-32 value
- applies to the whole dictionary even if only a subset of the dictionary is
- actually used by the compressor.) If a raw deflate was requested, then the
- Adler-32 value is not computed and strm->adler is not set.
-
- deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
- parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is
- inconsistent (for example if deflate has already been called for this stream
- or if not at a block boundary for raw deflate). deflateSetDictionary does
- not perform any compression: this will be done by deflate().
-*/
-
-ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm,
- Bytef *dictionary,
- uInt *dictLength));
-/*
- Returns the sliding dictionary being maintained by deflate. dictLength is
- set to the number of bytes in the dictionary, and that many bytes are copied
- to dictionary. dictionary must have enough space, where 32768 bytes is
- always enough. If deflateGetDictionary() is called with dictionary equal to
- Z_NULL, then only the dictionary length is returned, and nothing is copied.
- Similary, if dictLength is Z_NULL, then it is not set.
-
- deflateGetDictionary() may return a length less than the window size, even
- when more than the window size in input has been provided. It may return up
- to 258 bytes less in that case, due to how zlib's implementation of deflate
- manages the sliding window and lookahead for matches, where matches can be
- up to 258 bytes long. If the application needs the last window-size bytes of
- input, then that would need to be saved by the application outside of zlib.
-
- deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the
- stream state is inconsistent.
-*/
-
-ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
- z_streamp source));
-/*
- Sets the destination stream as a complete copy of the source stream.
-
- This function can be useful when several compression strategies will be
- tried, for example when there are several ways of pre-processing the input
- data with a filter. The streams that will be discarded should then be freed
- by calling deflateEnd. Note that deflateCopy duplicates the internal
- compression state which can be quite large, so this strategy is slow and can
- consume lots of memory.
-
- deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
- enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
- (such as zalloc being Z_NULL). msg is left unchanged in both source and
- destination.
-*/
-
-ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
-/*
- This function is equivalent to deflateEnd followed by deflateInit, but
- does not free and reallocate the internal compression state. The stream
- will leave the compression level and any other attributes that may have been
- set unchanged.
-
- deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
- stream state was inconsistent (such as zalloc or state being Z_NULL).
-*/
-
-ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
- int level,
- int strategy));
-/*
- Dynamically update the compression level and compression strategy. The
- interpretation of level and strategy is as in deflateInit2(). This can be
- used to switch between compression and straight copy of the input data, or
- to switch to a different kind of input data requiring a different strategy.
- If the compression approach (which is a function of the level) or the
- strategy is changed, and if any input has been consumed in a previous
- deflate() call, then the input available so far is compressed with the old
- level and strategy using deflate(strm, Z_BLOCK). There are three approaches
- for the compression levels 0, 1..3, and 4..9 respectively. The new level
- and strategy will take effect at the next call of deflate().
-
- If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does
- not have enough output space to complete, then the parameter change will not
- take effect. In this case, deflateParams() can be called again with the
- same parameters and more output space to try again.
-
- In order to assure a change in the parameters on the first try, the
- deflate stream should be flushed using deflate() with Z_BLOCK or other flush
- request until strm.avail_out is not zero, before calling deflateParams().
- Then no more input data should be provided before the deflateParams() call.
- If this is done, the old level and strategy will be applied to the data
- compressed before deflateParams(), and the new level and strategy will be
- applied to the the data compressed after deflateParams().
-
- deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream
- state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if
- there was not enough output space to complete the compression of the
- available input data before a change in the strategy or approach. Note that
- in the case of a Z_BUF_ERROR, the parameters are not changed. A return
- value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be
- retried with more output space.
-*/
-
-ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm,
- int good_length,
- int max_lazy,
- int nice_length,
- int max_chain));
-/*
- Fine tune deflate's internal compression parameters. This should only be
- used by someone who understands the algorithm used by zlib's deflate for
- searching for the best matching string, and even then only by the most
- fanatic optimizer trying to squeeze out the last compressed bit for their
- specific input data. Read the deflate.c source code for the meaning of the
- max_lazy, good_length, nice_length, and max_chain parameters.
-
- deflateTune() can be called after deflateInit() or deflateInit2(), and
- returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream.
- */
-
-ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm,
- uLong sourceLen));
-/*
- deflateBound() returns an upper bound on the compressed size after
- deflation of sourceLen bytes. It must be called after deflateInit() or
- deflateInit2(), and after deflateSetHeader(), if used. This would be used
- to allocate an output buffer for deflation in a single pass, and so would be
- called before deflate(). If that first deflate() call is provided the
- sourceLen input bytes, an output buffer allocated to the size returned by
- deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed
- to return Z_STREAM_END. Note that it is possible for the compressed size to
- be larger than the value returned by deflateBound() if flush options other
- than Z_FINISH or Z_NO_FLUSH are used.
-*/
-
-ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm,
- unsigned *pending,
- int *bits));
-/*
- deflatePending() returns the number of bytes and bits of output that have
- been generated, but not yet provided in the available output. The bytes not
- provided would be due to the available output space having being consumed.
- The number of bits of output not provided are between 0 and 7, where they
- await more bits to join them in order to fill out a full byte. If pending
- or bits are Z_NULL, then those values are not set.
-
- deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source
- stream state was inconsistent.
- */
-
-ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,
- int bits,
- int value));
-/*
- deflatePrime() inserts bits in the deflate output stream. The intent
- is that this function is used to start off the deflate output with the bits
- leftover from a previous deflate stream when appending to it. As such, this
- function can only be used for raw deflate, and must be used before the first
- deflate() call after a deflateInit2() or deflateReset(). bits must be less
- than or equal to 16, and that many of the least significant bits of value
- will be inserted in the output.
-
- deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough
- room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the
- source stream state was inconsistent.
-*/
-
-ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm,
- gz_headerp head));
-/*
- deflateSetHeader() provides gzip header information for when a gzip
- stream is requested by deflateInit2(). deflateSetHeader() may be called
- after deflateInit2() or deflateReset() and before the first call of
- deflate(). The text, time, os, extra field, name, and comment information
- in the provided gz_header structure are written to the gzip header (xflag is
- ignored -- the extra flags are set according to the compression level). The
- caller must assure that, if not Z_NULL, name and comment are terminated with
- a zero byte, and that if extra is not Z_NULL, that extra_len bytes are
- available there. If hcrc is true, a gzip header crc is included. Note that
- the current versions of the command-line version of gzip (up through version
- 1.3.x) do not support header crc's, and will report that it is a "multi-part
- gzip file" and give up.
-
- If deflateSetHeader is not used, the default gzip header has text false,
- the time set to zero, and os set to 255, with no extra, name, or comment
- fields. The gzip header is returned to the default state by deflateReset().
-
- deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
- stream state was inconsistent.
-*/
-
-/*
-ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
- int windowBits));
-
- This is another version of inflateInit with an extra parameter. The
- fields next_in, avail_in, zalloc, zfree and opaque must be initialized
- before by the caller.
-
- The windowBits parameter is the base two logarithm of the maximum window
- size (the size of the history buffer). It should be in the range 8..15 for
- this version of the library. The default value is 15 if inflateInit is used
- instead. windowBits must be greater than or equal to the windowBits value
- provided to deflateInit2() while compressing, or it must be equal to 15 if
- deflateInit2() was not used. If a compressed stream with a larger window
- size is given as input, inflate() will return with the error code
- Z_DATA_ERROR instead of trying to allocate a larger window.
-
- windowBits can also be zero to request that inflate use the window size in
- the zlib header of the compressed stream.
-
- windowBits can also be -8..-15 for raw inflate. In this case, -windowBits
- determines the window size. inflate() will then process raw deflate data,
- not looking for a zlib or gzip header, not generating a check value, and not
- looking for any check values for comparison at the end of the stream. This
- is for use with other formats that use the deflate compressed data format
- such as zip. Those formats provide their own check values. If a custom
- format is developed using the raw deflate format for compressed data, it is
- recommended that a check value such as an Adler-32 or a CRC-32 be applied to
- the uncompressed data as is done in the zlib, gzip, and zip formats. For
- most applications, the zlib format should be used as is. Note that comments
- above on the use in deflateInit2() applies to the magnitude of windowBits.
-
- windowBits can also be greater than 15 for optional gzip decoding. Add
- 32 to windowBits to enable zlib and gzip decoding with automatic header
- detection, or add 16 to decode only the gzip format (the zlib format will
- return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a
- CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see
- below), inflate() will not automatically decode concatenated gzip streams.
- inflate() will return Z_STREAM_END at the end of the gzip stream. The state
- would need to be reset to continue decoding a subsequent gzip stream.
-
- inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
- memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
- version assumed by the caller, or Z_STREAM_ERROR if the parameters are
- invalid, such as a null pointer to the structure. msg is set to null if
- there is no error message. inflateInit2 does not perform any decompression
- apart from possibly reading the zlib header if present: actual decompression
- will be done by inflate(). (So next_in and avail_in may be modified, but
- next_out and avail_out are unused and unchanged.) The current implementation
- of inflateInit2() does not process any header information -- that is
- deferred until inflate() is called.
-*/
-
-ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
- const Bytef *dictionary,
- uInt dictLength));
-/*
- Initializes the decompression dictionary from the given uncompressed byte
- sequence. This function must be called immediately after a call of inflate,
- if that call returned Z_NEED_DICT. The dictionary chosen by the compressor
- can be determined from the Adler-32 value returned by that call of inflate.
- The compressor and decompressor must use exactly the same dictionary (see
- deflateSetDictionary). For raw inflate, this function can be called at any
- time to set the dictionary. If the provided dictionary is smaller than the
- window and there is already data in the window, then the provided dictionary
- will amend what's there. The application must insure that the dictionary
- that was used for compression is provided.
-
- inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
- parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is
- inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
- expected one (incorrect Adler-32 value). inflateSetDictionary does not
- perform any decompression: this will be done by subsequent calls of
- inflate().
-*/
-
-ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm,
- Bytef *dictionary,
- uInt *dictLength));
-/*
- Returns the sliding dictionary being maintained by inflate. dictLength is
- set to the number of bytes in the dictionary, and that many bytes are copied
- to dictionary. dictionary must have enough space, where 32768 bytes is
- always enough. If inflateGetDictionary() is called with dictionary equal to
- Z_NULL, then only the dictionary length is returned, and nothing is copied.
- Similary, if dictLength is Z_NULL, then it is not set.
-
- inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the
- stream state is inconsistent.
-*/
-
-ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
-/*
- Skips invalid compressed data until a possible full flush point (see above
- for the description of deflate with Z_FULL_FLUSH) can be found, or until all
- available input is skipped. No output is provided.
-
- inflateSync searches for a 00 00 FF FF pattern in the compressed data.
- All full flush points have this pattern, but not all occurrences of this
- pattern are full flush points.
-
- inflateSync returns Z_OK if a possible full flush point has been found,
- Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point
- has been found, or Z_STREAM_ERROR if the stream structure was inconsistent.
- In the success case, the application may save the current current value of
- total_in which indicates where valid compressed data was found. In the
- error case, the application may repeatedly call inflateSync, providing more
- input each time, until success or end of the input data.
-*/
-
-ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest,
- z_streamp source));
-/*
- Sets the destination stream as a complete copy of the source stream.
-
- This function can be useful when randomly accessing a large stream. The
- first pass through the stream can periodically record the inflate state,
- allowing restarting inflate at those points when randomly accessing the
- stream.
-
- inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
- enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
- (such as zalloc being Z_NULL). msg is left unchanged in both source and
- destination.
-*/
-
-ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
-/*
- This function is equivalent to inflateEnd followed by inflateInit,
- but does not free and reallocate the internal decompression state. The
- stream will keep attributes that may have been set by inflateInit2.
-
- inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
- stream state was inconsistent (such as zalloc or state being Z_NULL).
-*/
-
-ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm,
- int windowBits));
-/*
- This function is the same as inflateReset, but it also permits changing
- the wrap and window size requests. The windowBits parameter is interpreted
- the same as it is for inflateInit2. If the window size is changed, then the
- memory allocated for the window is freed, and the window will be reallocated
- by inflate() if needed.
-
- inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source
- stream state was inconsistent (such as zalloc or state being Z_NULL), or if
- the windowBits parameter is invalid.
-*/
-
-ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm,
- int bits,
- int value));
-/*
- This function inserts bits in the inflate input stream. The intent is
- that this function is used to start inflating at a bit position in the
- middle of a byte. The provided bits will be used before any bytes are used
- from next_in. This function should only be used with raw inflate, and
- should be used before the first inflate() call after inflateInit2() or
- inflateReset(). bits must be less than or equal to 16, and that many of the
- least significant bits of value will be inserted in the input.
-
- If bits is negative, then the input stream bit buffer is emptied. Then
- inflatePrime() can be called again to put bits in the buffer. This is used
- to clear out bits leftover after feeding inflate a block description prior
- to feeding inflate codes.
-
- inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
- stream state was inconsistent.
-*/
-
-ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm));
-/*
- This function returns two values, one in the lower 16 bits of the return
- value, and the other in the remaining upper bits, obtained by shifting the
- return value down 16 bits. If the upper value is -1 and the lower value is
- zero, then inflate() is currently decoding information outside of a block.
- If the upper value is -1 and the lower value is non-zero, then inflate is in
- the middle of a stored block, with the lower value equaling the number of
- bytes from the input remaining to copy. If the upper value is not -1, then
- it is the number of bits back from the current bit position in the input of
- the code (literal or length/distance pair) currently being processed. In
- that case the lower value is the number of bytes already emitted for that
- code.
-
- A code is being processed if inflate is waiting for more input to complete
- decoding of the code, or if it has completed decoding but is waiting for
- more output space to write the literal or match data.
-
- inflateMark() is used to mark locations in the input data for random
- access, which may be at bit positions, and to note those cases where the
- output of a code may span boundaries of random access blocks. The current
- location in the input stream can be determined from avail_in and data_type
- as noted in the description for the Z_BLOCK flush parameter for inflate.
-
- inflateMark returns the value noted above, or -65536 if the provided
- source stream state was inconsistent.
-*/
-
-ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm,
- gz_headerp head));
-/*
- inflateGetHeader() requests that gzip header information be stored in the
- provided gz_header structure. inflateGetHeader() may be called after
- inflateInit2() or inflateReset(), and before the first call of inflate().
- As inflate() processes the gzip stream, head->done is zero until the header
- is completed, at which time head->done is set to one. If a zlib stream is
- being decoded, then head->done is set to -1 to indicate that there will be
- no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be
- used to force inflate() to return immediately after header processing is
- complete and before any actual data is decompressed.
-
- The text, time, xflags, and os fields are filled in with the gzip header
- contents. hcrc is set to true if there is a header CRC. (The header CRC
- was valid if done is set to one.) If extra is not Z_NULL, then extra_max
- contains the maximum number of bytes to write to extra. Once done is true,
- extra_len contains the actual extra field length, and extra contains the
- extra field, or that field truncated if extra_max is less than extra_len.
- If name is not Z_NULL, then up to name_max characters are written there,
- terminated with a zero unless the length is greater than name_max. If
- comment is not Z_NULL, then up to comm_max characters are written there,
- terminated with a zero unless the length is greater than comm_max. When any
- of extra, name, or comment are not Z_NULL and the respective field is not
- present in the header, then that field is set to Z_NULL to signal its
- absence. This allows the use of deflateSetHeader() with the returned
- structure to duplicate the header. However if those fields are set to
- allocated memory, then the application will need to save those pointers
- elsewhere so that they can be eventually freed.
-
- If inflateGetHeader is not used, then the header information is simply
- discarded. The header is always checked for validity, including the header
- CRC if present. inflateReset() will reset the process to discard the header
- information. The application would need to call inflateGetHeader() again to
- retrieve the header from the next gzip stream.
-
- inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
- stream state was inconsistent.
-*/
-
-/*
-ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits,
- unsigned char FAR *window));
-
- Initialize the internal stream state for decompression using inflateBack()
- calls. The fields zalloc, zfree and opaque in strm must be initialized
- before the call. If zalloc and zfree are Z_NULL, then the default library-
- derived memory allocation routines are used. windowBits is the base two
- logarithm of the window size, in the range 8..15. window is a caller
- supplied buffer of that size. Except for special applications where it is
- assured that deflate was used with small window sizes, windowBits must be 15
- and a 32K byte window must be supplied to be able to decompress general
- deflate streams.
-
- See inflateBack() for the usage of these routines.
-
- inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of
- the parameters are invalid, Z_MEM_ERROR if the internal state could not be
- allocated, or Z_VERSION_ERROR if the version of the library does not match
- the version of the header file.
-*/
-
-typedef unsigned (*in_func) OF((void FAR *,
- z_const unsigned char FAR * FAR *));
-typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned));
-
-ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm,
- in_func in, void FAR *in_desc,
- out_func out, void FAR *out_desc));
-/*
- inflateBack() does a raw inflate with a single call using a call-back
- interface for input and output. This is potentially more efficient than
- inflate() for file i/o applications, in that it avoids copying between the
- output and the sliding window by simply making the window itself the output
- buffer. inflate() can be faster on modern CPUs when used with large
- buffers. inflateBack() trusts the application to not change the output
- buffer passed by the output function, at least until inflateBack() returns.
-
- inflateBackInit() must be called first to allocate the internal state
- and to initialize the state with the user-provided window buffer.
- inflateBack() may then be used multiple times to inflate a complete, raw
- deflate stream with each call. inflateBackEnd() is then called to free the
- allocated state.
-
- A raw deflate stream is one with no zlib or gzip header or trailer.
- This routine would normally be used in a utility that reads zip or gzip
- files and writes out uncompressed files. The utility would decode the
- header and process the trailer on its own, hence this routine expects only
- the raw deflate stream to decompress. This is different from the default
- behavior of inflate(), which expects a zlib header and trailer around the
- deflate stream.
-
- inflateBack() uses two subroutines supplied by the caller that are then
- called by inflateBack() for input and output. inflateBack() calls those
- routines until it reads a complete deflate stream and writes out all of the
- uncompressed data, or until it encounters an error. The function's
- parameters and return types are defined above in the in_func and out_func
- typedefs. inflateBack() will call in(in_desc, &buf) which should return the
- number of bytes of provided input, and a pointer to that input in buf. If
- there is no input available, in() must return zero -- buf is ignored in that
- case -- and inflateBack() will return a buffer error. inflateBack() will
- call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1].
- out() should return zero on success, or non-zero on failure. If out()
- returns non-zero, inflateBack() will return with an error. Neither in() nor
- out() are permitted to change the contents of the window provided to
- inflateBackInit(), which is also the buffer that out() uses to write from.
- The length written by out() will be at most the window size. Any non-zero
- amount of input may be provided by in().
-
- For convenience, inflateBack() can be provided input on the first call by
- setting strm->next_in and strm->avail_in. If that input is exhausted, then
- in() will be called. Therefore strm->next_in must be initialized before
- calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called
- immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in
- must also be initialized, and then if strm->avail_in is not zero, input will
- initially be taken from strm->next_in[0 .. strm->avail_in - 1].
-
- The in_desc and out_desc parameters of inflateBack() is passed as the
- first parameter of in() and out() respectively when they are called. These
- descriptors can be optionally used to pass any information that the caller-
- supplied in() and out() functions need to do their job.
-
- On return, inflateBack() will set strm->next_in and strm->avail_in to
- pass back any unused input that was provided by the last in() call. The
- return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR
- if in() or out() returned an error, Z_DATA_ERROR if there was a format error
- in the deflate stream (in which case strm->msg is set to indicate the nature
- of the error), or Z_STREAM_ERROR if the stream was not properly initialized.
- In the case of Z_BUF_ERROR, an input or output error can be distinguished
- using strm->next_in which will be Z_NULL only if in() returned an error. If
- strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning
- non-zero. (in() will always be called before out(), so strm->next_in is
- assured to be defined if out() returns non-zero.) Note that inflateBack()
- cannot return Z_OK.
-*/
-
-ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm));
-/*
- All memory allocated by inflateBackInit() is freed.
-
- inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream
- state was inconsistent.
-*/
-
-ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void));
-/* Return flags indicating compile-time options.
-
- Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other:
- 1.0: size of uInt
- 3.2: size of uLong
- 5.4: size of voidpf (pointer)
- 7.6: size of z_off_t
-
- Compiler, assembler, and debug options:
- 8: ZLIB_DEBUG
- 9: ASMV or ASMINF -- use ASM code
- 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention
- 11: 0 (reserved)
-
- One-time table building (smaller code, but not thread-safe if true):
- 12: BUILDFIXED -- build static block decoding tables when needed
- 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed
- 14,15: 0 (reserved)
-
- Library content (indicates missing functionality):
- 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking
- deflate code when not needed)
- 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect
- and decode gzip streams (to avoid linking crc code)
- 18-19: 0 (reserved)
-
- Operation variations (changes in library functionality):
- 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate
- 21: FASTEST -- deflate algorithm with only one, lowest compression level
- 22,23: 0 (reserved)
-
- The sprintf variant used by gzprintf (zero is best):
- 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format
- 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure!
- 26: 0 = returns value, 1 = void -- 1 means inferred string length returned
-
- Remainder:
- 27-31: 0 (reserved)
- */
-
-#ifndef Z_SOLO
-
- /* utility functions */
-
-/*
- The following utility functions are implemented on top of the basic
- stream-oriented functions. To simplify the interface, some default options
- are assumed (compression level and memory usage, standard memory allocation
- functions). The source code of these utility functions can be modified if
- you need special options.
-*/
-
-ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen,
- const Bytef *source, uLong sourceLen));
-/*
- Compresses the source buffer into the destination buffer. sourceLen is
- the byte length of the source buffer. Upon entry, destLen is the total size
- of the destination buffer, which must be at least the value returned by
- compressBound(sourceLen). Upon exit, destLen is the actual size of the
- compressed data. compress() is equivalent to compress2() with a level
- parameter of Z_DEFAULT_COMPRESSION.
-
- compress returns Z_OK if success, Z_MEM_ERROR if there was not
- enough memory, Z_BUF_ERROR if there was not enough room in the output
- buffer.
-*/
-
-ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen,
- const Bytef *source, uLong sourceLen,
- int level));
-/*
- Compresses the source buffer into the destination buffer. The level
- parameter has the same meaning as in deflateInit. sourceLen is the byte
- length of the source buffer. Upon entry, destLen is the total size of the
- destination buffer, which must be at least the value returned by
- compressBound(sourceLen). Upon exit, destLen is the actual size of the
- compressed data.
-
- compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
- memory, Z_BUF_ERROR if there was not enough room in the output buffer,
- Z_STREAM_ERROR if the level parameter is invalid.
-*/
-
-ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen));
-/*
- compressBound() returns an upper bound on the compressed size after
- compress() or compress2() on sourceLen bytes. It would be used before a
- compress() or compress2() call to allocate the destination buffer.
-*/
-
-ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
- const Bytef *source, uLong sourceLen));
-/*
- Decompresses the source buffer into the destination buffer. sourceLen is
- the byte length of the source buffer. Upon entry, destLen is the total size
- of the destination buffer, which must be large enough to hold the entire
- uncompressed data. (The size of the uncompressed data must have been saved
- previously by the compressor and transmitted to the decompressor by some
- mechanism outside the scope of this compression library.) Upon exit, destLen
- is the actual size of the uncompressed data.
-
- uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
- enough memory, Z_BUF_ERROR if there was not enough room in the output
- buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In
- the case where there is not enough room, uncompress() will fill the output
- buffer with the uncompressed data up to that point.
-*/
-
-ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen,
- const Bytef *source, uLong *sourceLen));
-/*
- Same as uncompress, except that sourceLen is a pointer, where the
- length of the source is *sourceLen. On return, *sourceLen is the number of
- source bytes consumed.
-*/
-
- /* gzip file access functions */
-
-/*
- This library supports reading and writing files in gzip (.gz) format with
- an interface similar to that of stdio, using the functions that start with
- "gz". The gzip format is different from the zlib format. gzip is a gzip
- wrapper, documented in RFC 1952, wrapped around a deflate stream.
-*/
-
-typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */
-
-/*
-ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
-
- Opens a gzip (.gz) file for reading or writing. The mode parameter is as
- in fopen ("rb" or "wb") but can also include a compression level ("wb9") or
- a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only
- compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F'
- for fixed code compression as in "wb9F". (See the description of
- deflateInit2 for more information about the strategy parameter.) 'T' will
- request transparent writing or appending with no compression and not using
- the gzip format.
-
- "a" can be used instead of "w" to request that the gzip stream that will
- be written be appended to the file. "+" will result in an error, since
- reading and writing to the same gzip file is not supported. The addition of
- "x" when writing will create the file exclusively, which fails if the file
- already exists. On systems that support it, the addition of "e" when
- reading or writing will set the flag to close the file on an execve() call.
-
- These functions, as well as gzip, will read and decode a sequence of gzip
- streams in a file. The append function of gzopen() can be used to create
- such a file. (Also see gzflush() for another way to do this.) When
- appending, gzopen does not test whether the file begins with a gzip stream,
- nor does it look for the end of the gzip streams to begin appending. gzopen
- will simply append a gzip stream to the existing file.
-
- gzopen can be used to read a file which is not in gzip format; in this
- case gzread will directly read from the file without decompression. When
- reading, this will be detected automatically by looking for the magic two-
- byte gzip header.
-
- gzopen returns NULL if the file could not be opened, if there was
- insufficient memory to allocate the gzFile state, or if an invalid mode was
- specified (an 'r', 'w', or 'a' was not provided, or '+' was provided).
- errno can be checked to determine if the reason gzopen failed was that the
- file could not be opened.
-*/
-
-ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
-/*
- gzdopen associates a gzFile with the file descriptor fd. File descriptors
- are obtained from calls like open, dup, creat, pipe or fileno (if the file
- has been previously opened with fopen). The mode parameter is as in gzopen.
-
- The next call of gzclose on the returned gzFile will also close the file
- descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor
- fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd,
- mode);. The duplicated descriptor should be saved to avoid a leak, since
- gzdopen does not close fd if it fails. If you are using fileno() to get the
- file descriptor from a FILE *, then you will have to use dup() to avoid
- double-close()ing the file descriptor. Both gzclose() and fclose() will
- close the associated file descriptor, so they need to have different file
- descriptors.
-
- gzdopen returns NULL if there was insufficient memory to allocate the
- gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not
- provided, or '+' was provided), or if fd is -1. The file descriptor is not
- used until the next gz* read, write, seek, or close operation, so gzdopen
- will not detect if fd is invalid (unless fd is -1).
-*/
-
-ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size));
-/*
- Set the internal buffer size used by this library's functions. The
- default buffer size is 8192 bytes. This function must be called after
- gzopen() or gzdopen(), and before any other calls that read or write the
- file. The buffer memory allocation is always deferred to the first read or
- write. Three times that size in buffer space is allocated. A larger buffer
- size of, for example, 64K or 128K bytes will noticeably increase the speed
- of decompression (reading).
-
- The new buffer size also affects the maximum length for gzprintf().
-
- gzbuffer() returns 0 on success, or -1 on failure, such as being called
- too late.
-*/
-
-ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
-/*
- Dynamically update the compression level or strategy. See the description
- of deflateInit2 for the meaning of these parameters. Previously provided
- data is flushed before the parameter change.
-
- gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not
- opened for writing, Z_ERRNO if there is an error writing the flushed data,
- or Z_MEM_ERROR if there is a memory allocation error.
-*/
-
-ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
-/*
- Reads the given number of uncompressed bytes from the compressed file. If
- the input file is not in gzip format, gzread copies the given number of
- bytes into the buffer directly from the file.
-
- After reaching the end of a gzip stream in the input, gzread will continue
- to read, looking for another gzip stream. Any number of gzip streams may be
- concatenated in the input file, and will all be decompressed by gzread().
- If something other than a gzip stream is encountered after a gzip stream,
- that remaining trailing garbage is ignored (and no error is returned).
-
- gzread can be used to read a gzip file that is being concurrently written.
- Upon reaching the end of the input, gzread will return with the available
- data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then
- gzclearerr can be used to clear the end of file indicator in order to permit
- gzread to be tried again. Z_OK indicates that a gzip stream was completed
- on the last gzread. Z_BUF_ERROR indicates that the input file ended in the
- middle of a gzip stream. Note that gzread does not return -1 in the event
- of an incomplete gzip stream. This error is deferred until gzclose(), which
- will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip
- stream. Alternatively, gzerror can be used before gzclose to detect this
- case.
-
- gzread returns the number of uncompressed bytes actually read, less than
- len for end of file, or -1 for error. If len is too large to fit in an int,
- then nothing is read, -1 is returned, and the error state is set to
- Z_STREAM_ERROR.
-*/
-
-ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems,
- gzFile file));
-/*
- Read up to nitems items of size size from file to buf, otherwise operating
- as gzread() does. This duplicates the interface of stdio's fread(), with
- size_t request and return types. If the library defines size_t, then
- z_size_t is identical to size_t. If not, then z_size_t is an unsigned
- integer type that can contain a pointer.
-
- gzfread() returns the number of full items read of size size, or zero if
- the end of the file was reached and a full item could not be read, or if
- there was an error. gzerror() must be consulted if zero is returned in
- order to determine if there was an error. If the multiplication of size and
- nitems overflows, i.e. the product does not fit in a z_size_t, then nothing
- is read, zero is returned, and the error state is set to Z_STREAM_ERROR.
-
- In the event that the end of file is reached and only a partial item is
- available at the end, i.e. the remaining uncompressed data length is not a
- multiple of size, then the final partial item is nevetheless read into buf
- and the end-of-file flag is set. The length of the partial item read is not
- provided, but could be inferred from the result of gztell(). This behavior
- is the same as the behavior of fread() implementations in common libraries,
- but it prevents the direct use of gzfread() to read a concurrently written
- file, reseting and retrying on end-of-file, when size is not 1.
-*/
-
-ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
- voidpc buf, unsigned len));
-/*
- Writes the given number of uncompressed bytes into the compressed file.
- gzwrite returns the number of uncompressed bytes written or 0 in case of
- error.
-*/
-
-ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size,
- z_size_t nitems, gzFile file));
-/*
- gzfwrite() writes nitems items of size size from buf to file, duplicating
- the interface of stdio's fwrite(), with size_t request and return types. If
- the library defines size_t, then z_size_t is identical to size_t. If not,
- then z_size_t is an unsigned integer type that can contain a pointer.
-
- gzfwrite() returns the number of full items written of size size, or zero
- if there was an error. If the multiplication of size and nitems overflows,
- i.e. the product does not fit in a z_size_t, then nothing is written, zero
- is returned, and the error state is set to Z_STREAM_ERROR.
-*/
-
-ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...));
-/*
- Converts, formats, and writes the arguments to the compressed file under
- control of the format string, as in fprintf. gzprintf returns the number of
- uncompressed bytes actually written, or a negative zlib error code in case
- of error. The number of uncompressed bytes written is limited to 8191, or
- one less than the buffer size given to gzbuffer(). The caller should assure
- that this limit is not exceeded. If it is exceeded, then gzprintf() will
- return an error (0) with nothing written. In this case, there may also be a
- buffer overflow with unpredictable consequences, which is possible only if
- zlib was compiled with the insecure functions sprintf() or vsprintf()
- because the secure snprintf() or vsnprintf() functions were not available.
- This can be determined using zlibCompileFlags().
-*/
-
-ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
-/*
- Writes the given null-terminated string to the compressed file, excluding
- the terminating null character.
-
- gzputs returns the number of characters written, or -1 in case of error.
-*/
-
-ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
-/*
- Reads bytes from the compressed file until len-1 characters are read, or a
- newline character is read and transferred to buf, or an end-of-file
- condition is encountered. If any characters are read or if len == 1, the
- string is terminated with a null character. If no characters are read due
- to an end-of-file or len < 1, then the buffer is left untouched.
-
- gzgets returns buf which is a null-terminated string, or it returns NULL
- for end-of-file or in case of error. If there was an error, the contents at
- buf are indeterminate.
-*/
-
-ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));
-/*
- Writes c, converted to an unsigned char, into the compressed file. gzputc
- returns the value that was written, or -1 in case of error.
-*/
-
-ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
-/*
- Reads one byte from the compressed file. gzgetc returns this byte or -1
- in case of end of file or error. This is implemented as a macro for speed.
- As such, it does not do all of the checking the other functions do. I.e.
- it does not check to see if file is NULL, nor whether the structure file
- points to has been clobbered or not.
-*/
-
-ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file));
-/*
- Push one character back onto the stream to be read as the first character
- on the next read. At least one character of push-back is allowed.
- gzungetc() returns the character pushed, or -1 on failure. gzungetc() will
- fail if c is -1, and may fail if a character has been pushed but not read
- yet. If gzungetc is used immediately after gzopen or gzdopen, at least the
- output buffer size of pushed characters is allowed. (See gzbuffer above.)
- The pushed character will be discarded if the stream is repositioned with
- gzseek() or gzrewind().
-*/
-
-ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
-/*
- Flushes all pending output into the compressed file. The parameter flush
- is as in the deflate() function. The return value is the zlib error number
- (see function gzerror below). gzflush is only permitted when writing.
-
- If the flush parameter is Z_FINISH, the remaining data is written and the
- gzip stream is completed in the output. If gzwrite() is called again, a new
- gzip stream will be started in the output. gzread() is able to read such
- concatenated gzip streams.
-
- gzflush should be called only when strictly necessary because it will
- degrade compression if called too often.
-*/
-
-/*
-ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
- z_off_t offset, int whence));
-
- Sets the starting position for the next gzread or gzwrite on the given
- compressed file. The offset represents a number of bytes in the
- uncompressed data stream. The whence parameter is defined as in lseek(2);
- the value SEEK_END is not supported.
-
- If the file is opened for reading, this function is emulated but can be
- extremely slow. If the file is opened for writing, only forward seeks are
- supported; gzseek then compresses a sequence of zeroes up to the new
- starting position.
-
- gzseek returns the resulting offset location as measured in bytes from
- the beginning of the uncompressed stream, or -1 in case of error, in
- particular if the file is opened for writing and the new starting position
- would be before the current position.
-*/
-
-ZEXTERN int ZEXPORT gzrewind OF((gzFile file));
-/*
- Rewinds the given file. This function is supported only for reading.
-
- gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
-*/
-
-/*
-ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file));
-
- Returns the starting position for the next gzread or gzwrite on the given
- compressed file. This position represents a number of bytes in the
- uncompressed data stream, and is zero when starting, even if appending or
- reading a gzip stream from the middle of a file using gzdopen().
-
- gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
-*/
-
-/*
-ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file));
-
- Returns the current offset in the file being read or written. This offset
- includes the count of bytes that precede the gzip stream, for example when
- appending or when using gzdopen() for reading. When reading, the offset
- does not include as yet unused buffered input. This information can be used
- for a progress indicator. On error, gzoffset() returns -1.
-*/
-
-ZEXTERN int ZEXPORT gzeof OF((gzFile file));
-/*
- Returns true (1) if the end-of-file indicator has been set while reading,
- false (0) otherwise. Note that the end-of-file indicator is set only if the
- read tried to go past the end of the input, but came up short. Therefore,
- just like feof(), gzeof() may return false even if there is no more data to
- read, in the event that the last read request was for the exact number of
- bytes remaining in the input file. This will happen if the input file size
- is an exact multiple of the buffer size.
-
- If gzeof() returns true, then the read functions will return no more data,
- unless the end-of-file indicator is reset by gzclearerr() and the input file
- has grown since the previous end of file was detected.
-*/
-
-ZEXTERN int ZEXPORT gzdirect OF((gzFile file));
-/*
- Returns true (1) if file is being copied directly while reading, or false
- (0) if file is a gzip stream being decompressed.
-
- If the input file is empty, gzdirect() will return true, since the input
- does not contain a gzip stream.
-
- If gzdirect() is used immediately after gzopen() or gzdopen() it will
- cause buffers to be allocated to allow reading the file to determine if it
- is a gzip file. Therefore if gzbuffer() is used, it should be called before
- gzdirect().
-
- When writing, gzdirect() returns true (1) if transparent writing was
- requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note:
- gzdirect() is not needed when writing. Transparent writing must be
- explicitly requested, so the application already knows the answer. When
- linking statically, using gzdirect() will include all of the zlib code for
- gzip file reading and decompression, which may not be desired.)
-*/
-
-ZEXTERN int ZEXPORT gzclose OF((gzFile file));
-/*
- Flushes all pending output if necessary, closes the compressed file and
- deallocates the (de)compression state. Note that once file is closed, you
- cannot call gzerror with file, since its structures have been deallocated.
- gzclose must not be called more than once on the same file, just as free
- must not be called more than once on the same allocation.
-
- gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a
- file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the
- last read ended in the middle of a gzip stream, or Z_OK on success.
-*/
-
-ZEXTERN int ZEXPORT gzclose_r OF((gzFile file));
-ZEXTERN int ZEXPORT gzclose_w OF((gzFile file));
-/*
- Same as gzclose(), but gzclose_r() is only for use when reading, and
- gzclose_w() is only for use when writing or appending. The advantage to
- using these instead of gzclose() is that they avoid linking in zlib
- compression or decompression code that is not used when only reading or only
- writing respectively. If gzclose() is used, then both compression and
- decompression code will be included the application when linking to a static
- zlib library.
-*/
-
-ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
-/*
- Returns the error message for the last error which occurred on the given
- compressed file. errnum is set to zlib error number. If an error occurred
- in the file system and not in the compression library, errnum is set to
- Z_ERRNO and the application may consult errno to get the exact error code.
-
- The application must not modify the returned string. Future calls to
- this function may invalidate the previously returned string. If file is
- closed, then the string previously returned by gzerror will no longer be
- available.
-
- gzerror() should be used to distinguish errors from end-of-file for those
- functions above that do not distinguish those cases in their return values.
-*/
-
-ZEXTERN void ZEXPORT gzclearerr OF((gzFile file));
-/*
- Clears the error and end-of-file flags for file. This is analogous to the
- clearerr() function in stdio. This is useful for continuing to read a gzip
- file that is being written concurrently.
-*/
-
-#endif /* !Z_SOLO */
-
- /* checksum functions */
-
-/*
- These functions are not related to compression but are exported
- anyway because they might be useful in applications using the compression
- library.
-*/
-
-ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
-/*
- Update a running Adler-32 checksum with the bytes buf[0..len-1] and
- return the updated checksum. If buf is Z_NULL, this function returns the
- required initial value for the checksum.
-
- An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed
- much faster.
-
- Usage example:
-
- uLong adler = adler32(0L, Z_NULL, 0);
-
- while (read_buffer(buffer, length) != EOF) {
- adler = adler32(adler, buffer, length);
- }
- if (adler != original_adler) error();
-*/
-
-ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf,
- z_size_t len));
-/*
- Same as adler32(), but with a size_t length.
-*/
-
-/*
-ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2,
- z_off_t len2));
-
- Combine two Adler-32 checksums into one. For two sequences of bytes, seq1
- and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for
- each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of
- seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note
- that the z_off_t type (like off_t) is a signed integer. If len2 is
- negative, the result has no meaning or utility.
-*/
-
-ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
-/*
- Update a running CRC-32 with the bytes buf[0..len-1] and return the
- updated CRC-32. If buf is Z_NULL, this function returns the required
- initial value for the crc. Pre- and post-conditioning (one's complement) is
- performed within this function so it shouldn't be done by the application.
-
- Usage example:
-
- uLong crc = crc32(0L, Z_NULL, 0);
-
- while (read_buffer(buffer, length) != EOF) {
- crc = crc32(crc, buffer, length);
- }
- if (crc != original_crc) error();
-*/
-
-ZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf,
- z_size_t len));
-/*
- Same as crc32(), but with a size_t length.
-*/
-
-/*
-ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2));
-
- Combine two CRC-32 check values into one. For two sequences of bytes,
- seq1 and seq2 with lengths len1 and len2, CRC-32 check values were
- calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32
- check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and
- len2.
-*/
-
-
- /* various hacks, don't look :) */
-
-/* deflateInit and inflateInit are macros to allow checking the zlib version
- * and the compiler's view of z_stream:
- */
-ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
- const char *version, int stream_size));
-ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
- const char *version, int stream_size));
-ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method,
- int windowBits, int memLevel,
- int strategy, const char *version,
- int stream_size));
-ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits,
- const char *version, int stream_size));
-ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,
- unsigned char FAR *window,
- const char *version,
- int stream_size));
-#ifdef Z_PREFIX_SET
-# define z_deflateInit(strm, level) \
- deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream))
-# define z_inflateInit(strm) \
- inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream))
-# define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
- deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
- (strategy), ZLIB_VERSION, (int)sizeof(z_stream))
-# define z_inflateInit2(strm, windowBits) \
- inflateInit2_((strm), (windowBits), ZLIB_VERSION, \
- (int)sizeof(z_stream))
-# define z_inflateBackInit(strm, windowBits, window) \
- inflateBackInit_((strm), (windowBits), (window), \
- ZLIB_VERSION, (int)sizeof(z_stream))
-#else
-# define deflateInit(strm, level) \
- deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream))
-# define inflateInit(strm) \
- inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream))
-# define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
- deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
- (strategy), ZLIB_VERSION, (int)sizeof(z_stream))
-# define inflateInit2(strm, windowBits) \
- inflateInit2_((strm), (windowBits), ZLIB_VERSION, \
- (int)sizeof(z_stream))
-# define inflateBackInit(strm, windowBits, window) \
- inflateBackInit_((strm), (windowBits), (window), \
- ZLIB_VERSION, (int)sizeof(z_stream))
-#endif
-
-#ifndef Z_SOLO
-
-/* gzgetc() macro and its supporting function and exposed data structure. Note
- * that the real internal state is much larger than the exposed structure.
- * This abbreviated structure exposes just enough for the gzgetc() macro. The
- * user should not mess with these exposed elements, since their names or
- * behavior could change in the future, perhaps even capriciously. They can
- * only be used by the gzgetc() macro. You have been warned.
- */
-struct gzFile_s {
- unsigned have;
- unsigned char *next;
- z_off64_t pos;
-};
-ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */
-#ifdef Z_PREFIX_SET
-# undef z_gzgetc
-# define z_gzgetc(g) \
- ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g))
-#else
-# define gzgetc(g) \
- ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g))
-#endif
-
-/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or
- * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if
- * both are true, the application gets the *64 functions, and the regular
- * functions are changed to 64 bits) -- in case these are set on systems
- * without large file support, _LFS64_LARGEFILE must also be true
- */
-#ifdef Z_LARGE64
- ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
- ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int));
- ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile));
- ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile));
- ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t));
- ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t));
-#endif
-
-#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64)
-# ifdef Z_PREFIX_SET
-# define z_gzopen z_gzopen64
-# define z_gzseek z_gzseek64
-# define z_gztell z_gztell64
-# define z_gzoffset z_gzoffset64
-# define z_adler32_combine z_adler32_combine64
-# define z_crc32_combine z_crc32_combine64
-# else
-# define gzopen gzopen64
-# define gzseek gzseek64
-# define gztell gztell64
-# define gzoffset gzoffset64
-# define adler32_combine adler32_combine64
-# define crc32_combine crc32_combine64
-# endif
-# ifndef Z_LARGE64
- ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
- ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int));
- ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile));
- ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile));
- ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));
- ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));
-# endif
-#else
- ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *));
- ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int));
- ZEXTERN z_off_t ZEXPORT gztell OF((gzFile));
- ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile));
- ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t));
- ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));
-#endif
-
-#else /* Z_SOLO */
-
- ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t));
- ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));
-
-#endif /* !Z_SOLO */
-
-/* undocumented functions */
-ZEXTERN const char * ZEXPORT zError OF((int));
-ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp));
-ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void));
-ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int));
-ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int));
-ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp));
-ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp));
-ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp));
-#if defined(_WIN32) && !defined(Z_SOLO)
-ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path,
- const char *mode));
-#endif
-#if defined(STDC) || defined(Z_HAVE_STDARG_H)
-# ifndef Z_SOLO
-ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file,
- const char *format,
- va_list va));
-# endif
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* ZLIB_H */
+++ /dev/null
-/* zutil.c -- target dependent utility functions for the compression library
- * Copyright (C) 1995-2017 Jean-loup Gailly
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-/* @(#) $Id$ */
-
-#include "zutil.h"
-#ifndef Z_SOLO
-# include "gzguts.h"
-#endif
-
-z_const char * const z_errmsg[10] = {
- (z_const char *)"need dictionary", /* Z_NEED_DICT 2 */
- (z_const char *)"stream end", /* Z_STREAM_END 1 */
- (z_const char *)"", /* Z_OK 0 */
- (z_const char *)"file error", /* Z_ERRNO (-1) */
- (z_const char *)"stream error", /* Z_STREAM_ERROR (-2) */
- (z_const char *)"data error", /* Z_DATA_ERROR (-3) */
- (z_const char *)"insufficient memory", /* Z_MEM_ERROR (-4) */
- (z_const char *)"buffer error", /* Z_BUF_ERROR (-5) */
- (z_const char *)"incompatible version",/* Z_VERSION_ERROR (-6) */
- (z_const char *)""
-};
-
-
-const char * ZEXPORT zlibVersion()
-{
- return ZLIB_VERSION;
-}
-
-uLong ZEXPORT zlibCompileFlags()
-{
- uLong flags;
-
- flags = 0;
- switch ((int)(sizeof(uInt))) {
- case 2: break;
- case 4: flags += 1; break;
- case 8: flags += 2; break;
- default: flags += 3;
- }
- switch ((int)(sizeof(uLong))) {
- case 2: break;
- case 4: flags += 1 << 2; break;
- case 8: flags += 2 << 2; break;
- default: flags += 3 << 2;
- }
- switch ((int)(sizeof(voidpf))) {
- case 2: break;
- case 4: flags += 1 << 4; break;
- case 8: flags += 2 << 4; break;
- default: flags += 3 << 4;
- }
- switch ((int)(sizeof(z_off_t))) {
- case 2: break;
- case 4: flags += 1 << 6; break;
- case 8: flags += 2 << 6; break;
- default: flags += 3 << 6;
- }
-#ifdef ZLIB_DEBUG
- flags += 1 << 8;
-#endif
-#if defined(ASMV) || defined(ASMINF)
- flags += 1 << 9;
-#endif
-#ifdef ZLIB_WINAPI
- flags += 1 << 10;
-#endif
-#ifdef BUILDFIXED
- flags += 1 << 12;
-#endif
-#ifdef DYNAMIC_CRC_TABLE
- flags += 1 << 13;
-#endif
-#ifdef NO_GZCOMPRESS
- flags += 1L << 16;
-#endif
-#ifdef NO_GZIP
- flags += 1L << 17;
-#endif
-#ifdef PKZIP_BUG_WORKAROUND
- flags += 1L << 20;
-#endif
-#ifdef FASTEST
- flags += 1L << 21;
-#endif
-#if defined(STDC) || defined(Z_HAVE_STDARG_H)
-# ifdef NO_vsnprintf
- flags += 1L << 25;
-# ifdef HAS_vsprintf_void
- flags += 1L << 26;
-# endif
-# else
-# ifdef HAS_vsnprintf_void
- flags += 1L << 26;
-# endif
-# endif
-#else
- flags += 1L << 24;
-# ifdef NO_snprintf
- flags += 1L << 25;
-# ifdef HAS_sprintf_void
- flags += 1L << 26;
-# endif
-# else
-# ifdef HAS_snprintf_void
- flags += 1L << 26;
-# endif
-# endif
-#endif
- return flags;
-}
-
-#ifdef ZLIB_DEBUG
-#include <stdlib.h>
-# ifndef verbose
-# define verbose 0
-# endif
-int ZLIB_INTERNAL z_verbose = verbose;
-
-void ZLIB_INTERNAL z_error (m)
- char *m;
-{
- fprintf(stderr, "%s\n", m);
- exit(1);
-}
-#endif
-
-/* exported to allow conversion of error code to string for compress() and
- * uncompress()
- */
-const char * ZEXPORT zError(err)
- int err;
-{
- return ERR_MSG(err);
-}
-
-#if defined(_WIN32_WCE)
- /* The Microsoft C Run-Time Library for Windows CE doesn't have
- * errno. We define it as a global variable to simplify porting.
- * Its value is always 0 and should not be used.
- */
- int errno = 0;
-#endif
-
-#ifndef HAVE_MEMCPY
-
-void ZLIB_INTERNAL zmemcpy(dest, source, len)
- Bytef* dest;
- const Bytef* source;
- uInt len;
-{
- if (len == 0) return;
- do {
- *dest++ = *source++; /* ??? to be unrolled */
- } while (--len != 0);
-}
-
-int ZLIB_INTERNAL zmemcmp(s1, s2, len)
- const Bytef* s1;
- const Bytef* s2;
- uInt len;
-{
- uInt j;
-
- for (j = 0; j < len; j++) {
- if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1;
- }
- return 0;
-}
-
-void ZLIB_INTERNAL zmemzero(dest, len)
- Bytef* dest;
- uInt len;
-{
- if (len == 0) return;
- do {
- *dest++ = 0; /* ??? to be unrolled */
- } while (--len != 0);
-}
-#endif
-
-#ifndef Z_SOLO
-
-#ifdef SYS16BIT
-
-#ifdef __TURBOC__
-/* Turbo C in 16-bit mode */
-
-# define MY_ZCALLOC
-
-/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
- * and farmalloc(64K) returns a pointer with an offset of 8, so we
- * must fix the pointer. Warning: the pointer must be put back to its
- * original form in order to free it, use zcfree().
- */
-
-#define MAX_PTR 10
-/* 10*64K = 640K */
-
-local int next_ptr = 0;
-
-typedef struct ptr_table_s {
- voidpf org_ptr;
- voidpf new_ptr;
-} ptr_table;
-
-local ptr_table table[MAX_PTR];
-/* This table is used to remember the original form of pointers
- * to large buffers (64K). Such pointers are normalized with a zero offset.
- * Since MSDOS is not a preemptive multitasking OS, this table is not
- * protected from concurrent access. This hack doesn't work anyway on
- * a protected system like OS/2. Use Microsoft C instead.
- */
-
-voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size)
-{
- voidpf buf;
- ulg bsize = (ulg)items*size;
-
- (void)opaque;
-
- /* If we allocate less than 65520 bytes, we assume that farmalloc
- * will return a usable pointer which doesn't have to be normalized.
- */
- if (bsize < 65520L) {
- buf = farmalloc(bsize);
- if (*(ush*)&buf != 0) return buf;
- } else {
- buf = farmalloc(bsize + 16L);
- }
- if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
- table[next_ptr].org_ptr = buf;
-
- /* Normalize the pointer to seg:0 */
- *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4;
- *(ush*)&buf = 0;
- table[next_ptr++].new_ptr = buf;
- return buf;
-}
-
-void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr)
-{
- int n;
-
- (void)opaque;
-
- if (*(ush*)&ptr != 0) { /* object < 64K */
- farfree(ptr);
- return;
- }
- /* Find the original pointer */
- for (n = 0; n < next_ptr; n++) {
- if (ptr != table[n].new_ptr) continue;
-
- farfree(table[n].org_ptr);
- while (++n < next_ptr) {
- table[n-1] = table[n];
- }
- next_ptr--;
- return;
- }
- Assert(0, "zcfree: ptr not found");
-}
-
-#endif /* __TURBOC__ */
-
-
-#ifdef M_I86
-/* Microsoft C in 16-bit mode */
-
-# define MY_ZCALLOC
-
-#if (!defined(_MSC_VER) || (_MSC_VER <= 600))
-# define _halloc halloc
-# define _hfree hfree
-#endif
-
-voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size)
-{
- (void)opaque;
- return _halloc((long)items, size);
-}
-
-void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr)
-{
- (void)opaque;
- _hfree(ptr);
-}
-
-#endif /* M_I86 */
-
-#endif /* SYS16BIT */
-
-
-#ifndef MY_ZCALLOC /* Any system without a special alloc function */
-
-#ifndef STDC
-extern voidp malloc OF((uInt size));
-extern voidp calloc OF((uInt items, uInt size));
-extern void free OF((voidpf ptr));
-#endif
-
-voidpf ZLIB_INTERNAL zcalloc (opaque, items, size)
- voidpf opaque;
- unsigned items;
- unsigned size;
-{
- (void)opaque;
- return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) :
- (voidpf)calloc(items, size);
-}
-
-void ZLIB_INTERNAL zcfree (opaque, ptr)
- voidpf opaque;
- voidpf ptr;
-{
- (void)opaque;
- free(ptr);
-}
-
-#endif /* MY_ZCALLOC */
-
-#endif /* !Z_SOLO */
+++ /dev/null
-/* zutil.h -- internal interface and configuration of the compression library
- * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler
- * For conditions of distribution and use, see copyright notice in zlib.h
- */
-
-/* WARNING: this file should *not* be used by applications. It is
- part of the implementation of the compression library and is
- subject to change. Applications should only use zlib.h.
- */
-
-/* @(#) $Id$ */
-
-#ifndef ZUTIL_H
-#define ZUTIL_H
-
-#ifdef HAVE_HIDDEN
-# define ZLIB_INTERNAL __attribute__((visibility ("hidden")))
-#else
-# define ZLIB_INTERNAL
-#endif
-
-#include "zlib.h"
-
-#if defined(STDC) && !defined(Z_SOLO)
-# if !(defined(_WIN32_WCE) && defined(_MSC_VER))
-# include <stddef.h>
-# endif
-# include <string.h>
-# include <stdlib.h>
-#endif
-
-#ifdef Z_SOLO
- typedef long ptrdiff_t; /* guess -- will be caught if guess is wrong */
-#endif
-
-#ifndef local
-# define local static
-#endif
-/* since "static" is used to mean two completely different things in C, we
- define "local" for the non-static meaning of "static", for readability
- (compile with -Dlocal if your debugger can't find static symbols) */
-
-typedef unsigned char uch;
-typedef uch FAR uchf;
-typedef unsigned short ush;
-typedef ush FAR ushf;
-typedef unsigned long ulg;
-
-extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
-/* (size given to avoid silly warnings with Visual C++) */
-
-#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
-
-#define ERR_RETURN(strm,err) \
- return (strm->msg = ERR_MSG(err), (err))
-/* To be used only when the state is known to be valid */
-
- /* common constants */
-
-#ifndef DEF_WBITS
-# define DEF_WBITS MAX_WBITS
-#endif
-/* default windowBits for decompression. MAX_WBITS is for compression only */
-
-#if MAX_MEM_LEVEL >= 8
-# define DEF_MEM_LEVEL 8
-#else
-# define DEF_MEM_LEVEL MAX_MEM_LEVEL
-#endif
-/* default memLevel */
-
-#define STORED_BLOCK 0
-#define STATIC_TREES 1
-#define DYN_TREES 2
-/* The three kinds of block type */
-
-#define MIN_MATCH 3
-#define MAX_MATCH 258
-/* The minimum and maximum match lengths */
-
-#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */
-
- /* target dependencies */
-
-#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32))
-# define OS_CODE 0x00
-# ifndef Z_SOLO
-# if defined(__TURBOC__) || defined(__BORLANDC__)
-# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))
- /* Allow compilation with ANSI keywords only enabled */
- void _Cdecl farfree( void *block );
- void *_Cdecl farmalloc( unsigned long nbytes );
-# else
-# include <alloc.h>
-# endif
-# else /* MSC or DJGPP */
-# include <malloc.h>
-# endif
-# endif
-#endif
-
-#ifdef AMIGA
-# define OS_CODE 1
-#endif
-
-#if defined(VAXC) || defined(VMS)
-# define OS_CODE 2
-# define F_OPEN(name, mode) \
- fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
-#endif
-
-#ifdef __370__
-# if __TARGET_LIB__ < 0x20000000
-# define OS_CODE 4
-# elif __TARGET_LIB__ < 0x40000000
-# define OS_CODE 11
-# else
-# define OS_CODE 8
-# endif
-#endif
-
-#if defined(ATARI) || defined(atarist)
-# define OS_CODE 5
-#endif
-
-#ifdef OS2
-# define OS_CODE 6
-# if defined(M_I86) && !defined(Z_SOLO)
-# include <malloc.h>
-# endif
-#endif
-
-#if defined(MACOS) || defined(TARGET_OS_MAC)
-# define OS_CODE 7
-# ifndef Z_SOLO
-# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
-# include <unix.h> /* for fdopen */
-# else
-# ifndef fdopen
-# define fdopen(fd,mode) NULL /* No fdopen() */
-# endif
-# endif
-# endif
-#endif
-
-#ifdef __acorn
-# define OS_CODE 13
-#endif
-
-#if defined(WIN32)
-# define OS_CODE 10
-#endif
-
-#ifdef _BEOS_
-# define OS_CODE 16
-#endif
-
-#ifdef __TOS_OS400__
-# define OS_CODE 18
-#endif
-
-#ifdef __APPLE__
-# define OS_CODE 19
-#endif
-
-#if defined(_BEOS_) || defined(RISCOS)
-# define fdopen(fd,mode) NULL /* No fdopen() */
-#endif
-
-#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX
-# if defined(_WIN32_WCE)
-# define fdopen(fd,mode) NULL /* No fdopen() */
-# ifndef _PTRDIFF_T_DEFINED
- typedef int ptrdiff_t;
-# define _PTRDIFF_T_DEFINED
-# endif
-# else
-# define fdopen(fd,type) _fdopen(fd,type)
-# endif
-#endif
-
-#if defined(__BORLANDC__) && !defined(MSDOS)
- #pragma warn -8004
- #pragma warn -8008
- #pragma warn -8066
-#endif
-
-/* provide prototypes for these when building zlib without LFS */
-#if !defined(_WIN32) && \
- (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0)
- ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));
- ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));
-#endif
-
- /* common defaults */
-
-#ifndef OS_CODE
-# define OS_CODE 3 /* assume Unix */
-#endif
-
-#ifndef F_OPEN
-# define F_OPEN(name, mode) fopen((name), (mode))
-#endif
-
- /* functions */
-
-#if defined(pyr) || defined(Z_SOLO)
-# define NO_MEMCPY
-#endif
-#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__)
- /* Use our own functions for small and medium model with MSC <= 5.0.
- * You may have to use the same strategy for Borland C (untested).
- * The __SC__ check is for Symantec.
- */
-# define NO_MEMCPY
-#endif
-#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
-# define HAVE_MEMCPY
-#endif
-#ifdef HAVE_MEMCPY
-# ifdef SMALL_MEDIUM /* MSDOS small or medium model */
-# define zmemcpy _fmemcpy
-# define zmemcmp _fmemcmp
-# define zmemzero(dest, len) _fmemset(dest, 0, len)
-# else
-# define zmemcpy memcpy
-# define zmemcmp memcmp
-# define zmemzero(dest, len) memset(dest, 0, len)
-# endif
-#else
- void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len));
- int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len));
- void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len));
-#endif
-
-/* Diagnostic functions */
-#ifdef ZLIB_DEBUG
-# include <stdio.h>
- extern int ZLIB_INTERNAL z_verbose;
- extern void ZLIB_INTERNAL z_error OF((char *m));
-# define Assert(cond,msg) {if(!(cond)) z_error(msg);}
-# define Trace(x) {if (z_verbose>=0) fprintf x ;}
-# define Tracev(x) {if (z_verbose>0) fprintf x ;}
-# define Tracevv(x) {if (z_verbose>1) fprintf x ;}
-# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}
-# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;}
-#else
-# define Assert(cond,msg)
-# define Trace(x)
-# define Tracev(x)
-# define Tracevv(x)
-# define Tracec(c,x)
-# define Tracecv(c,x)
-#endif
-
-#ifndef Z_SOLO
- voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items,
- unsigned size));
- void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr));
-#endif
-
-#define ZALLOC(strm, items, size) \
- (*((strm)->zalloc))((strm)->opaque, (items), (size))
-#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))
-#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
-
-/* Reverse the bytes in a 32-bit value */
-#define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \
- (((q) & 0xff00) << 8) + (((q) & 0xff) << 24))
-
-#endif /* ZUTIL_H */
--- /dev/null
+function(addtest name)
+ add_test(
+ NAME "test.${name}"
+ COMMAND bash ${CMAKE_SOURCE_DIR}/test/run ${name}
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+ )
+ set_tests_properties(
+ "test.${name}"
+ PROPERTIES
+ ENVIRONMENT "CCACHE=${CMAKE_BINARY_DIR}/ccache;EXIT_IF_SKIPPED=true")
+
+ if(${CMAKE_VERSION} VERSION_LESS "3.9")
+ # Older CMake versions treat skipped tests as errors. Therefore, resort to
+ # parsing output for those cases (exit code is not considered). Skipped
+ # tests will appear as "Passed".
+ set_tests_properties(
+ "test.${name}"
+ PROPERTIES
+ PASS_REGULAR_EXPRESSION "PASSED|Passed|Skipped"
+ FAIL_REGULAR_EXPRESSION "[Ww]arning|[Ff]ail|[Er]rror")
+ else()
+ set_tests_properties("test.${name}" PROPERTIES SKIP_RETURN_CODE 125)
+ endif()
+
+endfunction()
+
+if(${CMAKE_VERSION} VERSION_LESS "3.15")
+ set(clean_files_prop_name ADDITIONAL_MAKE_CLEAN_FILES)
+else()
+ set(clean_files_prop_name ADDITIONAL_CLEAN_FILES)
+endif()
+set_property(
+ DIRECTORY PROPERTY
+ ${clean_files_prop_name} "${CMAKE_BINARY_DIR}/testdir")
+
+addtest(base)
+addtest(nocpp2)
+addtest(cpp1)
+addtest(multi_arch)
+addtest(serialize_diagnostics)
+addtest(color_diagnostics)
+addtest(sanitize_blacklist)
+addtest(debug_prefix_map)
+addtest(profiling)
+addtest(profiling_gcc)
+addtest(profiling_clang)
+addtest(profiling_hip_clang)
+addtest(split_dwarf)
+addtest(masquerading)
+addtest(hardlink)
+addtest(fileclone)
+addtest(direct)
+addtest(direct_gcc)
+addtest(depend)
+addtest(basedir)
+addtest(no_compression)
+addtest(readonly)
+addtest(readonly_direct)
+addtest(cache_levels)
+addtest(cleanup)
+addtest(pch)
+addtest(modules)
+addtest(upgrade)
+addtest(input_charset)
+addtest(nvcc)
+addtest(nvcc_direct)
+addtest(nvcc_ldir)
+addtest(nvcc_nocpp2)
+addtest(inode_cache)
# A simple test suite for ccache.
#
# Copyright (C) 2002-2007 Andrew Tridgell
-# Copyright (C) 2009-2020 Joel Rosdahl
+# 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
# this program; if not, write to the Free Software Foundation, Inc., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+skip_code=125
+
+# only use ansi color codes if output is to a terminal
+if [[ -t 1 ]]; then
+ ansi_boldgreen='\033[1;32m'
+ ansi_boldred='\033[1;31m'
+ ansi_bold='\033[1m'
+ ansi_reset='\033[1;0m'
+fi
+
green() {
- printf "\033[1;32m$*\033[0;0m\n"
+ printf "$ansi_boldgreen$*$ansi_reset\n"
}
red() {
- printf "\033[1;31m$*\033[0;0m\n"
+ printf "$ansi_boldred$*$ansi_reset\n"
}
bold() {
- printf "\033[1;37m$*\033[0;0m\n"
+ printf "$ansi_bold$*$ansi_reset\n"
}
test_failed() {
echo "ccache -s:"
$CCACHE -s
echo
- echo "Test data and log file have been left in $TESTDIR"
+ echo "Test data and log file have been left in $TESTDIR / $TEST_FAILED_SYMLINK"
+ symlink_testdir_on_failure
exit 1
}
touch -t 1999010100$(printf "%02u" $m) "$@"
}
+file_size() {
+ wc -c $1 | awk '{print $1}'
+}
+
+objdump_cmd() {
+ local file="$1"
+
+ if $HOST_OS_APPLE; then
+ xcrun dwarfdump -r 0 "$file"
+ elif $HOST_OS_WINDOWS || $HOST_OS_CYGWIN; then
+ # For some reason objdump only shows the basename of the file, so fall
+ # back to brute force and ignorance.
+ strings "$1"
+ else
+ objdump -W "$file"
+ fi
+}
+
+objdump_grep_cmd() {
+ if $HOST_OS_APPLE; then
+ fgrep -q "\"$1\""
+ else
+ fgrep -q ": $1"
+ fi
+}
+
expect_stat() {
local stat="$1"
local expected_value="$2"
- local value="$(echo $($CCACHE -s | fgrep "$stat" | cut -c34-))"
+ local value="$(echo $($CCACHE -s | fgrep "$stat" | cut -c33-))"
if [ "$expected_value" != "$value" ]; then
test_failed "Expected \"$stat\" to be $expected_value, actual $value"
fi
}
-expect_file_exists() {
- if [ ! -f "$1" ]; then
+expect_exists() {
+ if [ ! -e "$1" ]; then
test_failed "Expected $1 to exist, but it's missing"
fi
}
-expect_file_missing() {
- if [ -f "$1" ]; then
+expect_missing() {
+ if [ -e "$1" ]; then
test_failed "Expected $1 to be missing, but it exists"
fi
}
-expect_equal_files() {
+expect_equal_content() {
if [ ! -e "$1" ]; then
- test_failed "expect_equal_files: $1 missing"
+ test_failed "expect_equal_content: $1 missing"
fi
if [ ! -e "$2" ]; then
- test_failed "expect_equal_files: $2 missing"
+ test_failed "expect_equal_content: $2 missing"
fi
if ! cmp -s "$1" "$2"; then
test_failed "$1 and $2 differ"
fi
}
-expect_different_files() {
+expect_equal_text_content() {
if [ ! -e "$1" ]; then
- test_failed "expect_different_files: $1 missing"
+ test_failed "expect_equal_text_content: $1 missing"
fi
if [ ! -e "$2" ]; then
- test_failed "expect_different_files: $2 missing"
+ test_failed "expect_equal_text_content: $2 missing"
+ fi
+ if ! cmp -s "$1" "$2"; then
+ test_failed "$1 and $2 differ: $(echo; diff -u "$1" "$2")"
+ fi
+}
+
+expect_different_content() {
+ if [ ! -e "$1" ]; then
+ test_failed "expect_different_content: $1 missing"
+ fi
+ if [ ! -e "$2" ]; then
+ test_failed "expect_different_content: $2 missing"
fi
if cmp -s "$1" "$2"; then
test_failed "$1 and $2 are identical"
is_equal_object_files() {
if $HOST_OS_LINUX && $COMPILER_TYPE_CLANG; then
- if ! which eu-elfcmp >/dev/null 2>&1; then
+ if ! command -v eu-elfcmp >/dev/null; then
test_failed "Please install elfutils to get eu-elfcmp"
fi
eu-elfcmp -q "$1" "$2"
fi
}
-expect_file_content() {
+expect_content() {
local file="$1"
local content="$2"
- if [ ! -f "$file" ]; then
+ if [ ! -e "$file" ]; then
test_failed "$file not found"
fi
if [ "$(cat $file)" != "$content" ]; then
fi
}
+expect_contains() {
+ local file="$1"
+ local string="$2"
+
+ if [ ! -e "$file" ]; then
+ test_failed "$file not found"
+ fi
+ if ! fgrep -q "$string" "$file"; then
+ test_failed "File $file does not contain \"$string\"\nActual content: $(cat $file)"
+ fi
+}
+
+expect_not_contains() {
+ local file="$1"
+ local string="$2"
+
+ if [ ! -e "$file" ]; then
+ test_failed "$file not found"
+ fi
+ if fgrep -q "$string" "$file"; then
+ test_failed "File $file contains \"$string\"\nActual content: $(cat $file)"
+ fi
+}
+
+expect_objdump_contains() {
+ local file="$1"
+ local string="$2"
+
+ if ! objdump_cmd "$file" | objdump_grep_cmd "$string"; then
+ test_failed "File $file does not contain \"$string\""
+ fi
+}
+
+expect_objdump_not_contains() {
+ local file="$1"
+ local string="$2"
+
+ if objdump_cmd "$file" | objdump_grep_cmd "$string"; then
+ test_failed "File $file contains \"$string\""
+ fi
+}
+
expect_file_count() {
local expected=$1
local pattern=$2
}
# Verify that $1 is newer than (or same age as) $2.
-expect_file_newer_than() {
+expect_newer_than() {
local newer_file=$1
local older_file=$2
if [ "$newer_file" -ot "$older_file" ]; then
fi
}
+expect_perm() {
+ local path="$1"
+ local expected_perm="$2"
+ local actual_perm=$(ls -ld "$path" | awk '{print substr($1, 1, 10)}')
+ if [ "$expected_perm" != "$actual_perm" ]; then
+ test_failed "Expected permissions for $path to be $expected_perm, actual $actual_perm"
+ fi
+}
+
+reset_environment() {
+ while read name; do
+ unset $name
+ done <<EOF
+$(env | sed -n 's/^\(CCACHE_[A-Z0-9_]*\)=.*$/\1/p')
+EOF
+ unset GCC_COLORS
+ unset TERM
+ unset XDG_CACHE_HOME
+ unset XDG_CONFIG_HOME
+
+ export CCACHE_DETECT_SHEBANG=1
+ export CCACHE_DIR=$ABS_TESTDIR/.ccache
+ export CCACHE_CONFIGPATH=$CCACHE_DIR/ccache.conf # skip secondary config
+ export CCACHE_LOGFILE=$ABS_TESTDIR/ccache.log
+ export CCACHE_NODIRECT=1
+
+ # Many tests backdate files, which updates their ctimes. In those tests, we
+ # must ignore ctimes. Might as well do so everywhere.
+ DEFAULT_SLOPPINESS=include_file_ctime
+ export CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS"
+}
+
run_suite() {
local suite_name=$1
cd $ABS_TESTDIR
rm -rf $ABS_TESTDIR/fixture
+ reset_environment
+
if type SUITE_${suite_name}_PROBE >/dev/null 2>&1; then
mkdir $ABS_TESTDIR/probe
cd $ABS_TESTDIR/probe
rm -rf $ABS_TESTDIR/probe
if [ -n "$skip_reason" ]; then
echo "Skipped test suite $suite_name [$skip_reason]"
- return
+ if [ -n "$EXIT_IF_SKIPPED" ]; then
+ return $skip_code
+ fi
+ return 0
fi
fi
printf "Running test suite %s" "$(bold $suite_name)"
SUITE_$suite_name
echo
+
+ return 0
}
TEST() {
CURRENT_TEST=$1
-
- while read name; do
- unset $name
- done <<EOF
-$(env | sed -n 's/^\(CCACHE_[A-Z0-9_]*\)=.*$/\1/p')
-EOF
- unset GCC_COLORS
-
- export CCACHE_CONFIGPATH=$ABS_TESTDIR/ccache.conf
- export CCACHE_DETECT_SHEBANG=1
- export CCACHE_DIR=$ABS_TESTDIR/.ccache
- export CCACHE_LOGFILE=$ABS_TESTDIR/ccache.log
- export CCACHE_NODIRECT=1
-
- # Many tests backdate files, which updates their ctimes. In those tests, we
- # must ignore ctimes. Might as well do so everywhere.
- DEFAULT_SLOPPINESS=include_file_ctime
- export CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS"
-
CCACHE_COMPILE="$CCACHE $COMPILER"
- if $VERBOSE; then
+ reset_environment
+
+ if $verbose; then
printf "\n %s" "$CURRENT_TEST"
else
printf .
HOST_OS_LINUX=false
HOST_OS_FREEBSD=false
HOST_OS_WINDOWS=false
+HOST_OS_CYGWIN=false
compiler_version="`$COMPILER --version 2>&1 | head -1`"
case $compiler_version in
;;
*clang*)
COMPILER_TYPE_CLANG=true
- CLANG_VERSION_SUFFIX=$(echo $COMPILER | sed -r 's/.*clang//')
+ CLANG_VERSION_SUFFIX=$(echo $COMPILER | sed 's/.*clang//')
;;
*)
echo "WARNING: Compiler $COMPILER not supported (version: $compiler_version) -- not running tests" >&2
*MINGW*|*mingw*)
HOST_OS_WINDOWS=true
;;
+ *CYGWIN*|*MSYS*)
+ HOST_OS_CYGWIN=true
+ ;;
*Darwin*)
HOST_OS_APPLE=true
;;
fi
if $HOST_OS_APPLE; then
- # Grab the developer directory from the environment or try xcode-select
- if [ "$XCODE_DEVELOPER_DIR" = "" ]; then
- XCODE_DEVELOPER_DIR=`xcode-select --print-path`
- if [ "$XCODE_DEVELOPER_DIR" = "" ]; then
- echo "Error: XCODE_DEVELOPER_DIR environment variable not set and xcode-select path not set"
- exit 1
- fi
- fi
-
- # Choose the latest SDK if an SDK root is not set
- MAC_PLATFORM_DIR=$XCODE_DEVELOPER_DIR/Platforms/MacOSX.platform
+ SDKROOT=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)
if [ "$SDKROOT" = "" ]; then
- SDKROOT="`eval ls -f -1 -d \"$MAC_PLATFORM_DIR/Developer/SDKs/\"*.sdk | tail -1`"
- if [ "$SDKROOT" = "" ]; then
- echo "Error: Cannot find a valid SDK root directory"
- exit 1
- fi
+ echo "Error: xcrun --show-sdk-path failure"
+ exit 1
fi
SYSROOT="-isysroot `echo \"$SDKROOT\" | sed 's/ /\\ /g'`"
# ---------------------------------------
-all_suites="
-base
-nocpp2
-cpp1
-multi_arch
-serialize_diagnostics
-sanitize_blacklist
-debug_prefix_map
-profiling
-profiling_gcc
-profiling_clang
-split_dwarf
-masquerading
-hardlink
-direct
-direct_gcc
-depend
-basedir
-compression
-readonly
-readonly_direct
-cleanup
-pch
-upgrade
-input_charset
-nvcc
-nvcc_direct
-nvcc_ldir
-nvcc_nocpp2
-"
+all_suites="$(sed -rn 's/^addtest\((.*)\)$/\1/p' $(dirname $0)/CMakeLists.txt)"
for suite in $all_suites; do
. $(dirname $0)/suites/$suite.bash
# ---------------------------------------
-TESTDIR=testdir.$$
+TESTDIR=testdir/$$
+TEST_FAILED_SYMLINK=testdir/failed
ABS_TESTDIR=$PWD/$TESTDIR
rm -rf $TESTDIR
-mkdir $TESTDIR
+mkdir -p $TESTDIR
+
+START_PWD="$PWD"
+symlink_testdir_on_failure() {
+ cd "$START_PWD"
+ rm -f "$TEST_FAILED_SYMLINK"
+ ln -s "$$" "$TEST_FAILED_SYMLINK"
+}
+
cd $TESTDIR || exit 1
-compiler_bin=$(echo $COMPILER | awk '{print $1}')
-compiler_args=$(echo $COMPILER | awk '{$1 = ""; print}')
-REAL_COMPILER_BIN=$(find_compiler $compiler_bin)
-REAL_COMPILER="$REAL_COMPILER_BIN$compiler_args"
+COMPILER_BIN=$(echo $COMPILER | awk '{print $1}')
+COMPILER_ARGS=$(echo $COMPILER | awk '{$1 = ""; print}')
+REAL_COMPILER_BIN=$(find_compiler $COMPILER_BIN)
+REAL_COMPILER="$REAL_COMPILER_BIN$COMPILER_ARGS"
if [ "$REAL_COMPILER" = "$COMPILER" ]; then
echo "Compiler: $COMPILER"
fi
echo
-VERBOSE=false
-[ "$1" = "-v" ] && { VERBOSE=true; shift; }
+[ -z "${VERBOSE:-}" ] && verbose=false || verbose=true
+[ "$1" = "-v" ] && { verbose=true; shift; }
suites="$*"
if [ -z "$suites" ]; then
suites="$all_suites"
fi
+skipped=false
for suite in $suites; do
run_suite $suite
+ if [ $? -eq $skip_code ]; then
+ skipped=true
+ break
+ fi
done
cd /
-rm -rf $ABS_TESTDIR
-green PASSED
-exit 0
+if [ -z "$KEEP_TESTDIR"]; then
+ rm -rf $ABS_TESTDIR
+fi
+if $skipped; then
+ exit $skip_code
+else
+ green PASSED
+ exit 0
+fi
+
expect_stat 'files in cache' 1
expect_equal_object_files reference_test1.o test1.o
+ # -------------------------------------------------------------------------
+ TEST "Version output readable"
+
+ # The exact output is not tested, but at least it's something human readable
+ # and not random memory.
+ if [ $($CCACHE --version | grep -c '^ccache version [a-zA-Z0-9_./+-]*$') -ne 1 ]; then
+ test_failed "Unexpected output of --version"
+ fi
+
# -------------------------------------------------------------------------
TEST "Debug option"
TEST "Couldn't find the compiler"
$CCACHE blahblah -c test1.c 2>/dev/null
- expect_stat "couldn't find the compiler" 1
+ exit_code=$?
+ if [ $exit_code -ne 1 ]; then
+ test_failed "Expected exit code to be 1, actual value $exit_code"
+ fi
# -------------------------------------------------------------------------
TEST "Bad compiler arguments"
$CCACHE_COMPILE -c test1.c -o out/foo.o 2>/dev/null
expect_stat 'could not write to output file' 1
expect_stat 'cache miss' 1
- expect_file_missing out/foo.o
+ expect_missing out/foo.o
# -------------------------------------------------------------------------
TEST "No input file"
$CCACHE_COMPILE -x c -c src/foo
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_file_exists foo.o
+ expect_exists foo.o
rm foo.o
$CCACHE_COMPILE -x c -c src/foo
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
- expect_file_exists foo.o
+ expect_exists foo.o
rm foo.o
rm -rf src
$CCACHE_COMPILE -x c -c src/foo.
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_file_exists foo.o
+ expect_exists foo.o
rm foo.o
$CCACHE_COMPILE -x c -c src/foo.
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
- expect_file_exists foo.o
+ expect_exists foo.o
rm foo.o
rm -rf src
$CCACHE_COMPILE -c src/foo.c.c
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_file_exists foo.c.o
+ expect_exists foo.c.o
rm foo.c.o
$CCACHE_COMPILE -c src/foo.c.c
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
- expect_file_exists foo.c.o
+ expect_exists foo.c.o
rm foo.c.o
rm -rf src
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
+ # -------------------------------------------------------------------------
+ TEST "Result file is compressed"
+
+ $CCACHE_COMPILE -c test1.c
+ result_file=$(find $CCACHE_DIR -name '*R')
+ if ! $CCACHE --dump-result $result_file | grep 'Compression type: zstd' >/dev/null 2>&1; then
+ test_failed "Result file not uncompressed according to metadata"
+ fi
+ if [ $(file_size $result_file) -ge $(file_size test1.o) ]; then
+ test_failed "Result file seems to be uncompressed"
+ fi
+
+ # -------------------------------------------------------------------------
+ TEST "Corrupt result file"
+
+ $CCACHE_COMPILE -c test1.c
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 1
+
+ $CCACHE_COMPILE -c test1.c
+ expect_stat 'cache hit (preprocessed)' 1
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 1
+
+ result_file=$(find $CCACHE_DIR -name '*R')
+ printf foo | dd of=$result_file bs=3 count=1 seek=20 conv=notrunc >&/dev/null
+
+ $CCACHE_COMPILE -c test1.c
+ expect_stat 'cache hit (preprocessed)' 1
+ expect_stat 'cache miss' 2
+ expect_stat 'files in cache' 1
+
+ $CCACHE_COMPILE -c test1.c
+ expect_stat 'cache hit (preprocessed)' 2
+ expect_stat 'cache miss' 2
+ expect_stat 'files in cache' 1
+
+ # -------------------------------------------------------------------------
+ TEST "CCACHE_DEBUG"
+
+ unset CCACHE_LOGFILE
+ unset CCACHE_NODIRECT
+ CCACHE_DEBUG=1 $CCACHE_COMPILE -c test1.c
+ if ! grep -q Result: test1.o.ccache-log; then
+ test_failed "Unexpected data in <obj>.ccache-log"
+ fi
+ if ! grep -q "PREPROCESSOR MODE" test1.o.ccache-input-text; then
+ test_failed "Unexpected data in <obj>.ccache-input-text"
+ fi
+ for ext in c p d; do
+ if ! [ -f test1.o.ccache-input-$ext ]; then
+ test_failed "<obj>.ccache-input-$ext missing"
+ fi
+ done
+
# -------------------------------------------------------------------------
TEST "CCACHE_DISABLE"
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 0
+ # -------------------------------------------------------------------------
+ TEST "stats file forward compatibility"
+
+ mkdir -p "$CCACHE_DIR/4/"
+ stats_file="$CCACHE_DIR/4/stats"
+ touch "$CCACHE_DIR/timestamp_reference"
+
+ for i in `seq 101`; do
+ echo $i
+ done > "$stats_file"
+
+ expect_stat 'cache miss' 5
+ $CCACHE_COMPILE -c test1.c
+ expect_stat 'cache miss' 6
+ expect_contains "$stats_file" 101
+ expect_newer_than "$stats_file" "$CCACHE_DIR/timestamp_reference"
+
+ # -------------------------------------------------------------------------
+ TEST "stats file with large counter values"
+
+ mkdir -p "$CCACHE_DIR/4/"
+ stats_file="$CCACHE_DIR/4/stats"
+
+ echo "0 0 0 0 1234567890123456789" >"$stats_file"
+
+ expect_stat 'cache miss' 1234567890123456789
+ $CCACHE_COMPILE -c test1.c
+ expect_stat 'cache miss' 1234567890123456790
+
# -------------------------------------------------------------------------
TEST "CCACHE_RECACHE"
expect_stat 'cache hit (preprocessed)' 2
expect_stat 'cache miss' 1
- # -------------------------------------------------------------------------
- TEST "CCACHE_NLEVELS"
-
- CCACHE_NLEVELS=4 $CCACHE_COMPILE -c test1.c
- expect_stat 'cache hit (preprocessed)' 0
- expect_stat 'cache miss' 1
- expect_stat 'files in cache' 1
-
- CCACHE_NLEVELS=4 $CCACHE_COMPILE -c test1.c
- expect_stat 'cache hit (preprocessed)' 1
- expect_stat 'cache miss' 1
- expect_stat 'files in cache' 1
-
- # Directories in $CCACHE_DIR:
- # - .
- # - tmp
- # - a
- # - a/b
- # - a/b/c
- # - a/b/c/d
- actual_dirs=$(find $CCACHE_DIR -type d | wc -l)
- expected_dirs=6
- if [ $actual_dirs -ne $expected_dirs ]; then
- test_failed "Expected $expected_dirs directories, found $actual_dirs"
- fi
-
# -------------------------------------------------------------------------
TEST "CCACHE_EXTRAFILES"
cat <<'EOF' >prefix-a
#!/bin/sh
-echo a >>prefix.result
+echo a >prefix.result
exec "$@"
EOF
cat <<'EOF' >prefix-b
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_file_content prefix.result "a
+ expect_content prefix.result "a
b"
PATH=.:$PATH CCACHE_PREFIX="prefix-a prefix-b" $CCACHE_COMPILE -c file.c
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
- expect_file_content prefix.result "a
+ expect_content prefix.result "a
b"
rm -f prefix.result
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 2
expect_stat 'cache miss' 1
- expect_file_content prefix.result "a
+ expect_content prefix.result "a
b"
# -------------------------------------------------------------------------
expect_stat 'cache hit (preprocessed)' 2
expect_stat 'cache miss' 2
+ # -------------------------------------------------------------------------
+ TEST "CCACHE_COMPILER"
+
+ $REAL_COMPILER -c -o reference_test1.o test1.c
+
+ $CCACHE_COMPILE -c test1.c
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 1
+ expect_equal_object_files reference_test1.o test1.o
+
+ CCACHE_COMPILER=$COMPILER $CCACHE non_existing_compiler_will_be_overridden_anyway -c test1.c
+ expect_stat 'cache hit (preprocessed)' 1
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 1
+ expect_equal_object_files reference_test1.o test1.o
+
+ CCACHE_COMPILER=$COMPILER $CCACHE same/for/relative -c test1.c
+ expect_stat 'cache hit (preprocessed)' 2
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 1
+ expect_equal_object_files reference_test1.o test1.o
+
+ CCACHE_COMPILER=$COMPILER $CCACHE /and/even/absolute/compilers -c test1.c
+ expect_stat 'cache hit (preprocessed)' 3
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 1
+ expect_equal_object_files reference_test1.o test1.o
+
# -------------------------------------------------------------------------
TEST "CCACHE_PATH"
chmod +x compiler.sh
CCACHE_COMPILERCHECK="unknown_command" $CCACHE ./compiler.sh -c test1.c 2>/dev/null
- if [ "$?" -eq 0 ]; then
- test_failed "Expected failure running unknown_command to verify compiler but was success"
- fi
expect_stat 'compiler check failed' 1
+
# -------------------------------------------------------------------------
- TEST "CCACHE_RECACHE should remove previous .stderr"
+ TEST "CCACHE_UMASK"
- $CCACHE_COMPILE -c test1.c
+ saved_umask=$(umask)
+ umask 022
+ export CCACHE_UMASK=002
+
+ cat <<EOF >test.c
+int main() {}
+EOF
+
+ $CCACHE -M 5 >/dev/null
+ $CCACHE_COMPILE -MMD -c test.c
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_file_count 0 '*.stderr' $CCACHE_DIR
+ result_file=$(find $CCACHE_DIR -name '*R')
+ level_2_dir=$(dirname $result_file)
+ level_1_dir=$(dirname $(dirname $result_file))
+ expect_perm test.o -rw-r--r--
+ expect_perm test.d -rw-r--r--
+ expect_perm "$CCACHE_CONFIGPATH" -rw-rw-r--
+ expect_perm "$CCACHE_DIR" drwxrwxr-x
+ expect_perm "$level_1_dir" drwxrwxr-x
+ expect_perm "$level_1_dir/stats" -rw-rw-r--
+ expect_perm "$level_2_dir" drwxrwxr-x
+ expect_perm "$result_file" -rw-rw-r--
+
+ rm test.o test.d
+ $CCACHE_COMPILE -MMD -c test.c
+ expect_stat 'cache hit (preprocessed)' 1
+ expect_stat 'cache miss' 1
+ expect_perm test.o -rw-r--r--
+ expect_perm test.d -rw-r--r--
- obj_file=`find $CCACHE_DIR -name '*.o'`
- stderr_file=`echo $obj_file | sed 's/..$/.stderr/'`
- echo "Warning: foo" >$stderr_file
- CCACHE_RECACHE=1 $CCACHE_COMPILE -c test1.c
- expect_file_count 0 '*.stderr' $CCACHE_DIR
+ $CCACHE_COMPILE -o test test.o
+ expect_stat 'cache hit (preprocessed)' 1
+ expect_stat 'cache miss' 1
+ expect_stat 'called for link' 1
+ expect_perm test -rwxr-xr-x
+
+ umask $saved_umask
# -------------------------------------------------------------------------
TEST "No object file"
// Trigger warning by having no return statement.
}
EOF
- $CCACHE_COMPILE -Wall -W -c stderr.c 2>/dev/null
- expect_file_count 1 '*.stderr' $CCACHE_DIR
+ $REAL_COMPILER -c -Wall -W -c stderr.c 2>reference_stderr.txt
+ $CCACHE_COMPILE -Wall -W -c stderr.c 2>stderr.txt
+ expect_equal_content reference_stderr.txt stderr.txt
+
+ # -------------------------------------------------------------------------
+ TEST "Merging stderr"
+
+ cat >compiler.sh <<EOF
+#!/bin/sh
+if [ \$1 = -E ]; then
+ echo preprocessed
+ printf "[%s]" cpp_stderr >&2
+else
+ echo object >test1.o
+ printf "[%s]" cc_stderr >&2
+fi
+EOF
+ chmod +x compiler.sh
+
+ unset CCACHE_NOCPP2
+ stderr=$($CCACHE ./compiler.sh -c test1.c 2>stderr)
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 1
+ expect_content stderr "[cc_stderr]"
+
+ stderr=$(CCACHE_NOCPP2=1 $CCACHE ./compiler.sh -c test1.c 2>stderr)
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 2
expect_stat 'files in cache' 2
+ expect_content stderr "[cpp_stderr][cc_stderr]"
+
+ # -------------------------------------------------------------------------
+ TEST "Stderr and dependency file"
+
+ cat <<EOF >test.c
+#warning Foo
+EOF
+ $REAL_COMPILER -c test.c -MMD 2>reference.stderr
+ mv test.d reference.d
+
+ $CCACHE_COMPILE -c test.c -MMD 2>test.stderr
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ expect_equal_content reference.stderr test.stderr
+ expect_equal_content reference.d test.d
+
+ $CCACHE_COMPILE -c test.c -MMD 2>test.stderr
+ expect_stat 'cache hit (preprocessed)' 1
+ expect_stat 'cache miss' 1
+ expect_equal_content reference.stderr test.stderr
+ expect_equal_content reference.d test.d
# -------------------------------------------------------------------------
TEST "--zero-stats"
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
if [ -z "$CCACHE_NOCPP2" ]; then
- expect_file_content compiler.args "[-E test1.c][-c -o test1.o test1.c]"
+ expect_content compiler.args "[-E test1.c][-c -o test1.o test1.c]"
fi
rm compiler.args
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
- expect_file_content compiler.args "[-E test1.c]"
+ expect_content compiler.args "[-E test1.c]"
rm compiler.args
# Even though -Werror is not passed to the preprocessor, it should be part
expect_stat 'cache miss' 2
expect_stat 'files in cache' 2
if [ -z "$CCACHE_NOCPP2" ]; then
- expect_file_content compiler.args "[-E test1.c][-Werror -rdynamic -c -o test1.o test1.c]"
+ expect_content compiler.args "[-E test1.c][-Werror -rdynamic -c -o test1.o test1.c]"
fi
rm compiler.args
for obj in test1.o build/test1.o; do
$CCACHE_COMPILE -c -MMD $src -o $obj
dep=$(echo $obj | sed 's/\.o$/.d/')
- expect_file_content $dep "$obj: $src"
+ expect_content $dep "$obj: $src"
done
done
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
- # -------------------------------------------------------------------------
- TEST "Symlink to source directory"
-
- mkdir dir
- cd dir
- mkdir -p d1/d2
- echo '#define A "OK"' >d1/h.h
- cat <<EOF >d1/d2/c.c
-#include <stdio.h>
-#include "../h.h"
-int main() { printf("%s\n", A); }
-EOF
- echo '#define A "BUG"' >h.h
- ln -s d1/d2 d3
-
- CCACHE_BASEDIR=/ $CCACHE_COMPILE -c $PWD/d3/c.c
- $REAL_COMPILER c.o -o c
- if [ "$(./c)" != OK ]; then
- test_failed "Incorrect header file used"
- fi
-
- # -------------------------------------------------------------------------
- TEST "Symlink to source file"
-
- mkdir dir
- cd dir
- mkdir d
- echo '#define A "BUG"' >d/h.h
- cat <<EOF >d/c.c
-#include <stdio.h>
-#include "h.h"
-int main() { printf("%s\n", A); }
-EOF
- echo '#define A "OK"' >h.h
- ln -s d/c.c c.c
-
- CCACHE_BASEDIR=/ $CCACHE_COMPILE -c $PWD/c.c
- $REAL_COMPILER c.o -o c
- if [ "$(./c)" != OK ]; then
- test_failed "Incorrect header file used"
- fi
-
# -------------------------------------------------------------------------
if ! $HOST_OS_WINDOWS; then
TEST ".incbin"
# -------------------------------------------------------------------------
TEST "--hash-file"
- >empty
- $CCACHE --hash-file empty > hash.out
+ $CCACHE --hash-file /dev/null > hash.out
printf "a" | $CCACHE --hash-file - >> hash.out
- if grep '31d6cfe0d16ae931b73c59d7e0c089c0-0' hash.out >/dev/null 2>&1 && \
- grep 'bde52cb31de33e46245e05fbdbd6fb24-1' hash.out >/dev/null 2>&1; then
+ hash_0='af1396svbud1kqg40jfa6reciicrpcisi'
+ hash_1='17765vetiqd4ae95qpbhfb1ut8gj42r6m'
+
+ if grep "$hash_0" hash.out >/dev/null 2>&1 && \
+ grep "$hash_1" hash.out >/dev/null 2>&1; then
: OK
else
test_failed "Unexpected output of --hash-file"
expect_stat 'cache miss' 2
# -------------------------------------------------------------------------
+if ! $HOST_OS_WINDOWS && ! $HOST_OS_CYGWIN; then
TEST "Path normalization"
cd dir1
- CCACHE_BASEDIR="`pwd`" $CCACHE_COMPILE -I`pwd`/include -c src/test.c
+ CCACHE_BASEDIR="`pwd`" $CCACHE_COMPILE -I$(pwd)/include -c src/test.c
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
mkdir subdir
- ln -s `pwd`/include subdir/symlink
# Rewriting triggered by CCACHE_BASEDIR should handle paths with multiple
- # slashes, redundant "/." parts and "foo/.." parts correctly. Note that the
- # ".." part of the path is resolved after the symlink has been resolved.
- CCACHE_BASEDIR=`pwd` $CCACHE_COMPILE -I`pwd`//./subdir/symlink/../include -c `pwd`/src/test.c
+ # slashes, redundant "/." parts and "foo/.." parts correctly.
+ CCACHE_BASEDIR=$(pwd) $CCACHE_COMPILE -I$(pwd)//./subdir/../include -c $(pwd)/src/test.c
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
+fi
+
+ # -------------------------------------------------------------------------
+if ! $HOST_OS_WINDOWS && ! $HOST_OS_CYGWIN; then
+ TEST "Symlink to source directory"
+
+ mkdir dir
+ cd dir
+ mkdir -p d1/d2
+ echo '#define A "OK"' >d1/h.h
+ cat <<EOF >d1/d2/c.c
+#include <stdio.h>
+#include "../h.h"
+int main() { printf("%s\n", A); }
+EOF
+ echo '#define A "BUG"' >h.h
+ ln -s d1/d2 d3
+
+ CCACHE_BASEDIR=/ $CCACHE_COMPILE -c $PWD/d3/c.c
+ $REAL_COMPILER c.o -o c
+ if [ "$(./c)" != OK ]; then
+ test_failed "Incorrect header file used"
+ fi
+fi
+
+ # -------------------------------------------------------------------------
+if ! $HOST_OS_WINDOWS && ! $HOST_OS_CYGWIN; then
+ TEST "Symlink to source file"
+
+ mkdir dir
+ cd dir
+ mkdir d
+ echo '#define A "BUG"' >d/h.h
+ cat <<EOF >d/c.c
+#include <stdio.h>
+#include "h.h"
+int main() { printf("%s\n", A); }
+EOF
+ echo '#define A "OK"' >h.h
+ ln -s d/c.c c.c
+
+ CCACHE_BASEDIR=/ $CCACHE_COMPILE -c $PWD/c.c
+ $REAL_COMPILER c.o -o c
+ if [ "$(./c)" != OK ]; then
+ test_failed "Incorrect header file used"
+ fi
+fi
+
+ # -------------------------------------------------------------------------
+if ! $HOST_OS_WINDOWS && ! $HOST_OS_CYGWIN; then
+ TEST "Symlinked build dir inside source dir"
+
+ mkdir build1
+ ln -s $(pwd)/build1 dir1/src/build
+
+ mkdir build2
+ ln -s $(pwd)/build2 dir2/src/build
+
+ # The file structure now looks like this:
+ #
+ # build1
+ # dir1/include/test.h
+ # dir1/src/test.c
+ # dir1/src/build -> /absolute/path/to/build1
+ #
+ # build2
+ # dir2/include/test.h
+ # dir2/src/test.c
+ # dir2/src/build -> /absolute/path/to/build2
+
+ cd dir1/src
+ CCACHE_BASEDIR=/ $CCACHE_COMPILE -I$(pwd)/../include -c $(pwd)/test.c -o $(pwd)/build/test.o
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+
+ cd ../../dir2/src
+ # Apparent CWD:
+ CCACHE_BASEDIR=/ $CCACHE_COMPILE -I$(pwd)/../include -c $(pwd)/test.c -o $(pwd)/build/test.o
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 1
+
+ # Actual CWD (e.g. from $(CURDIR) in a Makefile):
+ CCACHE_BASEDIR=/ $CCACHE_COMPILE -I$(pwd -P)/../include -c $(pwd -P)/test.c -o $(pwd -P)/build/test.o
+ expect_stat 'cache hit (direct)' 2
+ expect_stat 'cache miss' 1
+fi
+
+ # -------------------------------------------------------------------------
+if ! $HOST_OS_WINDOWS && ! $HOST_OS_CYGWIN; then
+ TEST "Symlinked source dir inside build dir"
+
+ mkdir build1
+ ln -s $(pwd)/dir1 build1/src
+
+ mkdir build2
+ ln -s $(pwd)/dir2 build2/src
+
+ # The file structure now looks like this:
+ #
+ # build1
+ # build1/src -> /absolute/path/to/dir1
+ # dir1/include/test.h
+ # dir1/src/test.c
+ #
+ # build2
+ # build2/src -> /absolute/path/to/dir2
+ # dir2/include/test.h
+ # dir2/src/test.c
+
+ cd build1
+ CCACHE_BASEDIR=/ $CCACHE_COMPILE -I$(pwd)/src/include -c $(pwd)/src/src/test.c -o $(pwd)/test.o
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+
+ cd ../build2
+ # Apparent CWD:
+ CCACHE_BASEDIR=/ $CCACHE_COMPILE -I$(pwd)/src/include -c $(pwd)/src/src/test.c -o $(pwd)/test.o
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 1
+
+ # Actual CWD:
+ CCACHE_BASEDIR=/ $CCACHE_COMPILE -I$(pwd -P)/src/include -c $(pwd -P)/src/src/test.c -o $(pwd -P)/test.o
+ expect_stat 'cache hit (direct)' 2
+ expect_stat 'cache miss' 1
+fi
# -------------------------------------------------------------------------
TEST "Rewriting in stderr"
expect_stat 'cache miss' 1
cd ..
done
+
+ # -------------------------------------------------------------------------
+ TEST "Absolute paths in stderr"
+
+ cat <<EOF >test.c
+#include "test.h"
+#warning test.c
+EOF
+ cat <<EOF >test.h
+#warning test.h
+EOF
+ backdate test.h
+
+ pwd=$PWD.real
+ $REAL_COMPILER -c $pwd/test.c 2>reference.stderr
+
+ CCACHE_ABSSTDERR=1 CCACHE_BASEDIR="$pwd" $CCACHE_COMPILE -c $pwd/test.c 2>ccache.stderr
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ expect_equal_content reference.stderr ccache.stderr
+
+ CCACHE_ABSSTDERR=1 CCACHE_BASEDIR="$pwd" $CCACHE_COMPILE -c $pwd/test.c 2>ccache.stderr
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ expect_equal_content reference.stderr ccache.stderr
+
+ if $REAL_COMPILER -fdiagnostics-color=always -c test.c 2>/dev/null; then
+ $REAL_COMPILER -fdiagnostics-color=always -c $pwd/test.c 2>reference.stderr
+
+ CCACHE_ABSSTDERR=1 CCACHE_BASEDIR="$pwd" $CCACHE_COMPILE -fdiagnostics-color=always -c $pwd/test.c 2>ccache.stderr
+ expect_stat 'cache hit (direct)' 2
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ expect_equal_content reference.stderr ccache.stderr
+
+ CCACHE_ABSSTDERR=1 CCACHE_BASEDIR="$pwd" $CCACHE_COMPILE -fdiagnostics-color=always -c $pwd/test.c 2>ccache.stderr
+ expect_stat 'cache hit (direct)' 3
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ expect_equal_content reference.stderr ccache.stderr
+ fi
}
--- /dev/null
+add_fake_files_counters() {
+ local files=$1
+ for x in 0 1 2 3 4 5 6 7 8 9 a b c d e f; do
+ mkdir -p $CCACHE_DIR/$x
+ echo "0 0 0 0 0 0 0 0 0 0 0 $((files / 16))" >$CCACHE_DIR/$x/stats
+ done
+}
+
+expect_on_level() {
+ local type="$1"
+ local expected_level="$2"
+
+ slashes=$(find $CCACHE_DIR -name "*$type" \
+ | sed -r -e 's!.*\.ccache/!!' -e 's![^/]*$!!' -e 's![^/]!!g')
+ actual_level=$(echo -n "$slashes" | wc -c)
+ if [ "$actual_level" -ne "$expected_level" ]; then
+ test_failed "$type file on level $actual_level, expected level $expected_level"
+ fi
+}
+
+
+SUITE_cache_levels_SETUP() {
+ generate_code 1 test1.c
+ unset CCACHE_NODIRECT
+}
+
+SUITE_cache_levels() {
+ # -------------------------------------------------------------------------
+ TEST "Empty cache"
+
+ $CCACHE_COMPILE -c test1.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 2
+ expect_on_level R 2
+ expect_on_level M 2
+
+ $CCACHE_COMPILE -c test1.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 2
+ expect_on_level R 2
+ expect_on_level M 2
+
+ # -------------------------------------------------------------------------
+ TEST "Many files but still level 2"
+
+ files=$((16 * 16 * 1999))
+ add_fake_files_counters $files
+
+ $CCACHE_COMPILE -c test1.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' $((files + 2))
+ expect_on_level R 2
+ expect_on_level M 2
+
+ $CCACHE_COMPILE -c test1.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' $((files + 2))
+ expect_on_level R 2
+ expect_on_level M 2
+
+ # -------------------------------------------------------------------------
+ TEST "Level 3"
+
+ files=$((16 * 16 * 2001))
+ add_fake_files_counters $files
+
+ $CCACHE_COMPILE -c test1.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' $((files + 2))
+ expect_on_level R 3
+ expect_on_level M 3
+
+ $CCACHE_COMPILE -c test1.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' $((files + 2))
+ expect_on_level R 3
+ expect_on_level M 3
+
+ # -------------------------------------------------------------------------
+ TEST "Level 4"
+
+ files=$((16 * 16 * 16 * 2001))
+ add_fake_files_counters $files
+
+ $CCACHE_COMPILE -c test1.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' $((files + 2))
+ expect_on_level R 4
+ expect_on_level M 4
+
+ $CCACHE_COMPILE -c test1.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' $((files + 2))
+ expect_on_level R 4
+ expect_on_level M 4
+
+ # -------------------------------------------------------------------------
+ TEST "No deeper than 4 levels"
+
+ files=$((16 * 16 * 16 * 16 * 2001))
+ add_fake_files_counters $files
+
+ $CCACHE_COMPILE -c test1.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' $((files + 2))
+ expect_on_level R 4
+ expect_on_level M 4
+
+ $CCACHE_COMPILE -c test1.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' $((files + 2))
+ expect_on_level R 4
+ expect_on_level M 4
+}
rm -rf $dir
mkdir -p $dir
for i in $(seq 0 9); do
- printf '%4017s' '' | tr ' ' 'A' >$dir/result$i-4017.o
- backdate $((3 * i + 1)) $dir/result$i-4017.o
- backdate $((3 * i + 2)) $dir/result$i-4017.d
- backdate $((3 * i + 3)) $dir/result$i-4017.stderr
+ printf '%4017s' '' | tr ' ' 'A' >$dir/result${i}R
+ backdate $((3 * i + 1)) $dir/result${i}R
done
- # NUMFILES: 30, TOTALSIZE: 40 KiB, MAXFILES: 0, MAXSIZE: 0
- echo "0 0 0 0 0 0 0 0 0 0 0 30 40 0 0" >$dir/stats
+ # NUMFILES: 10, TOTALSIZE: 13 KiB, MAXFILES: 0, MAXSIZE: 0
+ echo "0 0 0 0 0 0 0 0 0 0 0 10 13 0 0" >$dir/stats
}
SUITE_cleanup() {
prepare_cleanup_test_dir $CCACHE_DIR/a
$CCACHE -C >/dev/null
- expect_file_count 0 '*.o' $CCACHE_DIR
- expect_file_count 0 '*.d' $CCACHE_DIR
- expect_file_count 0 '*.stderr' $CCACHE_DIR
+ expect_file_count 0 '*R' $CCACHE_DIR
expect_stat 'files in cache' 0
expect_stat 'cleanups performed' 1
$CCACHE -F 0 -M 0 >/dev/null
$CCACHE -c >/dev/null
- expect_file_count 10 '*.o' $CCACHE_DIR
- expect_file_count 10 '*.d' $CCACHE_DIR
- expect_file_count 10 '*.stderr' $CCACHE_DIR
- expect_stat 'files in cache' 30
+ expect_file_count 10 '*R' $CCACHE_DIR
+ expect_stat 'files in cache' 10
expect_stat 'cleanups performed' 0
# -------------------------------------------------------------------------
# No cleanup needed.
#
- # 30 * 16 = 480
- $CCACHE -F 480 -M 0 >/dev/null
+ # 10 * 16 = 160
+ $CCACHE -F 160 -M 0 >/dev/null
$CCACHE -c >/dev/null
- expect_file_count 10 '*.o' $CCACHE_DIR
- expect_file_count 10 '*.d' $CCACHE_DIR
- expect_file_count 10 '*.stderr' $CCACHE_DIR
- expect_stat 'files in cache' 30
+ expect_file_count 10 '*R' $CCACHE_DIR
+ expect_stat 'files in cache' 10
expect_stat 'cleanups performed' 0
# Reduce file limit
#
- # 22 * 16 = 352
- $CCACHE -F 352 -M 0 >/dev/null
+ # 7 * 16 = 112
+ $CCACHE -F 112 -M 0 >/dev/null
$CCACHE -c >/dev/null
- expect_file_count 7 '*.o' $CCACHE_DIR
- expect_file_count 7 '*.d' $CCACHE_DIR
- expect_file_count 8 '*.stderr' $CCACHE_DIR
- expect_stat 'files in cache' 22
+ expect_file_count 7 '*R' $CCACHE_DIR
+ expect_stat 'files in cache' 7
expect_stat 'cleanups performed' 1
for i in 0 1 2; do
- file=$CCACHE_DIR/a/result$i-4017.o
- expect_file_missing $CCACHE_DIR/a/result$i-4017.o
+ file=$CCACHE_DIR/a/result${i}R
+ expect_missing $CCACHE_DIR/a/result${i}R
done
for i in 3 4 5 6 7 8 9; do
- file=$CCACHE_DIR/a/result$i-4017.o
- expect_file_exists $file
+ file=$CCACHE_DIR/a/result${i}R
+ expect_exists $file
done
# -------------------------------------------------------------------------
$CCACHE -F 0 -M 256K >/dev/null
$CCACHE -c >/dev/null
- expect_file_count 3 '*.o' $CCACHE_DIR
- expect_file_count 4 '*.d' $CCACHE_DIR
- expect_file_count 4 '*.stderr' $CCACHE_DIR
- expect_stat 'files in cache' 11
+ expect_file_count 3 '*R' $CCACHE_DIR
+ expect_stat 'files in cache' 3
expect_stat 'cleanups performed' 1
for i in 0 1 2 3 4 5 6; do
- file=$CCACHE_DIR/a/result$i-4017.o
- expect_file_missing $file
+ file=$CCACHE_DIR/a/result${i}R
+ expect_missing $file
done
for i in 7 8 9; do
- file=$CCACHE_DIR/a/result$i-4017.o
- expect_file_exists $file
+ file=$CCACHE_DIR/a/result${i}R
+ expect_exists $file
done
fi
-
# -------------------------------------------------------------------------
TEST "Automatic cache cleanup, limit_multiple 0.9"
prepare_cleanup_test_dir $CCACHE_DIR/$x
done
- $CCACHE -F 480 -M 0 >/dev/null
+ $CCACHE -F 160 -M 0 >/dev/null
- expect_file_count 160 '*.o' $CCACHE_DIR
- expect_file_count 160 '*.d' $CCACHE_DIR
- expect_file_count 160 '*.stderr' $CCACHE_DIR
- expect_stat 'files in cache' 480
+ expect_file_count 160 '*R' $CCACHE_DIR
+ expect_stat 'files in cache' 160
expect_stat 'cleanups performed' 0
touch empty.c
CCACHE_LIMIT_MULTIPLE=0.9 $CCACHE_COMPILE -c empty.c -o empty.o
- expect_file_count 159 '*.o' $CCACHE_DIR
- expect_file_count 159 '*.d' $CCACHE_DIR
- expect_file_count 159 '*.stderr' $CCACHE_DIR
- expect_stat 'files in cache' 477
+ expect_file_count 159 '*R' $CCACHE_DIR
+ expect_stat 'files in cache' 159
expect_stat 'cleanups performed' 1
# -------------------------------------------------------------------------
prepare_cleanup_test_dir $CCACHE_DIR/$x
done
- $CCACHE -F 480 -M 0 >/dev/null
+ $CCACHE -F 160 -M 0 >/dev/null
- expect_file_count 160 '*.o' $CCACHE_DIR
- expect_file_count 160 '*.d' $CCACHE_DIR
- expect_file_count 160 '*.stderr' $CCACHE_DIR
- expect_stat 'files in cache' 480
+ expect_file_count 160 '*R' $CCACHE_DIR
+ expect_stat 'files in cache' 160
expect_stat 'cleanups performed' 0
touch empty.c
CCACHE_LIMIT_MULTIPLE=0.7 $CCACHE_COMPILE -c empty.c -o empty.o
- expect_file_count 157 '*.o' $CCACHE_DIR
- expect_file_count 157 '*.d' $CCACHE_DIR
- expect_file_count 157 '*.stderr' $CCACHE_DIR
- expect_stat 'files in cache' 471
+ expect_file_count 157 '*R' $CCACHE_DIR
+ expect_stat 'files in cache' 157
expect_stat 'cleanups performed' 1
- # -------------------------------------------------------------------------
- TEST ".o file is removed before .stderr"
-
- prepare_cleanup_test_dir $CCACHE_DIR/a
- $CCACHE -F 464 -M 0 >/dev/null
- backdate 0 $CCACHE_DIR/a/result9-4017.stderr
- $CCACHE -c >/dev/null
- expect_file_missing $CCACHE_DIR/a/result9-4017.stderr
- expect_file_missing $CCACHE_DIR/a/result9-4017.o
-
- # Counters expectedly doesn't match reality if x.stderr is found before
- # x.o and the cleanup stops before x.o is found.
- expect_stat 'files in cache' 29
- expect_file_count 28 '*.*' $CCACHE_DIR/a
-
- # -------------------------------------------------------------------------
- TEST ".stderr file is not removed before .o"
-
- prepare_cleanup_test_dir $CCACHE_DIR/a
- $CCACHE -F 464 -M 0 >/dev/null
- backdate 0 $CCACHE_DIR/a/result9-4017.o
- $CCACHE -c >/dev/null
- expect_file_exists $CCACHE_DIR/a/result9-4017.stderr
- expect_file_missing $CCACHE_DIR/a/result9-4017.o
-
- expect_stat 'files in cache' 29
- expect_file_count 29 '*.*' $CCACHE_DIR/a
-
# -------------------------------------------------------------------------
TEST "No cleanup of new unknown file"
touch $CCACHE_DIR/a/abcd.unknown
$CCACHE -F 0 -M 0 -c >/dev/null # update counters
- expect_stat 'files in cache' 31
+ expect_stat 'files in cache' 11
- $CCACHE -F 480 -M 0 >/dev/null
+ $CCACHE -F 160 -M 0 >/dev/null
$CCACHE -c >/dev/null
- expect_file_exists $CCACHE_DIR/a/abcd.unknown
- expect_stat 'files in cache' 30
+ expect_exists $CCACHE_DIR/a/abcd.unknown
+ expect_stat 'files in cache' 10
# -------------------------------------------------------------------------
TEST "Cleanup of old unknown file"
prepare_cleanup_test_dir $CCACHE_DIR/a
- $CCACHE -F 480 -M 0 >/dev/null
+ $CCACHE -F 160 -M 0 >/dev/null
touch $CCACHE_DIR/a/abcd.unknown
backdate $CCACHE_DIR/a/abcd.unknown
$CCACHE -F 0 -M 0 -c >/dev/null # update counters
- expect_stat 'files in cache' 31
+ expect_stat 'files in cache' 11
- $CCACHE -F 480 -M 0 -c >/dev/null
- expect_file_missing $CCACHE_DIR/a/abcd.unknown
- expect_stat 'files in cache' 30
+ $CCACHE -F 160 -M 0 -c >/dev/null
+ expect_missing $CCACHE_DIR/a/abcd.unknown
+ expect_stat 'files in cache' 10
# -------------------------------------------------------------------------
TEST "Cleanup of tmp file"
expect_stat 'files in cache' 1
backdate $CCACHE_DIR/a/abcd.tmp.efgh
$CCACHE -c >/dev/null
- expect_file_missing $CCACHE_DIR/a/abcd.tmp.efgh
+ expect_missing $CCACHE_DIR/a/abcd.tmp.efgh
expect_stat 'files in cache' 0
# -------------------------------------------------------------------------
$CCACHE -F 0 -M 0 >/dev/null
$CCACHE -c >/dev/null
expect_file_count 1 '.nfs*' $CCACHE_DIR
- expect_stat 'files in cache' 30
+ expect_stat 'files in cache' 10
+
+ # -------------------------------------------------------------------------
+ TEST "Cleanup of old files by age"
+
+ prepare_cleanup_test_dir $CCACHE_DIR/a
+ touch $CCACHE_DIR/a/nowR
+ $CCACHE -F 0 -M 0 >/dev/null
+
+ $CCACHE --evict-older-than 1d >/dev/null
+ expect_file_count 1 '*R' $CCACHE_DIR
+ expect_stat 'files in cache' 1
+
+ $CCACHE --evict-older-than 1d >/dev/null
+ expect_file_count 1 '*R' $CCACHE_DIR
+ expect_stat 'files in cache' 1
+
+ backdate $CCACHE_DIR/a/nowR
+ $CCACHE --evict-older-than 10s >/dev/null
+ expect_stat 'files in cache' 0
}
--- /dev/null
+if $COMPILER_TYPE_GCC ; then
+ color_diagnostics_enable='-fdiagnostics-color'
+ color_diagnostics_disable='-fno-diagnostics-color'
+elif $COMPILER_TYPE_CLANG ; then
+ color_diagnostics_enable='-fcolor-diagnostics'
+ color_diagnostics_disable='-fno-color-diagnostics'
+fi
+
+SUITE_color_diagnostics_PROBE() {
+ if ! script --return --quiet --command true /dev/null </dev/null >/dev/null 2>&1; then
+ echo "the script tool is not installed or does not support required options"
+ return
+ fi
+
+ # Probe that real compiler actually supports colored diagnostics.
+ if [[ ! $color_diagnostics_enable || ! $color_diagnostics_disable ]] ; then
+ echo "compiler $COMPILER does not support colored diagnostics"
+ elif ! $REAL_COMPILER $color_diagnostics_enable -E - </dev/null >/dev/null 2>&1 ; then
+ echo "compiler $COMPILER (version: $compiler_version) does not support $color_diagnostics_enable"
+ elif ! $REAL_COMPILER $color_diagnostics_disable -E - </dev/null >/dev/null 2>&1 ; then
+ echo "compiler $COMPILER (version: $compiler_version) does not support $color_diagnostics_disable"
+ fi
+}
+
+SUITE_color_diagnostics_SETUP() {
+ if $run_second_cpp ; then
+ export CCACHE_CPP2=1
+ else
+ export CCACHE_NOCPP2=1
+ fi
+
+ unset GCC_COLORS
+ export TERM=vt100
+}
+
+color_diagnostics_expect_color() {
+ expect_contains "${1:?}" $'\033['
+ expect_contains <(fgrep 'previous prototype' "$1") $'\033['
+ expect_contains <(fgrep 'from preprocessor' "$1") $'\033['
+}
+
+color_diagnostics_expect_no_color() {
+ expect_not_contains "${1:?}" $'\033['
+}
+
+color_diagnostics_generate_code() {
+ generate_code "$@"
+ echo '#warning "Warning from preprocessor"' >>"$2"
+}
+
+# Heap's permutation algorithm
+color_diagnostics_generate_permutations() {
+ local -i i k="${1:?}-1"
+ if (( k )) ; then
+ color_diagnostics_generate_permutations "$k"
+ for (( i = 0 ; i < k ; ++i )) ; do
+ if (( k & 1 )) ; then
+ local tmp=${A[$i]} ; A[$i]=${A[$k]} ; A[$k]=$tmp
+ else
+ local tmp=${A[0]} ; A[0]=${A[$k]} ; A[$k]=$tmp
+ fi
+ color_diagnostics_generate_permutations "$k"
+ done
+ else
+ echo "${A[@]}"
+ fi
+}
+
+color_diagnostics_run_on_pty() {
+ script --return --quiet --command "unset GCC_COLORS; CCACHE_DIR='$CCACHE_DIR' ${2:?}" /dev/null </dev/null >"${1:?}"
+
+ # script returns early on some platforms (leaving a child process living for
+ # a short while) and the output may therefore still be pending. Work around
+ # this by sleeping until the output file has content.
+ while [ ! -s "$1" ]; do
+ sleep 0.1
+ done
+}
+
+color_diagnostics_test() {
+ # -------------------------------------------------------------------------
+ TEST "Colored diagnostics automatically disabled when stderr is not a TTY (run_second_cpp=$run_second_cpp)"
+ color_diagnostics_generate_code 1 test1.c
+ $CCACHE_COMPILE -Wmissing-prototypes -c -o test1.o test1.c 2>test1.stderr
+ color_diagnostics_expect_no_color test1.stderr
+
+ # Check that subsequently running on a TTY generates a cache hit.
+ color_diagnostics_run_on_pty test1.output "$CCACHE_COMPILE -Wmissing-prototypes -c -o test1.o test1.c"
+ color_diagnostics_expect_color test1.output
+ expect_stat 'cache miss' 1
+ expect_stat 'cache hit (preprocessed)' 1
+
+ # -------------------------------------------------------------------------
+ TEST "Colored diagnostics automatically enabled when stderr is a TTY (run_second_cpp=$run_second_cpp)"
+ color_diagnostics_generate_code 1 test1.c
+ color_diagnostics_run_on_pty test1.output "$CCACHE_COMPILE -Wmissing-prototypes -c -o test1.o test1.c"
+ color_diagnostics_expect_color test1.output
+
+ # Check that subsequently running without a TTY generates a cache hit.
+ $CCACHE_COMPILE -Wmissing-prototypes -c -o test1.o test1.c 2>test1.stderr
+ color_diagnostics_expect_no_color test1.stderr
+ expect_stat 'cache miss' 1
+ expect_stat 'cache hit (preprocessed)' 1
+
+ # -------------------------------------------------------------------------
+ while read -r case ; do
+ TEST "Cache object shared across ${case} (run_second_cpp=$run_second_cpp)"
+ color_diagnostics_generate_code 1 test1.c
+ local each ; for each in ${case} ; do
+ case $each in
+ color,*)
+ local color_flag=$color_diagnostics_enable color_expect=color
+ ;;
+ nocolor,*)
+ local color_flag=$color_diagnostics_disable color_expect=no_color
+ ;;
+ esac
+ case $each in
+ *,tty)
+ color_diagnostics_run_on_pty test1.output "$CCACHE_COMPILE $color_flag -Wmissing-prototypes -c -o test1.o test1.c"
+ color_diagnostics_expect_$color_expect test1.output
+ ;;
+ *,notty)
+ $CCACHE_COMPILE $color_flag -Wmissing-prototypes -c -o test1.o test1.c 2>test1.stderr
+ color_diagnostics_expect_$color_expect test1.stderr
+ ;;
+ esac
+ done
+ expect_stat 'cache miss' 1
+ expect_stat 'cache hit (preprocessed)' 3
+ done < <(
+ A=( {color,nocolor},{tty,notty} )
+ color_diagnostics_generate_permutations "${#A[@]}"
+ )
+}
+
+SUITE_color_diagnostics() {
+ run_second_cpp=true color_diagnostics_test
+ run_second_cpp=false color_diagnostics_test
+}
+++ /dev/null
-SUITE_compression_SETUP() {
- generate_code 1 test.c
-}
-
-SUITE_compression() {
- # -------------------------------------------------------------------------
- TEST "Hash sum equal for compressed and uncompressed files"
-
- CCACHE_COMPRESS=1 $CCACHE_COMPILE -c test.c
- expect_stat 'cache hit (direct)' 0
- expect_stat 'cache hit (preprocessed)' 0
- expect_stat 'cache miss' 1
-
- CCACHE_COMPRESS=1 $CCACHE_COMPILE -c test.c
- expect_stat 'cache hit (direct)' 0
- expect_stat 'cache hit (preprocessed)' 1
- expect_stat 'cache miss' 1
-
- $CCACHE_COMPILE -c test.c
- expect_stat 'cache hit (direct)' 0
- expect_stat 'cache hit (preprocessed)' 2
- expect_stat 'cache miss' 1
-}
backdate dir1/include/test.h dir2/include/test.h
}
-objdump_cmd() {
- if $HOST_OS_APPLE; then
- xcrun dwarfdump -r0 $1
- else
- objdump -W $1
- fi
-}
-
-grep_cmd() {
- if $HOST_OS_APPLE; then
- grep "( \"$1\" )"
- else
- grep ": $1[[:space:]]*$"
- fi
-}
-
SUITE_debug_prefix_map() {
# -------------------------------------------------------------------------
TEST "Mapping of debug info CWD"
cd dir1
- CCACHE_BASEDIR=`pwd` $CCACHE_COMPILE -I`pwd`/include -g -fdebug-prefix-map=`pwd`=dir -c `pwd`/src/test.c -o `pwd`/test.o
+ CCACHE_BASEDIR=$(pwd) $CCACHE_COMPILE -I$(pwd)/include -g -fdebug-prefix-map=$(pwd)=some_name_not_likely_to_exist_in_path -c $(pwd)/src/test.c -o $(pwd)/test.o
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
expect_stat 'files in cache' 2
- if objdump_cmd test.o | grep_cmd "`pwd`" >/dev/null 2>&1; then
- test_failed "Source dir (`pwd`) found in test.o"
- fi
+ expect_objdump_not_contains test.o "$(pwd)"
+ expect_objdump_contains test.o some_name_not_likely_to_exist_in_path
cd ../dir2
- CCACHE_BASEDIR=`pwd` $CCACHE_COMPILE -I`pwd`/include -g -fdebug-prefix-map=`pwd`=dir -c `pwd`/src/test.c -o `pwd`/test.o
+ CCACHE_BASEDIR=$(pwd) $CCACHE_COMPILE -I$(pwd)/include -g -fdebug-prefix-map=$(pwd)=some_name_not_likely_to_exist_in_path -c $(pwd)/src/test.c -o $(pwd)/test.o
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
expect_stat 'files in cache' 2
- if objdump_cmd test.o | grep_cmd "`pwd`" >/dev/null 2>&1; then
- test_failed "Source dir (`pwd`) found in test.o"
- fi
+ expect_objdump_not_contains test.o "$(pwd)"
# -------------------------------------------------------------------------
TEST "Multiple -fdebug-prefix-map"
cd dir1
- CCACHE_BASEDIR=`pwd` $CCACHE_COMPILE -I`pwd`/include -g -fdebug-prefix-map=`pwd`=name -fdebug-prefix-map=foo=bar -c `pwd`/src/test.c -o `pwd`/test.o
+ CCACHE_BASEDIR=$(pwd) $CCACHE_COMPILE -I$(pwd)/include -g -fdebug-prefix-map=$(pwd)=some_name_not_likely_to_exist_in_path -fdebug-prefix-map=foo=bar -c $(pwd)/src/test.c -o $(pwd)/test.o
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
expect_stat 'files in cache' 2
- if objdump_cmd test.o | grep_cmd "`pwd`" >/dev/null 2>&1; then
- test_failed "Source dir (`pwd`) found in test.o"
- fi
- if ! objdump_cmd test.o | grep_cmd "name" >/dev/null 2>&1; then
- test_failed "Relocation (name) not found in test.o"
- fi
+ expect_objdump_not_contains test.o "$(pwd)"
+ expect_objdump_contains test.o some_name_not_likely_to_exist_in_path
cd ../dir2
- CCACHE_BASEDIR=`pwd` $CCACHE_COMPILE -I`pwd`/include -g -fdebug-prefix-map=`pwd`=name -fdebug-prefix-map=foo=bar -c `pwd`/src/test.c -o `pwd`/test.o
+ CCACHE_BASEDIR=$(pwd) $CCACHE_COMPILE -I$(pwd)/include -g -fdebug-prefix-map=$(pwd)=some_name_not_likely_to_exist_in_path -fdebug-prefix-map=foo=bar -c $(pwd)/src/test.c -o $(pwd)/test.o
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
expect_stat 'files in cache' 2
- if objdump_cmd test.o | grep_cmd "`pwd`" >/dev/null 2>&1; then
- test_failed "Source dir (`pwd`) found in test.o"
- fi
+ expect_objdump_not_contains test.o "$(pwd)"
}
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_stat 'files in cache' 3 # .o + .manifest + .d
+ expect_stat 'files in cache' 2 # result + manifest
CCACHE_DEPEND=1 $CCACHE_COMPILE $DEPSFLAGS_CCACHE -c test.c
expect_equal_object_files reference_test.o test.o
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_stat 'files in cache' 3
+ expect_stat 'files in cache' 2
# -------------------------------------------------------------------------
TEST "No dependency file"
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_stat 'files in cache' 2 # .o + .manifest
+ expect_stat 'files in cache' 2 # result + manifest
CCACHE_DEPEND=1 $CCACHE_COMPILE -MP -MMD -MF /dev/null -c test.c
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_stat 'files in cache' 3 # .o + .manifest + .d
+ expect_stat 'files in cache' 2 # result + manifest
CCACHE_DEPEND=1 $CCACHE_COMPILE -MD -c test.c
expect_equal_object_files reference_test.o test.o
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_stat 'files in cache' 3
+ expect_stat 'files in cache' 2
# -------------------------------------------------------------------------
TEST "Dependency file paths converted to relative if CCACHE_BASEDIR specified"
-
+
CCACHE_DEPEND=1 CCACHE_BASEDIR="`pwd`" $CCACHE_COMPILE $DEPSFLAGS_CCACHE -c "`pwd`/test.c"
if grep -q "[^.]/test.c" "test.d"; then
test_failed "Dependency file does not contain relative path to test.c"
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_file_content stderr-orig.txt "`cat stderr-baseline.txt`"
+ expect_content stderr-orig.txt "`cat stderr-baseline.txt`"
CCACHE_DEPEND=1 $CCACHE_COMPILE -MD -Wall -W -c cpp-warning.c 2>stderr-mf.txt
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_file_content stderr-mf.txt "`cat stderr-baseline.txt`"
+ expect_content stderr-mf.txt "`cat stderr-baseline.txt`"
# -------------------------------------------------------------------------
# This test case covers a case in depend mode with unchanged source file
# between compilations, but with changed headers. Header contents do not
- # affect the common hash (by which .manifest is stored in cache), only the
- # object's hash.
+ # affect the common hash (by which the manifest is stored in the cache),
+ # only the object's hash.
#
# dir1 is baseline
# dir2 has a change in header which affects object file
generate_reference_compiler_output
CCACHE_DEPEND=1 CCACHE_BASEDIR=$BASEDIR1 $CCACHE_COMPILE $DEPFLAGS -c test.c
expect_equal_object_files reference_test.o test.o
- expect_equal_files reference_test.d test.d
+ expect_equal_content reference_test.d test.d
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_stat 'files in cache' 3 # .o + .manifest + .d
+ expect_stat 'files in cache' 2 # result + manifest
# Recompile dir1 first time.
generate_reference_compiler_output
CCACHE_DEPEND=1 CCACHE_BASEDIR=$BASEDIR1 $CCACHE_COMPILE $DEPFLAGS -c test.c
expect_equal_object_files reference_test.o test.o
- expect_equal_files reference_test.d test.d
+ expect_equal_content reference_test.d test.d
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_stat 'files in cache' 3
+ expect_stat 'files in cache' 2
# Compile dir2. dir2 header changes the object file compared to dir1.
cd $BASEDIR2
generate_reference_compiler_output
CCACHE_DEPEND=1 CCACHE_BASEDIR=$BASEDIR2 $CCACHE_COMPILE $DEPFLAGS -c test.c
expect_equal_object_files reference_test.o test.o
- expect_equal_files reference_test.d test.d
+ expect_equal_content reference_test.d test.d
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 2
- expect_stat 'files in cache' 5 # 2x .o, 2x .d, 1x manifest
+ expect_stat 'files in cache' 3 # 2x result, 1x manifest
# Compile dir3. dir3 header change does not change object file compared to
# dir1, but ccache still adds an additional .o/.d file in the cache due to
generate_reference_compiler_output
CCACHE_DEPEND=1 CCACHE_BASEDIR=$BASEDIR3 $CCACHE_COMPILE $DEPFLAGS -c test.c
expect_equal_object_files reference_test.o test.o
- expect_equal_files reference_test.d test.d
+ expect_equal_content reference_test.d test.d
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 3
- expect_stat 'files in cache' 7 # 3x .o, 3x .d, 1x manifest
+ expect_stat 'files in cache' 4 # 3x result, 1x manifest
# Compile dir4. dir4 header adds a new dependency.
cd $BASEDIR4
generate_reference_compiler_output
CCACHE_DEPEND=1 CCACHE_BASEDIR=$BASEDIR4 $CCACHE_COMPILE $DEPFLAGS -c test.c
expect_equal_object_files reference_test.o test.o
- expect_equal_files reference_test.d test.d
- expect_different_files reference_test.d $BASEDIR1/test.d
+ expect_equal_content reference_test.d test.d
+ expect_different_content reference_test.d $BASEDIR1/test.d
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 4
- expect_stat 'files in cache' 9 # 4x .o, 4x .d, 1x manifest
+ expect_stat 'files in cache' 5 # 4x result, 1x manifest
# Recompile dir1 second time.
cd $BASEDIR1
generate_reference_compiler_output
CCACHE_DEPEND=1 CCACHE_BASEDIR=$BASEDIR1 $CCACHE_COMPILE $DEPFLAGS -c test.c
expect_equal_object_files reference_test.o test.o
- expect_equal_files reference_test.d test.d
+ expect_equal_content reference_test.d test.d
expect_stat 'cache hit (direct)' 2
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 4
- expect_stat 'files in cache' 9
+ expect_stat 'files in cache' 5
# Recompile dir2.
cd $BASEDIR2
generate_reference_compiler_output
CCACHE_DEPEND=1 CCACHE_BASEDIR=$BASEDIR2 $CCACHE_COMPILE $DEPFLAGS -c test.c
expect_equal_object_files test.o test.o
- expect_equal_files reference_test.d test.d
+ expect_equal_content reference_test.d test.d
expect_stat 'cache hit (direct)' 3
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 4
- expect_stat 'files in cache' 9
+ expect_stat 'files in cache' 5
# Recompile dir3.
cd $BASEDIR3
generate_reference_compiler_output
CCACHE_DEPEND=1 CCACHE_BASEDIR=$BASEDIR3 $CCACHE_COMPILE $DEPFLAGS -c test.c
expect_equal_object_files reference_test.o test.o
- expect_equal_files reference_test.d test.d
+ expect_equal_content reference_test.d test.d
expect_stat 'cache hit (direct)' 4
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 4
- expect_stat 'files in cache' 9
+ expect_stat 'files in cache' 5
# Recompile dir4.
cd $BASEDIR4
generate_reference_compiler_output
CCACHE_DEPEND=1 CCACHE_BASEDIR=$BASEDIR4 $CCACHE_COMPILE $DEPFLAGS -c test.c
expect_equal_object_files reference_test.o test.o
- expect_equal_files reference_test.d test.d
- expect_different_files reference_test.d $BASEDIR1/test.d
+ expect_equal_content reference_test.d test.d
+ expect_different_content reference_test.d $BASEDIR1/test.d
expect_stat 'cache hit (direct)' 5
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 4
- expect_stat 'files in cache' 9
+ expect_stat 'files in cache' 5
# -------------------------------------------------------------------------
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_stat 'files in cache' 2 # .o + .manifest
+ expect_stat 'files in cache' 2 # result + manifest
expect_equal_object_files reference_test.o test.o
- manifest_file=$(find $CCACHE_DIR -name '*.manifest')
+ manifest_file=$(find $CCACHE_DIR -name '*M')
backdate $manifest_file
$CCACHE_COMPILE -c test.c
expect_stat 'cache miss' 1
expect_stat 'files in cache' 2
expect_equal_object_files reference_test.o test.o
- expect_file_newer_than $manifest_file test.c
+ expect_newer_than $manifest_file test.c
# -------------------------------------------------------------------------
TEST "Corrupt manifest file"
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- manifest_file=`find $CCACHE_DIR -name '*.manifest'`
+ manifest_file=`find $CCACHE_DIR -name '*M'`
rm $manifest_file
touch $manifest_file
# -------------------------------------------------------------------------
TEST "Calculation of dependency file names"
- mkdir test.dir
+ i=0
for ext in .o .obj "" . .foo.bar; do
- dep_file=test.dir/`echo test$ext | sed 's/\.[^.]*\$//'`.d
+ rm -rf testdir
+ mkdir testdir
- $CCACHE_COMPILE -MD -c test.c -o test.dir/test$ext
+ dep_file=testdir/`echo test$ext | sed 's/\.[^.]*\$//'`.d
+
+ $CCACHE_COMPILE -MD -c test.c -o testdir/test$ext
+ expect_stat 'cache hit (direct)' $((3 * i))
+ expect_stat 'cache miss' $((i + 1))
rm -f $dep_file
- $CCACHE_COMPILE -MD -c test.c -o test.dir/test$ext
- expect_file_exists $dep_file
+
+ $CCACHE_COMPILE -MD -c test.c -o testdir/test$ext
+ expect_stat 'cache hit (direct)' $((3 * i + 1))
+ expect_stat 'cache miss' $((i + 1))
+ expect_exists $dep_file
if ! grep "test$ext:" $dep_file >/dev/null 2>&1; then
- test_failed "$dep_file does not contain test$ext"
+ test_failed "$dep_file does not contain \"test$ext:\""
fi
dep_target=foo.bar
- $CCACHE_COMPILE -MD -MQ $dep_target -c test.c -o test.dir/test$ext
+ $CCACHE_COMPILE -MD -MQ $dep_target -c test.c -o testdir/test$ext
+ expect_stat 'cache hit (direct)' $((3 * i + 1))
+ expect_stat 'cache miss' $((i + 2))
rm -f $dep_target
- $CCACHE_COMPILE -MD -MQ $dep_target -c test.c -o test.dir/test$ext
- expect_file_exists $dep_file
+
+ $CCACHE_COMPILE -MD -MQ $dep_target -c test.c -o testdir/test$ext
+ expect_stat 'cache hit (direct)' $((3 * i + 2))
+ expect_stat 'cache miss' $((i + 2))
+ expect_exists $dep_file
if ! grep $dep_target $dep_file >/dev/null 2>&1; then
test_failed "$dep_file does not contain $dep_target"
fi
+
+ i=$((i + 1))
done
- expect_stat 'files in cache' 18
+ expect_stat 'files in cache' $((2 * i + 2))
# -------------------------------------------------------------------------
TEST "-MMD for different source files"
touch a/source.c b/source.c
backdate a/source.h b/source.h
$CCACHE_COMPILE -MMD -c a/source.c -o a/source.o
- expect_file_content a/source.d "a/source.o: a/source.c"
+ expect_content a/source.d "a/source.o: a/source.c"
$CCACHE_COMPILE -MMD -c b/source.c -o b/source.o
- expect_file_content b/source.d "b/source.o: b/source.c"
+ expect_content b/source.d "b/source.o: b/source.c"
$CCACHE_COMPILE -MMD -c a/source.c -o a/source.o
- expect_file_content a/source.d "a/source.o: a/source.c"
+ expect_content a/source.d "a/source.o: a/source.c"
+
+ # -------------------------------------------------------------------------
+ for dep_args in "-MMD" "-MMD -MF foo.d" "-Wp,-MMD,foo.d"; do
+ for obj_args in "" "-o bar.o"; do
+ if [ "$dep_args" != "-MMD" ]; then
+ dep_file=foo.d
+ another_dep_file=foo.d
+ elif [ -z "$obj_args" ]; then
+ dep_file=test.d
+ another_dep_file=another.d
+ else
+ dep_file=bar.d
+ another_dep_file=another.d
+ fi
+
+ TEST "Dependency file content, $dep_args $obj_args"
+ # -----------------------------------------------------------------
+
+ $REAL_COMPILER -c test.c $dep_args $obj_args
+ mv $dep_file $dep_file.real
+
+ $REAL_COMPILER -c test.c $dep_args -o another.o
+ mv $another_dep_file another.d.real
+
+ # cache miss
+ $CCACHE_COMPILE -c test.c $dep_args $obj_args
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+ expect_equal_text_content $dep_file.real $dep_file
+
+ # cache hit
+ $CCACHE_COMPILE -c test.c $dep_args $obj_args
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 1
+ expect_equal_text_content $dep_file.real $dep_file
+
+ # change object file name
+ $CCACHE_COMPILE -c test.c $dep_args -o another.o
+ expect_equal_text_content another.d.real $another_dep_file
+ done
+ done
+
+ # -------------------------------------------------------------------------
+ TEST "-MD/-MMD dependency target rewriting"
+
+ touch test1.c
+
+ for option in -MD -MMD; do
+ $CCACHE -z >/dev/null
+ i=0
+ for dir in build1 build2 dir/dir2/dir3; do
+ mkdir -p $dir
+ for name in test1 obj1 random2; do
+ obj=$dir/$name.o
+ dep=$(echo $obj | sed 's/\.o$/.d/')
+
+ $REAL_COMPILER $option -c test1.c -o $obj
+ mv $dep orig.d
+
+ $CCACHE_COMPILE $option -c test1.c -o $obj
+ diff -u orig.d $dep
+ expect_equal_content $dep orig.d
+ expect_stat 'cache hit (direct)' $i
+ expect_stat 'cache miss' 1
+
+ i=$((i + 1))
+ done
+ rm -rf $dir
+ done
+ done
+
+ # -------------------------------------------------------------------------
+ TEST "-MMD: cache hits and miss and dependency"
+
+ hit=0
+ src=test2.c
+ touch $src
+ orig_dep=orig.d
+ for dir1 in build1 build2 dir1/dir2/dir3; do
+ mkdir -p $dir1
+ for name in test2 obj1 obj2; do
+ obj=$dir1/$name.o
+ dep=$(echo $obj | sed 's/\.o$/.d/')
+ $REAL_COMPILER -MMD -c $src -o $obj
+ mv $dep $orig_dep
+ rm $obj
+
+ $CCACHE_COMPILE -MMD -c $src -o $obj
+ dep=$(echo $obj | sed 's/\.o$/.d/')
+ expect_content $dep "$obj: $src"
+ expect_stat 'cache hit (direct)' $hit
+ expect_stat 'cache miss' 1
+ hit=$((hit + 1))
+
+ rm $orig_dep
+ done
+ rm -rf $dir1
+ done
# -------------------------------------------------------------------------
TEST "Dependency file content"
for obj in test1.o build/test1.o; do
$CCACHE_COMPILE -c -MMD $src -o $obj
dep=$(echo $obj | sed 's/\.o$/.d/')
- expect_file_content $dep "$obj: $src"
+ expect_content $dep "$obj: $src"
done
done
backdate a/source.h b/source.h
echo '#include <source.h>' >source.c
$CCACHE_COMPILE -MMD -Ia -c source.c
- expect_file_content source.d "source.o: source.c a/source.h"
+ expect_content source.d "source.o: source.c a/source.h"
$CCACHE_COMPILE -MMD -Ib -c source.c
- expect_file_content source.d "source.o: source.c b/source.h"
+ expect_content source.d "source.o: source.c b/source.h"
$CCACHE_COMPILE -MMD -Ia -c source.c
- expect_file_content source.d "source.o: source.c a/source.h"
+ expect_content source.d "source.o: source.c a/source.h"
# -------------------------------------------------------------------------
TEST "-Wp,-MD"
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files other.d expected.d
+ expect_equal_content other.d expected.d
$REAL_COMPILER -c -Wp,-MD,other.d test.c -o reference_test.o
expect_equal_object_files reference_test.o test.o
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files other.d expected.d
+ expect_equal_content other.d expected.d
expect_equal_object_files reference_test.o test.o
$CCACHE_COMPILE -c -Wp,-MD,different_name.d test.c
expect_stat 'cache hit (direct)' 2
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files different_name.d expected.d
+ expect_equal_content different_name.d expected.d
expect_equal_object_files reference_test.o test.o
# -------------------------------------------------------------------------
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files other.d expected_mmd.d
+ expect_equal_content other.d expected_mmd.d
$REAL_COMPILER -c -Wp,-MMD,other.d test.c -o reference_test.o
expect_equal_object_files reference_test.o test.o
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files other.d expected_mmd.d
+ expect_equal_content other.d expected_mmd.d
expect_equal_object_files reference_test.o test.o
$CCACHE_COMPILE -c -Wp,-MMD,different_name.d test.c
expect_stat 'cache hit (direct)' 2
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files different_name.d expected_mmd.d
+ expect_equal_content different_name.d expected_mmd.d
expect_equal_object_files reference_test.o test.o
# -------------------------------------------------------------------------
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_file_content source.d "source.o: source.c"
+ expect_content source.d "source.o: source.c"
$CCACHE_COMPILE -c -Wp,-MMD,source.d,-MT,source.o source.c 2>/dev/null
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
- expect_file_content source.d "source.o: source.c"
+ expect_content source.d "source.o: source.c"
# -------------------------------------------------------------------------
TEST "-MMD for different source files"
mkdir a b
touch a/source.c b/source.c
$CCACHE_COMPILE -MMD -c a/source.c
- expect_file_content source.d "source.o: a/source.c"
+ expect_content source.d "source.o: a/source.c"
$CCACHE_COMPILE -MMD -c b/source.c
- expect_file_content source.d "source.o: b/source.c"
+ expect_content source.d "source.o: b/source.c"
$CCACHE_COMPILE -MMD -c a/source.c
- expect_file_content source.d "source.o: a/source.c"
+ expect_content source.d "source.o: a/source.c"
# -------------------------------------------------------------------------
TEST "Multiple object entries in manifest"
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files test.d expected.d
+ expect_equal_content test.d expected.d
$REAL_COMPILER -c -MD test.c -o reference_test.o
expect_equal_object_files reference_test.o test.o
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files test.d expected.d
+ expect_equal_content test.d expected.d
expect_equal_object_files reference_test.o test.o
# -------------------------------------------------------------------------
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_file_exists code.gcno
+ expect_exists code.gcno
rm code.gcno
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_file_exists code.gcno
+ expect_exists code.gcno
# -------------------------------------------------------------------------
TEST "-fstack-usage"
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_file_exists code.su
+ expect_exists code.su
rm code.su
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_file_exists code.su
+ expect_exists code.su
fi
# -------------------------------------------------------------------------
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files test.d expected.d
+ expect_equal_content test.d expected.d
$REAL_COMPILER -c -MD test.c -o reference_test.o
expect_equal_object_files reference_test.o test.o
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
- expect_equal_files test.d expected.d
+ expect_equal_content test.d expected.d
expect_equal_object_files reference_test.o test.o
rm -f test.d
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 2
expect_stat 'cache miss' 1
- expect_equal_files test.d expected.d
+ expect_equal_content test.d expected.d
expect_equal_object_files reference_test.o test.o
rm -f test.d
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 2
expect_stat 'cache miss' 1
- expect_equal_files test.d expected.d
+ expect_equal_content test.d expected.d
expect_equal_object_files reference_test.o test.o
# -------------------------------------------------------------------------
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files other.d expected.d
+ expect_equal_content other.d expected.d
$REAL_COMPILER -c -MD -MF other.d test.c -o reference_test.o
expect_equal_object_files reference_test.o test.o
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files other.d expected.d
+ expect_equal_content other.d expected.d
expect_equal_object_files reference_test.o test.o
$CCACHE_COMPILE -c -MD -MF different_name.d test.c
expect_stat 'cache hit (direct)' 2
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files different_name.d expected.d
+ expect_equal_content different_name.d expected.d
expect_equal_object_files reference_test.o test.o
rm -f different_name.d
expect_stat 'cache hit (direct)' 3
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files third_name.d expected.d
+ expect_equal_content third_name.d expected.d
expect_equal_object_files reference_test.o test.o
rm -f third_name.d
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_stat 'files in cache' 2 # .o + .manifest
+ expect_stat 'files in cache' 2 # result + manifest
$CCACHE_COMPILE -c -MD -MF /dev/null test.c
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 2
- expect_stat 'files in cache' 5
- expect_equal_files test.d expected.d
+ expect_stat 'files in cache' 4
+ expect_equal_content test.d expected.d
rm -f test.d
expect_stat 'cache hit (direct)' 2
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 2
- expect_stat 'files in cache' 5
- expect_equal_files test.d expected.d
-
- # -------------------------------------------------------------------------
- TEST "Missing .d file"
-
- $CCACHE_COMPILE -c -MD test.c
- expect_stat 'cache hit (direct)' 0
- expect_stat 'cache hit (preprocessed)' 0
- expect_stat 'cache miss' 1
-
- $CCACHE_COMPILE -c -MD test.c
- expect_stat 'cache hit (direct)' 1
- expect_stat 'cache hit (preprocessed)' 0
- expect_stat 'cache miss' 1
- expect_equal_files test.d expected.d
-
- find $CCACHE_DIR -name '*.d' -exec rm '{}' +
-
- # Missing file -> consider the cached result broken.
- $CCACHE_COMPILE -c -MD test.c
- expect_stat 'cache hit (direct)' 1
- expect_stat 'cache hit (preprocessed)' 0
- expect_stat 'cache miss' 1
- expect_stat 'cache file missing' 1
+ expect_stat 'files in cache' 4
+ expect_equal_content test.d expected.d
# -------------------------------------------------------------------------
TEST "stderr from both preprocessor and compiler"
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
- expect_file_content stderr-cpp.txt "`cat stderr-orig.txt`"
+ expect_content stderr-cpp.txt "`cat stderr-orig.txt`"
$CCACHE_COMPILE -Wall -W -c cpp-warning.c 2>stderr-mf.txt
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
- expect_file_content stderr-mf.txt "`cat stderr-orig.txt`"
+ expect_content stderr-mf.txt "`cat stderr-orig.txt`"
# -------------------------------------------------------------------------
TEST "Empty source file"
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
+ # -------------------------------------------------------------------------
+ TEST "__DATE__ in header file results in direct cache hit as the date remains the same"
+
+ cat <<EOF >test_date2.c
+// test_date2.c
+#include "test_date2.h"
+char date_str[] = MACRO_STRING;
+EOF
+ cat <<EOF >test_date2.h
+#define MACRO_STRING __DATE__
+EOF
+
+ backdate test_date2.c test_date2.h
+
+ $CCACHE_COMPILE -MP -MMD -MF test_date2.d -c test_date2.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+
+ $CCACHE_COMPILE -MP -MMD -MF test_date2.d -c test_date2.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+
# -------------------------------------------------------------------------
TEST "New include file ignored if sloppy"
CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS include_file_mtime" $CCACHE_COMPILE -c strange.c
- manifest=`find $CCACHE_DIR -name '*.manifest'`
+ manifest=`find $CCACHE_DIR -name '*M'`
if [ -n "$manifest" ]; then
data="`$CCACHE --dump-manifest $manifest | egrep '/dev/(stdout|tty|sda|hda'`"
if [ -n "$data" ]; then
$CCACHE_COMPILE test.c -c -o test.o
- manifest=`find $CCACHE_DIR -name '*.manifest'`
+ manifest=`find $CCACHE_DIR -name '*M'`
$CCACHE --dump-manifest $manifest >manifest.dump
- if grep 'Hash: d4de2f956b4a386c6660990a7a1ab13f' manifest.dump >/dev/null 2>&1 && \
- grep 'Hash: e94ceb9f1b196c387d098a5f1f4fe862' manifest.dump >/dev/null 2>&1 && \
- grep 'Hash: ba753bebf9b5eb99524bb7447095e2e6' manifest.dump >/dev/null 2>&1; then
+ checksum_test1_h='b7273h0ksdehi0o4pitg5jeehal3i54ns'
+ checksum_test2_h='24f1315jch5tcndjbm6uejtu8q3lf9100'
+ checksum_test3_h='56a6dkffffv485aepk44seaq3i6lbepq2'
+
+ if grep "Hash: $checksum_test1_h" manifest.dump >/dev/null 2>&1 && \
+ grep "Hash: $checksum_test2_h" manifest.dump >/dev/null 2>&1 && \
+ grep "Hash: $checksum_test3_h" manifest.dump >/dev/null 2>&1; then
: OK
else
test_failed "Unexpected output of --dump-manifest"
EOF
CCACHE_IGNOREHEADERS="subdir/ignore.h" $CCACHE_COMPILE -c ignore.c
- manifest=`find $CCACHE_DIR -name '*.manifest'`
+ manifest=`find $CCACHE_DIR -name '*M'`
data="`$CCACHE --dump-manifest $manifest | grep subdir/ignore.h`"
if [ -n "$data" ]; then
test_failed "$manifest contained ignored header: $data"
EOF
CCACHE_IGNOREHEADERS="subdir" $CCACHE_COMPILE -c ignore.c
- manifest=`find $CCACHE_DIR -name '*.manifest'`
+ manifest=`find $CCACHE_DIR -name '*M'`
data="`$CCACHE --dump-manifest $manifest | grep subdir/ignore.h`"
if [ -n "$data" ]; then
test_failed "$manifest contained ignored header: $data"
fi
+
+ # -------------------------------------------------------------------------
+ TEST "CCACHE_IGNOREOPTIONS"
+
+ CCACHE_IGNOREOPTIONS="-DTEST=1" $CCACHE_COMPILE -DTEST=1 -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+
+ CCACHE_IGNOREOPTIONS="-DTEST=1*" $CCACHE_COMPILE -DTEST=1 -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+
+ CCACHE_IGNOREOPTIONS="-DTEST=1*" $CCACHE_COMPILE -DTEST=12 -c test.c
+ expect_stat 'cache hit (direct)' 2
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+
+ CCACHE_IGNOREOPTIONS="-DTEST=2*" $CCACHE_COMPILE -DTEST=12 -c test.c
+ expect_stat 'cache hit (direct)' 2
+ expect_stat 'cache hit (preprocessed)' 1
+ expect_stat 'cache miss' 1
}
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files other.d expected_dependencies_output.d
+ expect_equal_content other.d expected_dependencies_output.d
DEPENDENCIES_OUTPUT="other.d" $REAL_COMPILER -c test.c -o reference_test.o
expect_equal_object_files reference_test.o test.o
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files other.d expected_dependencies_output.d
+ expect_equal_content other.d expected_dependencies_output.d
expect_equal_object_files reference_test.o test.o
DEPENDENCIES_OUTPUT="different_name.d" $CCACHE_COMPILE -c test.c
expect_stat 'cache hit (direct)' 2
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files different_name.d expected_dependencies_output.d
+ expect_equal_content different_name.d expected_dependencies_output.d
expect_equal_object_files reference_test.o test.o
# -------------------------------------------------------------------------
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files other.d expected_dependencies_output_target.d
+ expect_equal_content other.d expected_dependencies_output_target.d
DEPENDENCIES_OUTPUT="other.d target.o" $REAL_COMPILER -c test.c -o reference_test.o
expect_equal_object_files reference_test.o test.o
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files other.d expected_dependencies_output_target.d
+ expect_equal_content other.d expected_dependencies_output_target.d
expect_equal_object_files reference_test.o test.o
DEPENDENCIES_OUTPUT="different_name.d target.o" $CCACHE_COMPILE -c test.c
expect_stat 'cache hit (direct)' 2
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files different_name.d expected_dependencies_output_target.d
+ expect_equal_content different_name.d expected_dependencies_output_target.d
expect_equal_object_files reference_test.o test.o
# -------------------------------------------------------------------------
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files other.d expected_sunpro_dependencies.d
+ expect_equal_content other.d expected_sunpro_dependencies.d
SUNPRO_DEPENDENCIES="other.d" $REAL_COMPILER -c test.c -o reference_test.o
expect_equal_object_files reference_test.o test.o
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files other.d expected_sunpro_dependencies.d
+ expect_equal_content other.d expected_sunpro_dependencies.d
expect_equal_object_files reference_test.o test.o
SUNPRO_DEPENDENCIES="different_name.d" $CCACHE_COMPILE -c test.c
expect_stat 'cache hit (direct)' 2
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files different_name.d expected_sunpro_dependencies.d
+ expect_equal_content different_name.d expected_sunpro_dependencies.d
expect_equal_object_files reference_test.o test.o
# -------------------------------------------------------------------------
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files other.d expected_sunpro_dependencies_target.d
+ expect_equal_content other.d expected_sunpro_dependencies_target.d
SUNPRO_DEPENDENCIES="other.d target.o" $REAL_COMPILER -c test.c -o reference_test.o
expect_equal_object_files reference_test.o test.o
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files other.d expected_sunpro_dependencies_target.d
+ expect_equal_content other.d expected_sunpro_dependencies_target.d
expect_equal_object_files reference_test.o test.o
SUNPRO_DEPENDENCIES="different_name.d target.o" $CCACHE_COMPILE -c test.c
expect_stat 'cache hit (direct)' 2
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_equal_files different_name.d expected_sunpro_dependencies_target.d
+ expect_equal_content different_name.d expected_sunpro_dependencies_target.d
expect_equal_object_files reference_test.o test.o
# -------------------------------------------------------------------------
--- /dev/null
+SUITE_fileclone_PROBE() {
+ touch file1
+ if ! cp --reflink=always file1 file2 >/dev/null 2>&1; then
+ echo "file system doesn't support file cloning"
+ fi
+}
+
+SUITE_fileclone() {
+ # -------------------------------------------------------------------------
+ TEST "Base case"
+
+ generate_code 1 test.c
+
+ $REAL_COMPILER -c -o reference_test.o test.c
+
+ CCACHE_FILECLONE=1 $CCACHE_COMPILE -c test.c
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 2
+ expect_equal_object_files reference_test.o test.o
+
+ # Note: CCACHE_DEBUG=1 below is needed for the test case.
+ CCACHE_FILECLONE=1 CCACHE_DEBUG=1 $CCACHE_COMPILE -c test.c
+ expect_stat 'cache hit (preprocessed)' 1
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 2
+ expect_equal_object_files reference_test.o test.o
+ if ! grep -q 'Cloning.*to test.o' test.o.ccache-log; then
+ test_failed "Did not try to clone file"
+ fi
+ if grep -q 'Failed to clone' test.o.ccache-log; then
+ test_failed "Failed to clone"
+ fi
+
+ # -------------------------------------------------------------------------
+ TEST "Cloning not used for stored non-raw result"
+
+ generate_code 1 test.c
+
+ $REAL_COMPILER -c -o reference_test.o test.c
+
+ $CCACHE_COMPILE -c test.c
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 1
+ expect_equal_object_files reference_test.o test.o
+
+ # Note: CCACHE_DEBUG=1 below is needed for the test case.
+ CCACHE_FILECLONE=1 CCACHE_DEBUG=1 $CCACHE_COMPILE -c test.c
+ expect_stat 'cache hit (preprocessed)' 1
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 1
+ expect_equal_object_files reference_test.o test.o
+ if grep -q 'Cloning' test.o.ccache-log; then
+ test_failed "Tried to clone"
+ fi
+}
SUITE_hardlink_PROBE() {
- touch file1
- if ! ln file1 file2 >/dev/null 2>&1; then
+ # Probe hard link across directories since AFS doesn't support those.
+ mkdir dir
+ touch dir/file1
+ if ! ln dir/file1 file2 >/dev/null 2>&1; then
echo "file system doesn't support hardlinks"
fi
}
$REAL_COMPILER -c -o reference_test1.o test1.c
- $CCACHE_COMPILE -c test1.c
+ CCACHE_HARDLINK=1 $CCACHE_COMPILE -c test1.c
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_stat 'files in cache' 1
+ expect_stat 'files in cache' 2
expect_equal_object_files reference_test1.o test1.o
+ mv test1.o test1.o.saved
+
CCACHE_HARDLINK=1 $CCACHE_COMPILE -c test1.c
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
- expect_stat 'files in cache' 1
- expect_equal_object_files reference_test1.o test1.o
-
- local obj_in_cache
- obj_in_cache=$(find $CCACHE_DIR -name '*.o')
- if [ ! $obj_in_cache -ef test1.o ]; then
- test_failed "Object file not hard-linked to cached object file"
+ expect_stat 'files in cache' 2
+ if [ ! test1.o -ef test1.o.saved ]; then
+ test_failed "Object files not hard linked"
fi
+ # -------------------------------------------------------------------------
+ TEST "Corrupted file size is detected"
+
+ generate_code 1 test1.c
+
+ CCACHE_HARDLINK=1 $CCACHE_COMPILE -c test1.c
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 2
+
+ mv test1.o test1.o.saved
+
+ CCACHE_HARDLINK=1 $CCACHE_COMPILE -c test1.c
+ expect_stat 'cache hit (preprocessed)' 1
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 2
+
# -------------------------------------------------------------------------
TEST "Overwrite assembler"
CCACHE_HARDLINK=1 $CCACHE_COMPILE -c test1.s
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_stat 'files in cache' 1
+ expect_stat 'files in cache' 2
generate_code 2 test1.c
$REAL_COMPILER -S -o test1.s test1.c
CCACHE_HARDLINK=1 $CCACHE_COMPILE -c test1.s
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 2
- expect_stat 'files in cache' 2
+ expect_stat 'files in cache' 4
generate_code 1 test1.c
$REAL_COMPILER -S -o test1.s test1.c
CCACHE_HARDLINK=1 $CCACHE_COMPILE -c test1.s
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 2
- expect_stat 'files in cache' 2
+ expect_stat 'files in cache' 4
expect_equal_object_files reference_test1.o test1.o
# -------------------------------------------------------------------------
$CCACHE_COMPILE -c -MMD test1.c
expect_stat 'cache hit (direct)' 0
expect_stat 'cache miss' 1
- expect_file_content test1.d "test1.o: test1.c"
+ expect_content test1.d "test1.o: test1.c"
touch test1.h
echo '#include "test1.h"' >>test1.c
$CCACHE_COMPILE -c -MMD test1.c
expect_stat 'cache hit (direct)' 0
expect_stat 'cache miss' 2
- expect_file_content test1.d "test1.o: test1.c test1.h"
+ expect_content test1.d "test1.o: test1.c test1.h"
echo "int x;" >test1.c
$CCACHE_COMPILE -c -MMD test1.c
expect_stat 'cache hit (direct)' 1
expect_stat 'cache miss' 2
- expect_file_content test1.d "test1.o: test1.c"
+ expect_content test1.d "test1.o: test1.c"
}
--- /dev/null
+SUITE_inode_cache_PROBE() {
+ temp_dir=$(dirname $($CCACHE -k temporary_dir))
+ fs=$(stat -fLc %T $temp_dir)
+ if [ "$fs" = "nfs" ]; then
+ echo "ccache temporary directory is on NFS"
+ fi
+}
+
+SUITE_inode_cache_SETUP() {
+ export CCACHE_INODECACHE=1
+ export CCACHE_DEBUG=1
+ unset CCACHE_NODIRECT
+}
+
+SUITE_inode_cache() {
+ inode_cache_tests
+}
+
+expect_inode_cache_type() {
+ local expected=$1
+ local source_file=$2
+ local type=$3
+
+ local log_file=$(echo $source_file | sed 's/\.c$/.o.ccache-log/')
+ local actual=$(grep -c "inode cache $type: $source_file" "$log_file")
+ if [ $actual -ne $expected ]; then
+ test_failed "Found $actual (expected $expected) $type for $source_file"
+ fi
+}
+
+expect_inode_cache() {
+ expect_inode_cache_type $1 $4 hit
+ expect_inode_cache_type $2 $4 miss
+ expect_inode_cache_type $3 $4 insert
+}
+
+inode_cache_tests() {
+ # -------------------------------------------------------------------------
+ TEST "Compile once"
+
+ echo "// compile once" > test1.c
+ $CCACHE_COMPILE -c test1.c
+ expect_inode_cache 0 1 1 test1.c
+
+ # -------------------------------------------------------------------------
+ TEST "Recompile"
+
+ echo "// recompile" > test1.c
+ $CCACHE_COMPILE -c test1.c
+ expect_inode_cache 0 1 1 test1.c
+ $CCACHE_COMPILE -c test1.c
+ expect_inode_cache 1 0 0 test1.c
+
+ # -------------------------------------------------------------------------
+ TEST "Backdate"
+
+ echo "// backdate" > test1.c
+ $CCACHE_COMPILE -c test1.c
+ expect_inode_cache 0 1 1 test1.c
+
+ backdate test1.c
+ $CCACHE_COMPILE -c test1.c
+ expect_inode_cache 0 1 1 test1.c
+
+ # -------------------------------------------------------------------------
+ TEST "Hard link"
+
+ echo "// hard linked" > test1.c
+ ln -f test1.c test2.c
+ $CCACHE_COMPILE -c test1.c
+ $CCACHE_COMPILE -c test2.c
+ expect_inode_cache 0 1 1 test1.c
+ expect_inode_cache 1 0 0 test2.c
+
+ # -------------------------------------------------------------------------
+ TEST "Soft link"
+
+ echo "// soft linked" > test1.c
+ ln -fs test1.c test2.c
+ $CCACHE_COMPILE -c test1.c
+ $CCACHE_COMPILE -c test2.c
+ expect_inode_cache 0 1 1 test1.c
+ expect_inode_cache 1 0 0 test2.c
+
+ # -------------------------------------------------------------------------
+ TEST "Replace"
+
+ echo "// replace" > test1.c
+ $CCACHE_COMPILE -c test1.c
+ expect_inode_cache 0 1 1 test1.c
+
+ rm test1.c
+ echo "// replace" > test1.c
+ $CCACHE_COMPILE -c test1.c
+ expect_inode_cache 0 1 1 test1.c
+}
SUITE_masquerading_PROBE() {
local compiler_binary
- compiler_binary=$(echo $COMPILER | cut -d' ' -f1)
- if [ "$(dirname $compiler_binary)" != . ]; then
- echo "compiler ($compiler_binary) not taken from PATH"
+ if $HOST_OS_WINDOWS || $HOST_OS_CYGWIN; then
+ echo "symlinks not supported on $(uname -s)"
+ return
+ fi
+ if [ "$(dirname $COMPILER_BIN)" != . ]; then
+ echo "compiler ($COMPILER_BIN) not taken from PATH"
+ return
fi
}
SUITE_masquerading_SETUP() {
- local compiler_binary
- compiler_binary=$(echo $COMPILER | cut -d' ' -f1)
- local compiler_args
- compiler_args=$(echo $COMPILER | cut -s -d' ' -f2-)
-
- ln -s "$CCACHE" $compiler_binary
- CCACHE_COMPILE="./$compiler_binary $compiler_args"
+ ln -s "$CCACHE" $COMPILER_BIN
generate_code 1 test1.c
}
SUITE_masquerading() {
# -------------------------------------------------------------------------
- TEST "Masquerading via symlink"
+ TEST "Masquerading via symlink, relative path"
+
+ $REAL_COMPILER -c -o reference_test1.o test1.c
+
+ ./$COMPILER_BIN $COMPILER_ARGS -c test1.c
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 1
+ expect_equal_object_files reference_test1.o test1.o
+
+ ./$COMPILER_BIN $COMPILER_ARGS -c test1.c
+ expect_stat 'cache hit (preprocessed)' 1
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 1
+ expect_equal_object_files reference_test1.o test1.o
+
+ # -------------------------------------------------------------------------
+ TEST "Masquerading via symlink, absolute path"
$REAL_COMPILER -c -o reference_test1.o test1.c
- $CCACHE_COMPILE -c test1.c
+ $PWD/$COMPILER_BIN $COMPILER_ARGS -c test1.c
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
expect_equal_object_files reference_test1.o test1.o
- $CCACHE_COMPILE -c test1.c
+ $PWD/$COMPILER_BIN $COMPILER_ARGS -c test1.c
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
--- /dev/null
+SUITE_modules_PROBE() {
+ if ! $COMPILER_TYPE_CLANG; then
+ echo "-fmodules/-fcxx-modules not supported by compiler"
+ return
+ fi
+}
+
+SUITE_modules_SETUP() {
+ unset CCACHE_NODIRECT
+ export CCACHE_DEPEND=1
+
+ cat <<EOF >test1.h
+#include <string>
+EOF
+ backdate test1.h
+
+cat <<EOF >module.modulemap
+module "Test1" {
+ header "test1.h"
+ export *
+}
+EOF
+ backdate module.modulemap
+
+ cat <<EOF >test1.cpp
+#import "test1.h"
+int main() { return 0; }
+EOF
+}
+
+SUITE_modules() {
+ # -------------------------------------------------------------------------
+ TEST "fall back to real compiler, no sloppiness"
+
+ $CCACHE_COMPILE -fmodules -fcxx-modules -c test1.cpp -MD
+ expect_stat "can't use modules" 1
+
+ $CCACHE_COMPILE -fmodules -fcxx-modules -c test1.cpp -MD
+ expect_stat "can't use modules" 2
+
+ # -------------------------------------------------------------------------
+ TEST "fall back to real compiler, no depend mode"
+
+ unset CCACHE_DEPEND
+
+ CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS modules" $CCACHE_COMPILE -fmodules -fcxx-modules -c test1.cpp -MD
+ expect_stat "can't use modules" 1
+
+ CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS modules" $CCACHE_COMPILE -fmodules -fcxx-modules -c test1.cpp -MD
+ expect_stat "can't use modules" 2
+
+ # -------------------------------------------------------------------------
+ TEST "cache hit"
+
+ CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS modules" $CCACHE_COMPILE -fmodules -fcxx-modules -c test1.cpp -MD
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+
+ CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS modules" $CCACHE_COMPILE -fmodules -fcxx-modules -c test1.cpp -MD
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 1
+
+ # -------------------------------------------------------------------------
+ TEST "cache miss"
+
+ CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS modules" $CCACHE_COMPILE -MD -fmodules -fcxx-modules -c test1.cpp -MD
+ expect_stat 'cache miss' 1
+
+ cat <<EOF >test1.h
+#include <string>
+void f();
+EOF
+ backdate test1.h
+
+ CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS modules" $CCACHE_COMPILE -MD -fmodules -fcxx-modules -c test1.cpp -MD
+ expect_stat 'cache miss' 2
+
+ echo >>module.modulemap
+ backdate test1.h
+
+ CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS modules" $CCACHE_COMPILE -MD -fmodules -fcxx-modules -c test1.cpp -MD
+ expect_stat 'cache miss' 3
+}
--- /dev/null
+SUITE_no_compression_SETUP() {
+ generate_code 1 test.c
+
+ unset CCACHE_NODIRECT
+ export CCACHE_NOCOMPRESS=1
+}
+
+SUITE_no_compression() {
+ # -------------------------------------------------------------------------
+ TEST "Base case"
+
+ $REAL_COMPILER -c -o reference_test.o test.c
+
+ $CCACHE_COMPILE -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 2
+ expect_equal_object_files reference_test.o test.o
+
+ $CCACHE_COMPILE -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 2
+ expect_equal_object_files reference_test.o test.o
+
+ # -------------------------------------------------------------------------
+ TEST "Result file is uncompressed"
+
+ $CCACHE_COMPILE -c test.c
+ result_file=$(find $CCACHE_DIR -name '*R')
+ if ! $CCACHE --dump-result $result_file | grep 'Compression type: none' >/dev/null 2>&1; then
+ test_failed "Result file not uncompressed according to metadata"
+ fi
+ if [ $(file_size $result_file) -le $(file_size test.o) ]; then
+ test_failed "Result file seems to be compressed"
+ fi
+
+ # -------------------------------------------------------------------------
+ TEST "Hash sum equal for compressed and uncompressed files"
+
+ $CCACHE_COMPILE -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+
+ $CCACHE_COMPILE -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 1
+
+ unset CCACHE_NOCOMPRESS
+ $CCACHE_COMPILE -c test.c
+ expect_stat 'cache hit (direct)' 2
+ expect_stat 'cache miss' 1
+
+ # -------------------------------------------------------------------------
+ TEST "Corrupt result file"
+
+ $CCACHE_COMPILE -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 2
+
+ $CCACHE_COMPILE -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 1
+ expect_stat 'files in cache' 2
+
+ result_file=$(find $CCACHE_DIR -name '*R')
+ printf foo | dd of=$result_file bs=3 count=1 seek=20 conv=notrunc >&/dev/null
+
+ $CCACHE_COMPILE -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 2
+ expect_stat 'files in cache' 2
+
+ $CCACHE_COMPILE -c test.c
+ expect_stat 'cache hit (direct)' 2
+ expect_stat 'cache miss' 2
+ expect_stat 'files in cache' 2
+}
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
- expect_equal_files reference_test1.o test_cpp.o
+ expect_equal_content reference_test1.o test_cpp.o
$ccache_nvcc_cpp test_cpp.cu
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
- expect_equal_files reference_test1.o test_cpp.o
+ expect_equal_content reference_test1.o test_cpp.o
# -------------------------------------------------------------------------
TEST "Different GPU architectures"
$cuobjdump reference_test1.o > reference_test1.dump
$cuobjdump reference_test2.o > reference_test2.dump
$cuobjdump reference_test3.o > reference_test3.dump
- expect_different_files reference_test1.dump reference_test2.dump
- expect_different_files reference_test1.dump reference_test3.dump
- expect_different_files reference_test2.dump reference_test3.dump
+ expect_different_content reference_test1.dump reference_test2.dump
+ expect_different_content reference_test1.dump reference_test3.dump
+ expect_different_content reference_test2.dump reference_test3.dump
$ccache_nvcc_cuda test_cuda.cu
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
$cuobjdump test_cuda.o > test1.dump
- expect_equal_files reference_test1.dump test1.dump
+ expect_equal_content reference_test1.dump test1.dump
# Other GPU.
$ccache_nvcc_cuda $nvcc_opts_gpu1 test_cuda.cu
expect_stat 'cache miss' 2
expect_stat 'files in cache' 2
$cuobjdump test_cuda.o > test1.dump
- expect_equal_files reference_test2.dump test1.dump
+ expect_equal_content reference_test2.dump test1.dump
$ccache_nvcc_cuda $nvcc_opts_gpu1 test_cuda.cu
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 2
expect_stat 'files in cache' 2
$cuobjdump test_cuda.o > test1.dump
- expect_equal_files reference_test2.dump test1.dump
+ expect_equal_content reference_test2.dump test1.dump
# Another GPU.
$ccache_nvcc_cuda $nvcc_opts_gpu2 test_cuda.cu
expect_stat 'cache miss' 3
expect_stat 'files in cache' 3
$cuobjdump test_cuda.o > test1.dump
- expect_equal_files reference_test3.dump test1.dump
+ expect_equal_content reference_test3.dump test1.dump
$ccache_nvcc_cuda $nvcc_opts_gpu2 test_cuda.cu
expect_stat 'cache hit (preprocessed)' 2
expect_stat 'cache miss' 3
expect_stat 'files in cache' 3
$cuobjdump test_cuda.o > test1.dump
- expect_equal_files reference_test3.dump test1.dump
-
+ expect_equal_content reference_test3.dump test1.dump
+
# -------------------------------------------------------------------------
TEST "Option -dc"
-
+
$REAL_NVCC $nvcc_opts_cuda -dc -o reference_test4.o test_cuda.cu
$cuobjdump reference_test4.o > reference_test4.dump
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
$cuobjdump test_cuda.o > test4.dump
- expect_equal_files test4.dump reference_test4.dump
+ expect_equal_content test4.dump reference_test4.dump
$ccache_nvcc_cuda -dc -o test_cuda.o test_cuda.cu
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
$cuobjdump test_cuda.o > test4.dump
- expect_equal_files test4.dump reference_test4.dump
+ expect_equal_content test4.dump reference_test4.dump
# -------------------------------------------------------------------------
TEST "Different defines"
$REAL_NVCC $nvcc_opts_cpp -o reference_test1.o test_cpp.cu
$REAL_NVCC $nvcc_opts_cpp -DNUM=10 -o reference_test2.o test_cpp.cu
- expect_different_files reference_test1.o reference_test2.o
+ expect_different_content reference_test1.o reference_test2.o
$ccache_nvcc_cpp test_cpp.cu
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
- expect_equal_files reference_test1.o test_cpp.o
+ expect_equal_content reference_test1.o test_cpp.o
# Specified define, but unused. Can only be found by preprocessed mode.
$ccache_nvcc_cpp -DDUMMYENV=1 test_cpp.cu
expect_stat "cache hit (preprocessed)" 1
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
- expect_equal_files reference_test1.o test_cpp.o
+ expect_equal_content reference_test1.o test_cpp.o
# Specified used define.
$ccache_nvcc_cpp -DNUM=10 test_cpp.cu
expect_stat "cache hit (preprocessed)" 1
expect_stat 'cache miss' 2
expect_stat 'files in cache' 2
- expect_equal_files reference_test2.o test_cpp.o
+ expect_equal_content reference_test2.o test_cpp.o
$ccache_nvcc_cpp -DNUM=10 test_cpp.cu
expect_stat 'cache hit (preprocessed)' 2
expect_stat 'cache miss' 2
expect_stat 'files in cache' 2
- expect_equal_files reference_test2.o test_cpp.o
+ expect_equal_content reference_test2.o test_cpp.o
# -------------------------------------------------------------------------
TEST "Option file"
$REAL_NVCC $nvcc_opts_cpp -optf test1.optf -o reference_test1.o test_cpp.cu
$REAL_NVCC $nvcc_opts_cpp -optf test2.optf -o reference_test2.o test_cpp.cu
- expect_different_files reference_test1.o reference_test2.o
+ expect_different_content reference_test1.o reference_test2.o
$ccache_nvcc_cpp -optf test1.optf test_cpp.cu
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
- expect_equal_files reference_test1.o test_cpp.o
+ expect_equal_content reference_test1.o test_cpp.o
$ccache_nvcc_cpp -optf test1.optf test_cpp.cu
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
- expect_equal_files reference_test1.o test_cpp.o
+ expect_equal_content reference_test1.o test_cpp.o
$ccache_nvcc_cpp -optf test2.optf test_cpp.cu
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 2
expect_stat 'files in cache' 2
- expect_equal_files reference_test2.o test_cpp.o
+ expect_equal_content reference_test2.o test_cpp.o
$ccache_nvcc_cpp -optf test2.optf test_cpp.cu
expect_stat 'cache hit (preprocessed)' 2
expect_stat 'cache miss' 2
expect_stat 'files in cache' 2
- expect_equal_files reference_test2.o test_cpp.o
+ expect_equal_content reference_test2.o test_cpp.o
# -------------------------------------------------------------------------
TEST "Option --compiler-bindir"
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
- expect_equal_files reference_test1.o test_cpp.o
+ expect_equal_content reference_test1.o test_cpp.o
$ccache_nvcc_cpp --compiler-bindir $REAL_COMPILER_BIN test_cpp.cu
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
- expect_equal_files reference_test1.o test_cpp.o
+ expect_equal_content reference_test1.o test_cpp.o
# -------------------------------------------------------------------------
TEST "Option -ccbin"
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
- expect_equal_files reference_test1.o test_cpp.o
+ expect_equal_content reference_test1.o test_cpp.o
$ccache_nvcc_cpp -ccbin $REAL_COMPILER_BIN test_cpp.cu
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
- expect_equal_files reference_test1.o test_cpp.o
+ expect_equal_content reference_test1.o test_cpp.o
# -------------------------------------------------------------------------
TEST "Option --output-directory"
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
- expect_equal_files reference_test1.o test_cpp.o
+ expect_equal_content reference_test1.o test_cpp.o
$ccache_nvcc_cpp --output-directory . test_cpp.cu
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
- expect_equal_files reference_test1.o test_cpp.o
+ expect_equal_content reference_test1.o test_cpp.o
# -------------------------------------------------------------------------
TEST "Option -odir"
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
- expect_equal_files reference_test1.o test_cpp.o
+ expect_equal_content reference_test1.o test_cpp.o
$ccache_nvcc_cpp -odir . test_cpp.cu
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
- expect_equal_files reference_test1.o test_cpp.o
+ expect_equal_content reference_test1.o test_cpp.o
# -------------------------------------------------------------------------
TEST "Option -Werror"
expect_stat 'cache hit (direct)' 0
expect_stat 'cache miss' 1
expect_stat 'files in cache' 2
- expect_equal_files reference_test1.o test_cpp.o
+ expect_equal_content reference_test1.o test_cpp.o
$ccache_nvcc_cpp test_cpp.cu
expect_stat 'cache hit (direct)' 1
expect_stat 'cache miss' 1
expect_stat 'files in cache' 2
- expect_equal_files reference_test1.o test_cpp.o
+ expect_equal_content reference_test1.o test_cpp.o
# -------------------------------------------------------------------------
TEST "Different GPU architectures"
$cuobjdump reference_test1.o > reference_test1.dump
$cuobjdump reference_test2.o > reference_test2.dump
$cuobjdump reference_test3.o > reference_test3.dump
- expect_different_files reference_test1.dump reference_test2.dump
- expect_different_files reference_test1.dump reference_test3.dump
- expect_different_files reference_test2.dump reference_test3.dump
+ expect_different_content reference_test1.dump reference_test2.dump
+ expect_different_content reference_test1.dump reference_test3.dump
+ expect_different_content reference_test2.dump reference_test3.dump
$ccache_nvcc_cuda test_cuda.cu
expect_stat 'cache hit (direct)' 0
expect_stat 'cache miss' 1
expect_stat 'files in cache' 2
$cuobjdump test_cuda.o > test1.dump
- expect_equal_files reference_test1.dump test1.dump
+ expect_equal_content reference_test1.dump test1.dump
# Other GPU.
$ccache_nvcc_cuda $nvcc_opts_gpu1 test_cuda.cu
expect_stat 'cache miss' 2
expect_stat 'files in cache' 4
$cuobjdump test_cuda.o > test1.dump
- expect_equal_files reference_test2.dump test1.dump
+ expect_equal_content reference_test2.dump test1.dump
$ccache_nvcc_cuda $nvcc_opts_gpu1 test_cuda.cu
expect_stat 'cache hit (direct)' 1
expect_stat 'cache miss' 2
expect_stat 'files in cache' 4
$cuobjdump test_cuda.o > test1.dump
- expect_equal_files reference_test2.dump test1.dump
+ expect_equal_content reference_test2.dump test1.dump
# Another GPU.
$ccache_nvcc_cuda $nvcc_opts_gpu2 test_cuda.cu
expect_stat 'cache miss' 3
expect_stat 'files in cache' 6
$cuobjdump test_cuda.o > test1.dump
- expect_equal_files reference_test3.dump test1.dump
+ expect_equal_content reference_test3.dump test1.dump
$ccache_nvcc_cuda $nvcc_opts_gpu2 test_cuda.cu
expect_stat 'cache hit (direct)' 2
expect_stat 'cache miss' 3
expect_stat 'files in cache' 6
$cuobjdump test_cuda.o > test1.dump
- expect_equal_files reference_test3.dump test1.dump
+ expect_equal_content reference_test3.dump test1.dump
# -------------------------------------------------------------------------
TEST "Different defines"
$REAL_NVCC $nvcc_opts_cpp -o reference_test1.o test_cpp.cu
$REAL_NVCC $nvcc_opts_cpp -DNUM=10 -o reference_test2.o test_cpp.cu
- expect_different_files reference_test1.o reference_test2.o
+ expect_different_content reference_test1.o reference_test2.o
$ccache_nvcc_cpp test_cpp.cu
expect_stat 'cache hit (direct)' 0
expect_stat 'cache miss' 1
expect_stat 'files in cache' 2
- expect_equal_files reference_test1.o test_cpp.o
+ expect_equal_content reference_test1.o test_cpp.o
# Specified define, but unused. Can only be found by preprocessed mode.
$ccache_nvcc_cpp -DDUMMYENV=1 test_cpp.cu
expect_stat "cache hit (direct)" 0
expect_stat 'cache miss' 1
expect_stat 'files in cache' 3
- expect_equal_files reference_test1.o test_cpp.o
+ expect_equal_content reference_test1.o test_cpp.o
# Specified used define.
$ccache_nvcc_cpp -DNUM=10 test_cpp.cu
expect_stat "cache hit (direct)" 0
expect_stat 'cache miss' 2
expect_stat 'files in cache' 5
- expect_equal_files reference_test2.o test_cpp.o
+ expect_equal_content reference_test2.o test_cpp.o
$ccache_nvcc_cpp -DNUM=10 test_cpp.cu
expect_stat 'cache hit (direct)' 1
expect_stat 'cache miss' 2
expect_stat 'files in cache' 5
- expect_equal_files reference_test2.o test_cpp.o
+ expect_equal_content reference_test2.o test_cpp.o
# -------------------------------------------------------------------------
TEST "Option file"
$REAL_NVCC $nvcc_opts_cpp -optf test1.optf -o reference_test1.o test_cpp.cu
$REAL_NVCC $nvcc_opts_cpp -optf test2.optf -o reference_test2.o test_cpp.cu
- expect_different_files reference_test1.o reference_test2.o
+ expect_different_content reference_test1.o reference_test2.o
$ccache_nvcc_cpp -optf test1.optf test_cpp.cu
expect_stat 'cache hit (direct)' 0
expect_stat 'cache miss' 1
expect_stat 'files in cache' 2
- expect_equal_files reference_test1.o test_cpp.o
+ expect_equal_content reference_test1.o test_cpp.o
$ccache_nvcc_cpp -optf test1.optf test_cpp.cu
expect_stat 'cache hit (direct)' 1
expect_stat 'cache miss' 1
expect_stat 'files in cache' 2
- expect_equal_files reference_test1.o test_cpp.o
+ expect_equal_content reference_test1.o test_cpp.o
$ccache_nvcc_cpp -optf test2.optf test_cpp.cu
expect_stat 'cache hit (direct)' 1
expect_stat 'cache miss' 2
expect_stat 'files in cache' 4
- expect_equal_files reference_test2.o test_cpp.o
+ expect_equal_content reference_test2.o test_cpp.o
$ccache_nvcc_cpp -optf test2.optf test_cpp.cu
expect_stat 'cache hit (direct)' 2
expect_stat 'cache miss' 2
expect_stat 'files in cache' 4
- expect_equal_files reference_test2.o test_cpp.o
+ expect_equal_content reference_test2.o test_cpp.o
}
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
$cuobjdump test_cuda.o > test1.dump
- expect_equal_files reference_test1.dump test1.dump
+ expect_equal_content reference_test1.dump test1.dump
$ccache_nvcc_cuda $TEST_OPTS test_cuda.cu
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
$cuobjdump test_cuda.o > test1.dump
- expect_equal_files reference_test1.dump test1.dump
+ expect_equal_content reference_test1.dump test1.dump
# ---------------------------------------------------------------------
TEST "Option -ldir"
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
$cuobjdump test_cuda.o > test1.dump
- expect_equal_files reference_test1.dump test1.dump
+ expect_equal_content reference_test1.dump test1.dump
$ccache_nvcc_cuda $TEST_OPTS test_cuda.cu
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
expect_stat 'files in cache' 1
$cuobjdump test_cuda.o > test1.dump
- expect_equal_files reference_test1.dump test1.dump
+ expect_equal_content reference_test1.dump test1.dump
export PATH=$OLD_PATH
}
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_file_exists pch.h.gch
+ expect_exists pch.h.gch
echo '#include <string.h> /*change pch*/' >>pch.h
backdate pch.h
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 2
- expect_file_exists pch.h.gch
+ expect_exists pch.h.gch
# -------------------------------------------------------------------------
TEST "Create .gch, no -c, -o, with opt-in"
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_file_exists pch.gch
+ expect_exists pch.gch
# -------------------------------------------------------------------------
TEST "Use .gch, #include, remove pch.h"
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_file_exists pch.h.gch
+ expect_exists pch.h.gch
echo '#include <string.h> /*change pch*/' >>pch.h
backdate pch.h
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 2
- expect_file_exists pch.h.gch
+ expect_exists pch.h.gch
# -------------------------------------------------------------------------
TEST "Use .gch, -include, PCH_EXTSUM=1"
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_file_exists pch.h.gch/foo
+ expect_exists pch.h.gch/foo
backdate pch.h.gch/foo
pch_suite_clang() {
# -------------------------------------------------------------------------
- TEST "Create .gch, include file mtime changed"
+ TEST "Create .pch, include file mtime changed"
backdate test.h
cat <<EOF >pch2.h
expect_stat 'cache miss' 2
$REAL_COMPILER $SYSROOT -c -include pch2.h pch2.c
- expect_file_exists pch2.o
+ expect_exists pch2.o
CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines" $CCACHE_COMPILE $SYSROOT -c pch2.h
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 2
expect_stat 'cache miss' 2
+
+ # -------------------------------------------------------------------------
+ TEST "Create .pch with -Xclang options"
+
+ CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines" $CCACHE_COMPILE $SYSROOT -Xclang -emit-pch -o pch.h.pch -c pch.h
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ rm pch.h.pch
+
+ CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines" $CCACHE_COMPILE $SYSROOT -Xclang -emit-pch -o pch.h.pch -c pch.h
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ expect_exists pch.h.pch
+
+ echo '#include <string.h> /*change pch*/' >>pch.h
+ backdate pch.h
+ rm pch.h.pch
+ CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines" $CCACHE_COMPILE $SYSROOT -Xclang -emit-pch -o pch.h.pch -c pch.h
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 2
+ expect_exists pch.h.pch
+
+ # -------------------------------------------------------------------------
+ TEST "Use .pch the with -Xclang options"
+
+ $REAL_COMPILER $SYSROOT -Xclang -emit-pch -o pch.h.pch -c pch.h
+ backdate pch.h.pch
+
+ CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS time_macros" $CCACHE_COMPILE $SYSROOT -Xclang -include-pch -Xclang pch.h.pch -Xclang -include -Xclang pch.h -c pch.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+
+ CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS time_macros" $CCACHE_COMPILE $SYSROOT -Xclang -include-pch -Xclang pch.h.pch -Xclang -include -Xclang pch.h -c pch.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+
+ echo '#include <string.h> /*change pch*/' >>pch.h
+ backdate pch.h
+ $REAL_COMPILER $SYSROOT -Xclang -emit-pch -o pch.h.pch -c pch.h
+ backdate pch.h.pch
+
+ CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS time_macros" $CCACHE_COMPILE $SYSROOT -Xclang -include-pch -Xclang pch.h.pch -Xclang -include -Xclang pch.h -c pch.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 2
+
+ CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS time_macros" $CCACHE_COMPILE $SYSROOT -Xclang -include-pch -Xclang pch.h.pch -Xclang -include -Xclang pch.h -c pch.c
+ expect_stat 'cache hit (direct)' 2
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 2
}
if ! $COMPILER -fprofile-generate -c test.c 2>/dev/null; then
echo "compiler does not support profiling"
fi
- if $COMPILER_TYPE_CLANG && ! which llvm-profdata$CLANG_VERSION_SUFFIX >/dev/null 2>/dev/null; then
+ if $COMPILER_TYPE_CLANG && ! command -v llvm-profdata$CLANG_VERSION_SUFFIX >/dev/null; then
echo "llvm-profdata$CLANG_VERSION_SUFFIX tool not found"
fi
}
expect_stat 'cache hit (direct)' 0
expect_stat 'cache miss' 1
- $COMPILER -fprofile-generate=data test.o -lgcov -o test
+ $COMPILER -fprofile-generate test.o -o test
./test
merge_profiling_data data
$CCACHE_COMPILE -fprofile-use=data -c test.c
expect_stat 'cache hit (direct)' 1
expect_stat 'cache miss' 3
+
+ # -------------------------------------------------------------------------
+ TEST "-ftest-coverage with -fprofile-dir"
+
+ # GCC 9 and newer creates a mangled .gcno filename (still in the current
+ # working directory) if -fprofile-dir is given.
+
+ for flag in "" -fprofile-dir=dir; do
+ for dir in . subdir; do
+ $CCACHE -z >/dev/null
+
+ mkdir -p "$dir"
+ touch "$dir/test.c"
+ find -name '*.gcno' -delete
+
+ $REAL_COMPILER $flag -ftest-coverage -c $dir/test.c -o $dir/test.o
+ gcno_name=$(find -name '*.gcno')
+ rm "$gcno_name"
+
+ $CCACHE_COMPILE $flag -ftest-coverage -c $dir/test.c -o $dir/test.o
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+ expect_exists "$gcno_name"
+ rm "$gcno_name"
+
+ $CCACHE_COMPILE $flag -ftest-coverage -c $dir/test.c -o $dir/test.o
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 1
+ expect_exists "$gcno_name"
+ rm "$gcno_name"
+ done
+ done
}
merge_profiling_data() {
SUITE_profiling_clang_PROBE() {
if ! $COMPILER_TYPE_CLANG; then
echo "compiler is not Clang"
- fi
- if ! which llvm-profdata$CLANG_VERSION_SUFFIX >/dev/null 2>/dev/null; then
+ elif ! command -v llvm-profdata$CLANG_VERSION_SUFFIX >/dev/null; then
echo "llvm-profdata$CLANG_VERSION_SUFFIX tool not found"
fi
}
expect_stat 'cache hit (direct)' 0
expect_stat 'cache miss' 1
- $COMPILER -fprofile-generate test.o -lgcov -o test
+ $COMPILER -fprofile-generate test.o -o test
./test
expect_stat 'cache hit (direct)' 0
expect_stat 'cache miss' 1
- $COMPILER -fprofile-dir=data -fprofile-generate test.o -lgcov -o test
+ $COMPILER -fprofile-generate test.o -o test
./test
expect_stat 'cache hit (direct)' 0
expect_stat 'cache miss' 1
- $COMPILER -fprofile-generate -fprofile-dir=data test.o -lgcov -o test
+ $COMPILER -fprofile-generate test.o -o test
./test
expect_stat 'cache hit (direct)' 0
expect_stat 'cache miss' 1
- $COMPILER -fprofile-dir=data2 -fprofile-generate=data test.o -lgcov -o test
+ $COMPILER -fprofile-generate test.o -o test
./test
--- /dev/null
+SUITE_profiling_hip_clang_PROBE() {
+ if ! $COMPILER_TYPE_CLANG; then
+ echo "compiler is not Clang"
+ elif ! echo | $COMPILER -x hip --cuda-gpu-arch=gfx900 -nogpulib -c - > /dev/null; then
+ echo "Hip not supported"
+ fi
+}
+
+SUITE_profiling_hip_clang_SETUP() {
+ echo 'int main(void) { return 0; }' >test1.hip
+ echo 'int main(void) { int x = 0+0; return 0; }' >test2.hip
+ unset CCACHE_NODIRECT
+}
+
+SUITE_profiling_hip_clang() {
+ # -------------------------------------------------------------------------
+ TEST "hip-clang"
+
+ hip_opts="-x hip --cuda-gpu-arch=gfx900 -nogpulib"
+
+ $CCACHE_COMPILE $hip_opts -c test1.hip
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+
+ $CCACHE_COMPILE $hip_opts -c test1.hip
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 1
+
+ $CCACHE_COMPILE $hip_opts --cuda-gpu-arch=gfx906 -c test1.hip
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 2
+
+ $CCACHE_COMPILE $hip_opts -c test2.hip
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 3
+
+ $CCACHE_COMPILE $hip_opts -c test2.hip
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache hit (direct)' 2
+ expect_stat 'cache miss' 3
+
+ $CCACHE_COMPILE $hip_opts -Dx=x -c test2.hip
+ expect_stat 'cache hit (preprocessed)' 1
+ expect_stat 'cache hit (direct)' 2
+ expect_stat 'cache miss' 3
+
+ $CCACHE_COMPILE $hip_opts -Dx=y -c test2.hip
+ expect_stat 'cache hit (preprocessed)' 1
+ expect_stat 'cache hit (direct)' 2
+ expect_stat 'cache miss' 4
+}
+SUITE_readonly_PROBE() {
+ mkdir dir
+ chmod a-w dir
+ if [ -w dir ]; then
+ echo "File system doesn't support read-only mode"
+ fi
+ rmdir dir
+}
+
SUITE_readonly_SETUP() {
generate_code 1 test.c
generate_code 2 test2.c
if [ $status2 -ne 0 ]; then
test_failed "Failure when compiling test2.c read-only"
fi
- expect_file_exists test.o
- expect_file_exists test2.o
+ expect_exists test.o
+ expect_exists test2.o
# -------------------------------------------------------------------------
TEST "Cache miss"
+SUITE_readonly_direct_PROBE() {
+ mkdir dir
+ chmod a-w dir
+ if [ -w dir ]; then
+ echo "File system doesn't support read-only mode"
+ fi
+ rmdir dir
+}
+
SUITE_readonly_direct_SETUP() {
unset CCACHE_NODIRECT
$CCACHE_COMPILE -c --serialize-diagnostics test.dia test1.c
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_stat 'files in cache' 2
- expect_equal_files expected.dia test.dia
+ expect_stat 'files in cache' 1
+ expect_equal_content expected.dia test.dia
rm test.dia
$CCACHE_COMPILE -c --serialize-diagnostics test.dia test1.c
expect_stat 'cache hit (preprocessed)' 1
expect_stat 'cache miss' 1
- expect_stat 'files in cache' 2
- expect_equal_files expected.dia test.dia
+ expect_stat 'files in cache' 1
+ expect_equal_content expected.dia test.dia
# -------------------------------------------------------------------------
TEST "Compile failed"
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 0
expect_stat 'files in cache' 0
- expect_equal_files expected.dia test.dia
- expect_equal_files expected.stderr test.stderr
+ expect_equal_content expected.dia test.dia
+ expect_equal_content expected.stderr test.stderr
# -------------------------------------------------------------------------
TEST "--serialize-diagnostics + CCACHE_BASEDIR"
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_stat 'files in cache' 4
+ expect_stat 'files in cache' 2
cd ../dir2
CCACHE_BASEDIR=`pwd` $CCACHE_COMPILE -w -MD -MF `pwd`/test.d -I`pwd`/include --serialize-diagnostics `pwd`/test.dia -c src/test.c -o `pwd`/test.o
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_stat 'files in cache' 4
+ expect_stat 'files in cache' 2
}
SUITE_split_dwarf_SETUP() {
unset CCACHE_NODIRECT
+ touch test.c
+
mkdir -p dir1/src dir1/include
cat <<EOF >dir1/src/test.c
#include <stdarg.h>
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_stat 'files in cache' 3
+ expect_stat 'files in cache' 2
$CCACHE_COMPILE -I$(pwd)/include -c src/test.c -o test.o -gsplit-dwarf
expect_equal_object_files reference.o test.o
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_stat 'files in cache' 3
+ expect_stat 'files in cache' 2
$REAL_COMPILER -I$(pwd)/include -c src/test.c -o test2.o -gsplit-dwarf
mv test2.o reference2.o
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 2
- expect_stat 'files in cache' 6
+ expect_stat 'files in cache' 4
$CCACHE_COMPILE -I$(pwd)/include -c src/test.c -o test2.o -gsplit-dwarf
expect_equal_object_files reference2.o test2.o
expect_stat 'cache hit (direct)' 2
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 2
- expect_stat 'files in cache' 6
+ expect_stat 'files in cache' 4
fi
# Else: Compiler does not produce stable object file output when compiling
# the same source to the same output filename twice (DW_AT_GNU_dwo_id
expect_stat 'cache hit (direct)' 0
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_stat 'files in cache' 3
- if objdump_cmd test.o | grep_cmd "$(pwd)" >/dev/null 2>&1; then
- test_failed "Source dir ($(pwd)) found in test.o"
- fi
+ expect_stat 'files in cache' 2
+ expect_objdump_not_contains test.o "$(pwd)"
cd ../dir2
CCACHE_BASEDIR=$(pwd) $CCACHE_COMPILE -I$(pwd)/include -gsplit-dwarf -fdebug-prefix-map=$(pwd)=. -c $(pwd)/src/test.c -o $(pwd)/test.o
expect_stat 'cache hit (direct)' 1
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
- expect_stat 'files in cache' 3
- if objdump_cmd test.o | grep_cmd "$(pwd)" >/dev/null 2>&1; then
- test_failed "Source dir ($(pwd)) found in test.o"
+ expect_stat 'files in cache' 2
+ expect_objdump_not_contains test.o "$(pwd)"
+
+ # -------------------------------------------------------------------------
+ TEST "-gsplit-dwarf -g1"
+
+ # "gcc -gsplit-dwarf -g1" produces a .dwo file, but "clang -gsplit-dwarf
+ # -g1" doesn't, so test that ccache handles it gracefully either way.
+
+ $REAL_COMPILER -gsplit-dwarf -g1 -c test.c -o reference.o
+
+ $CCACHE_COMPILE -gsplit-dwarf -g1 -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+ rm -f test.dwo
+
+ $CCACHE_COMPILE -gsplit-dwarf -g1 -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 1
+
+ if [ -f reference.dwo ] && [ ! -f test.dwo ]; then
+ test_failed ".dwo missing"
+ elif [ ! -f reference.dwo ] && [ -f test.dwo ]; then
+ test_failed ".dwo not missing"
fi
}
SUITE_upgrade() {
- TEST "Keep maxfiles and maxsize settings"
+ # -------------------------------------------------------------------------
+ TEST "Default cache config/directory without XDG variables"
- rm $CCACHE_CONFIGPATH
- mkdir -p $CCACHE_DIR/0
- echo "0 0 0 0 0 0 0 0 0 0 0 0 0 2000 131072" >$CCACHE_DIR/0/stats
- expect_stat 'max files' 32000
- expect_stat 'max cache size' '2.1 GB'
+ unset CCACHE_CONFIGPATH
+ unset CCACHE_DIR
+ export HOME=/home/user
+
+ if $HOST_OS_APPLE; then
+ expected=$HOME/Library/Caches/ccache
+ else
+ expected=$HOME/.cache/ccache
+ fi
+ actual=$($CCACHE -s | sed -n 's/^cache directory *//p')
+ if [ "$actual" != "$expected" ]; then
+ test_failed "expected cache directory $expected, actual $actual"
+ fi
+
+ if $HOST_OS_APPLE; then
+ expected=$HOME/Library/Preferences/ccache/ccache.conf
+ else
+ expected=$HOME/.config/ccache/ccache.conf
+ fi
+ actual=$($CCACHE -s | sed -n 's/^primary config *//p')
+ if [ "$actual" != "$expected" ]; then
+ test_failed "expected primary config $expected actual $actual"
+ fi
+
+ # -------------------------------------------------------------------------
+ TEST "Default cache config/directory with XDG variables"
+
+ unset CCACHE_CONFIGPATH
+ unset CCACHE_DIR
+ export HOME=$PWD
+ export XDG_CACHE_HOME=/somewhere/cache
+ export XDG_CONFIG_HOME=/elsewhere/config
+
+ expected=$XDG_CACHE_HOME/ccache
+ actual=$($CCACHE -s | sed -n 's/^cache directory *//p')
+ if [ "$actual" != "$expected" ]; then
+ test_failed "expected cache directory $expected, actual $actual"
+ fi
+
+ expected=$XDG_CONFIG_HOME/ccache/ccache.conf
+ actual=$($CCACHE -s | sed -n 's/^primary config *//p')
+ if [ "$actual" != "$expected" ]; then
+ test_failed "expected primary config $expected actual $actual"
+ fi
+
+ # -------------------------------------------------------------------------
+ TEST "Cache config/directory with XDG variables and legacy directory"
+
+ unset CCACHE_CONFIGPATH
+ unset CCACHE_DIR
+ export HOME=$PWD
+ export XDG_CACHE_HOME=/somewhere/cache
+ export XDG_CONFIG_HOME=/elsewhere/config
+ mkdir $HOME/.ccache
+
+ expected=$HOME/.ccache
+ actual=$($CCACHE -s | sed -n 's/^cache directory *//p')
+ if [ "$actual" != "$expected" ]; then
+ test_failed "expected cache directory $expected, actual $actual"
+ fi
+
+ expected=$HOME/.ccache/ccache.conf
+ actual=$($CCACHE -s | sed -n 's/^primary config *//p')
+ if [ "$actual" != "$expected" ]; then
+ test_failed "expected primary config $expected actual $actual"
+ fi
+
+ # -------------------------------------------------------------------------
+ TEST "Cache config/directory with XDG variables and CCACHE_DIR"
+
+ unset CCACHE_CONFIGPATH
+ export CCACHE_DIR=$PWD/test
+ export HOME=/home/user
+ export XDG_CACHE_HOME=/somewhere/cache
+ export XDG_CONFIG_HOME=/elsewhere/config
+
+ expected=$CCACHE_DIR
+ actual=$($CCACHE -s | sed -n 's/^cache directory *//p')
+ if [ "$actual" != "$expected" ]; then
+ test_failed "expected cache directory $expected, actual $actual"
+ fi
+
+ expected=$CCACHE_DIR/ccache.conf
+ actual=$($CCACHE -s | sed -n 's/^primary config *//p')
+ if [ "$actual" != "$expected" ]; then
+ test_failed "expected primary config $expected actual $actual"
+ fi
+
+ # -------------------------------------------------------------------------
+ TEST "Cache config/directory with empty CCACHE_DIR"
+
+ # Empty (but set) CCACHE_DIR means "use defaults" and should thus override
+ # cache_dir set in the secondary config.
+
+ unset CCACHE_CONFIGPATH
+ export CCACHE_CONFIGPATH2=$PWD/ccache.conf2
+ export HOME=/home/user
+ export XDG_CACHE_HOME=/somewhere/cache
+ export XDG_CONFIG_HOME=/elsewhere/config
+ export CCACHE_DIR= # Set but empty
+ echo 'cache_dir = /nowhere' > $CCACHE_CONFIGPATH2
+
+ expected=$XDG_CACHE_HOME/ccache
+ actual=$($CCACHE -s | sed -n 's/^cache directory *//p')
+ if [ "$actual" != "$expected" ]; then
+ test_failed "expected cache directory $expected, actual $actual"
+ fi
+
+ expected=$XDG_CONFIG_HOME/ccache/ccache.conf
+ actual=$($CCACHE -s | sed -n 's/^primary config *//p')
+ if [ "$actual" != "$expected" ]; then
+ test_failed "expected primary config $expected actual $actual"
+ fi
}
--- /dev/null
+---
+Checks: '-*,readability-function-size'
+WarningsAsErrors: '*'
+# Only include headers directly in unittest.
+HeaderFilterRegex: 'unittest/[^/]*$'
+CheckOptions:
+ # Always add braces (added here just in case Clang-Tidy default changes).
+ - key: readability-braces-around-statements.ShortStatementLines
+ value: 0
+
+ # If you hit the limits, please change the code and not the limits!!
+ # Note: some limits "disabled" due to TEST_SUITE macro.
+ # The macro generates hundreds of statements, branches and variables.
+ - key: readability-function-size.LineThreshold
+ value: 130
+ - key: readability-function-size.StatementThreshold
+ value: 999999
+ - key: readability-function-size.ParameterThreshold
+ value: 7
+ - key: readability-function-size.NestingThreshold
+ value: 6
+ - key: readability-function-size.NestingThreshold
+ value: 999999
+ - key: readability-function-size.VariableThreshold
+ value: 999999
+...
+
--- /dev/null
+set(
+ source_files
+ TestUtil.cpp
+ main.cpp
+ test_Args.cpp
+ test_AtomicFile.cpp
+ test_Checksum.cpp
+ test_Compression.cpp
+ test_Config.cpp
+ test_Counters.cpp
+ test_FormatNonstdStringView.cpp
+ test_Hash.cpp
+ test_Lockfile.cpp
+ test_NullCompression.cpp
+ test_Stat.cpp
+ test_Statistics.cpp
+ test_Util.cpp
+ test_ZstdCompression.cpp
+ test_argprocessing.cpp
+ test_ccache.cpp
+ test_compopt.cpp
+ test_hashutil.cpp)
+
+if(INODE_CACHE_SUPPORTED)
+ list(APPEND source_files test_InodeCache.cpp)
+endif()
+
+if(WIN32)
+ list(APPEND source_files test_Win32Util.cpp)
+endif()
+
+add_executable(unittest ${source_files})
+
+target_link_libraries(
+ unittest
+ PRIVATE standard_settings standard_warnings ccache_lib third_party_lib)
+
+target_include_directories(unittest PRIVATE ${CMAKE_BINARY_DIR} . ../src)
+
+add_test(NAME unittest COMMAND unittest)
--- /dev/null
+// 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 "TestUtil.hpp"
+
+#include "../src/Util.hpp"
+#include "../src/exceptions.hpp"
+
+namespace TestUtil {
+
+size_t TestContext::m_subdir_counter = 0;
+
+TestContext::TestContext() : m_test_dir(Util::get_actual_cwd())
+{
+ if (!Util::base_name(m_test_dir).starts_with("testdir.")) {
+ throw Error("TestContext instantiated outside test directory");
+ }
+ ++m_subdir_counter;
+ std::string subtest_dir =
+ fmt::format("{}/test_{}", m_test_dir, m_subdir_counter);
+ Util::create_dir(subtest_dir);
+ if (chdir(subtest_dir.c_str()) != 0) {
+ abort();
+ }
+}
+
+TestContext::~TestContext()
+{
+ if (chdir(m_test_dir.c_str()) != 0) {
+ abort();
+ }
+}
+
+void
+check_chdir(const std::string& dir)
+{
+ if (chdir(dir.c_str()) != 0) {
+ throw Error("failed to change directory to {}: {}", dir, strerror(errno));
+ }
+}
+
+} // namespace TestUtil
--- /dev/null
+// 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 <string>
+
+#ifdef _MSC_VER
+# define DOCTEST_CONFIG_USE_STD_HEADERS
+#endif
+
+namespace TestUtil {
+
+// This class is intended to be instantiated in all test cases that create local
+// files.
+class TestContext
+{
+public:
+ TestContext();
+ ~TestContext();
+
+private:
+ std::string m_test_dir;
+ static size_t m_subdir_counter;
+};
+
+// Change directory to `dir`, throwing Error on failure.
+void check_chdir(const std::string& dir);
+
+} // namespace TestUtil
+++ /dev/null
-// 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 "framework.h"
-#include "util.h"
-
-#include <float.h>
-#include <math.h>
-#if defined(HAVE_TERMIOS_H)
-#define USE_COLOR
-#include <termios.h>
-#endif
-
-static unsigned total_asserts;
-static unsigned total_tests;
-static unsigned total_suites;
-static unsigned failed_tests;
-static const char *current_suite;
-static const char *current_test;
-static char *dir_before_suite;
-static char *dir_before_test;
-static int verbose;
-
-static const char COLOR_END[] = "\x1b[m";
-static const char COLOR_GREEN[] = "\x1b[1;32m";
-static const char COLOR_RED[] = "\x1b[1;31m";
-
-#define COLOR(tty, color) ((tty) ? COLOR_ ## color : "")
-
-static int
-is_tty(int fd)
-{
-#ifdef USE_COLOR
- struct termios t;
- return tcgetattr(fd, &t) == 0;
-#else
- (void)fd;
- return 0;
-#endif
-}
-
-static const char *
-plural_s(unsigned n)
-{
- return n == 1 ? "" : "s";
-}
-
-int
-cct_run(suite_fn *suites, int verbose_output)
-{
- suite_fn *suite;
- int tty = is_tty(1);
-
- x_unsetenv("GCC_COLORS"); // Avoid confusing argument processing tests.
- verbose = verbose_output;
-
- for (suite = suites; *suite; suite++) {
- unsigned test_index = 0;
- while (true) {
- test_index = (*suite)(test_index + 1);
- if (test_index == 0) {
- // We have reached the end of the suite.
- break;
- }
- }
- }
-
- if (failed_tests == 0) {
- printf("%sPASSED%s: %u assertion%s, %u test%s, %u suite%s\n",
- COLOR(tty, GREEN), COLOR(tty, END),
- total_asserts, plural_s(total_asserts),
- total_tests, plural_s(total_tests),
- total_suites, plural_s(total_suites));
- } else {
- printf("%sFAILED%s: %u test%s\n",
- COLOR(tty, RED), COLOR(tty, END),
- failed_tests, plural_s(failed_tests));
- }
- return failed_tests > 0 ? 1 : 0;
-}
-
-void
-cct_suite_begin(const char *name)
-{
- ++total_suites;
- if (verbose) {
- printf("=== SUITE: %s ===\n", name);
- }
- dir_before_suite = gnu_getcwd();
- create_dir(name);
- cct_chdir(name);
- current_suite = name;
-}
-
-void
-cct_suite_end()
-{
- cct_chdir(dir_before_suite);
- free(dir_before_suite);
- dir_before_suite = NULL;
-}
-
-void
-cct_test_begin(const char *name)
-{
- ++total_tests;
- if (verbose) {
- printf("--- TEST: %s ---\n", name);
- }
- dir_before_test = gnu_getcwd();
- create_dir(name);
- cct_chdir(name);
- current_test = name;
-
- putenv("CCACHE_CONFIG_PATH=/dev/null");
- cc_reset();
-}
-
-void
-cct_test_end()
-{
- if (dir_before_test) {
- cct_chdir(dir_before_test);
- free(dir_before_test);
- dir_before_test = NULL;
- }
-}
-
-void
-cct_check_passed(const char *file, int line, const char *what)
-{
- ++total_asserts;
- if (verbose) {
- printf("%s:%d: Passed assertion: %s\n", file, line, what);
- }
-}
-
-void
-cct_check_failed(const char *file, int line, const char *what,
- const char *expected, const char *actual)
-{
- ++total_asserts;
- ++failed_tests;
- fprintf(stderr, "%s:%d: Failed assertion:\n", file, line);
- fprintf(stderr, " Suite: %s\n", current_suite);
- fprintf(stderr, " Test: %s\n", current_test);
- if (expected) {
- fprintf(stderr, " Expression: %s\n", what);
- if (actual) {
- fprintf(stderr, " Expected: %s\n", expected);
- fprintf(stderr, " Actual: %s\n", actual);
- } else {
- fprintf(stderr, " Message: %s\n", expected);
- }
- } else {
- fprintf(stderr, " Assertion: %s\n", what);
- }
- fprintf(stderr, "\n");
-}
-
-bool
-cct_check_double_eq(const char *file, int line, const char *expression,
- double expected, double actual)
-{
- if (fabs(expected - actual) < DBL_EPSILON) {
- cct_check_passed(file, line, expression);
- return true;
- } else {
- char *exp_str = format("%.1f", expected);
- char *act_str = format("%.1f", actual);
- cct_check_failed(file, line, expression, exp_str, act_str);
- free(exp_str);
- free(act_str);
- return false;
- }
-}
-bool
-cct_check_int_eq(const char *file, int line, const char *expression,
- int64_t expected, int64_t actual)
-{
- if (expected == actual) {
- cct_check_passed(file, line, expression);
- return true;
- } else {
-#if defined(HAVE_LONG_LONG) && !defined(__MINGW32__)
- char *exp_str = format("%lld", (long long)expected);
- char *act_str = format("%lld", (long long)actual);
-#else
- char *exp_str = format("%ld", (long)expected);
- char *act_str = format("%ld", (long)actual);
-#endif
- cct_check_failed(file, line, expression, exp_str, act_str);
- free(exp_str);
- free(act_str);
- return false;
- }
-}
-
-bool
-cct_check_str_eq(const char *file, int line, const char *expression,
- char *expected, char *actual,
- bool free1, bool free2)
-{
- bool result;
-
- if (expected && actual && str_eq(actual, expected)) {
- cct_check_passed(file, line, expression);
- result = true;
- } else {
- char *exp_str = expected ? format("\"%s\"", expected) : x_strdup("(null)");
- char *act_str = actual ? format("\"%s\"", actual) : x_strdup("(null)");
- cct_check_failed(file, line, expression, exp_str, act_str);
- free(exp_str);
- free(act_str);
- result = false;
- }
-
- if (free1) {
- free(expected);
- }
- if (free2) {
- free(actual);
- }
- return result;
-}
-
-bool
-cct_check_args_eq(const char *file, int line, const char *expression,
- struct args *expected, struct args *actual,
- bool free1, bool free2)
-{
- bool result;
-
- if (expected && actual && args_equal(actual, expected)) {
- cct_check_passed(file, line, expression);
- result = true;
- } else {
- char *exp_str = expected ? args_to_string(expected) : x_strdup("(null)");
- char *act_str = actual ? args_to_string(actual) : x_strdup("(null)");
- cct_check_failed(file, line, expression, exp_str, act_str);
- free(exp_str);
- free(act_str);
- result = false;
- }
-
- if (free1) {
- args_free(expected);
- }
- if (free2) {
- args_free(actual);
- }
- return result;
-}
-
-void
-cct_chdir(const char *path)
-{
- if (chdir(path) != 0) {
- fprintf(stderr, "chdir: %s: %s", path, strerror(errno));
- abort();
- }
-}
-
-void
-cct_wipe(const char *path)
-{
- // TODO: rewrite using traverse().
-#ifndef __MINGW32__
- char *command = format("rm -rf %s", path);
-#else
- char *command = format("rd /s /q %s", path);
-#endif
- if (system(command) != 0) {
- perror(command);
- }
- free(command);
-}
-
-void
-cct_create_fresh_dir(const char *path)
-{
- cct_wipe(path);
- if (mkdir(path, 0777) != 0) {
- fprintf(stderr, "mkdir: %s: %s", path, strerror(errno));
- abort();
- }
-}
+++ /dev/null
-// 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
-
-#ifndef TEST_FRAMEWORK_H
-#define TEST_FRAMEWORK_H
-
-#include "../src/ccache.h"
-
-// ============================================================================
-
-#define TEST_SUITE(name) \
- unsigned suite_##name(unsigned _start_point); \
- unsigned suite_##name(unsigned _start_point) \
- { \
- unsigned _test_counter = 0; \
- cct_suite_begin(#name); \
- { \
- // Empty due to macro trickery.
-
-#define TEST(name) \
- cct_test_end(); \
- } \
- ++_test_counter; \
- { static int name = 0; (void)name; /* Verify test name. */ } \
- if (_test_counter >= _start_point) { \
- cct_test_begin(#name);
-
-#define TEST_SUITE_END \
- cct_test_end(); \
- } \
- cct_suite_end(); \
- return 0; /* We have reached the end. */ \
- }
-
-// ============================================================================
-
-#define CHECKM(assertion, message) \
- do { \
- if ((assertion)) { \
- cct_check_passed(__FILE__, __LINE__, #assertion); \
- } else { \
- cct_check_failed(__FILE__, __LINE__, #assertion, (message), NULL); \
- cct_test_end(); \
- cct_suite_end(); \
- return _test_counter; \
- } \
- } while (false)
-
-#define CHECK(assertion) \
- CHECKM(assertion, NULL)
-
-#define CHECK_POINTER_EQ_BASE(t, e, a, f1, f2) \
- do { \
- if (!cct_check_##t##_eq(__FILE__, __LINE__, #a, (e), (a), (f1), (f2))) { \
- cct_test_end(); \
- cct_suite_end(); \
- return _test_counter; \
- } \
- } while (false)
-
-// ============================================================================
-
-#define CHECK_INT_EQ(expected, actual) \
- do { \
- if (!cct_check_int_eq(__FILE__, __LINE__, #actual, (expected), \
- (actual))) { \
- cct_test_end(); \
- cct_suite_end(); \
- return _test_counter; \
- } \
- } while (false)
-
-// ============================================================================
-
-#define CHECK_DOUBLE_EQ(expected, actual) \
- do { \
- if (!cct_check_double_eq(__FILE__, __LINE__, #actual, (expected), \
- (actual))) { \
- cct_test_end(); \
- cct_suite_end(); \
- return _test_counter; \
- } \
- } while (false)
-
-// ============================================================================
-
-#define CHECK_STR_EQ(expected, actual) \
- CHECK_POINTER_EQ_BASE(str, expected, actual, false, false)
-
-#define CHECK_STR_EQ_FREE1(expected, actual) \
- CHECK_POINTER_EQ_BASE(str, expected, actual, true, false)
-
-#define CHECK_STR_EQ_FREE2(expected, actual) \
- CHECK_POINTER_EQ_BASE(str, expected, actual, false, true)
-
-#define CHECK_STR_EQ_FREE12(expected, actual) \
- CHECK_POINTER_EQ_BASE(str, expected, actual, true, true)
-
-// ============================================================================
-
-#define CHECK_ARGS_EQ(expected, actual) \
- CHECK_POINTER_EQ_BASE(args, expected, actual, false, false)
-
-#define CHECK_ARGS_EQ_FREE1(expected, actual) \
- CHECK_POINTER_EQ_BASE(args, expected, actual, true, false)
-
-#define CHECK_ARGS_EQ_FREE2(expected, actual) \
- CHECK_POINTER_EQ_BASE(args, expected, actual, false, true)
-
-#define CHECK_ARGS_EQ_FREE12(expected, actual) \
- CHECK_POINTER_EQ_BASE(args, expected, actual, true, true)
-
-// ============================================================================
-
-typedef unsigned (*suite_fn)(unsigned);
-int cct_run(suite_fn *suites, int verbose);
-
-void cct_suite_begin(const char *name);
-void cct_suite_end(void);
-void cct_test_begin(const char *name);
-void cct_test_end(void);
-void cct_check_passed(const char *file, int line, const char *assertion);
-void cct_check_failed(const char *file, int line, const char *assertion,
- const char *expected, const char *actual);
-bool cct_check_double_eq(const char *file, int line, const char *expression,
- double expected, double actual);
-bool cct_check_int_eq(const char *file, int line, const char *expression,
- int64_t expected, int64_t actual);
-bool cct_check_str_eq(const char *file, int line, const char *expression,
- char *expected, char *actual,
- bool free1, bool free2);
-bool cct_check_args_eq(const char *file, int line, const char *expression,
- struct args *expected, struct args *actual,
- bool free1, bool free2);
-void cct_chdir(const char *path);
-void cct_wipe(const char *path);
-void cct_create_fresh_dir(const char *path);
-
-#endif
+++ /dev/null
-// 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 "framework.h"
-#ifdef HAVE_GETOPT_LONG
-#include <getopt.h>
-#else
-#include "../src/getopt_long.h"
-#endif
-
-// *INDENT-OFF* disable uncrustify
-#define SUITE(name) unsigned suite_ ## name(unsigned);
-#include "suites.h"
-#undef SUITE
-// *INDENT-ON* enable uncrustify
-
-static const char USAGE_TEXT[] =
- "Usage:\n"
- " test [options]\n"
- "\n"
- "Options:\n"
- " -h, --help print this help text\n"
- " -v, --verbose enable verbose logging of tests\n";
-
-int
-main(int argc, char **argv)
-{
- suite_fn suites[] = {
-#define SUITE(name) &suite_ ## name,
-#include "suites.h"
-#undef SUITE
- NULL
- };
- static const struct option options[] = {
- {"help", no_argument, NULL, 'h'},
- {"verbose", no_argument, NULL, 'v'},
- {NULL, 0, NULL, 0}
- };
- int verbose = 0;
- int c;
- char *testdir, *dir_before;
- int result;
-
-#ifdef _WIN32
- putenv("CCACHE_DETECT_SHEBANG=1");
-#endif
-
- while ((c = getopt_long(argc, argv, "hv", options, NULL)) != -1) {
- switch (c) {
- case 'h':
- fprintf(stdout, USAGE_TEXT);
- return 0;
-
- case 'v':
- verbose = 1;
- break;
-
- default:
- fprintf(stderr, USAGE_TEXT);
- return 1;
- }
- }
-
- testdir = format("testdir.%d", (int)getpid());
- cct_create_fresh_dir(testdir);
- dir_before = gnu_getcwd();
- cct_chdir(testdir);
- result = cct_run(suites, verbose);
- if (result == 0) {
- cct_chdir(dir_before);
- cct_wipe(testdir);
- }
- free(testdir);
- free(dir_before);
- return result;
-}
--- /dev/null
+// 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 "../src/Util.hpp"
+#include "TestUtil.hpp"
+
+#include "third_party/fmt/core.h"
+
+#define DOCTEST_THREAD_LOCAL // Avoid MinGW thread_local bug
+#define DOCTEST_CONFIG_IMPLEMENT
+#include "third_party/doctest.h"
+
+int
+main(int argc, char** argv)
+{
+#ifdef _WIN32
+ Util::setenv("CCACHE_DETECT_SHEBANG", "1");
+#endif
+ Util::unsetenv("GCC_COLORS"); // Don't confuse argument processing tests.
+
+ std::string dir_before = Util::get_actual_cwd();
+ std::string testdir = fmt::format("testdir.{}", getpid());
+ Util::wipe_path(testdir);
+ Util::create_dir(testdir);
+ TestUtil::check_chdir(testdir);
+
+ doctest::Context context;
+ context.applyCommandLine(argc, argv);
+ int result = context.run();
+
+ if (result == 0) {
+ TestUtil::check_chdir(dir_before);
+ Util::wipe_path(testdir);
+ } else {
+ fmt::print(stderr, "Note: Test data has been left in {}\n", testdir);
+ }
+
+ return result;
+}
--- /dev/null
+// 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 "../src/Args.hpp"
+#include "TestUtil.hpp"
+
+#include "third_party/doctest.h"
+
+TEST_SUITE_BEGIN("Args");
+
+using TestUtil::TestContext;
+
+TEST_CASE("Args default constructor")
+{
+ Args args;
+ CHECK(args.size() == 0);
+}
+
+TEST_CASE("Args copy constructor")
+{
+ Args args1;
+ args1.push_back("foo");
+ args1.push_back("bar");
+
+ Args args2(args1);
+ CHECK(args1 == args2);
+}
+
+TEST_CASE("Args move constructor")
+{
+ Args args1;
+ args1.push_back("foo");
+ args1.push_back("bar");
+ const char* foo_pointer = args1[0].c_str();
+ const char* bar_pointer = args1[1].c_str();
+
+ Args args2(std::move(args1));
+ CHECK(args1.size() == 0);
+ CHECK(args2.size() == 2);
+ CHECK(args2[0].c_str() == foo_pointer);
+ CHECK(args2[1].c_str() == bar_pointer);
+}
+
+TEST_CASE("Args::from_argv")
+{
+ int argc = 2;
+ const char* argv[] = {"a", "b"};
+ Args args = Args::from_argv(argc, argv);
+ CHECK(args.size() == 2);
+ CHECK(args[0] == "a");
+ CHECK(args[1] == "b");
+}
+
+TEST_CASE("Args::from_string")
+{
+ Args args = Args::from_string(" c d\te\r\nf ");
+ CHECK(args.size() == 4);
+ CHECK(args[0] == "c");
+ CHECK(args[1] == "d");
+ CHECK(args[2] == "e");
+ CHECK(args[3] == "f");
+}
+
+TEST_CASE("Args::from_gcc_atfile")
+{
+ TestContext test_context;
+
+ Args args;
+
+ SUBCASE("Non-existing file")
+ {
+ CHECK(Args::from_gcc_atfile("at_file") == nonstd::nullopt);
+ }
+
+ SUBCASE("Empty")
+ {
+ Util::write_file("at_file", "");
+ args = *Args::from_gcc_atfile("at_file");
+ CHECK(args.size() == 0);
+ }
+
+ SUBCASE("One argument without newline")
+ {
+ Util::write_file("at_file", "foo");
+ args = *Args::from_gcc_atfile("at_file");
+ CHECK(args.size() == 1);
+ CHECK(args[0] == "foo");
+ }
+
+ SUBCASE("One argument with newline")
+ {
+ Util::write_file("at_file", "foo\n");
+ args = *Args::from_gcc_atfile("at_file");
+ CHECK(args.size() == 1);
+ CHECK(args[0] == "foo");
+ }
+
+ SUBCASE("Multiple simple arguments")
+ {
+ Util::write_file("at_file", "x y z\n");
+ args = *Args::from_gcc_atfile("at_file");
+ CHECK(args.size() == 3);
+ CHECK(args[0] == "x");
+ CHECK(args[1] == "y");
+ CHECK(args[2] == "z");
+ }
+
+ SUBCASE("Tricky quoting")
+ {
+ Util::write_file(
+ "at_file",
+ "first\rsec\\\tond\tthi\\\\rd\nfourth \tfif\\ th \"si'x\\\" th\""
+ " 'seve\nth'\\");
+ args = *Args::from_gcc_atfile("at_file");
+ CHECK(args.size() == 7);
+ CHECK(args[0] == "first");
+ CHECK(args[1] == "sec\tond");
+ CHECK(args[2] == "thi\\rd");
+ CHECK(args[3] == "fourth");
+ CHECK(args[4] == "fif th");
+ CHECK(args[5] == "si'x\" th");
+ CHECK(args[6] == "seve\nth");
+ }
+}
+
+TEST_CASE("Args copy assignment operator")
+{
+ Args args1 = Args::from_string("x y");
+ Args args2;
+ args2 = args1;
+ CHECK(args2.size() == 2);
+ CHECK(args2[0] == "x");
+ CHECK(args2[1] == "y");
+}
+
+TEST_CASE("Args move assignment operator")
+{
+ Args args1 = Args::from_string("x y");
+ const char* x_pointer = args1[0].c_str();
+ const char* y_pointer = args1[1].c_str();
+
+ Args args2;
+ args2 = std::move(args1);
+ CHECK(args1.size() == 0);
+ CHECK(args2.size() == 2);
+ CHECK(args2[0].c_str() == x_pointer);
+ CHECK(args2[1] == y_pointer);
+}
+
+TEST_CASE("Args equality operators")
+{
+ Args args1 = Args::from_string("x y");
+ Args args2 = Args::from_string("x y");
+ Args args3 = Args::from_string("y x");
+ CHECK(args1 == args1);
+ CHECK(args1 == args2);
+ CHECK(args2 == args1);
+ CHECK(args1 != args3);
+ CHECK(args3 != args1);
+}
+
+TEST_CASE("Args::empty")
+{
+ Args args;
+ CHECK(args.empty());
+ args.push_back("1");
+ CHECK(!args.empty());
+}
+
+TEST_CASE("Args::size")
+{
+ Args args;
+ CHECK(args.size() == 0);
+ args.push_back("1");
+ CHECK(args.size() == 1);
+ args.push_back("2");
+ CHECK(args.size() == 2);
+}
+
+TEST_CASE("Args indexing")
+{
+ const Args args1 = Args::from_string("1 2 3");
+ CHECK(args1[0] == "1");
+ CHECK(args1[1] == "2");
+ CHECK(args1[2] == "3");
+
+ Args args2 = Args::from_string("1 2 3");
+ CHECK(args2[0] == "1");
+ CHECK(args2[1] == "2");
+ CHECK(args2[2] == "3");
+}
+
+TEST_CASE("Args::to_argv")
+{
+ Args args = Args::from_string("1 2 3");
+ auto argv = args.to_argv();
+ CHECK(std::string(argv[0]) == "1");
+ CHECK(std::string(argv[1]) == "2");
+ CHECK(std::string(argv[2]) == "3");
+ CHECK(argv[3] == nullptr);
+}
+
+TEST_CASE("Args::to_string")
+{
+ CHECK(Args::from_string("a little string").to_string() == "a little string");
+}
+
+TEST_CASE("Args operations")
+{
+ Args args = Args::from_string("eeny meeny miny moe");
+ Args more_args = Args::from_string("x y");
+ Args no_args;
+
+ SUBCASE("erase_with_prefix")
+ {
+ args.erase_with_prefix("m");
+ CHECK(args == Args::from_string("eeny"));
+ }
+
+ SUBCASE("insert empty args")
+ {
+ args.insert(2, no_args);
+ CHECK(args == Args::from_string("eeny meeny miny moe"));
+ }
+
+ SUBCASE("insert non-empty args")
+ {
+ args.insert(4, more_args);
+ args.insert(2, more_args);
+ args.insert(0, more_args);
+ CHECK(args == Args::from_string("x y eeny meeny x y miny moe x y"));
+ }
+
+ SUBCASE("pop_back")
+ {
+ args.pop_back();
+ CHECK(args == Args::from_string("eeny meeny miny"));
+
+ args.pop_back(2);
+ CHECK(args == Args::from_string("eeny"));
+ }
+
+ SUBCASE("pop_front")
+ {
+ args.pop_front();
+ CHECK(args == Args::from_string("meeny miny moe"));
+
+ args.pop_front(2);
+ CHECK(args == Args::from_string("moe"));
+ }
+
+ SUBCASE("push_back string")
+ {
+ args.push_back("foo");
+ CHECK(args == Args::from_string("eeny meeny miny moe foo"));
+ }
+
+ SUBCASE("push_back args")
+ {
+ args.push_back(more_args);
+ CHECK(args == Args::from_string("eeny meeny miny moe x y"));
+ }
+
+ SUBCASE("push_front string")
+ {
+ args.push_front("foo");
+ CHECK(args == Args::from_string("foo eeny meeny miny moe"));
+ }
+
+ SUBCASE("replace")
+ {
+ args.replace(3, more_args);
+ args.replace(2, no_args);
+ args.replace(0, more_args);
+ CHECK(args == Args::from_string("x y meeny x y"));
+ }
+}
+
+TEST_SUITE_END();
--- /dev/null
+// 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 "../src/AtomicFile.hpp"
+#include "../src/Util.hpp"
+#include "TestUtil.hpp"
+
+#include "third_party/doctest.h"
+
+using TestUtil::TestContext;
+
+TEST_SUITE_BEGIN("AtomicFile");
+
+TEST_CASE("Base case")
+{
+ TestContext test_context;
+
+ AtomicFile atomic_file("test", AtomicFile::Mode::text);
+ atomic_file.write("h");
+ atomic_file.write(std::vector<uint8_t>{0x65, 0x6c});
+ fputs("lo", atomic_file.stream());
+ atomic_file.commit();
+ CHECK(Util::read_file("test") == "hello");
+}
+
+TEST_CASE("Not committing")
+{
+ TestContext test_context;
+
+ {
+ AtomicFile atomic_file("test", AtomicFile::Mode::text);
+ atomic_file.write("hello");
+ }
+ CHECK_THROWS_WITH(Util::read_file("test"), "No such file or directory");
+}
+
+TEST_SUITE_END();
--- /dev/null
+// 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 "../src/Checksum.hpp"
+
+#include "third_party/doctest.h"
+
+TEST_SUITE_BEGIN("Checksum");
+
+TEST_CASE("Checksums")
+{
+ Checksum checksum;
+ CHECK(checksum.digest() == 0x2d06800538d394c2);
+
+ checksum.update("foo", 3);
+ CHECK(checksum.digest() == 0xab6e5f64077e7d8a);
+
+ checksum.update("t", 1);
+ CHECK(checksum.digest() == 0x3fd918aed1a9e7e4);
+
+ checksum.reset();
+ CHECK(checksum.digest() == 0x2d06800538d394c2);
+}
+
+TEST_SUITE_END();
--- /dev/null
+// 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 "../src/Compression.hpp"
+#include "../src/Config.hpp"
+
+#include "third_party/doctest.h"
+
+TEST_SUITE_BEGIN("Compression");
+
+TEST_CASE("Compression::level_from_config")
+{
+ Config config;
+ CHECK(Compression::level_from_config(config) == 0);
+}
+
+TEST_CASE("Compression::type_from_config")
+{
+ Config config;
+ CHECK(Compression::type_from_config(config) == Compression::Type::zstd);
+}
+
+TEST_CASE("Compression::type_from_int")
+{
+ CHECK(Compression::type_from_int(0) == Compression::Type::none);
+ CHECK(Compression::type_from_int(1) == Compression::Type::zstd);
+ CHECK_THROWS_WITH(Compression::type_from_int(2), "Unknown type: 2");
+}
+
+TEST_CASE("Compression::type_to_string")
+{
+ CHECK(Compression::type_to_string(Compression::Type::none) == "none");
+ CHECK(Compression::type_to_string(Compression::Type::zstd) == "zstd");
+}
+
+TEST_SUITE_END();
--- /dev/null
+// 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 "../src/Config.hpp"
+#include "../src/Util.hpp"
+#include "../src/ccache.hpp"
+#include "../src/exceptions.hpp"
+#include "TestUtil.hpp"
+
+#include "third_party/doctest.h"
+#include "third_party/fmt/core.h"
+
+#include <limits>
+#include <string>
+#include <vector>
+
+using doctest::Approx;
+using TestUtil::TestContext;
+
+TEST_SUITE_BEGIN("Config");
+
+TEST_CASE("Config: default values")
+{
+ Config config;
+
+ CHECK(config.base_dir().empty());
+ CHECK(config.cache_dir().empty()); // Set later
+ CHECK(config.compiler().empty());
+ CHECK(config.compiler_check() == "mtime");
+ CHECK(config.compression());
+ CHECK(config.compression_level() == 0);
+ CHECK(config.cpp_extension().empty());
+ CHECK(!config.debug());
+ CHECK(!config.depend_mode());
+ CHECK(config.direct_mode());
+ CHECK(!config.disable());
+ CHECK(config.extra_files_to_hash().empty());
+ CHECK(!config.file_clone());
+ CHECK(!config.hard_link());
+ CHECK(config.hash_dir());
+ CHECK(config.ignore_headers_in_manifest().empty());
+ CHECK(config.ignore_options().empty());
+ CHECK_FALSE(config.keep_comments_cpp());
+ CHECK(config.limit_multiple() == Approx(0.8));
+ CHECK(config.log_file().empty());
+ CHECK(config.max_files() == 0);
+ CHECK(config.max_size() == static_cast<uint64_t>(5) * 1000 * 1000 * 1000);
+ CHECK(config.path().empty());
+ CHECK_FALSE(config.pch_external_checksum());
+ CHECK(config.prefix_command().empty());
+ CHECK(config.prefix_command_cpp().empty());
+ CHECK_FALSE(config.read_only());
+ CHECK_FALSE(config.read_only_direct());
+ CHECK_FALSE(config.recache());
+ CHECK(config.run_second_cpp());
+ CHECK(config.sloppiness() == 0);
+ CHECK(config.stats());
+ CHECK(config.temporary_dir().empty()); // Set later
+ CHECK(config.umask() == std::numeric_limits<uint32_t>::max());
+}
+
+TEST_CASE("Config::update_from_file")
+{
+ TestContext test_context;
+
+ const char user[] = "rabbit";
+ Util::setenv("USER", user);
+
+#ifndef _WIN32
+ std::string base_dir = fmt::format("/{0}/foo/{0}", user);
+#else
+ std::string base_dir = fmt::format("C:/{0}/foo/{0}", user);
+#endif
+
+ Util::write_file(
+ "ccache.conf",
+ "base_dir = " + base_dir + "\n"
+ "cache_dir=\n"
+ "cache_dir = $USER$/${USER}/.ccache\n"
+ "\n"
+ "\n"
+ " #A comment\n"
+ "\t compiler = foo\n"
+ "compiler_check = none\n"
+ "compression=false\n"
+ "compression_level= 2\n"
+ "cpp_extension = .foo\n"
+ "depend_mode = true\n"
+ "direct_mode = false\n"
+ "disable = true\n"
+ "extra_files_to_hash = a:b c:$USER\n"
+ "file_clone = true\n"
+ "hard_link = true\n"
+ "hash_dir = false\n"
+ "ignore_headers_in_manifest = a:b/c\n"
+ "ignore_options = -a=* -b\n"
+ "keep_comments_cpp = true\n"
+ "limit_multiple = 1.0\n"
+ "log_file = $USER${USER} \n"
+ "max_files = 17\n"
+ "max_size = 123M\n"
+ "path = $USER.x\n"
+ "pch_external_checksum = true\n"
+ "prefix_command = x$USER\n"
+ "prefix_command_cpp = y\n"
+ "read_only = true\n"
+ "read_only_direct = true\n"
+ "recache = true\n"
+ "run_second_cpp = false\n"
+ "sloppiness = time_macros ,include_file_mtime"
+ " include_file_ctime,file_stat_matches,file_stat_matches_ctime,pch_defines"
+ " , no_system_headers,system_headers,clang_index_store\n"
+ "stats = false\n"
+ "temporary_dir = ${USER}_foo\n"
+ "umask = 777"); // Note: no newline.
+
+ Config config;
+ REQUIRE(config.update_from_file("ccache.conf"));
+ CHECK(config.base_dir() == base_dir);
+ CHECK(config.cache_dir() == fmt::format("{0}$/{0}/.ccache", user));
+ CHECK(config.compiler() == "foo");
+ CHECK(config.compiler_check() == "none");
+ CHECK_FALSE(config.compression());
+ CHECK(config.compression_level() == 2);
+ CHECK(config.cpp_extension() == ".foo");
+ CHECK(config.depend_mode());
+ CHECK_FALSE(config.direct_mode());
+ CHECK(config.disable());
+ CHECK(config.extra_files_to_hash() == fmt::format("a:b c:{}", user));
+ CHECK(config.file_clone());
+ CHECK(config.hard_link());
+ CHECK_FALSE(config.hash_dir());
+ CHECK(config.ignore_headers_in_manifest() == "a:b/c");
+ CHECK(config.ignore_options() == "-a=* -b");
+ CHECK(config.keep_comments_cpp());
+ CHECK(config.limit_multiple() == Approx(1.0));
+ CHECK(config.log_file() == fmt::format("{0}{0}", user));
+ CHECK(config.max_files() == 17);
+ CHECK(config.max_size() == 123 * 1000 * 1000);
+ CHECK(config.path() == fmt::format("{}.x", user));
+ CHECK(config.pch_external_checksum());
+ CHECK(config.prefix_command() == fmt::format("x{}", user));
+ CHECK(config.prefix_command_cpp() == "y");
+ CHECK(config.read_only());
+ CHECK(config.read_only_direct());
+ CHECK(config.recache());
+ CHECK_FALSE(config.run_second_cpp());
+ CHECK(config.sloppiness()
+ == (SLOPPY_INCLUDE_FILE_MTIME | SLOPPY_INCLUDE_FILE_CTIME
+ | SLOPPY_TIME_MACROS | SLOPPY_FILE_STAT_MATCHES
+ | SLOPPY_FILE_STAT_MATCHES_CTIME | SLOPPY_SYSTEM_HEADERS
+ | SLOPPY_PCH_DEFINES | SLOPPY_CLANG_INDEX_STORE));
+ CHECK_FALSE(config.stats());
+ CHECK(config.temporary_dir() == fmt::format("{}_foo", user));
+ CHECK(config.umask() == 0777);
+}
+
+TEST_CASE("Config::update_from_file, error handling")
+{
+ TestContext test_context;
+
+ Config config;
+
+ SUBCASE("missing equal sign")
+ {
+ Util::write_file("ccache.conf", "no equal sign");
+ REQUIRE_THROWS_WITH(config.update_from_file("ccache.conf"),
+ "ccache.conf:1: missing equal sign");
+ }
+
+ SUBCASE("unknown key")
+ {
+ Util::write_file("ccache.conf", "# Comment\nfoo = bar");
+ CHECK(config.update_from_file("ccache.conf"));
+ }
+
+ SUBCASE("invalid bool")
+ {
+ Util::write_file("ccache.conf", "disable=");
+ REQUIRE_THROWS_WITH(config.update_from_file("ccache.conf"),
+ "ccache.conf:1: not a boolean value: \"\"");
+
+ Util::write_file("ccache.conf", "disable=foo");
+ REQUIRE_THROWS_WITH(config.update_from_file("ccache.conf"),
+ "ccache.conf:1: not a boolean value: \"foo\"");
+ }
+
+ SUBCASE("invalid variable reference")
+ {
+ Util::write_file("ccache.conf", "base_dir = ${foo");
+ REQUIRE_THROWS_WITH(
+ config.update_from_file("ccache.conf"),
+ "ccache.conf:1: syntax error: missing '}' after \"foo\"");
+ // Other cases tested in test_Util.c.
+ }
+
+ SUBCASE("empty umask")
+ {
+ Util::write_file("ccache.conf", "umask = ");
+ CHECK(config.update_from_file("ccache.conf"));
+ CHECK(config.umask() == std::numeric_limits<uint32_t>::max());
+ }
+
+ SUBCASE("invalid size")
+ {
+ Util::write_file("ccache.conf", "max_size = foo");
+ REQUIRE_THROWS_WITH(config.update_from_file("ccache.conf"),
+ "ccache.conf:1: invalid size: \"foo\"");
+ // Other cases tested in test_Util.c.
+ }
+
+ SUBCASE("unknown sloppiness")
+ {
+ Util::write_file("ccache.conf", "sloppiness = time_macros, foo");
+ CHECK(config.update_from_file("ccache.conf"));
+ CHECK(config.sloppiness() == SLOPPY_TIME_MACROS);
+ }
+
+ SUBCASE("invalid unsigned")
+ {
+ Util::write_file("ccache.conf", "max_files =");
+ REQUIRE_THROWS_WITH(config.update_from_file("ccache.conf"),
+ "ccache.conf:1: invalid unsigned integer: \"\"");
+
+ Util::write_file("ccache.conf", "max_files = -42");
+ REQUIRE_THROWS_WITH(config.update_from_file("ccache.conf"),
+ "ccache.conf:1: invalid unsigned integer: \"-42\"");
+
+ Util::write_file("ccache.conf", "max_files = foo");
+ REQUIRE_THROWS_WITH(config.update_from_file("ccache.conf"),
+ "ccache.conf:1: invalid unsigned integer: \"foo\"");
+ }
+
+ SUBCASE("missing file")
+ {
+ CHECK(!config.update_from_file("ccache.conf"));
+ }
+
+ SUBCASE("relative base dir")
+ {
+ Util::write_file("ccache.conf", "base_dir = relative/path");
+ REQUIRE_THROWS_WITH(
+ config.update_from_file("ccache.conf"),
+ "ccache.conf:1: not an absolute path: \"relative/path\"");
+
+ Util::write_file("ccache.conf", "base_dir =");
+ CHECK(config.update_from_file("ccache.conf"));
+ }
+}
+
+TEST_CASE("Config::update_from_environment")
+{
+ Config config;
+
+ Util::setenv("CCACHE_COMPRESS", "1");
+ config.update_from_environment();
+ CHECK(config.compression());
+
+ Util::unsetenv("CCACHE_COMPRESS");
+
+ Util::setenv("CCACHE_NOCOMPRESS", "1");
+ config.update_from_environment();
+ CHECK(!config.compression());
+}
+
+TEST_CASE("Config::set_value_in_file")
+{
+ TestContext test_context;
+
+ SUBCASE("set new value")
+ {
+ Util::write_file("ccache.conf", "path = vanilla\n");
+ Config::set_value_in_file("ccache.conf", "compiler", "chocolate");
+ std::string content = Util::read_file("ccache.conf");
+ CHECK(content == "path = vanilla\ncompiler = chocolate\n");
+ }
+
+ SUBCASE("existing value")
+ {
+ Util::write_file("ccache.conf", "path = chocolate\nstats = chocolate\n");
+ Config::set_value_in_file("ccache.conf", "path", "vanilla");
+ std::string content = Util::read_file("ccache.conf");
+ CHECK(content == "path = vanilla\nstats = chocolate\n");
+ }
+
+ SUBCASE("unknown option")
+ {
+ Util::write_file("ccache.conf", "path = chocolate\nstats = chocolate\n");
+ try {
+ Config::set_value_in_file("ccache.conf", "foo", "bar");
+ CHECK(false);
+ } catch (const Error& e) {
+ CHECK(std::string(e.what()) == "unknown configuration option \"foo\"");
+ }
+
+ std::string content = Util::read_file("ccache.conf");
+ CHECK(content == "path = chocolate\nstats = chocolate\n");
+ }
+
+ SUBCASE("unknown sloppiness")
+ {
+ Util::write_file("ccache.conf", "path = vanilla\n");
+ Config::set_value_in_file("ccache.conf", "sloppiness", "foo");
+ std::string content = Util::read_file("ccache.conf");
+ CHECK(content == "path = vanilla\nsloppiness = foo\n");
+ }
+
+ SUBCASE("comments are kept")
+ {
+ Util::write_file("ccache.conf", "# c1\npath = blueberry\n#c2\n");
+ Config::set_value_in_file("ccache.conf", "path", "vanilla");
+ Config::set_value_in_file("ccache.conf", "compiler", "chocolate");
+ std::string content = Util::read_file("ccache.conf");
+ CHECK(content == "# c1\npath = vanilla\n#c2\ncompiler = chocolate\n");
+ }
+}
+
+TEST_CASE("Config::get_string_value")
+{
+ Config config;
+
+ SUBCASE("base case")
+ {
+ config.set_max_files(42);
+ CHECK(config.get_string_value("max_files") == "42");
+ }
+
+ SUBCASE("unknown key")
+ {
+ try {
+ config.get_string_value("foo");
+ CHECK(false);
+ } catch (const Error& e) {
+ CHECK(std::string(e.what()) == "unknown configuration option \"foo\"");
+ }
+ }
+}
+
+TEST_CASE("Config::visit_items")
+{
+ TestContext test_context;
+
+ Util::write_file(
+ "test.conf",
+ "absolute_paths_in_stderr = true\n"
+#ifndef _WIN32
+ "base_dir = /bd\n"
+#else
+ "base_dir = C:/bd\n"
+#endif
+ "cache_dir = cd\n"
+ "compiler = c\n"
+ "compiler_check = cc\n"
+ "compression = true\n"
+ "compression_level = 8\n"
+ "cpp_extension = ce\n"
+ "debug = false\n"
+ "depend_mode = true\n"
+ "direct_mode = false\n"
+ "disable = true\n"
+ "extra_files_to_hash = efth\n"
+ "file_clone = true\n"
+ "hard_link = true\n"
+ "hash_dir = false\n"
+ "ignore_headers_in_manifest = ihim\n"
+ "ignore_options = -a=* -b\n"
+ "inode_cache = false\n"
+ "keep_comments_cpp = true\n"
+ "limit_multiple = 0.0\n"
+ "log_file = lf\n"
+ "max_files = 4711\n"
+ "max_size = 98.7M\n"
+ "path = p\n"
+ "pch_external_checksum = true\n"
+ "prefix_command = pc\n"
+ "prefix_command_cpp = pcc\n"
+ "read_only = true\n"
+ "read_only_direct = true\n"
+ "recache = true\n"
+ "run_second_cpp = false\n"
+ "sloppiness = include_file_mtime, include_file_ctime, time_macros,"
+ " file_stat_matches, file_stat_matches_ctime, pch_defines, system_headers,"
+ " clang_index_store\n"
+ "stats = false\n"
+ "temporary_dir = td\n"
+ "umask = 022\n");
+
+ Config config;
+ config.update_from_file("test.conf");
+
+ std::vector<std::string> received_items;
+
+ config.visit_items([&](const std::string& key,
+ const std::string& value,
+ const std::string& origin) {
+ received_items.push_back(fmt::format("({}) {} = {}", origin, key, value));
+ });
+
+ std::vector<std::string> expected = {
+ "(test.conf) absolute_paths_in_stderr = true",
+#ifndef _WIN32
+ "(test.conf) base_dir = /bd",
+#else
+ "(test.conf) base_dir = C:/bd",
+#endif
+ "(test.conf) cache_dir = cd",
+ "(test.conf) compiler = c",
+ "(test.conf) compiler_check = cc",
+ "(test.conf) compression = true",
+ "(test.conf) compression_level = 8",
+ "(test.conf) cpp_extension = ce",
+ "(test.conf) debug = false",
+ "(test.conf) depend_mode = true",
+ "(test.conf) direct_mode = false",
+ "(test.conf) disable = true",
+ "(test.conf) extra_files_to_hash = efth",
+ "(test.conf) file_clone = true",
+ "(test.conf) hard_link = true",
+ "(test.conf) hash_dir = false",
+ "(test.conf) ignore_headers_in_manifest = ihim",
+ "(test.conf) ignore_options = -a=* -b",
+ "(test.conf) inode_cache = false",
+ "(test.conf) keep_comments_cpp = true",
+ "(test.conf) limit_multiple = 0.0",
+ "(test.conf) log_file = lf",
+ "(test.conf) max_files = 4711",
+ "(test.conf) max_size = 98.7M",
+ "(test.conf) path = p",
+ "(test.conf) pch_external_checksum = true",
+ "(test.conf) prefix_command = pc",
+ "(test.conf) prefix_command_cpp = pcc",
+ "(test.conf) read_only = true",
+ "(test.conf) read_only_direct = true",
+ "(test.conf) recache = true",
+ "(test.conf) run_second_cpp = false",
+ "(test.conf) sloppiness = include_file_mtime, include_file_ctime,"
+ " time_macros, pch_defines, file_stat_matches, file_stat_matches_ctime,"
+ " system_headers, clang_index_store",
+ "(test.conf) stats = false",
+ "(test.conf) temporary_dir = td",
+ "(test.conf) umask = 022",
+ };
+
+ REQUIRE(received_items.size() == expected.size());
+ for (size_t i = 0; i < expected.size(); ++i) {
+ CHECK(received_items[i] == expected[i]);
+ }
+}
+
+TEST_CASE("Check key tables consistency")
+{
+ CHECK_NOTHROW(Config::check_key_tables_consistency());
+}
--- /dev/null
+// 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 "../src/Counters.hpp"
+#include "../src/Statistics.hpp"
+#include "TestUtil.hpp"
+
+#include "third_party/doctest.h"
+
+using TestUtil::TestContext;
+
+TEST_SUITE_BEGIN("Counters");
+
+TEST_CASE("Counters")
+{
+ TestContext test_context;
+
+ Counters counters;
+ CHECK(counters.size() == static_cast<size_t>(Statistic::END));
+
+ SUBCASE("Get and set statistic")
+ {
+ CHECK(counters.get(Statistic::cache_miss) == 0);
+ counters.set(Statistic::cache_miss, 27);
+ CHECK(counters.get(Statistic::cache_miss) == 27);
+ }
+
+ SUBCASE("Get and set raw index")
+ {
+ CHECK(counters.get_raw(4) == 0);
+ counters.set_raw(4, 27);
+ CHECK(counters.get(Statistic::cache_miss) == 27);
+ }
+
+ SUBCASE("Set future raw counter")
+ {
+ const auto future_index = static_cast<size_t>(Statistic::END) + 2;
+ counters.set_raw(future_index, 42);
+ CHECK(counters.get_raw(future_index) == 42);
+ }
+
+ SUBCASE("Increment single counter")
+ {
+ counters.set(Statistic::cache_miss, 4);
+
+ counters.increment(Statistic::cache_miss);
+ CHECK(counters.get(Statistic::cache_miss) == 5);
+
+ counters.increment(Statistic::cache_miss, -3);
+ CHECK(counters.get(Statistic::cache_miss) == 2);
+
+ counters.increment(Statistic::cache_miss, -3);
+ CHECK(counters.get(Statistic::cache_miss) == 0);
+ }
+
+ SUBCASE("Increment many counters")
+ {
+ counters.set(Statistic::direct_cache_hit, 3);
+ counters.set(Statistic::cache_miss, 2);
+ counters.set(Statistic::files_in_cache, 10);
+ counters.set(Statistic::cache_size_kibibyte, 1);
+
+ Counters updates;
+ updates.set(Statistic::direct_cache_hit, 6);
+ updates.set(Statistic::cache_miss, 5);
+ updates.set(Statistic::files_in_cache, -1);
+ updates.set(Statistic::cache_size_kibibyte, -4);
+
+ counters.increment(updates);
+ CHECK(counters.get(Statistic::direct_cache_hit) == 9);
+ CHECK(counters.get(Statistic::cache_miss) == 7);
+ CHECK(counters.get(Statistic::files_in_cache) == 9);
+ CHECK(counters.get(Statistic::cache_size_kibibyte) == 0); // No wrap-around
+ }
+}
+
+TEST_SUITE_END();
--- /dev/null
+// 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 "FormatNonstdStringView.hpp"
+
+#include "third_party/doctest.h"
+
+using nonstd::string_view;
+
+TEST_SUITE_BEGIN("FormatNonstdStringView");
+
+TEST_CASE("fmt::format and nonstd::string_view")
+{
+ string_view null;
+ CHECK(fmt::format("{}", null) == "");
+
+ const std::string s = "0123456789";
+
+ string_view empty(s.data(), 0);
+ CHECK(fmt::format("{}", empty) == "");
+
+ string_view empty_end(s.data() + s.length(), 0);
+ CHECK(fmt::format("{}", empty_end) == "");
+
+ string_view start(s.data(), 2);
+ CHECK(fmt::format("{}", start) == "01");
+
+ string_view middle(s.data() + 3, 4);
+ CHECK(fmt::format("{}", middle) == "3456");
+
+ string_view end(s.data() + s.length() - 2, 2);
+ CHECK(fmt::format("{}", end) == "89");
+}
+
+TEST_SUITE_END();
--- /dev/null
+// 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 "../src/Hash.hpp"
+
+#include "third_party/doctest.h"
+
+TEST_SUITE_BEGIN("Hash");
+
+TEST_CASE("known strings")
+{
+ SUBCASE("initial state")
+ {
+ CHECK(Hash().digest().to_string() == "af1396svbud1kqg40jfa6reciicrpcisi");
+ }
+
+ SUBCASE("empty string")
+ {
+ CHECK(Hash().hash("").digest().to_string()
+ == "af1396svbud1kqg40jfa6reciicrpcisi");
+ }
+
+ SUBCASE("a")
+ {
+ CHECK(Hash().hash("a").digest().to_string()
+ == "17765vetiqd4ae95qpbhfb1ut8gj42r6m");
+ }
+
+ SUBCASE("message digest")
+ {
+ CHECK(Hash().hash("message digest").digest().to_string()
+ == "7bc2kbnbinerv6ruptldpdrb8ko93hcdo");
+ }
+
+ SUBCASE("long string")
+ {
+ const char long_string[] =
+ "123456789012345678901234567890123456789012345678901234567890"
+ "12345678901234567890";
+ CHECK(Hash().hash(long_string).digest().to_string()
+ == "f263ljqhc8co1ee8rpeq98bt654o9o2qm");
+ }
+}
+
+TEST_CASE("Hash::digest should not alter state")
+{
+ Hash h;
+ h.hash("message");
+ h.digest();
+ h.hash(" digest");
+ CHECK(h.digest().to_string() == "7bc2kbnbinerv6ruptldpdrb8ko93hcdo");
+}
+
+TEST_CASE("Hash::digest should be idempotent")
+{
+ Hash h;
+ CHECK(h.digest().to_string() == "af1396svbud1kqg40jfa6reciicrpcisi");
+ CHECK(h.digest().to_string() == "af1396svbud1kqg40jfa6reciicrpcisi");
+}
+
+TEST_CASE("Digest::bytes")
+{
+ Digest d = Hash().hash("message digest").digest();
+ uint8_t expected[Digest::size()] = {
+ 0x7b, 0xc2, 0xa2, 0xee, 0xb9, 0x5d, 0xdb, 0xf9, 0xb7, 0xec,
+ 0xf6, 0xad, 0xcb, 0x76, 0xb4, 0x53, 0x09, 0x1c, 0x58, 0xdc,
+ };
+ CHECK(memcmp(d.bytes(), expected, Digest::size()) == 0);
+}
+
+TEST_SUITE_END();
--- /dev/null
+// 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 "../src/Config.hpp"
+#include "../src/Context.hpp"
+#include "../src/Hash.hpp"
+#include "../src/InodeCache.hpp"
+#include "../src/Util.hpp"
+#include "TestUtil.hpp"
+
+#include "third_party/doctest.h"
+
+using TestUtil::TestContext;
+
+namespace {
+
+void
+init(Context& ctx)
+{
+ ctx.config.set_debug(true);
+ ctx.config.set_inode_cache(true);
+ ctx.config.set_cache_dir(Util::get_home_directory());
+}
+
+bool
+put(const Context& ctx,
+ const std::string& filename,
+ const std::string& str,
+ int return_value)
+{
+ return ctx.inode_cache.put(filename,
+ InodeCache::ContentType::code,
+ Hash().hash(str).digest(),
+ return_value);
+}
+
+} // namespace
+
+TEST_SUITE_BEGIN("InodeCache");
+
+TEST_CASE("Test disabled")
+{
+ TestContext test_context;
+
+ Context ctx;
+ init(ctx);
+ ctx.config.set_inode_cache(false);
+
+ Digest digest;
+ int return_value;
+
+ CHECK(!ctx.inode_cache.get(
+ "a", InodeCache::ContentType::code, digest, &return_value));
+ CHECK(!ctx.inode_cache.put(
+ "a", InodeCache::ContentType::code, digest, return_value));
+ CHECK(ctx.inode_cache.get_hits() == -1);
+ CHECK(ctx.inode_cache.get_misses() == -1);
+ CHECK(ctx.inode_cache.get_errors() == -1);
+}
+
+TEST_CASE("Test lookup nonexistent")
+{
+ TestContext test_context;
+
+ Context ctx;
+ init(ctx);
+ ctx.inode_cache.drop();
+ Util::write_file("a", "");
+
+ Digest digest;
+ int return_value;
+
+ CHECK(!ctx.inode_cache.get(
+ "a", InodeCache::ContentType::code, digest, &return_value));
+ CHECK(ctx.inode_cache.get_hits() == 0);
+ CHECK(ctx.inode_cache.get_misses() == 1);
+ CHECK(ctx.inode_cache.get_errors() == 0);
+}
+
+TEST_CASE("Test put and lookup")
+{
+ TestContext test_context;
+
+ Context ctx;
+ init(ctx);
+ ctx.inode_cache.drop();
+ Util::write_file("a", "a text");
+
+ CHECK(put(ctx, "a", "a text", 1));
+
+ Digest digest;
+ int return_value;
+
+ CHECK(ctx.inode_cache.get(
+ "a", InodeCache::ContentType::code, digest, &return_value));
+ CHECK(digest == Hash().hash("a text").digest());
+ CHECK(return_value == 1);
+ CHECK(ctx.inode_cache.get_hits() == 1);
+ CHECK(ctx.inode_cache.get_misses() == 0);
+ CHECK(ctx.inode_cache.get_errors() == 0);
+
+ Util::write_file("a", "something else");
+
+ CHECK(!ctx.inode_cache.get(
+ "a", InodeCache::ContentType::code, digest, &return_value));
+ CHECK(ctx.inode_cache.get_hits() == 1);
+ CHECK(ctx.inode_cache.get_misses() == 1);
+ CHECK(ctx.inode_cache.get_errors() == 0);
+
+ CHECK(put(ctx, "a", "something else", 2));
+
+ CHECK(ctx.inode_cache.get(
+ "a", InodeCache::ContentType::code, digest, &return_value));
+ CHECK(digest == Hash().hash("something else").digest());
+ CHECK(return_value == 2);
+ CHECK(ctx.inode_cache.get_hits() == 2);
+ CHECK(ctx.inode_cache.get_misses() == 1);
+ CHECK(ctx.inode_cache.get_errors() == 0);
+}
+
+TEST_CASE("Drop file")
+{
+ TestContext test_context;
+
+ Context ctx;
+ init(ctx);
+
+ Digest digest;
+
+ ctx.inode_cache.get("a", InodeCache::ContentType::binary, digest);
+ CHECK(Stat::stat(ctx.inode_cache.get_file()));
+ CHECK(ctx.inode_cache.drop());
+ CHECK(!Stat::stat(ctx.inode_cache.get_file()));
+ CHECK(!ctx.inode_cache.drop());
+}
+
+TEST_CASE("Test content type")
+{
+ TestContext test_context;
+
+ Context ctx;
+ init(ctx);
+ ctx.inode_cache.drop();
+ Util::write_file("a", "a text");
+ Digest binary_digest = Hash().hash("binary").digest();
+ Digest code_digest = Hash().hash("code").digest();
+ Digest code_with_sloppy_time_macros_digest =
+ Hash().hash("sloppy_time_macros").digest();
+
+ CHECK(ctx.inode_cache.put(
+ "a", InodeCache::ContentType::binary, binary_digest, 1));
+ CHECK(
+ ctx.inode_cache.put("a", InodeCache::ContentType::code, code_digest, 2));
+ CHECK(
+ ctx.inode_cache.put("a",
+ InodeCache::ContentType::code_with_sloppy_time_macros,
+ code_with_sloppy_time_macros_digest,
+ 3));
+
+ Digest digest;
+ int return_value;
+
+ CHECK(ctx.inode_cache.get(
+ "a", InodeCache::ContentType::binary, digest, &return_value));
+ CHECK(digest == binary_digest);
+ CHECK(return_value == 1);
+
+ CHECK(ctx.inode_cache.get(
+ "a", InodeCache::ContentType::code, digest, &return_value));
+ CHECK(digest == code_digest);
+ CHECK(return_value == 2);
+
+ CHECK(
+ ctx.inode_cache.get("a",
+ InodeCache::ContentType::code_with_sloppy_time_macros,
+ digest,
+ &return_value));
+ CHECK(digest == code_with_sloppy_time_macros_digest);
+ CHECK(return_value == 3);
+}
+
+TEST_SUITE_END();
--- /dev/null
+// 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 "../src/Lockfile.hpp"
+#include "../src/Stat.hpp"
+#include "TestUtil.hpp"
+
+#include "third_party/doctest.h"
+
+TEST_SUITE_BEGIN("LockFile");
+
+using TestUtil::TestContext;
+
+TEST_CASE("Lockfile acquire and release")
+{
+ TestContext test_context;
+
+ {
+ Lockfile lock("test", 1000);
+ CHECK(lock.acquired());
+ auto st = Stat::lstat("test.lock");
+ CHECK(st);
+#ifndef _WIN32
+ CHECK(st.is_symlink());
+#else
+ CHECK(st.is_regular());
+#endif
+ }
+
+ CHECK(!Stat::lstat("test.lock"));
+}
+
+#ifndef _WIN32
+TEST_CASE("Lockfile breaking")
+{
+ TestContext test_context;
+
+ CHECK(symlink("foo", "test.lock") == 0);
+
+ Lockfile lock("test", 1000);
+ CHECK(lock.acquired());
+}
+#endif // !_WIN32
+
+TEST_SUITE_END();
--- /dev/null
+// 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 "../src/Compression.hpp"
+#include "../src/Compressor.hpp"
+#include "../src/Decompressor.hpp"
+#include "../src/File.hpp"
+#include "TestUtil.hpp"
+
+#include "third_party/doctest.h"
+
+using TestUtil::TestContext;
+
+TEST_SUITE_BEGIN("NullCompression");
+
+TEST_CASE("Compression::Type::none roundtrip")
+{
+ TestContext test_context;
+
+ File f("data.uncompressed", "w");
+ auto compressor =
+ Compressor::create_from_type(Compression::Type::none, f.get(), 1);
+ CHECK(compressor->actual_compression_level() == 0);
+ compressor->write("foobar", 6);
+ compressor->finalize();
+
+ f.open("data.uncompressed", "r");
+ auto decompressor =
+ Decompressor::create_from_type(Compression::Type::none, f.get());
+
+ char buffer[4];
+ decompressor->read(buffer, 4);
+ CHECK(memcmp(buffer, "foob", 4) == 0);
+
+ SUBCASE("Garbage data")
+ {
+ // Not reached the end.
+ CHECK_THROWS_WITH(decompressor->finalize(),
+ "garbage data at end of uncompressed stream");
+ }
+
+ SUBCASE("Read to end")
+ {
+ decompressor->read(buffer, 2);
+ CHECK(memcmp(buffer, "ar", 2) == 0);
+
+ // Reached the end.
+ decompressor->finalize();
+
+ // Nothing left to read.
+ CHECK_THROWS_WITH(decompressor->read(buffer, 1),
+ "failed to read from uncompressed stream");
+ }
+}
+
+TEST_SUITE_END();
--- /dev/null
+// 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 "../src/Stat.hpp"
+#include "../src/Util.hpp"
+#include "TestUtil.hpp"
+
+#include "third_party/doctest.h"
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+using TestUtil::TestContext;
+
+TEST_SUITE_BEGIN("Stat");
+
+TEST_CASE("Default constructor")
+{
+ Stat stat;
+ CHECK(!stat);
+ CHECK(stat.error_number() == -1);
+ CHECK(stat.device() == 0);
+ CHECK(stat.inode() == 0);
+ CHECK(stat.mode() == 0);
+ CHECK(stat.ctime() == 0);
+ CHECK(stat.mtime() == 0);
+ CHECK(stat.size() == 0);
+ CHECK(stat.size_on_disk() == 0);
+ CHECK(!stat.is_directory());
+ CHECK(!stat.is_regular());
+ CHECK(!stat.is_symlink());
+
+#ifdef HAVE_STRUCT_STAT_ST_CTIM
+ CHECK(stat.ctim().tv_sec == 0);
+ CHECK(stat.ctim().tv_nsec == 0);
+#endif
+
+#ifdef HAVE_STRUCT_STAT_ST_MTIM
+ CHECK(stat.mtim().tv_sec == 0);
+ CHECK(stat.mtim().tv_nsec == 0);
+#endif
+}
+
+TEST_CASE("Named constructors")
+{
+ CHECK(!Stat::stat("does_not_exist"));
+ CHECK(!Stat::stat("does_not_exist", Stat::OnError::ignore));
+ CHECK(!Stat::stat("does_not_exist", Stat::OnError::log));
+ CHECK_THROWS_WITH(Stat::stat("does_not_exist", Stat::OnError::throw_error),
+ "failed to stat does_not_exist: No such file or directory");
+}
+
+TEST_CASE("Same i-node as")
+{
+ TestContext test_context;
+
+ Util::write_file("a", "");
+ Util::write_file("b", "");
+ auto a_stat = Stat::stat("a");
+ auto b_stat = Stat::stat("b");
+
+ CHECK(a_stat.same_inode_as(a_stat));
+#ifdef _WIN32 // no i-node concept
+ (void)b_stat;
+#else
+ CHECK(!a_stat.same_inode_as(b_stat));
+#endif
+
+ Util::write_file("a", "change size");
+ auto new_a_stat = Stat::stat("a");
+ CHECK(new_a_stat.same_inode_as(a_stat));
+}
+
+TEST_CASE("Return values when file is missing")
+{
+ auto stat = Stat::stat("does_not_exist");
+ CHECK(!stat);
+ CHECK(stat.error_number() == ENOENT);
+ CHECK(stat.device() == 0);
+ CHECK(stat.inode() == 0);
+ CHECK(stat.mode() == 0);
+ CHECK(stat.ctime() == 0);
+ CHECK(stat.mtime() == 0);
+ CHECK(stat.size() == 0);
+ CHECK(stat.size_on_disk() == 0);
+ CHECK(!stat.is_directory());
+ CHECK(!stat.is_regular());
+ CHECK(!stat.is_symlink());
+
+#ifdef HAVE_STRUCT_STAT_ST_CTIM
+ CHECK(stat.ctim().tv_sec == 0);
+ CHECK(stat.ctim().tv_nsec == 0);
+#endif
+
+#ifdef HAVE_STRUCT_STAT_ST_MTIM
+ CHECK(stat.mtim().tv_sec == 0);
+ CHECK(stat.mtim().tv_nsec == 0);
+#endif
+}
+
+TEST_CASE("Return values when file exists")
+{
+ TestContext test_context;
+
+ Util::write_file("file", "1234567");
+
+ auto stat = Stat::stat("file");
+ struct stat st;
+ CHECK(::stat("file", &st) == 0);
+
+ CHECK(stat);
+ CHECK(stat.error_number() == 0);
+ CHECK(stat.device() == st.st_dev);
+ CHECK(stat.inode() == st.st_ino);
+ CHECK(stat.mode() == st.st_mode);
+ CHECK(stat.ctime() == st.st_ctime);
+ CHECK(stat.mtime() == st.st_mtime);
+ CHECK(stat.size() == st.st_size);
+#ifdef _WIN32
+ CHECK(stat.size_on_disk() == ((stat.size() + 1023) & ~1023));
+#else
+ CHECK(stat.size_on_disk() == st.st_blocks * 512);
+#endif
+ CHECK(!stat.is_directory());
+ CHECK(stat.is_regular());
+ CHECK(!stat.is_symlink());
+
+#ifdef HAVE_STRUCT_STAT_ST_CTIM
+ CHECK(stat.ctim().tv_sec == st.st_ctim.tv_sec);
+ CHECK(stat.ctim().tv_nsec == st.st_ctim.tv_nsec);
+#endif
+
+#ifdef HAVE_STRUCT_STAT_ST_MTIM
+ CHECK(stat.mtim().tv_sec == st.st_mtim.tv_sec);
+ CHECK(stat.mtim().tv_nsec == st.st_mtim.tv_nsec);
+#endif
+}
+
+TEST_CASE("Directory")
+{
+ TestContext test_context;
+
+ REQUIRE(mkdir("directory", 0456) == 0);
+ auto stat = Stat::stat("directory");
+
+ CHECK(stat);
+ CHECK(stat.error_number() == 0);
+ CHECK(stat.is_directory());
+ CHECK(!stat.is_regular());
+ CHECK(!stat.is_symlink());
+}
+
+#ifndef _WIN32
+TEST_CASE("Symlinks")
+{
+ TestContext test_context;
+
+ Util::write_file("file", "1234567");
+
+ SUBCASE("file lstat")
+ {
+ auto stat = Stat::lstat("file", Stat::OnError::ignore);
+ CHECK(stat);
+ CHECK(!stat.is_directory());
+ CHECK(stat.is_regular());
+ CHECK(!stat.is_symlink());
+ CHECK(stat.size() == 7);
+ }
+
+ SUBCASE("file stat")
+ {
+ auto stat = Stat::stat("file", Stat::OnError::ignore);
+ CHECK(stat);
+ CHECK(!stat.is_directory());
+ CHECK(stat.is_regular());
+ CHECK(!stat.is_symlink());
+ CHECK(stat.size() == 7);
+ }
+
+ SUBCASE("symlink lstat")
+ {
+ REQUIRE(symlink("file", "symlink") == 0);
+ auto stat = Stat::lstat("symlink", Stat::OnError::ignore);
+ CHECK(stat);
+ CHECK(!stat.is_directory());
+ CHECK(!stat.is_regular());
+ CHECK(stat.is_symlink());
+ CHECK(stat.size() == 4);
+ }
+
+ SUBCASE("symlink stat")
+ {
+ REQUIRE(symlink("file", "symlink") == 0);
+ auto stat = Stat::stat("symlink", Stat::OnError::ignore);
+ CHECK(stat);
+ CHECK(!stat.is_directory());
+ CHECK(stat.is_regular());
+ CHECK(!stat.is_symlink());
+ CHECK(stat.size() == 7);
+ }
+}
+#endif
+
+TEST_SUITE_END();
--- /dev/null
+// 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 "../src/Statistics.hpp"
+#include "../src/Util.hpp"
+#include "TestUtil.hpp"
+
+#include "third_party/doctest.h"
+
+using TestUtil::TestContext;
+
+TEST_SUITE_BEGIN("Statistics");
+
+TEST_CASE("Read nonexistent")
+{
+ TestContext test_context;
+
+ Counters counters = Statistics::read("test");
+
+ REQUIRE(counters.size() == static_cast<size_t>(Statistic::END));
+ CHECK(counters.get(Statistic::cache_miss) == 0);
+}
+
+TEST_CASE("Read bad")
+{
+ TestContext test_context;
+
+ Util::write_file("test", "bad 1 2 3 4 5\n");
+ Counters counters = Statistics::read("test");
+
+ REQUIRE(counters.size() == static_cast<size_t>(Statistic::END));
+ CHECK(counters.get(Statistic::cache_miss) == 0);
+}
+
+TEST_CASE("Read existing")
+{
+ TestContext test_context;
+
+ Util::write_file("test", "0 1 2 3 27 5\n");
+ Counters counters = Statistics::read("test");
+
+ REQUIRE(counters.size() == static_cast<size_t>(Statistic::END));
+ CHECK(counters.get(Statistic::cache_miss) == 27);
+ CHECK(counters.get(Statistic::could_not_use_modules) == 0);
+}
+
+TEST_CASE("Read future counters")
+{
+ TestContext test_context;
+
+ std::string content;
+ size_t count = static_cast<size_t>(Statistic::END) + 1;
+ for (size_t i = 0; i < count; ++i) {
+ content += fmt::format("{}\n", i);
+ }
+
+ Util::write_file("test", content);
+ Counters counters = Statistics::read("test");
+
+ REQUIRE(counters.size() == count);
+ for (size_t i = 0; i < count; ++i) {
+ CHECK(counters.get_raw(i) == i);
+ }
+}
+
+TEST_CASE("Update")
+{
+ TestContext test_context;
+
+ Util::write_file("test", "0 1 2 3 27 5\n");
+
+ auto counters = Statistics::update("test", [](Counters& cs) {
+ cs.increment(Statistic::internal_error, 1);
+ cs.increment(Statistic::cache_miss, 6);
+ });
+ REQUIRE(counters);
+
+ CHECK(counters->get(Statistic::internal_error) == 4);
+ CHECK(counters->get(Statistic::cache_miss) == 33);
+
+ counters = Statistics::read("test");
+ CHECK(counters->get(Statistic::internal_error) == 4);
+ CHECK(counters->get(Statistic::cache_miss) == 33);
+}
+
+TEST_SUITE_END();
--- /dev/null
+// 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 "../src/Config.hpp"
+#include "../src/Fd.hpp"
+#include "../src/Util.hpp"
+#include "TestUtil.hpp"
+
+#include "third_party/doctest.h"
+#include "third_party/nonstd/optional.hpp"
+
+#include <algorithm>
+
+using doctest::Approx;
+using nonstd::nullopt;
+using TestUtil::TestContext;
+
+TEST_SUITE_BEGIN("Util");
+
+TEST_CASE("Util::base_name")
+{
+ CHECK(Util::base_name("") == "");
+ CHECK(Util::base_name(".") == ".");
+ CHECK(Util::base_name("foo") == "foo");
+ CHECK(Util::base_name("/") == "");
+ CHECK(Util::base_name("/foo") == "foo");
+ CHECK(Util::base_name("/foo/bar/f.txt") == "f.txt");
+}
+
+TEST_CASE("Util::big_endian_to_int")
+{
+ uint8_t bytes[8] = {0x70, 0x9e, 0x9a, 0xbc, 0xd6, 0x54, 0x4b, 0xca};
+
+ uint8_t uint8;
+ Util::big_endian_to_int(bytes, uint8);
+ CHECK(uint8 == 0x70);
+
+ int8_t int8;
+ Util::big_endian_to_int(bytes, int8);
+ CHECK(int8 == 0x70);
+
+ uint16_t uint16;
+ Util::big_endian_to_int(bytes, uint16);
+ CHECK(uint16 == 0x709e);
+
+ int16_t int16;
+ Util::big_endian_to_int(bytes, int16);
+ CHECK(int16 == 0x709e);
+
+ uint32_t uint32;
+ Util::big_endian_to_int(bytes, uint32);
+ CHECK(uint32 == 0x709e9abc);
+
+ int32_t int32;
+ Util::big_endian_to_int(bytes, int32);
+ CHECK(int32 == 0x709e9abc);
+
+ uint64_t uint64;
+ Util::big_endian_to_int(bytes, uint64);
+ CHECK(uint64 == 0x709e9abcd6544bca);
+
+ int64_t int64;
+ Util::big_endian_to_int(bytes, int64);
+ CHECK(int64 == 0x709e9abcd6544bca);
+}
+
+TEST_CASE("Util::change_extension")
+{
+ CHECK(Util::change_extension("", "") == "");
+ CHECK(Util::change_extension("x", "") == "x");
+ CHECK(Util::change_extension("", "x") == "x");
+ CHECK(Util::change_extension("", ".") == ".");
+ CHECK(Util::change_extension(".", "") == "");
+ CHECK(Util::change_extension("...", "x") == "..x");
+ CHECK(Util::change_extension("abc", "def") == "abcdef");
+ CHECK(Util::change_extension("dot.", ".dot") == "dot.dot");
+ CHECK(Util::change_extension("foo.ext", "e2") == "fooe2");
+ CHECK(Util::change_extension("bar.txt", ".o") == "bar.o");
+ CHECK(Util::change_extension("foo.bar.txt", ".o") == "foo.bar.o");
+}
+
+TEST_CASE("Util::clamp")
+{
+ CHECK(Util::clamp(0, 1, 2) == 1);
+ CHECK(Util::clamp(1, 1, 2) == 1);
+ CHECK(Util::clamp(2, 1, 2) == 2);
+ CHECK(Util::clamp(3, 1, 2) == 2);
+
+ CHECK(Util::clamp(7.0, 7.7, 8.8) == Approx(7.7));
+ CHECK(Util::clamp(8.0, 7.7, 8.8) == Approx(8.0));
+ CHECK(Util::clamp(9.0, 7.7, 8.8) == Approx(8.8));
+}
+
+TEST_CASE("Util::common_dir_prefix_length")
+{
+ CHECK(Util::common_dir_prefix_length("", "") == 0);
+ CHECK(Util::common_dir_prefix_length("/", "") == 0);
+ CHECK(Util::common_dir_prefix_length("", "/") == 0);
+ CHECK(Util::common_dir_prefix_length("/", "/") == 0);
+ CHECK(Util::common_dir_prefix_length("/", "/b") == 0);
+ CHECK(Util::common_dir_prefix_length("/a", "/") == 0);
+ CHECK(Util::common_dir_prefix_length("/a", "/b") == 0);
+ CHECK(Util::common_dir_prefix_length("/a", "/a") == 2);
+ CHECK(Util::common_dir_prefix_length("/a", "/a/b") == 2);
+ CHECK(Util::common_dir_prefix_length("/a/b", "/a") == 2);
+ CHECK(Util::common_dir_prefix_length("/a/b", "/a/c") == 2);
+ CHECK(Util::common_dir_prefix_length("/a/b", "/a/b") == 4);
+ CHECK(Util::common_dir_prefix_length("/a/bc", "/a/b") == 2);
+ CHECK(Util::common_dir_prefix_length("/a/b", "/a/bc") == 2);
+}
+
+TEST_CASE("Util::create_dir")
+{
+ TestContext test_context;
+
+ CHECK(Util::create_dir("/"));
+
+ CHECK(Util::create_dir("create/dir"));
+ CHECK(Stat::stat("create/dir").is_directory());
+
+ Util::write_file("create/dir/file", "");
+ CHECK(!Util::create_dir("create/dir/file"));
+}
+
+TEST_CASE("Util::dir_name")
+{
+ CHECK(Util::dir_name("") == ".");
+ CHECK(Util::dir_name(".") == ".");
+ CHECK(Util::dir_name("foo") == ".");
+ CHECK(Util::dir_name("/") == "/");
+ CHECK(Util::dir_name("/foo") == "/");
+ CHECK(Util::dir_name("/foo/bar/f.txt") == "/foo/bar");
+}
+
+TEST_CASE("Util::strip_ansi_csi_seqs")
+{
+ const char input[] =
+ "Normal,"
+ " \x1B[K\x1B[1mbold\x1B[m,"
+ " \x1B[31mred\x1B[m,"
+ " \x1B[1;32mbold green\x1B[m.\n";
+
+ CHECK(Util::strip_ansi_csi_seqs(input) == "Normal, bold, red, bold green.\n");
+}
+
+TEST_CASE("Util::ends_with")
+{
+ CHECK(Util::ends_with("", ""));
+ CHECK(Util::ends_with("x", ""));
+ CHECK(Util::ends_with("x", "x"));
+ CHECK(Util::ends_with("xy", ""));
+ CHECK(Util::ends_with("xy", "y"));
+ CHECK(Util::ends_with("xy", "xy"));
+ CHECK(Util::ends_with("xyz", ""));
+ CHECK(Util::ends_with("xyz", "z"));
+ CHECK(Util::ends_with("xyz", "yz"));
+ CHECK(Util::ends_with("xyz", "xyz"));
+
+ CHECK_FALSE(Util::ends_with("", "x"));
+ CHECK_FALSE(Util::ends_with("x", "y"));
+ CHECK_FALSE(Util::ends_with("x", "xy"));
+}
+
+TEST_CASE("Util::ensure_dir_exists")
+{
+ TestContext test_context;
+
+ CHECK_NOTHROW(Util::ensure_dir_exists("/"));
+
+ CHECK_NOTHROW(Util::ensure_dir_exists("create/dir"));
+ CHECK(Stat::stat("create/dir").is_directory());
+
+ Util::write_file("create/dir/file", "");
+ CHECK_THROWS_WITH(
+ Util::ensure_dir_exists("create/dir/file"),
+ "Failed to create directory create/dir/file: Not a directory");
+}
+
+TEST_CASE("Util::expand_environment_variables")
+{
+ Util::setenv("FOO", "bar");
+
+ CHECK(Util::expand_environment_variables("") == "");
+ CHECK(Util::expand_environment_variables("$FOO") == "bar");
+ CHECK(Util::expand_environment_variables("$") == "$");
+ CHECK(Util::expand_environment_variables("$FOO $FOO:$FOO") == "bar bar:bar");
+ CHECK(Util::expand_environment_variables("x$FOO") == "xbar");
+ CHECK(Util::expand_environment_variables("${FOO}x") == "barx");
+
+ DOCTEST_GCC_SUPPRESS_WARNING_PUSH
+ DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-result")
+ CHECK_THROWS_WITH(
+ (void)Util::expand_environment_variables("$surelydoesntexist"),
+ "environment variable \"surelydoesntexist\" not set");
+ CHECK_THROWS_WITH((void)Util::expand_environment_variables("${FOO"),
+ "syntax error: missing '}' after \"FOO\"");
+ DOCTEST_GCC_SUPPRESS_WARNING_POP
+}
+
+TEST_CASE("Util::fallocate")
+{
+ TestContext test_context;
+
+ const char filename[] = "test-file";
+
+ CHECK(Util::fallocate(Fd(creat(filename, S_IRUSR | S_IWUSR)).get(), 10000)
+ == 0);
+ CHECK(Stat::stat(filename).size() == 10000);
+
+ CHECK(
+ Util::fallocate(Fd(open(filename, O_RDWR, S_IRUSR | S_IWUSR)).get(), 5000)
+ == 0);
+ CHECK(Stat::stat(filename).size() == 10000);
+
+ CHECK(
+ Util::fallocate(Fd(open(filename, O_RDWR, S_IRUSR | S_IWUSR)).get(), 20000)
+ == 0);
+ CHECK(Stat::stat(filename).size() == 20000);
+}
+
+TEST_CASE("Util::for_each_level_1_subdir")
+{
+ std::vector<std::string> actual;
+ Util::for_each_level_1_subdir(
+ "cache_dir",
+ [&](const std::string& subdir, const Util::ProgressReceiver&) {
+ actual.push_back(subdir);
+ },
+ [](double) {});
+
+ std::vector<std::string> expected = {
+ "cache_dir/0",
+ "cache_dir/1",
+ "cache_dir/2",
+ "cache_dir/3",
+ "cache_dir/4",
+ "cache_dir/5",
+ "cache_dir/6",
+ "cache_dir/7",
+ "cache_dir/8",
+ "cache_dir/9",
+ "cache_dir/a",
+ "cache_dir/b",
+ "cache_dir/c",
+ "cache_dir/d",
+ "cache_dir/e",
+ "cache_dir/f",
+ };
+ CHECK(actual == expected);
+}
+
+TEST_CASE("Util::format_argv_for_logging")
+{
+ const char* argv_0[] = {nullptr};
+ CHECK(Util::format_argv_for_logging(argv_0) == "");
+
+ const char* argv_2[] = {"foo", "bar", nullptr};
+ CHECK(Util::format_argv_for_logging(argv_2) == "foo bar");
+}
+
+TEST_CASE("Util::format_base16")
+{
+ uint8_t none[] = "";
+ uint8_t text[4] = "foo"; // incl. NUL
+ uint8_t data[4] = {0, 1, 2, 3};
+
+ CHECK(Util::format_base16(none, 0) == "");
+ CHECK(Util::format_base16(text, sizeof(text)) == "666f6f00");
+ CHECK(Util::format_base16(data, sizeof(data)) == "00010203");
+}
+
+TEST_CASE("Util::format_base32hex")
+{
+ // Test vectors (without padding) from RFC 4648.
+ const uint8_t input[] = {'f', 'o', 'o', 'b', 'a', 'r'};
+ CHECK(Util::format_base32hex(input, 0) == "");
+ CHECK(Util::format_base32hex(input, 1) == "co");
+ CHECK(Util::format_base32hex(input, 2) == "cpng");
+ CHECK(Util::format_base32hex(input, 3) == "cpnmu");
+ CHECK(Util::format_base32hex(input, 4) == "cpnmuog");
+ CHECK(Util::format_base32hex(input, 5) == "cpnmuoj1");
+ CHECK(Util::format_base32hex(input, 6) == "cpnmuoj1e8");
+}
+
+TEST_CASE("Util::format_human_readable_size")
+{
+ CHECK(Util::format_human_readable_size(0) == "0.0 kB");
+ CHECK(Util::format_human_readable_size(1) == "0.0 kB");
+ CHECK(Util::format_human_readable_size(49) == "0.0 kB");
+ CHECK(Util::format_human_readable_size(51) == "0.1 kB");
+ CHECK(Util::format_human_readable_size(949) == "0.9 kB");
+ CHECK(Util::format_human_readable_size(951) == "1.0 kB");
+ CHECK(Util::format_human_readable_size(499.7 * 1000) == "499.7 kB");
+ CHECK(Util::format_human_readable_size(1000 * 1000) == "1.0 MB");
+ CHECK(Util::format_human_readable_size(1234 * 1000) == "1.2 MB");
+ CHECK(Util::format_human_readable_size(438.5 * 1000 * 1000) == "438.5 MB");
+ CHECK(Util::format_human_readable_size(1000 * 1000 * 1000) == "1.0 GB");
+ CHECK(Util::format_human_readable_size(17.11 * 1000 * 1000 * 1000)
+ == "17.1 GB");
+}
+
+TEST_CASE("Util::format_parsable_size_with_suffix")
+{
+ CHECK(Util::format_parsable_size_with_suffix(0) == "0");
+ CHECK(Util::format_parsable_size_with_suffix(42 * 1000) == "42000");
+ CHECK(Util::format_parsable_size_with_suffix(1000 * 1000) == "1.0M");
+ CHECK(Util::format_parsable_size_with_suffix(1234 * 1000) == "1.2M");
+ CHECK(Util::format_parsable_size_with_suffix(438.5 * 1000 * 1000)
+ == "438.5M");
+ CHECK(Util::format_parsable_size_with_suffix(1000 * 1000 * 1000) == "1.0G");
+ CHECK(Util::format_parsable_size_with_suffix(17.11 * 1000 * 1000 * 1000)
+ == "17.1G");
+}
+
+TEST_CASE("Util::get_extension")
+{
+ CHECK(Util::get_extension("") == "");
+ CHECK(Util::get_extension(".") == ".");
+ CHECK(Util::get_extension("...") == ".");
+ CHECK(Util::get_extension("foo") == "");
+ CHECK(Util::get_extension("/") == "");
+ CHECK(Util::get_extension("/foo") == "");
+ CHECK(Util::get_extension("/foo/bar/f") == "");
+ CHECK(Util::get_extension("f.txt") == ".txt");
+ CHECK(Util::get_extension("f.abc.txt") == ".txt");
+ CHECK(Util::get_extension("/foo/bar/f.txt") == ".txt");
+ CHECK(Util::get_extension("/foo/bar/f.abc.txt") == ".txt");
+}
+
+static inline std::string
+os_path(std::string path)
+{
+#if !defined(HAVE_DIRENT_H) && DIR_DELIM_CH != '/'
+ std::replace(path.begin(), path.end(), '/', DIR_DELIM_CH);
+#endif
+
+ return path;
+}
+
+TEST_CASE("Util::get_level_1_files")
+{
+ TestContext test_context;
+
+ Util::create_dir("e/m/p/t/y");
+
+ Util::create_dir("0/1");
+ Util::create_dir("0/f/c");
+ Util::write_file("0/file_a", "");
+ Util::write_file("0/1/file_b", "1");
+ Util::write_file("0/1/file_c", "12");
+ Util::write_file("0/f/c/file_d", "123");
+
+ std::vector<std::shared_ptr<CacheFile>> files;
+ auto null_receiver = [](double) {};
+
+ SUBCASE("nonexistent subdirectory")
+ {
+ Util::get_level_1_files("2", null_receiver, files);
+ CHECK(files.empty());
+ }
+
+ SUBCASE("empty subdirectory")
+ {
+ Util::get_level_1_files("e", null_receiver, files);
+ CHECK(files.empty());
+ }
+
+ SUBCASE("simple case")
+ {
+ Util::get_level_1_files("0", null_receiver, files);
+ REQUIRE(files.size() == 4);
+
+ // Files within a level are in arbitrary order, sort them to be able to
+ // verify them.
+ std::sort(files.begin(),
+ files.end(),
+ [](const std::shared_ptr<CacheFile>& f1,
+ const std::shared_ptr<CacheFile>& f2) {
+ return f1->path() < f2->path();
+ });
+
+ CHECK(files[0]->path() == os_path("0/1/file_b"));
+ CHECK(files[0]->lstat().size() == 1);
+ CHECK(files[1]->path() == os_path("0/1/file_c"));
+ CHECK(files[1]->lstat().size() == 2);
+ CHECK(files[2]->path() == os_path("0/f/c/file_d"));
+ CHECK(files[2]->lstat().size() == 3);
+ CHECK(files[3]->path() == os_path("0/file_a"));
+ CHECK(files[3]->lstat().size() == 0);
+ }
+}
+
+TEST_CASE("Util::get_relative_path")
+{
+#ifdef _WIN32
+ CHECK(Util::get_relative_path("C:/a", "C:/a") == ".");
+ CHECK(Util::get_relative_path("C:/a", "Z:/a") == "Z:/a");
+ CHECK(Util::get_relative_path("C:/a/b", "C:/a") == "..");
+ CHECK(Util::get_relative_path("C:/a", "C:/a/b") == "b");
+ CHECK(Util::get_relative_path("C:/a", "C:/a/b/c") == "b/c");
+ CHECK(Util::get_relative_path("C:/a/b", "C:/a/c") == "../c");
+ CHECK(Util::get_relative_path("C:/a/b", "C:/a/c/d") == "../c/d");
+ CHECK(Util::get_relative_path("C:/a/b/c", "C:/a/c/d") == "../../c/d");
+ CHECK(Util::get_relative_path("C:/a/b", "C:/") == "../..");
+ CHECK(Util::get_relative_path("C:/a/b", "C:/c") == "../../c");
+ CHECK(Util::get_relative_path("C:/", "C:/a/b") == "a/b");
+ CHECK(Util::get_relative_path("C:/a", "D:/a/b") == "D:/a/b");
+#else
+ CHECK(Util::get_relative_path("/a", "/a") == ".");
+ CHECK(Util::get_relative_path("/a/b", "/a") == "..");
+ CHECK(Util::get_relative_path("/a", "/a/b") == "b");
+ CHECK(Util::get_relative_path("/a", "/a/b/c") == "b/c");
+ CHECK(Util::get_relative_path("/a/b", "/a/c") == "../c");
+ CHECK(Util::get_relative_path("/a/b", "/a/c/d") == "../c/d");
+ CHECK(Util::get_relative_path("/a/b/c", "/a/c/d") == "../../c/d");
+ CHECK(Util::get_relative_path("/a/b", "/") == "../..");
+ CHECK(Util::get_relative_path("/a/b", "/c") == "../../c");
+ CHECK(Util::get_relative_path("/", "/a/b") == "a/b");
+#endif
+}
+
+TEST_CASE("Util::get_path_in_cache")
+{
+ CHECK(Util::get_path_in_cache("/zz/ccache", 1, "ABCDEF.suffix")
+ == "/zz/ccache/A/BCDEF.suffix");
+ CHECK(Util::get_path_in_cache("/zz/ccache", 4, "ABCDEF.suffix")
+ == "/zz/ccache/A/B/C/D/EF.suffix");
+}
+
+TEST_CASE("Util::int_to_big_endian")
+{
+ uint8_t bytes[8];
+
+ uint8_t uint8 = 0x70;
+ Util::int_to_big_endian(uint8, bytes);
+ CHECK(bytes[0] == 0x70);
+
+ int8_t int8 = 0x70;
+ Util::int_to_big_endian(int8, bytes);
+ CHECK(bytes[0] == 0x70);
+
+ uint16_t uint16 = 0x709e;
+ Util::int_to_big_endian(uint16, bytes);
+ CHECK(bytes[0] == 0x70);
+ CHECK(bytes[1] == 0x9e);
+
+ int16_t int16 = 0x709e;
+ Util::int_to_big_endian(int16, bytes);
+ CHECK(bytes[0] == 0x70);
+ CHECK(bytes[1] == 0x9e);
+
+ uint32_t uint32 = 0x709e9abc;
+ Util::int_to_big_endian(uint32, bytes);
+ CHECK(bytes[0] == 0x70);
+ CHECK(bytes[1] == 0x9e);
+ CHECK(bytes[2] == 0x9a);
+ CHECK(bytes[3] == 0xbc);
+
+ int32_t int32 = 0x709e9abc;
+ Util::int_to_big_endian(int32, bytes);
+ CHECK(bytes[0] == 0x70);
+ CHECK(bytes[1] == 0x9e);
+ CHECK(bytes[2] == 0x9a);
+ CHECK(bytes[3] == 0xbc);
+
+ uint64_t uint64 = 0x709e9abcd6544bca;
+ Util::int_to_big_endian(uint64, bytes);
+ CHECK(bytes[0] == 0x70);
+ CHECK(bytes[1] == 0x9e);
+ CHECK(bytes[2] == 0x9a);
+ CHECK(bytes[3] == 0xbc);
+ CHECK(bytes[4] == 0xd6);
+ CHECK(bytes[5] == 0x54);
+ CHECK(bytes[6] == 0x4b);
+ CHECK(bytes[7] == 0xca);
+
+ int64_t int64 = 0x709e9abcd6544bca;
+ Util::int_to_big_endian(int64, bytes);
+ CHECK(bytes[0] == 0x70);
+ CHECK(bytes[1] == 0x9e);
+ CHECK(bytes[2] == 0x9a);
+ CHECK(bytes[3] == 0xbc);
+ CHECK(bytes[4] == 0xd6);
+ CHECK(bytes[5] == 0x54);
+ CHECK(bytes[6] == 0x4b);
+ CHECK(bytes[7] == 0xca);
+}
+
+TEST_CASE("Util::is_absolute_path")
+{
+#ifdef _WIN32
+ CHECK(Util::is_absolute_path("C:/"));
+ CHECK(Util::is_absolute_path("C:\\foo/fie"));
+ CHECK(Util::is_absolute_path("/C:\\foo/fie")); // MSYS/Cygwin path
+ CHECK(!Util::is_absolute_path(""));
+ CHECK(!Util::is_absolute_path("foo\\fie/fum"));
+ CHECK(!Util::is_absolute_path("C:foo/fie"));
+#endif
+ CHECK(Util::is_absolute_path("/"));
+ CHECK(Util::is_absolute_path("/foo/fie"));
+ CHECK(!Util::is_absolute_path(""));
+ CHECK(!Util::is_absolute_path("foo/fie"));
+}
+
+TEST_CASE("Util::is_dir_separator")
+{
+ CHECK(!Util::is_dir_separator('x'));
+ CHECK(Util::is_dir_separator('/'));
+#ifdef _WIN32
+ CHECK(Util::is_dir_separator('\\'));
+#else
+ CHECK(!Util::is_dir_separator('\\'));
+#endif
+}
+
+TEST_CASE("Util::matches_dir_prefix_or_file")
+{
+ CHECK(!Util::matches_dir_prefix_or_file("", ""));
+ CHECK(!Util::matches_dir_prefix_or_file("/", ""));
+ CHECK(!Util::matches_dir_prefix_or_file("", "/"));
+
+ CHECK(Util::matches_dir_prefix_or_file("aa", "aa"));
+ CHECK(!Util::matches_dir_prefix_or_file("aaa", "aa"));
+ CHECK(!Util::matches_dir_prefix_or_file("aa", "aaa"));
+ CHECK(!Util::matches_dir_prefix_or_file("aa/", "aa"));
+
+ CHECK(Util::matches_dir_prefix_or_file("/aa/bb", "/aa/bb"));
+ CHECK(!Util::matches_dir_prefix_or_file("/aa/b", "/aa/bb"));
+ CHECK(!Util::matches_dir_prefix_or_file("/aa/bbb", "/aa/bb"));
+
+ CHECK(Util::matches_dir_prefix_or_file("/aa", "/aa/bb"));
+ CHECK(Util::matches_dir_prefix_or_file("/aa/", "/aa/bb"));
+ CHECK(!Util::matches_dir_prefix_or_file("/aa/bb", "/aa"));
+ CHECK(!Util::matches_dir_prefix_or_file("/aa/bb", "/aa/"));
+
+#ifdef _WIN32
+ CHECK(Util::matches_dir_prefix_or_file("\\aa", "\\aa\\bb"));
+ CHECK(Util::matches_dir_prefix_or_file("\\aa\\", "\\aa\\bb"));
+#else
+ CHECK(!Util::matches_dir_prefix_or_file("\\aa", "\\aa\\bb"));
+ CHECK(!Util::matches_dir_prefix_or_file("\\aa\\", "\\aa\\bb"));
+#endif
+}
+
+TEST_CASE("Util::normalize_absolute_path")
+{
+ CHECK(Util::normalize_absolute_path("") == "");
+ CHECK(Util::normalize_absolute_path(".") == ".");
+ CHECK(Util::normalize_absolute_path("..") == "..");
+ CHECK(Util::normalize_absolute_path("...") == "...");
+ CHECK(Util::normalize_absolute_path("x/./") == "x/./");
+
+#ifdef _WIN32
+ CHECK(Util::normalize_absolute_path("c:/") == "c:/");
+ CHECK(Util::normalize_absolute_path("c:\\") == "c:/");
+ CHECK(Util::normalize_absolute_path("c:/.") == "c:/");
+ CHECK(Util::normalize_absolute_path("c:\\..") == "c:/");
+ CHECK(Util::normalize_absolute_path("c:\\x/..") == "c:/");
+ CHECK(Util::normalize_absolute_path("c:\\x/./y\\..\\\\z") == "c:/x/z");
+#else
+ CHECK(Util::normalize_absolute_path("/") == "/");
+ CHECK(Util::normalize_absolute_path("/.") == "/");
+ CHECK(Util::normalize_absolute_path("/..") == "/");
+ CHECK(Util::normalize_absolute_path("/./") == "/");
+ CHECK(Util::normalize_absolute_path("//") == "/");
+ CHECK(Util::normalize_absolute_path("/../x") == "/x");
+ CHECK(Util::normalize_absolute_path("/x/./y/z") == "/x/y/z");
+ CHECK(Util::normalize_absolute_path("/x/../y/z/") == "/y/z");
+ CHECK(Util::normalize_absolute_path("/x/.../y/z") == "/x/.../y/z");
+ CHECK(Util::normalize_absolute_path("/x/yyy/../zz") == "/x/zz");
+ CHECK(Util::normalize_absolute_path("//x/yyy///.././zz") == "/x/zz");
+#endif
+}
+
+TEST_CASE("Util::parse_duration")
+{
+ CHECK(Util::parse_duration("0s") == 0);
+ CHECK(Util::parse_duration("2s") == 2);
+ CHECK(Util::parse_duration("1d") == 3600 * 24);
+ CHECK(Util::parse_duration("2d") == 2 * 3600 * 24);
+ CHECK_THROWS_WITH(
+ Util::parse_duration("-2"),
+ "invalid suffix (supported: d (day) and s (second)): \"-2\"");
+ CHECK_THROWS_WITH(
+ Util::parse_duration("2x"),
+ "invalid suffix (supported: d (day) and s (second)): \"2x\"");
+ CHECK_THROWS_WITH(
+ Util::parse_duration("2"),
+ "invalid suffix (supported: d (day) and s (second)): \"2\"");
+}
+
+TEST_CASE("Util::parse_signed")
+{
+ CHECK(Util::parse_signed("0") == 0);
+ CHECK(Util::parse_signed("2") == 2);
+ CHECK(Util::parse_signed("-17") == -17);
+ CHECK(Util::parse_signed("42") == 42);
+ CHECK(Util::parse_signed("0666") == 666);
+ CHECK(Util::parse_signed(" 777 ") == 777);
+
+ CHECK_THROWS_WITH(Util::parse_signed(""), "invalid integer: \"\"");
+ CHECK_THROWS_WITH(Util::parse_signed("x"), "invalid integer: \"x\"");
+ CHECK_THROWS_WITH(Util::parse_signed("0x"), "invalid integer: \"0x\"");
+ CHECK_THROWS_WITH(Util::parse_signed("0x4"), "invalid integer: \"0x4\"");
+
+ // Custom description not used for invalid value.
+ CHECK_THROWS_WITH(Util::parse_signed("apple", nullopt, nullopt, "banana"),
+ "invalid integer: \"apple\"");
+
+ // Boundary values.
+ CHECK_THROWS_WITH(Util::parse_signed("-9223372036854775809"),
+ "invalid integer: \"-9223372036854775809\"");
+ CHECK(Util::parse_signed("-9223372036854775808") == INT64_MIN);
+ CHECK(Util::parse_signed("9223372036854775807") == INT64_MAX);
+ CHECK_THROWS_WITH(Util::parse_signed("9223372036854775808"),
+ "invalid integer: \"9223372036854775808\"");
+
+ // Min and max values.
+ CHECK_THROWS_WITH(Util::parse_signed("-2", -1, 1),
+ "integer must be between -1 and 1");
+ CHECK(Util::parse_signed("-1", -1, 1) == -1);
+ CHECK(Util::parse_signed("0", -1, 1) == 0);
+ CHECK(Util::parse_signed("1", -1, 1) == 1);
+ CHECK_THROWS_WITH(Util::parse_signed("2", -1, 1),
+ "integer must be between -1 and 1");
+
+ // Custom description used for boundary violation.
+ CHECK_THROWS_WITH(Util::parse_signed("0", 1, 2, "banana"),
+ "banana must be between 1 and 2");
+}
+
+TEST_CASE("Util::parse_size")
+{
+ CHECK(Util::parse_size("0") == 0);
+ CHECK(Util::parse_size("42") // Default suffix: G
+ == static_cast<uint64_t>(42) * 1000 * 1000 * 1000);
+ CHECK(Util::parse_size("78k") == 78 * 1000);
+ CHECK(Util::parse_size("78K") == 78 * 1000);
+ CHECK(Util::parse_size("1.1 M") == (int64_t(1.1 * 1000 * 1000)));
+ CHECK(Util::parse_size("438.55M") == (int64_t(438.55 * 1000 * 1000)));
+ CHECK(Util::parse_size("1 G") == 1 * 1000 * 1000 * 1000);
+ CHECK(Util::parse_size("2T")
+ == static_cast<uint64_t>(2) * 1000 * 1000 * 1000 * 1000);
+ CHECK(Util::parse_size("78 Ki") == 78 * 1024);
+ CHECK(Util::parse_size("1.1Mi") == (int64_t(1.1 * 1024 * 1024)));
+ CHECK(Util::parse_size("438.55 Mi") == (int64_t(438.55 * 1024 * 1024)));
+ CHECK(Util::parse_size("1Gi") == 1 * 1024 * 1024 * 1024);
+ CHECK(Util::parse_size("2 Ti")
+ == static_cast<uint64_t>(2) * 1024 * 1024 * 1024 * 1024);
+
+ CHECK_THROWS_WITH(Util::parse_size(""), "invalid size: \"\"");
+ CHECK_THROWS_WITH(Util::parse_size("x"), "invalid size: \"x\"");
+ CHECK_THROWS_WITH(Util::parse_size("10x"), "invalid size: \"10x\"");
+}
+
+TEST_CASE("Util::parse_unsigned")
+{
+ CHECK(Util::parse_unsigned("0") == 0);
+ CHECK(Util::parse_unsigned("2") == 2);
+ CHECK(Util::parse_unsigned("42") == 42);
+ CHECK(Util::parse_unsigned("0666") == 666);
+ CHECK(Util::parse_unsigned(" 777 ") == 777);
+
+ CHECK_THROWS_WITH(Util::parse_unsigned(""), "invalid unsigned integer: \"\"");
+ CHECK_THROWS_WITH(Util::parse_unsigned("x"),
+ "invalid unsigned integer: \"x\"");
+ CHECK_THROWS_WITH(Util::parse_unsigned("0x"),
+ "invalid unsigned integer: \"0x\"");
+ CHECK_THROWS_WITH(Util::parse_unsigned("0x4"),
+ "invalid unsigned integer: \"0x4\"");
+
+ // Custom description not used for invalid value.
+ CHECK_THROWS_WITH(Util::parse_unsigned("apple", nullopt, nullopt, "banana"),
+ "invalid unsigned integer: \"apple\"");
+
+ // Boundary values.
+ CHECK_THROWS_WITH(Util::parse_unsigned("-1"),
+ "invalid unsigned integer: \"-1\"");
+ CHECK(Util::parse_unsigned("0") == 0);
+ CHECK(Util::parse_unsigned("18446744073709551615") == UINT64_MAX);
+ CHECK_THROWS_WITH(Util::parse_unsigned("18446744073709551616"),
+ "invalid unsigned integer: \"18446744073709551616\"");
+}
+
+TEST_CASE("Util::read_file and Util::write_file")
+{
+ TestContext test_context;
+
+ Util::write_file("test", "foo\nbar\n");
+ std::string data = Util::read_file("test");
+ CHECK(data == "foo\nbar\n");
+
+ Util::write_file("test", "car");
+ data = Util::read_file("test");
+ CHECK(data == "car");
+
+ Util::write_file("test", "pet", std::ios::app);
+ data = Util::read_file("test");
+ CHECK(data == "carpet");
+
+ Util::write_file("test", "\n", std::ios::app | std::ios::binary);
+ data = Util::read_file("test");
+ CHECK(data == "carpet\n");
+
+ Util::write_file("test", "\n", std::ios::app); // text mode
+ data = Util::read_file("test");
+#ifdef _WIN32
+ CHECK(data == "carpet\n\r\n");
+#else
+ CHECK(data == "carpet\n\n");
+#endif
+ CHECK_THROWS_WITH(Util::read_file("does/not/exist"),
+ "No such file or directory");
+
+ CHECK_THROWS_WITH(Util::write_file("", "does/not/exist"),
+ "No such file or directory");
+
+ CHECK_THROWS_WITH(Util::write_file("does/not/exist", "does/not/exist"),
+ "No such file or directory");
+}
+
+TEST_CASE("Util::remove_extension")
+{
+ CHECK(Util::remove_extension("") == "");
+ CHECK(Util::remove_extension(".") == "");
+ CHECK(Util::remove_extension("...") == "..");
+ CHECK(Util::remove_extension("foo") == "foo");
+ CHECK(Util::remove_extension("/") == "/");
+ CHECK(Util::remove_extension("/foo") == "/foo");
+ CHECK(Util::remove_extension("/foo/bar/f") == "/foo/bar/f");
+ CHECK(Util::remove_extension("f.txt") == "f");
+ CHECK(Util::remove_extension("f.abc.txt") == "f.abc");
+ CHECK(Util::remove_extension("/foo/bar/f.txt") == "/foo/bar/f");
+ CHECK(Util::remove_extension("/foo/bar/f.abc.txt") == "/foo/bar/f.abc");
+}
+
+TEST_CASE("Util::same_program_name")
+{
+ CHECK(Util::same_program_name("foo", "foo"));
+#ifdef _WIN32
+ CHECK(Util::same_program_name("FOO", "foo"));
+ CHECK(Util::same_program_name("FOO.exe", "foo"));
+#else
+ CHECK(!Util::same_program_name("FOO", "foo"));
+ CHECK(!Util::same_program_name("FOO.exe", "foo"));
+#endif
+}
+
+TEST_CASE("Util::split_into_views")
+{
+ {
+ CHECK(Util::split_into_views("", "/").empty());
+ }
+ {
+ CHECK(Util::split_into_views("///", "/").empty());
+ }
+ {
+ auto s = Util::split_into_views("a/b", "/");
+ REQUIRE(s.size() == 2);
+ CHECK(s.at(0) == "a");
+ CHECK(s.at(1) == "b");
+ }
+ {
+ auto s = Util::split_into_views("a/b", "x");
+ REQUIRE(s.size() == 1);
+ CHECK(s.at(0) == "a/b");
+ }
+ {
+ auto s = Util::split_into_views("a/b:c", "/:");
+ REQUIRE(s.size() == 3);
+ CHECK(s.at(0) == "a");
+ CHECK(s.at(1) == "b");
+ CHECK(s.at(2) == "c");
+ }
+ {
+ auto s = Util::split_into_views(":a//b..:.c/:/.", "/:.");
+ REQUIRE(s.size() == 3);
+ CHECK(s.at(0) == "a");
+ CHECK(s.at(1) == "b");
+ CHECK(s.at(2) == "c");
+ }
+ {
+ auto s = Util::split_into_views(".0.1.2.3.4.5.6.7.8.9.", "/:.+_abcdef");
+ REQUIRE(s.size() == 10);
+ CHECK(s.at(0) == "0");
+ CHECK(s.at(9) == "9");
+ }
+}
+
+TEST_CASE("Util::split_into_strings")
+{
+ {
+ CHECK(Util::split_into_strings("", "/").empty());
+ }
+ {
+ CHECK(Util::split_into_strings("///", "/").empty());
+ }
+ {
+ auto s = Util::split_into_strings("a/b", "/");
+ REQUIRE(s.size() == 2);
+ CHECK(s.at(0) == "a");
+ CHECK(s.at(1) == "b");
+ }
+ {
+ auto s = Util::split_into_strings("a/b", "x");
+ REQUIRE(s.size() == 1);
+ CHECK(s.at(0) == "a/b");
+ }
+ {
+ auto s = Util::split_into_strings("a/b:c", "/:");
+ REQUIRE(s.size() == 3);
+ CHECK(s.at(0) == "a");
+ CHECK(s.at(1) == "b");
+ CHECK(s.at(2) == "c");
+ }
+ {
+ auto s = Util::split_into_strings(":a//b..:.c/:/.", "/:.");
+ REQUIRE(s.size() == 3);
+ CHECK(s.at(0) == "a");
+ CHECK(s.at(1) == "b");
+ CHECK(s.at(2) == "c");
+ }
+ {
+ auto s = Util::split_into_strings(".0.1.2.3.4.5.6.7.8.9.", "/:.+_abcdef");
+ REQUIRE(s.size() == 10);
+ CHECK(s.at(0) == "0");
+ CHECK(s.at(9) == "9");
+ }
+}
+
+TEST_CASE("Util::starts_with")
+{
+ // starts_with(const char*, string_view)
+ CHECK(Util::starts_with("", ""));
+ CHECK(Util::starts_with("x", ""));
+ CHECK(Util::starts_with("x", "x"));
+ CHECK(Util::starts_with("xy", ""));
+ CHECK(Util::starts_with("xy", "x"));
+ CHECK(Util::starts_with("xy", "xy"));
+ CHECK(Util::starts_with("xyz", ""));
+ CHECK(Util::starts_with("xyz", "x"));
+ CHECK(Util::starts_with("xyz", "xy"));
+ CHECK(Util::starts_with("xyz", "xyz"));
+
+ CHECK_FALSE(Util::starts_with("", "x"));
+ CHECK_FALSE(Util::starts_with("x", "y"));
+ CHECK_FALSE(Util::starts_with("x", "xy"));
+
+ // starts_with(string_view, string_view)
+ CHECK(Util::starts_with(std::string(""), ""));
+ CHECK(Util::starts_with(std::string("x"), ""));
+ CHECK(Util::starts_with(std::string("x"), "x"));
+ CHECK(Util::starts_with(std::string("xy"), ""));
+ CHECK(Util::starts_with(std::string("xy"), "x"));
+ CHECK(Util::starts_with(std::string("xy"), "xy"));
+ CHECK(Util::starts_with(std::string("xyz"), ""));
+ CHECK(Util::starts_with(std::string("xyz"), "x"));
+ CHECK(Util::starts_with(std::string("xyz"), "xy"));
+ CHECK(Util::starts_with(std::string("xyz"), "xyz"));
+
+ CHECK_FALSE(Util::starts_with(std::string(""), "x"));
+ CHECK_FALSE(Util::starts_with(std::string("x"), "y"));
+ CHECK_FALSE(Util::starts_with(std::string("x"), "xy"));
+}
+
+TEST_CASE("Util::strip_whitespace")
+{
+ CHECK(Util::strip_whitespace("") == "");
+ CHECK(Util::strip_whitespace("x") == "x");
+ CHECK(Util::strip_whitespace(" x") == "x");
+ CHECK(Util::strip_whitespace("x ") == "x");
+ CHECK(Util::strip_whitespace(" x ") == "x");
+ CHECK(Util::strip_whitespace(" \n\tx \n\t") == "x");
+ CHECK(Util::strip_whitespace(" x y ") == "x y");
+}
+
+TEST_CASE("Util::to_lowercase")
+{
+ CHECK(Util::to_lowercase("") == "");
+ CHECK(Util::to_lowercase("x") == "x");
+ CHECK(Util::to_lowercase("X") == "x");
+ CHECK(Util::to_lowercase(" x_X@") == " x_x@");
+}
+
+TEST_CASE("Util::traverse")
+{
+ TestContext test_context;
+
+ REQUIRE(Util::create_dir("dir-with-subdir-and-file/subdir"));
+ Util::write_file("dir-with-subdir-and-file/subdir/f", "");
+ REQUIRE(Util::create_dir("dir-with-files"));
+ Util::write_file("dir-with-files/f1", "");
+ Util::write_file("dir-with-files/f2", "");
+ REQUIRE(Util::create_dir("empty-dir"));
+
+ std::vector<std::string> visited;
+ auto visitor = [&visited](const std::string& path, bool is_dir) {
+ visited.push_back(fmt::format("[{}] {}", is_dir ? 'd' : 'f', path));
+ };
+
+ SUBCASE("traverse nonexistent path")
+ {
+ CHECK_THROWS_WITH(
+ Util::traverse("nonexistent", visitor),
+ "failed to open directory nonexistent: No such file or directory");
+ }
+
+ SUBCASE("traverse file")
+ {
+ CHECK_NOTHROW(Util::traverse("dir-with-subdir-and-file/subdir/f", visitor));
+ REQUIRE(visited.size() == 1);
+ CHECK(visited[0] == "[f] dir-with-subdir-and-file/subdir/f");
+ }
+
+ SUBCASE("traverse empty directory")
+ {
+ CHECK_NOTHROW(Util::traverse("empty-dir", visitor));
+ REQUIRE(visited.size() == 1);
+ CHECK(visited[0] == "[d] empty-dir");
+ }
+
+ SUBCASE("traverse directory with files")
+ {
+ CHECK_NOTHROW(Util::traverse("dir-with-files", visitor));
+ REQUIRE(visited.size() == 3);
+ std::string f1 = os_path("[f] dir-with-files/f1");
+ std::string f2 = os_path("[f] dir-with-files/f2");
+ CHECK(((visited[0] == f1 && visited[1] == f2)
+ || (visited[0] == f2 && visited[1] == f1)));
+ CHECK(visited[2] == "[d] dir-with-files");
+ }
+
+ SUBCASE("traverse directory hierarchy")
+ {
+ CHECK_NOTHROW(Util::traverse("dir-with-subdir-and-file", visitor));
+ REQUIRE(visited.size() == 3);
+ CHECK(visited[0] == os_path("[f] dir-with-subdir-and-file/subdir/f"));
+ CHECK(visited[1] == os_path("[d] dir-with-subdir-and-file/subdir"));
+ CHECK(visited[2] == "[d] dir-with-subdir-and-file");
+ }
+}
+
+TEST_CASE("Util::wipe_path")
+{
+ TestContext test_context;
+
+ SUBCASE("Wipe non-existing path")
+ {
+ CHECK_NOTHROW(Util::wipe_path("a"));
+ }
+
+ SUBCASE("Wipe file")
+ {
+ Util::write_file("a", "");
+ CHECK_NOTHROW(Util::wipe_path("a"));
+ CHECK(!Stat::stat("a"));
+ }
+
+ SUBCASE("Wipe directory")
+ {
+ REQUIRE(Util::create_dir("a/b"));
+ Util::write_file("a/1", "");
+ Util::write_file("a/b/1", "");
+ CHECK_NOTHROW(Util::wipe_path("a"));
+ CHECK(!Stat::stat("a"));
+ }
+
+ SUBCASE("Wipe bad path")
+ {
+#ifdef _WIN32
+ const char error[] = "failed to rmdir .: Permission denied";
+#else
+ const char error[] = "failed to rmdir .: Invalid argument";
+#endif
+ CHECK_THROWS_WITH(Util::wipe_path("."), error);
+ }
+}
+
+TEST_SUITE_END();
--- /dev/null
+// 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 "../src/Win32Util.hpp"
+#include "TestUtil.hpp"
+
+#include "third_party/doctest.h"
+
+TEST_SUITE_BEGIN("Win32Util");
+
+TEST_CASE("Win32Util::argv_to_string")
+{
+ {
+ const char* const argv[] = {"a", nullptr};
+ CHECK(Win32Util::argv_to_string(argv, "") == R"("a")");
+ }
+ {
+ const char* const argv[] = {"a", nullptr};
+ CHECK(Win32Util::argv_to_string(argv, "p") == R"("p" "a")");
+ }
+ {
+ const char* const argv[] = {"a", "b c", "\"d\"", "'e'", "\\\"h", nullptr};
+ CHECK(Win32Util::argv_to_string(argv, "")
+ == R"("a" "b c" "\"d\"" "'e'" "\\\"h")");
+ }
+}
+
+TEST_SUITE_END();
--- /dev/null
+// 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 "../src/Compression.hpp"
+#include "../src/Compressor.hpp"
+#include "../src/Decompressor.hpp"
+#include "../src/File.hpp"
+#include "TestUtil.hpp"
+
+#include "third_party/doctest.h"
+
+using TestUtil::TestContext;
+
+TEST_SUITE_BEGIN("ZstdCompression");
+
+TEST_CASE("Small Compression::Type::zstd roundtrip")
+{
+ TestContext test_context;
+
+ File f("data.zstd", "wb");
+ auto compressor =
+ Compressor::create_from_type(Compression::Type::zstd, f.get(), 1);
+ CHECK(compressor->actual_compression_level() == 1);
+ compressor->write("foobar", 6);
+ compressor->finalize();
+
+ f.open("data.zstd", "rb");
+ auto decompressor =
+ Decompressor::create_from_type(Compression::Type::zstd, f.get());
+
+ char buffer[4];
+ decompressor->read(buffer, 4);
+ CHECK(memcmp(buffer, "foob", 4) == 0);
+
+ // Not reached the end.
+ CHECK_THROWS_WITH(decompressor->finalize(),
+ "garbage data at end of zstd input stream");
+
+ decompressor->read(buffer, 2);
+ CHECK(memcmp(buffer, "ar", 2) == 0);
+
+ // Reached the end.
+ decompressor->finalize();
+
+ // Nothing left to read.
+ CHECK_THROWS_WITH(decompressor->read(buffer, 1),
+ "failed to read from zstd input stream");
+}
+
+TEST_CASE("Large compressible Compression::Type::zstd roundtrip")
+{
+ TestContext test_context;
+
+ char data[] = "The quick brown fox jumps over the lazy dog";
+
+ File f("data.zstd", "wb");
+ auto compressor =
+ Compressor::create_from_type(Compression::Type::zstd, f.get(), 1);
+ for (size_t i = 0; i < 1000; i++) {
+ compressor->write(data, sizeof(data));
+ }
+ compressor->finalize();
+
+ f.open("data.zstd", "rb");
+ auto decompressor =
+ Decompressor::create_from_type(Compression::Type::zstd, f.get());
+
+ char buffer[sizeof(data)];
+ for (size_t i = 0; i < 1000; i++) {
+ decompressor->read(buffer, sizeof(buffer));
+ CHECK(memcmp(buffer, data, sizeof(data)) == 0);
+ }
+
+ // Reached the end.
+ decompressor->finalize();
+
+ // Nothing left to read.
+ CHECK_THROWS_WITH(decompressor->read(buffer, 1),
+ "failed to read from zstd input stream");
+}
+
+TEST_CASE("Large uncompressible Compression::Type::zstd roundtrip")
+{
+ TestContext test_context;
+
+ char data[100000];
+ for (char& c : data) {
+ c = rand() % 256;
+ }
+
+ File f("data.zstd", "wb");
+ auto compressor =
+ Compressor::create_from_type(Compression::Type::zstd, f.get(), 1);
+ compressor->write(data, sizeof(data));
+ compressor->finalize();
+
+ f.open("data.zstd", "rb");
+ auto decompressor =
+ Decompressor::create_from_type(Compression::Type::zstd, f.get());
+
+ char buffer[sizeof(data)];
+ decompressor->read(buffer, sizeof(buffer));
+ CHECK(memcmp(buffer, data, sizeof(data)) == 0);
+
+ decompressor->finalize();
+}
+
+TEST_SUITE_END();
--- /dev/null
+// 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 "../src/Args.hpp"
+#include "../src/Config.hpp"
+#include "../src/Context.hpp"
+#include "../src/Statistics.hpp"
+#include "../src/Util.hpp"
+#include "TestUtil.hpp"
+#include "argprocessing.hpp"
+
+#include "third_party/doctest.h"
+
+#include <algorithm>
+
+using TestUtil::TestContext;
+
+namespace {
+
+std::string
+get_root()
+{
+#ifndef _WIN32
+ return "/";
+#else
+ char volume[4]; // "C:\"
+ GetVolumePathName(Util::get_actual_cwd().c_str(), volume, sizeof(volume));
+ volume[2] = '/'; // Since base directory is normalized to forward slashes
+ return volume;
+#endif
+}
+
+std::string
+get_posix_path(const std::string& path)
+{
+#ifndef _WIN32
+ return path;
+#else
+ std::string posix;
+
+ // /-escape volume.
+ if (path[0] >= 'A' && path[0] <= 'Z' && path[1] == ':') {
+ posix = "/" + path;
+ } else {
+ posix = path;
+ }
+ // Convert slashes.
+ std::replace(posix.begin(), posix.end(), '\\', '/');
+ return posix;
+#endif
+}
+
+} // namespace
+
+TEST_SUITE_BEGIN("argprocessing");
+
+TEST_CASE("dash_E_should_result_in_called_for_preprocessing")
+{
+ TestContext test_context;
+
+ Context ctx;
+ ctx.orig_args = Args::from_string("cc -c foo.c -E");
+
+ Util::write_file("foo.c", "");
+ CHECK(process_args(ctx).error == Statistic::called_for_preprocessing);
+}
+
+TEST_CASE("dash_M_should_be_unsupported")
+{
+ TestContext test_context;
+
+ Context ctx;
+ ctx.orig_args = Args::from_string("cc -c foo.c -M");
+
+ Util::write_file("foo.c", "");
+ CHECK(process_args(ctx).error == Statistic::unsupported_compiler_option);
+}
+
+TEST_CASE("dependency_args_to_preprocessor_if_run_second_cpp_is_false")
+{
+ TestContext test_context;
+ const std::string dep_args =
+ "-MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 -MQ mq1 -MQ mq2 -Wp,-MD,wpmd"
+ " -Wp,-MMD,wpmmd -Wp,-MP -Wp,-MT,wpmt -Wp,-MQ,wpmq -Wp,-MF,wpf";
+ Context ctx;
+ ctx.orig_args = Args::from_string("cc " + dep_args + " -c foo.c -o foo.o");
+ Util::write_file("foo.c", "");
+ ctx.config.set_run_second_cpp(false);
+
+ const ProcessArgsResult result = process_args(ctx);
+
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string() == "cc " + dep_args);
+ CHECK(result.extra_args_to_hash.to_string() == "");
+ CHECK(result.compiler_args.to_string() == "cc -c");
+}
+
+TEST_CASE("dependency_args_to_compiler_if_run_second_cpp_is_true")
+{
+ TestContext test_context;
+ const std::string dep_args =
+ "-MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 -MQ mq1 -MQ mq2 -Wp,-MD,wpmd"
+ " -Wp,-MMD,wpmmd -Wp,-MP -Wp,-MT,wpmt -Wp,-MQ,wpmq -Wp,-MF,wpf";
+ Context ctx;
+ ctx.orig_args = Args::from_string("cc " + dep_args + " -c foo.c -o foo.o");
+ Util::write_file("foo.c", "");
+
+ const ProcessArgsResult result = process_args(ctx);
+
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string() == "cc");
+ CHECK(result.extra_args_to_hash.to_string() == dep_args);
+ CHECK(result.compiler_args.to_string() == "cc -c " + dep_args);
+}
+
+TEST_CASE("cpp_only_args_to_preprocessor_if_run_second_cpp_is_false")
+{
+ TestContext test_context;
+ const std::string cpp_args =
+ "-I. -idirafter . -iframework. -imacros . -imultilib . -include test.h"
+ " -include-pch test.pch -iprefix . -iquote . -isysroot . -isystem ."
+ " -iwithprefix . -iwithprefixbefore . -DTEST_MACRO -DTEST_MACRO2=1 -F."
+ " -trigraphs -fworking-directory -fno-working-directory";
+ const std::string dep_args =
+ "-MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 -MQ mq1 -MQ mq2 -Wp,-MD,wpmd"
+ " -Wp,-MMD,wpmmd -Wp,-MP -Wp,-MT,wpmt -Wp,-MQ,wpmq -Wp,-MF,wpf";
+ Context ctx;
+ ctx.orig_args =
+ Args::from_string("cc " + cpp_args + " " + dep_args + " -c foo.c -o foo.o");
+ Util::write_file("foo.c", "");
+ ctx.config.set_run_second_cpp(false);
+
+ const ProcessArgsResult result = process_args(ctx);
+
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string()
+ == "cc " + cpp_args + " " + dep_args);
+ CHECK(result.extra_args_to_hash.to_string() == "");
+ CHECK(result.compiler_args.to_string() == "cc -c");
+}
+
+TEST_CASE(
+ "cpp_only_args_to_preprocessor_and_compiler_if_run_second_cpp_is_true")
+{
+ TestContext test_context;
+ const std::string cpp_args =
+ "-I. -idirafter . -iframework. -imacros . -imultilib . -include test.h"
+ " -include-pch test.pch -iprefix . -iquote . -isysroot . -isystem ."
+ " -iwithprefix . -iwithprefixbefore . -DTEST_MACRO -DTEST_MACRO2=1 -F."
+ " -trigraphs -fworking-directory -fno-working-directory";
+ const std::string dep_args =
+ "-MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 -MQ mq1 -MQ mq2 -Wp,-MD,wpmd"
+ " -Wp,-MMD,wpmmd";
+ Context ctx;
+ ctx.orig_args =
+ Args::from_string("cc " + cpp_args + " " + dep_args + " -c foo.c -o foo.o");
+ Util::write_file("foo.c", "");
+
+ const ProcessArgsResult result = process_args(ctx);
+
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string() == "cc " + cpp_args);
+ CHECK(result.extra_args_to_hash.to_string() == dep_args);
+ CHECK(result.compiler_args.to_string()
+ == "cc " + cpp_args + " -c " + dep_args);
+}
+
+TEST_CASE(
+ "dependency_args_that_take_an_argument_should_not_require_space_delimiter")
+{
+ TestContext test_context;
+ const std::string dep_args = "-MMD -MFfoo.d -MT mt -MTmt -MQmq";
+ Context ctx;
+ ctx.orig_args = Args::from_string("cc -c " + dep_args + " foo.c -o foo.o");
+ Util::write_file("foo.c", "");
+
+ const ProcessArgsResult result = process_args(ctx);
+
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string() == "cc");
+ CHECK(result.extra_args_to_hash.to_string() == dep_args);
+ CHECK(result.compiler_args.to_string() == "cc -c " + dep_args);
+}
+
+TEST_CASE("MQ_flag_should_not_be_added_if_run_second_cpp_is_true")
+{
+ TestContext test_context;
+ Context ctx;
+ ctx.orig_args = Args::from_string("cc -c -MD foo.c -MF foo.d -o foo.o");
+ Util::write_file("foo.c", "");
+
+ const ProcessArgsResult result = process_args(ctx);
+
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string() == "cc");
+ CHECK(result.extra_args_to_hash.to_string() == "-MD -MF foo.d");
+ CHECK(result.compiler_args.to_string() == "cc -c -MD -MF foo.d");
+}
+
+TEST_CASE("MQ_flag_should_be_added_if_run_second_cpp_is_false")
+{
+ TestContext test_context;
+ Context ctx;
+ ctx.orig_args = Args::from_string("cc -c -MD foo.c -MF foo.d -o foo.o");
+ Util::write_file("foo.c", "");
+ ctx.config.set_run_second_cpp(false);
+
+ const ProcessArgsResult result = process_args(ctx);
+
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string() == "cc -MD -MF foo.d -MQ foo.o");
+ CHECK(result.extra_args_to_hash.to_string() == "");
+ CHECK(result.compiler_args.to_string() == "cc -c");
+}
+
+TEST_CASE("MF_should_be_added_if_run_second_cpp_is_false")
+{
+ TestContext test_context;
+ Context ctx;
+ ctx.orig_args = Args::from_string("cc -c -MD foo.c -o foo.o");
+ Util::write_file("foo.c", "");
+ ctx.config.set_run_second_cpp(false);
+
+ const ProcessArgsResult result = process_args(ctx);
+
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string() == "cc -MD -MF foo.d -MQ foo.o");
+ CHECK(result.extra_args_to_hash.to_string() == "");
+ CHECK(result.compiler_args.to_string() == "cc -c");
+}
+
+TEST_CASE("MF_should_not_be_added_if_run_second_cpp_is_true")
+{
+ TestContext test_context;
+ Context ctx;
+ ctx.orig_args = Args::from_string("cc -c -MD foo.c -o foo.o");
+ Util::write_file("foo.c", "");
+
+ const ProcessArgsResult result = process_args(ctx);
+
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string() == "cc");
+ CHECK(result.extra_args_to_hash.to_string() == "-MD");
+ CHECK(result.compiler_args.to_string() == "cc -c -MD");
+}
+
+TEST_CASE("equal_sign_after_MF_should_be_removed")
+{
+ TestContext test_context;
+ Context ctx;
+ ctx.orig_args = Args::from_string("cc -c -MF=path foo.c -o foo.o");
+ Util::write_file("foo.c", "");
+
+ const ProcessArgsResult result = process_args(ctx);
+
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string() == "cc");
+ CHECK(result.extra_args_to_hash.to_string() == "-MFpath");
+ CHECK(result.compiler_args.to_string() == "cc -c -MFpath");
+}
+
+TEST_CASE("sysroot_should_be_rewritten_if_basedir_is_used")
+{
+ TestContext test_context;
+
+ Context ctx;
+
+ Util::write_file("foo.c", "");
+ ctx.config.set_base_dir(get_root());
+ std::string arg_string =
+ fmt::format("cc --sysroot={}/foo/bar -c foo.c", ctx.actual_cwd);
+ ctx.orig_args = Args::from_string(arg_string);
+
+ const ProcessArgsResult result = process_args(ctx);
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args[1] == "--sysroot=./foo/bar");
+}
+
+TEST_CASE(
+ "sysroot_with_separate_argument_should_be_rewritten_if_basedir_is_used")
+{
+ TestContext test_context;
+
+ Context ctx;
+
+ Util::write_file("foo.c", "");
+ ctx.config.set_base_dir(get_root());
+ std::string arg_string =
+ fmt::format("cc --sysroot {}/foo -c foo.c", ctx.actual_cwd);
+ ctx.orig_args = Args::from_string(arg_string);
+
+ const ProcessArgsResult result = process_args(ctx);
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args[1] == "--sysroot");
+ CHECK(result.preprocessor_args[2] == "./foo");
+}
+
+TEST_CASE("MF_flag_with_immediate_argument_should_work_as_last_argument")
+{
+ TestContext test_context;
+
+ Context ctx;
+ ctx.orig_args =
+ Args::from_string("cc -c foo.c -o foo.o -MMD -MT bar -MFfoo.d");
+
+ Util::write_file("foo.c", "");
+
+ const ProcessArgsResult result = process_args(ctx);
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string() == "cc");
+ CHECK(result.extra_args_to_hash.to_string() == "-MMD -MT bar -MFfoo.d");
+ CHECK(result.compiler_args.to_string() == "cc -c -MMD -MT bar -MFfoo.d");
+}
+
+TEST_CASE("MT_flag_with_immediate_argument_should_work_as_last_argument")
+{
+ TestContext test_context;
+
+ Context ctx;
+ ctx.orig_args =
+ Args::from_string("cc -c foo.c -o foo.o -MMD -MFfoo.d -MT foo -MTbar");
+
+ Util::write_file("foo.c", "");
+
+ const ProcessArgsResult result = process_args(ctx);
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string() == "cc");
+ CHECK(result.extra_args_to_hash.to_string()
+ == "-MMD -MFfoo.d -MT foo -MTbar");
+ CHECK(result.compiler_args.to_string()
+ == "cc -c -MMD -MFfoo.d -MT foo -MTbar");
+}
+
+TEST_CASE("MQ_flag_with_immediate_argument_should_work_as_last_argument")
+{
+ TestContext test_context;
+
+ Context ctx;
+ ctx.orig_args =
+ Args::from_string("cc -c foo.c -o foo.o -MMD -MFfoo.d -MQ foo -MQbar");
+
+ Util::write_file("foo.c", "");
+
+ const ProcessArgsResult result = process_args(ctx);
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string() == "cc");
+ CHECK(result.extra_args_to_hash.to_string()
+ == "-MMD -MFfoo.d -MQ foo -MQbar");
+ CHECK(result.compiler_args.to_string()
+ == "cc -c -MMD -MFfoo.d -MQ foo -MQbar");
+}
+
+TEST_CASE("MQ_flag_without_immediate_argument_should_not_add_MQobj")
+{
+ TestContext test_context;
+ Context ctx;
+ ctx.orig_args = Args::from_string("gcc -c -MD -MP -MFfoo.d -MQ foo.d foo.c");
+ Util::write_file("foo.c", "");
+
+ const ProcessArgsResult result = process_args(ctx);
+
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string() == "gcc");
+ CHECK(result.extra_args_to_hash.to_string() == "-MD -MP -MFfoo.d -MQ foo.d");
+ CHECK(result.compiler_args.to_string()
+ == "gcc -c -MD -MP -MFfoo.d -MQ foo.d");
+}
+
+TEST_CASE("MT_flag_without_immediate_argument_should_not_add_MTobj")
+{
+ TestContext test_context;
+ Context ctx;
+ ctx.orig_args = Args::from_string("gcc -c -MD -MP -MFfoo.d -MT foo.d foo.c");
+ Util::write_file("foo.c", "");
+
+ const ProcessArgsResult result = process_args(ctx);
+
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string() == "gcc");
+ CHECK(result.extra_args_to_hash.to_string() == "-MD -MP -MFfoo.d -MT foo.d");
+ CHECK(result.compiler_args.to_string()
+ == "gcc -c -MD -MP -MFfoo.d -MT foo.d");
+}
+
+TEST_CASE("MQ_flag_with_immediate_argument_should_not_add_MQobj")
+{
+ TestContext test_context;
+ Context ctx;
+ ctx.orig_args = Args::from_string("gcc -c -MD -MP -MFfoo.d -MQfoo.d foo.c");
+ Util::write_file("foo.c", "");
+
+ const ProcessArgsResult result = process_args(ctx);
+
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string() == "gcc");
+ CHECK(result.extra_args_to_hash.to_string() == "-MD -MP -MFfoo.d -MQfoo.d");
+ CHECK(result.compiler_args.to_string() == "gcc -c -MD -MP -MFfoo.d -MQfoo.d");
+}
+
+TEST_CASE("MT_flag_with_immediate_argument_should_not_add_MQobj")
+{
+ TestContext test_context;
+ Context ctx;
+ ctx.orig_args = Args::from_string("gcc -c -MD -MP -MFfoo.d -MTfoo.d foo.c");
+ Util::write_file("foo.c", "");
+
+ const ProcessArgsResult result = process_args(ctx);
+
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string() == "gcc");
+ CHECK(result.extra_args_to_hash.to_string() == "-MD -MP -MFfoo.d -MTfoo.d");
+ CHECK(result.compiler_args.to_string() == "gcc -c -MD -MP -MFfoo.d -MTfoo.d");
+}
+
+TEST_CASE(
+ "isystem_flag_with_separate_arg_should_be_rewritten_if_basedir_is_used")
+{
+ TestContext test_context;
+
+ Context ctx;
+
+ Util::write_file("foo.c", "");
+ ctx.config.set_base_dir(get_root());
+ std::string arg_string =
+ fmt::format("cc -isystem {}/foo -c foo.c", ctx.actual_cwd);
+ ctx.orig_args = Args::from_string(arg_string);
+
+ const ProcessArgsResult result = process_args(ctx);
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args[2] == "./foo");
+}
+
+TEST_CASE("isystem_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used")
+{
+ TestContext test_context;
+
+ Context ctx;
+
+ Util::write_file("foo.c", "");
+ ctx.config.set_base_dir("/"); // posix
+ // Windows path doesn't work concatenated.
+ std::string cwd = get_posix_path(ctx.actual_cwd);
+ std::string arg_string = fmt::format("cc -isystem{}/foo -c foo.c", cwd);
+ ctx.orig_args = Args::from_string(arg_string);
+
+ const ProcessArgsResult result = process_args(ctx);
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args[1] == "-isystem./foo");
+}
+
+TEST_CASE("I_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used")
+{
+ TestContext test_context;
+
+ Context ctx;
+
+ Util::write_file("foo.c", "");
+ ctx.config.set_base_dir("/"); // posix
+ // Windows path doesn't work concatenated.
+ std::string cwd = get_posix_path(ctx.actual_cwd);
+ std::string arg_string = fmt::format("cc -I{}/foo -c foo.c", cwd);
+ ctx.orig_args = Args::from_string(arg_string);
+
+ const ProcessArgsResult result = process_args(ctx);
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args[1] == "-I./foo");
+}
+
+TEST_CASE("debug_flag_order_with_known_option_first")
+{
+ TestContext test_context;
+ Context ctx;
+ ctx.orig_args = Args::from_string("cc -g1 -gsplit-dwarf foo.c -c");
+ Util::write_file("foo.c", "");
+
+ const ProcessArgsResult result = process_args(ctx);
+
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string() == "cc -g1 -gsplit-dwarf");
+ CHECK(result.extra_args_to_hash.to_string() == "");
+ CHECK(result.compiler_args.to_string() == "cc -g1 -gsplit-dwarf -c");
+}
+
+TEST_CASE("debug_flag_order_with_known_option_last")
+{
+ TestContext test_context;
+ Context ctx;
+ ctx.orig_args = Args::from_string("cc -gsplit-dwarf -g1 foo.c -c");
+ Util::write_file("foo.c", "");
+
+ const ProcessArgsResult result = process_args(ctx);
+
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string() == "cc -gsplit-dwarf -g1");
+ CHECK(result.extra_args_to_hash.to_string() == "");
+ CHECK(result.compiler_args.to_string() == "cc -gsplit-dwarf -g1 -c");
+}
+
+TEST_CASE("options_not_to_be_passed_to_the_preprocessor")
+{
+ TestContext test_context;
+ Context ctx;
+ ctx.orig_args = Args::from_string(
+ "cc -Wa,foo foo.c -g -c -DX -Werror -Xlinker fie -Xlinker,fum -Wno-error");
+ Util::write_file("foo.c", "");
+
+ const ProcessArgsResult result = process_args(ctx);
+
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string() == "cc -g -DX");
+ CHECK(result.extra_args_to_hash.to_string()
+ == "-Wa,foo -Werror -Xlinker fie -Xlinker,fum -Wno-error");
+ CHECK(result.compiler_args.to_string()
+ == "cc -g -Wa,foo -Werror -Xlinker fie -Xlinker,fum -Wno-error -DX -c");
+}
+
+TEST_CASE("cuda_option_file")
+{
+ TestContext test_context;
+ Context ctx;
+ ctx.guessed_compiler = GuessedCompiler::nvcc;
+ ctx.orig_args = Args::from_string("nvcc -optf foo.optf,bar.optf");
+ Util::write_file("foo.c", "");
+ Util::write_file("foo.optf", "-c foo.c -g -Wall -o");
+ Util::write_file("bar.optf", "out -DX");
+
+ const ProcessArgsResult result = process_args(ctx);
+
+ CHECK(!result.error);
+ CHECK(result.preprocessor_args.to_string() == "nvcc -g -Wall -DX");
+ CHECK(result.extra_args_to_hash.to_string() == "");
+ CHECK(result.compiler_args.to_string() == "nvcc -g -Wall -DX -c");
+}
+
+TEST_CASE("-Xclang")
+{
+ TestContext test_context;
+ Context ctx;
+ const std::string common_args =
+ "-Xclang -fno-pch-timestamp"
+ " -Xclang unsupported";
+
+ const std::string extra_args =
+ "-Xclang -emit-pch"
+ " -Xclang -emit-pth";
+
+ const std::string pch_pth_variants =
+ "-Xclang -include-pch pch_path1"
+ " -Xclang -include-pch -Xclang pch_path2"
+ " -Xclang -include-pth pth_path1"
+ " -Xclang -include-pth -Xclang pth_path2";
+
+ ctx.orig_args = Args::from_string("gcc -c foo.c " + common_args + " "
+ + extra_args + " " + pch_pth_variants);
+ Util::write_file("foo.c", "");
+
+ const ProcessArgsResult result = process_args(ctx);
+ CHECK(result.preprocessor_args.to_string()
+ == "gcc " + common_args + " " + pch_pth_variants);
+ CHECK(result.extra_args_to_hash.to_string() == extra_args);
+ CHECK(result.compiler_args.to_string()
+ == "gcc " + common_args + " " + extra_args + " " + pch_pth_variants
+ + " -c");
+}
+
+TEST_CASE("-x")
+{
+ TestContext test_context;
+ Context ctx;
+ Util::write_file("foo.c", "");
+
+ SUBCASE("intel option")
+ {
+ // -xCODE1 (where CODE1 can be e.g. Host or CORE-AVX2, always starting with
+ // an uppercase letter) is an ordinary Intel compiler option, not a language
+ // specification.
+ ctx.orig_args = Args::from_string("gcc -c foo.c -xCODE");
+
+ const ProcessArgsResult result = process_args(ctx);
+ CHECK(result.preprocessor_args.to_string() == "gcc -xCODE");
+ CHECK(result.extra_args_to_hash.to_string() == "");
+ CHECK(result.compiler_args.to_string() == "gcc -xCODE -c");
+ }
+
+ SUBCASE("compile .c as c++ (without space)")
+ {
+ ctx.orig_args = Args::from_string("gcc -xc++ -c foo.c");
+ const ProcessArgsResult result = process_args(ctx);
+ CHECK(ctx.args_info.actual_language == "c++");
+ CHECK(result.preprocessor_args.to_string() == "gcc -x c++");
+ CHECK(result.extra_args_to_hash.to_string() == "");
+ CHECK(result.compiler_args.to_string() == "gcc -x c++ -c");
+ }
+
+ SUBCASE("compile .c as c++ (with space)")
+ {
+ ctx.orig_args = Args::from_string("gcc -x c++ -c foo.c");
+ const ProcessArgsResult result = process_args(ctx);
+ CHECK(ctx.args_info.actual_language == "c++");
+ CHECK(result.preprocessor_args.to_string() == "gcc -x c++");
+ CHECK(result.extra_args_to_hash.to_string() == "");
+ CHECK(result.compiler_args.to_string() == "gcc -x c++ -c");
+ }
+
+ SUBCASE("compile .c as c++ (file first, no effect)")
+ {
+ ctx.orig_args = Args::from_string("gcc -c foo.c -x c++");
+ const ProcessArgsResult result = process_args(ctx);
+ CHECK(ctx.args_info.actual_language == "c");
+ CHECK(result.preprocessor_args.to_string() == "gcc");
+ CHECK(result.extra_args_to_hash.to_string() == "");
+ CHECK(result.compiler_args.to_string() == "gcc -c");
+ }
+
+ SUBCASE("unknown -x option (lowercase)")
+ {
+ ctx.orig_args = Args::from_string("gcc -x unsupported_language -c foo.c");
+ const ProcessArgsResult result = process_args(ctx);
+ CHECK(result.error == Statistic::unsupported_source_language);
+ CHECK(ctx.args_info.actual_language == "");
+ }
+
+ SUBCASE("UNKNOWN -x option (uppercase)")
+ {
+ ctx.orig_args = Args::from_string("gcc -x UNSUPPORTED_LANGUGAGE -c foo.c");
+ const ProcessArgsResult result = process_args(ctx);
+ CHECK(result.error == Statistic::unsupported_source_language);
+ CHECK(ctx.args_info.actual_language == "");
+ }
+
+ SUBCASE("missing param")
+ {
+ ctx.orig_args = Args::from_string("gcc -c foo.c -x");
+ const ProcessArgsResult result = process_args(ctx);
+ CHECK(result.error == Statistic::bad_compiler_arguments);
+ CHECK(ctx.args_info.actual_language == "");
+ }
+}
+
+TEST_SUITE_END();
+++ /dev/null
-// 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
-
-// This file contains tests for the functions operating on struct args.
-
-#include "../src/ccache.h"
-#include "framework.h"
-#include "util.h"
-
-TEST_SUITE(args)
-
-TEST(args_init_empty)
-{
- struct args *args = args_init(0, NULL);
- CHECK(args);
- CHECK_INT_EQ(0, args->argc);
- CHECK(!args->argv[0]);
- args_free(args);
-}
-
-TEST(args_init_populated)
-{
- char *argv[] = {"first", "second"};
- struct args *args = args_init(2, argv);
- CHECK(args);
- CHECK_INT_EQ(2, args->argc);
- CHECK_STR_EQ("first", args->argv[0]);
- CHECK_STR_EQ("second", args->argv[1]);
- CHECK(!args->argv[2]);
- args_free(args);
-}
-
-TEST(args_init_from_string)
-{
- struct args *args = args_init_from_string("first second\tthird\nfourth");
- CHECK(args);
- CHECK_INT_EQ(4, args->argc);
- CHECK_STR_EQ("first", args->argv[0]);
- CHECK_STR_EQ("second", args->argv[1]);
- CHECK_STR_EQ("third", args->argv[2]);
- CHECK_STR_EQ("fourth", args->argv[3]);
- CHECK(!args->argv[4]);
- args_free(args);
-}
-
-TEST(args_init_from_gcc_atfile)
-{
- struct args *args;
- const char *argtext =
- "first\rsec\\\tond\tthi\\\\rd\nfourth \tfif\\ th \"si'x\\\" th\""
- " 'seve\nth'\\";
-
- create_file("gcc_atfile", argtext);
-
- args = args_init_from_gcc_atfile("gcc_atfile");
- CHECK(args);
- CHECK_INT_EQ(7, args->argc);
- CHECK_STR_EQ("first", args->argv[0]);
- CHECK_STR_EQ("sec\tond", args->argv[1]);
- CHECK_STR_EQ("thi\\rd", args->argv[2]);
- CHECK_STR_EQ("fourth", args->argv[3]);
- CHECK_STR_EQ("fif th", args->argv[4]);
- CHECK_STR_EQ("si'x\" th", args->argv[5]);
- CHECK_STR_EQ("seve\nth", args->argv[6]);
- CHECK(!args->argv[7]);
- args_free(args);
-}
-
-TEST(args_copy)
-{
- struct args *args1 = args_init_from_string("foo");
- struct args *args2 = args_copy(args1);
- CHECK_ARGS_EQ_FREE12(args1, args2);
-}
-
-TEST(args_add)
-{
- struct args *args = args_init_from_string("first");
- CHECK_INT_EQ(1, args->argc);
- args_add(args, "second");
- CHECK_INT_EQ(2, args->argc);
- CHECK_STR_EQ("second", args->argv[1]);
- CHECK(!args->argv[2]);
- args_free(args);
-}
-
-TEST(args_extend)
-{
- struct args *args1 = args_init_from_string("first");
- struct args *args2 = args_init_from_string("second third");
- CHECK_INT_EQ(1, args1->argc);
- args_extend(args1, args2);
- CHECK_INT_EQ(3, args1->argc);
- CHECK_STR_EQ("second", args1->argv[1]);
- CHECK_STR_EQ("third", args1->argv[2]);
- CHECK(!args1->argv[3]);
- args_free(args1);
- args_free(args2);
-}
-
-TEST(args_pop)
-{
- struct args *args = args_init_from_string("first second third");
- args_pop(args, 2);
- CHECK_INT_EQ(1, args->argc);
- CHECK_STR_EQ("first", args->argv[0]);
- CHECK(!args->argv[1]);
- args_free(args);
-}
-
-TEST(args_set)
-{
- struct args *args = args_init_from_string("first second third");
- args_set(args, 1, "2nd");
- CHECK_INT_EQ(3, args->argc);
- CHECK_STR_EQ("first", args->argv[0]);
- CHECK_STR_EQ("2nd", args->argv[1]);
- CHECK_STR_EQ("third", args->argv[2]);
- CHECK(!args->argv[3]);
- args_free(args);
-}
-
-TEST(args_remove_first)
-{
- struct args *args1 = args_init_from_string("first second third");
- struct args *args2 = args_init_from_string("second third");
- args_remove_first(args1);
- CHECK_ARGS_EQ_FREE12(args1, args2);
-}
-
-TEST(args_add_prefix)
-{
- struct args *args1 = args_init_from_string("second third");
- struct args *args2 = args_init_from_string("first second third");
- args_add_prefix(args1, "first");
- CHECK_ARGS_EQ_FREE12(args1, args2);
-}
-
-TEST(args_strip)
-{
- struct args *args1 = args_init_from_string("first xsecond third xfourth");
- struct args *args2 = args_init_from_string("first third");
- args_strip(args1, "x");
- CHECK_ARGS_EQ_FREE12(args1, args2);
-}
-
-TEST(args_to_string)
-{
- struct args *args = args_init_from_string("first second");
- CHECK_STR_EQ_FREE2("first second", args_to_string(args));
- args_free(args);
-}
-
-TEST(args_insert)
-{
- struct args *args = args_init_from_string("first second third fourth fifth");
-
- struct args *src1 = args_init_from_string("alpha beta gamma");
- struct args *src2 = args_init_from_string("one");
- struct args *src3 = args_init_from_string("");
- struct args *src4 = args_init_from_string("alpha beta gamma");
- struct args *src5 = args_init_from_string("one");
- struct args *src6 = args_init_from_string("");
-
- args_insert(args, 2, src1, true);
- CHECK_STR_EQ_FREE2("first second alpha beta gamma fourth fifth",
- args_to_string(args));
- CHECK_INT_EQ(7, args->argc);
- args_insert(args, 2, src2, true);
- CHECK_STR_EQ_FREE2("first second one beta gamma fourth fifth",
- args_to_string(args));
- CHECK_INT_EQ(7, args->argc);
- args_insert(args, 2, src3, true);
- CHECK_STR_EQ_FREE2("first second beta gamma fourth fifth",
- args_to_string(args));
- CHECK_INT_EQ(6, args->argc);
-
- args_insert(args, 1, src4, false);
- CHECK_STR_EQ_FREE2("first alpha beta gamma second beta gamma fourth fifth",
- args_to_string(args));
- CHECK_INT_EQ(9, args->argc);
- args_insert(args, 1, src5, false);
- CHECK_STR_EQ_FREE2(
- "first one alpha beta gamma second beta gamma fourth fifth",
- args_to_string(args));
- CHECK_INT_EQ(10, args->argc);
- args_insert(args, 1, src6, false);
- CHECK_STR_EQ_FREE2(
- "first one alpha beta gamma second beta gamma fourth fifth",
- args_to_string(args));
- CHECK_INT_EQ(10, args->argc);
-
- args_free(args);
-}
-
-TEST_SUITE_END
+++ /dev/null
-// 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
-
-// This file contains tests for the processing of compiler arguments.
-
-#include "../src/ccache.h"
-#include "../src/conf.h"
-#include "framework.h"
-#include "util.h"
-
-extern struct conf *conf;
-
-static char *
-get_root(void)
-{
-#ifndef _WIN32
- return x_strdup("/");
-#else
- char volume[4]; // "C:\"
- GetVolumePathName(get_cwd(), volume, sizeof(volume));
- return x_strdup(volume);
-#endif
-}
-
-static char *
-get_posix_path(char *path)
-{
-#ifndef _WIN32
- return x_strdup(path);
-#else
- char *posix;
- char *p;
-
- // /-escape volume.
- if (path[0] >= 'A' && path[0] <= 'Z' && path[1] == ':') {
- posix = format("/%s", path);
- } else {
- posix = x_strdup(path);
- }
- // Convert slashes.
- for (p = posix; *p; p++) {
- if (*p == '\\') {
- *p = '/';
- }
- }
- return posix;
-#endif
-}
-
-TEST_SUITE(argument_processing)
-
-TEST(dash_E_should_result_in_called_for_preprocessing)
-{
- struct args *orig = args_init_from_string("cc -c foo.c -E");
- struct args *preprocessed, *compiler;
-
- create_file("foo.c", "");
- CHECK(!cc_process_args(orig, &preprocessed, NULL, &compiler));
- CHECK_INT_EQ(1, stats_get_pending(STATS_PREPROCESSING));
-
- args_free(orig);
-}
-
-TEST(dash_M_should_be_unsupported)
-{
- struct args *orig = args_init_from_string("cc -c foo.c -M");
- struct args *preprocessed, *compiler;
-
- create_file("foo.c", "");
- CHECK(!cc_process_args(orig, &preprocessed, NULL, &compiler));
- CHECK_INT_EQ(1, stats_get_pending(STATS_UNSUPPORTED_OPTION));
-
- args_free(orig);
-}
-
-TEST(dependency_flags_should_only_be_sent_to_the_preprocessor)
-{
-#define CMD \
- "cc -MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 -MQ mq1 -MQ mq2" \
- " -Wp,-MD,wpmd -Wp,-MMD,wpmmd -Wp,-MP -Wp,-MT,wpmt -Wp,-MQ,wpmq -Wp,-MF,wpf"
- struct args *orig = args_init_from_string(CMD " -c foo.c -o foo.o");
- struct args *exp_cpp = args_init_from_string(CMD);
-#undef CMD
- struct args *exp_cc = args_init_from_string("cc -c");
- struct args *act_cpp = NULL, *act_cc = NULL;
- create_file("foo.c", "");
-
- CHECK(cc_process_args(orig, &act_cpp, NULL, &act_cc));
- CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
- CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
-
- args_free(orig);
-}
-
-TEST(cpp_only_flags_to_preprocessor_if_run_second_cpp_is_false)
-{
-#define CMD \
- "cc -I. -idirafter . -iframework. -imacros . -imultilib ." \
- " -include test.h -include-pch test.pch -iprefix . -iquote ." \
- " -isysroot . -isystem . -iwithprefix . -iwithprefixbefore ." \
- " -DTEST_MACRO -DTEST_MACRO2=1 -F. -trigraphs -fworking-directory" \
- " -fno-working-directory -MD -MMD -MP -MF foo.d -MT mt1 -MT mt2" \
- " -MQ mq1 -MQ mq2 -Wp,-MD,wpmd -Wp,-MMD,wpmmd -Wp,-MP -Wp,-MT,wpmt" \
- " -Wp,-MQ,wpmq -Wp,-MF,wpf"
- struct args *orig = args_init_from_string(CMD " -c foo.c -o foo.o");
- struct args *exp_cpp = args_init_from_string(CMD);
-#undef CMD
- struct args *exp_cc = args_init_from_string("cc -c");
- struct args *act_cpp = NULL, *act_cc = NULL;
- create_file("foo.c", "");
-
- conf->run_second_cpp = false;
- CHECK(cc_process_args(orig, &act_cpp, NULL, &act_cc));
- CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
- CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
-
- args_free(orig);
-}
-
-TEST(cpp_only_flags_to_preprocessor_and_compiler_if_run_second_cpp_is_true)
-{
-#define CMD \
- "cc -I. -idirafter . -iframework. -imacros . -imultilib ." \
- " -include test.h -include-pch test.pch -iprefix . -iquote ." \
- " -isysroot . -isystem . -iwithprefix . -iwithprefixbefore ." \
- " -DTEST_MACRO -DTEST_MACRO2=1 -F. -trigraphs -fworking-directory" \
- " -fno-working-directory"
-#define DEP_OPTS \
- " -MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 " \
- " -MQ mq1 -MQ mq2 -Wp,-MD,wpmd -Wp,-MMD,wpmmd"
- struct args *orig = args_init_from_string(CMD DEP_OPTS " -c foo.c -o foo.o");
- struct args *exp_cpp = args_init_from_string(CMD DEP_OPTS);
- struct args *exp_cc = args_init_from_string(CMD " -c");
-#undef CMD
- struct args *act_cpp = NULL, *act_cc = NULL;
- create_file("foo.c", "");
-
- conf->run_second_cpp = true;
- CHECK(cc_process_args(orig, &act_cpp, NULL, &act_cc));
- CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
- CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
-
- args_free(orig);
-}
-
-TEST(dependency_flags_that_take_an_argument_should_not_require_space_delimiter)
-{
- struct args *orig = args_init_from_string(
- "cc -c -MMD -MFfoo.d -MT mt -MTmt -MQmq foo.c -o foo.o");
- struct args *exp_cpp = args_init_from_string(
- "cc -MMD -MFfoo.d -MT mt -MTmt -MQmq");
- struct args *exp_cc = args_init_from_string("cc -c");
- struct args *act_cpp = NULL, *act_cc = NULL;
- create_file("foo.c", "");
-
- CHECK(cc_process_args(orig, &act_cpp, NULL, &act_cc));
- CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
- CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
-
- args_free(orig);
-}
-
-TEST(equal_sign_after_MF_should_be_removed)
-{
- struct args *orig = args_init_from_string("cc -c -MF=path foo.c -o foo.o");
- struct args *exp_cpp = args_init_from_string("cc -MFpath");
- struct args *exp_cc = args_init_from_string("cc -c");
- struct args *act_cpp = NULL, *act_cc = NULL;
- create_file("foo.c", "");
-
- CHECK(cc_process_args(orig, &act_cpp, NULL, &act_cc));
- CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
- CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
-
- args_free(orig);
-}
-
-TEST(sysroot_should_be_rewritten_if_basedir_is_used)
-{
- extern char *current_working_dir;
- char *arg_string;
- struct args *orig;
- struct args *act_cpp = NULL, *act_cc = NULL;
-
- create_file("foo.c", "");
- free(conf->base_dir);
- conf->base_dir = get_root();
- current_working_dir = get_cwd();
- arg_string = format("cc --sysroot=%s/foo/bar -c foo.c", current_working_dir);
- orig = args_init_from_string(arg_string);
- free(arg_string);
-
- CHECK(cc_process_args(orig, &act_cpp, NULL, &act_cc));
- CHECK_STR_EQ(act_cpp->argv[1], "--sysroot=./foo/bar");
-
- args_free(orig);
- args_free(act_cpp);
- args_free(act_cc);
-}
-
-TEST(sysroot_with_separate_argument_should_be_rewritten_if_basedir_is_used)
-{
- extern char *current_working_dir;
- char *arg_string;
- struct args *orig;
- struct args *act_cpp = NULL, *act_cc = NULL;
-
- create_file("foo.c", "");
- free(conf->base_dir);
- conf->base_dir = get_root();
- current_working_dir = get_cwd();
- arg_string = format("cc --sysroot %s/foo -c foo.c", current_working_dir);
- orig = args_init_from_string(arg_string);
- free(arg_string);
-
- CHECK(cc_process_args(orig, &act_cpp, NULL, &act_cc));
- CHECK_STR_EQ(act_cpp->argv[1], "--sysroot");
- CHECK_STR_EQ(act_cpp->argv[2], "./foo");
-
- args_free(orig);
- args_free(act_cpp);
- args_free(act_cc);
-}
-
-TEST(MF_flag_with_immediate_argument_should_work_as_last_argument)
-{
- struct args *orig = args_init_from_string(
- "cc -c foo.c -o foo.o -MMD -MT bar -MFfoo.d");
- struct args *exp_cpp = args_init_from_string(
- "cc -MMD -MT bar -MFfoo.d");
- struct args *exp_cc = args_init_from_string("cc -c");
- struct args *act_cpp = NULL, *act_cc = NULL;
- create_file("foo.c", "");
-
- CHECK(cc_process_args(orig, &act_cpp, NULL, &act_cc));
- CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
- CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
-
- args_free(orig);
-}
-
-TEST(MT_flag_with_immediate_argument_should_work_as_last_argument)
-{
- struct args *orig = args_init_from_string(
- "cc -c foo.c -o foo.o -MMD -MFfoo.d -MT foo -MTbar");
- struct args *exp_cpp = args_init_from_string(
- "cc -MMD -MFfoo.d -MT foo -MTbar");
- struct args *exp_cc = args_init_from_string("cc -c");
- struct args *act_cpp = NULL, *act_cc = NULL;
- create_file("foo.c", "");
-
- CHECK(cc_process_args(orig, &act_cpp, NULL, &act_cc));
- CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
- CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
-
- args_free(orig);
-}
-
-TEST(MQ_flag_with_immediate_argument_should_work_as_last_argument)
-{
- struct args *orig = args_init_from_string(
- "cc -c foo.c -o foo.o -MMD -MFfoo.d -MQ foo -MQbar");
- struct args *exp_cpp = args_init_from_string(
- "cc -MMD -MFfoo.d -MQ foo -MQbar");
- struct args *exp_cc = args_init_from_string("cc -c");
- struct args *act_cpp = NULL, *act_cc = NULL;
- create_file("foo.c", "");
-
- CHECK(cc_process_args(orig, &act_cpp, NULL, &act_cc));
- CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
- CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
-
- args_free(orig);
-}
-
-TEST(MQ_flag_without_immediate_argument_should_not_add_MQobj)
-{
- struct args *orig = args_init_from_string(
- "gcc -c -MD -MP -MFfoo.d -MQ foo.d foo.c");
- struct args *exp_cpp = args_init_from_string(
- "gcc -MD -MP -MFfoo.d -MQ foo.d");
- struct args *exp_cc = args_init_from_string("gcc -c");
- struct args *act_cpp = NULL, *act_cc = NULL;
- create_file("foo.c", "");
-
- CHECK(cc_process_args(orig, &act_cpp, NULL, &act_cc));
- CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
- CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
-
- args_free(orig);
-}
-
-TEST(MT_flag_without_immediate_argument_should_not_add_MTobj)
-{
- struct args *orig = args_init_from_string(
- "gcc -c -MD -MP -MFfoo.d -MT foo.d foo.c");
- struct args *exp_cpp = args_init_from_string(
- "gcc -MD -MP -MFfoo.d -MT foo.d");
- struct args *exp_cc = args_init_from_string("gcc -c");
- struct args *act_cpp = NULL, *act_cc = NULL;
- create_file("foo.c", "");
-
- CHECK(cc_process_args(orig, &act_cpp, NULL, &act_cc));
- CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
- CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
-
- args_free(orig);
-}
-
-TEST(MQ_flag_with_immediate_argument_should_not_add_MQobj)
-{
- struct args *orig = args_init_from_string(
- "gcc -c -MD -MP -MFfoo.d -MQfoo.d foo.c");
- struct args *exp_cpp = args_init_from_string(
- "gcc -MD -MP -MFfoo.d -MQfoo.d");
- struct args *exp_cc = args_init_from_string("gcc -c");
- struct args *act_cpp = NULL, *act_cc = NULL;
- create_file("foo.c", "");
-
- CHECK(cc_process_args(orig, &act_cpp, NULL, &act_cc));
- CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
- CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
-
- args_free(orig);
-}
-
-TEST(MT_flag_with_immediate_argument_should_not_add_MQobj)
-{
- struct args *orig = args_init_from_string(
- "gcc -c -MD -MP -MFfoo.d -MTfoo.d foo.c");
- struct args *exp_cpp = args_init_from_string(
- "gcc -MD -MP -MFfoo.d -MTfoo.d");
- struct args *exp_cc = args_init_from_string("gcc -c");
- struct args *act_cpp = NULL, *act_cc = NULL;
- create_file("foo.c", "");
-
- CHECK(cc_process_args(orig, &act_cpp, NULL, &act_cc));
- CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
- CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
-
- args_free(orig);
-}
-
-TEST(isystem_flag_with_separate_arg_should_be_rewritten_if_basedir_is_used)
-{
- extern char *current_working_dir;
- char *arg_string;
- struct args *orig;
- struct args *act_cpp = NULL, *act_cc = NULL;
-
- create_file("foo.c", "");
- free(conf->base_dir);
- conf->base_dir = get_root();
- current_working_dir = get_cwd();
- arg_string = format("cc -isystem %s/foo -c foo.c", current_working_dir);
- orig = args_init_from_string(arg_string);
- free(arg_string);
-
- CHECK(cc_process_args(orig, &act_cpp, NULL, &act_cc));
- CHECK_STR_EQ("./foo", act_cpp->argv[2]);
-
- args_free(orig);
- args_free(act_cpp);
- args_free(act_cc);
-}
-
-TEST(isystem_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used)
-{
- extern char *current_working_dir;
- char *cwd;
- char *arg_string;
- struct args *orig;
- struct args *act_cpp = NULL, *act_cc = NULL;
-
- create_file("foo.c", "");
- free(conf->base_dir);
- conf->base_dir = x_strdup("/"); // posix
- current_working_dir = get_cwd();
- // Windows path doesn't work concatenated.
- cwd = get_posix_path(current_working_dir);
- arg_string = format("cc -isystem%s/foo -c foo.c", cwd);
- orig = args_init_from_string(arg_string);
- free(arg_string);
-
- CHECK(cc_process_args(orig, &act_cpp, NULL, &act_cc));
- CHECK_STR_EQ("-isystem./foo", act_cpp->argv[1]);
-
- free(cwd);
- args_free(orig);
- args_free(act_cpp);
- args_free(act_cc);
-}
-
-TEST(I_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used)
-{
- extern char *current_working_dir;
- char *cwd;
- char *arg_string;
- struct args *orig;
- struct args *act_cpp = NULL, *act_cc = NULL;
-
- create_file("foo.c", "");
- free(conf->base_dir);
- conf->base_dir = x_strdup("/"); // posix
- current_working_dir = get_cwd();
- // Windows path doesn't work concatenated.
- cwd = get_posix_path(current_working_dir);
- arg_string = format("cc -I%s/foo -c foo.c", cwd);
- orig = args_init_from_string(arg_string);
- free(arg_string);
-
- CHECK(cc_process_args(orig, &act_cpp, NULL, &act_cc));
- CHECK_STR_EQ("-I./foo", act_cpp->argv[1]);
-
- free(cwd);
- args_free(orig);
- args_free(act_cpp);
- args_free(act_cc);
-}
-
-TEST(debug_flag_order_with_known_option_first)
-{
- struct args *orig = args_init_from_string("cc -g1 -gsplit-dwarf foo.c -c");
- struct args *exp_cpp = args_init_from_string("cc -g1 -gsplit-dwarf");
- struct args *exp_cc = args_init_from_string("cc -g1 -gsplit-dwarf -c");
- struct args *act_cpp = NULL;
- struct args *act_cc = NULL;
-
- create_file("foo.c", "");
- CHECK(cc_process_args(orig, &act_cpp, NULL, &act_cc));
- CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
- CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
-
- args_free(orig);
-}
-
-TEST(debug_flag_order_with_known_option_last)
-{
- struct args *orig = args_init_from_string("cc -gsplit-dwarf -g1 foo.c -c");
- struct args *exp_cpp = args_init_from_string("cc -gsplit-dwarf -g1");
- struct args *exp_cc = args_init_from_string("cc -gsplit-dwarf -g1 -c");
- struct args *act_cpp = NULL;
- struct args *act_cc = NULL;
-
- create_file("foo.c", "");
- CHECK(cc_process_args(orig, &act_cpp, NULL, &act_cc));
- CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
- CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
-
- args_free(orig);
-}
-
-TEST(options_not_to_be_passed_to_the_preprocessor)
-{
- struct args *orig = args_init_from_string(
- "cc -Wa,foo foo.c -g -c -DX -Werror -Xlinker fie -Xlinker,fum -Wno-error");
- struct args *exp_cpp = args_init_from_string("cc -g -DX");
- struct args *exp_extra =
- args_init_from_string(
- " -Wa,foo -Werror -Xlinker fie -Xlinker,fum -Wno-error");
- struct args *exp_cc = args_init_from_string(
- "cc -g -Wa,foo -Werror -Xlinker fie -Xlinker,fum -Wno-error -DX -c");
- struct args *act_cpp = NULL;
- struct args *act_extra = NULL;
- struct args *act_cc = NULL;
-
- create_file("foo.c", "");
- CHECK(cc_process_args(orig, &act_cpp, &act_extra, &act_cc));
- CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
- CHECK_ARGS_EQ_FREE12(exp_extra, act_extra);
- CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
-
- args_free(orig);
-}
-
-TEST_SUITE_END
--- /dev/null
+// 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 "TestUtil.hpp"
+#include "ccache.hpp"
+
+#include "third_party/doctest.h"
+#include "third_party/nonstd/optional.hpp"
+
+#ifdef MYNAME
+# define CCACHE_NAME MYNAME
+#else
+# define CCACHE_NAME "ccache"
+#endif
+
+TEST_SUITE_BEGIN("ccache");
+
+// Wraps find_compiler in a test friendly interface.
+static std::string
+helper(const char* args,
+ const char* config_compiler,
+ const char* find_executable_return_string = nullptr)
+{
+ const auto find_executable_stub =
+ [&find_executable_return_string](
+ const Context&, const std::string& s, const std::string&) -> std::string {
+ return find_executable_return_string ? find_executable_return_string
+ : "resolved_" + s;
+ };
+
+ Context ctx;
+ ctx.config.set_compiler(config_compiler);
+ ctx.orig_args = Args::from_string(args);
+ find_compiler(ctx, find_executable_stub);
+ return ctx.orig_args.to_string();
+}
+
+TEST_CASE("find_compiler")
+{
+ SUBCASE("no config")
+ {
+ // In case the first parameter is gcc it must be a link to ccache, so
+ // find_compiler should call find_executable to locate the next best "gcc"
+ // and return that value.
+ CHECK(helper("gcc", "") == "resolved_gcc");
+ CHECK(helper("relative/gcc", "") == "resolved_gcc");
+ CHECK(helper("/absolute/gcc", "") == "resolved_gcc");
+
+ // In case the first parameter is ccache, resolve the second parameter to
+ // the real compiler unless it's a relative or absolute path.
+ CHECK(helper(CCACHE_NAME " gcc", "") == "resolved_gcc");
+ CHECK(helper(CCACHE_NAME " rel/gcc", "") == "rel/gcc");
+ CHECK(helper(CCACHE_NAME " /abs/gcc", "") == "/abs/gcc");
+
+ CHECK(helper("rel/" CCACHE_NAME " gcc", "") == "resolved_gcc");
+ CHECK(helper("rel/" CCACHE_NAME " rel/gcc", "") == "rel/gcc");
+ CHECK(helper("rel/" CCACHE_NAME " /abs/gcc", "") == "/abs/gcc");
+
+ CHECK(helper("/abs/" CCACHE_NAME " gcc", "") == "resolved_gcc");
+ CHECK(helper("/abs/" CCACHE_NAME " rel/gcc", "") == "rel/gcc");
+ CHECK(helper("/abs/" CCACHE_NAME " /abs/gcc", "") == "/abs/gcc");
+
+ // If gcc points back to ccache throw, unless either ccache or gcc is a
+ // relative or absolute path.
+ CHECK_THROWS(helper(CCACHE_NAME " gcc", "", CCACHE_NAME));
+ CHECK(helper(CCACHE_NAME " rel/gcc", "", CCACHE_NAME) == "rel/gcc");
+ CHECK(helper(CCACHE_NAME " /abs/gcc", "", CCACHE_NAME) == "/abs/gcc");
+
+ CHECK_THROWS(helper("rel/" CCACHE_NAME " gcc", "", CCACHE_NAME));
+ CHECK(helper("rel/" CCACHE_NAME " rel/gcc", "", CCACHE_NAME) == "rel/gcc");
+ CHECK(helper("rel/" CCACHE_NAME " /a/gcc", "", CCACHE_NAME) == "/a/gcc");
+
+ CHECK_THROWS(helper("/abs/" CCACHE_NAME " gcc", "", CCACHE_NAME));
+ CHECK(helper("/abs/" CCACHE_NAME " rel/gcc", "", CCACHE_NAME) == "rel/gcc");
+ CHECK(helper("/abs/" CCACHE_NAME " /a/gcc", "", CCACHE_NAME) == "/a/gcc");
+
+ // If compiler is not found then throw, unless the compiler has a relative
+ // or absolute path.
+ CHECK_THROWS(helper(CCACHE_NAME " gcc", "", ""));
+ CHECK(helper(CCACHE_NAME " rel/gcc", "", "") == "rel/gcc");
+ CHECK(helper(CCACHE_NAME " /abs/gcc", "", "") == "/abs/gcc");
+
+ CHECK_THROWS(helper("rel/" CCACHE_NAME " gcc", "", ""));
+ CHECK(helper("rel/" CCACHE_NAME " rel/gcc", "", "") == "rel/gcc");
+ CHECK(helper("rel/" CCACHE_NAME " /abs/gcc", "", "") == "/abs/gcc");
+
+ CHECK_THROWS(helper("/abs/" CCACHE_NAME " gcc", "", ""));
+ CHECK(helper("/abs/" CCACHE_NAME " rel/gcc", "", "") == "rel/gcc");
+ CHECK(helper("/abs/" CCACHE_NAME " /abs/gcc", "", "") == "/abs/gcc");
+ }
+
+ SUBCASE("config")
+ {
+ // In case the first parameter is gcc it must be a link to ccache so use
+ // config value instead. Don't resolve config if it's a relative or absolute
+ // path.
+ CHECK(helper("gcc", "config") == "resolved_config");
+ CHECK(helper("gcc", "rel/config") == "rel/config");
+ CHECK(helper("gcc", "/abs/config") == "/abs/config");
+ CHECK(helper("rel/gcc", "config") == "resolved_config");
+ CHECK(helper("rel/gcc", "rel/config") == "rel/config");
+ CHECK(helper("rel/gcc", "/abs/config") == "/abs/config");
+ CHECK(helper("/abs/gcc", "config") == "resolved_config");
+ CHECK(helper("/abs/gcc", "rel/config") == "rel/config");
+ CHECK(helper("/abs/gcc", "/abs/config") == "/abs/config");
+
+ // In case the first parameter is ccache, use the configuration value. Don't
+ // resolve configuration value if it's a relative or absolute path.
+ CHECK(helper(CCACHE_NAME " gcc", "config") == "resolved_config");
+ CHECK(helper(CCACHE_NAME " gcc", "rel/config") == "rel/config");
+ CHECK(helper(CCACHE_NAME " gcc", "/abs/config") == "/abs/config");
+ CHECK(helper(CCACHE_NAME " rel/gcc", "config") == "resolved_config");
+ CHECK(helper(CCACHE_NAME " /abs/gcc", "config") == "resolved_config");
+
+ // Same as above with relative path to ccache.
+ CHECK(helper("r/" CCACHE_NAME " gcc", "conf") == "resolved_conf");
+ CHECK(helper("r/" CCACHE_NAME " gcc", "rel/conf") == "rel/conf");
+ CHECK(helper("r/" CCACHE_NAME " gcc", "/abs/conf") == "/abs/conf");
+ CHECK(helper("r/" CCACHE_NAME " rel/gcc", "conf") == "resolved_conf");
+ CHECK(helper("r/" CCACHE_NAME " /abs/gcc", "conf") == "resolved_conf");
+
+ // Same as above with absolute path to ccache.
+ CHECK(helper("/a/" CCACHE_NAME " gcc", "conf") == "resolved_conf");
+ CHECK(helper("/a/" CCACHE_NAME " gcc", "rel/conf") == "rel/conf");
+ CHECK(helper("/a/" CCACHE_NAME " gcc", "/a/conf") == "/a/conf");
+ CHECK(helper("/a/" CCACHE_NAME " rel/gcc", "conf") == "resolved_conf");
+ CHECK(helper("/a/" CCACHE_NAME " /abs/gcc", "conf") == "resolved_conf");
+ }
+}
+
+TEST_CASE("rewrite_dep_file_paths")
+{
+ Context ctx;
+
+ const auto cwd = ctx.actual_cwd;
+ ctx.has_absolute_include_headers = true;
+
+ const auto content =
+ fmt::format("foo.o: bar.c {0}/bar.h \\\n {1}/fie.h {0}/fum.h\n",
+ cwd,
+ Util::dir_name(cwd));
+
+ SUBCASE("Base directory not in dep file content")
+ {
+ ctx.config.set_base_dir("/foo/bar");
+ CHECK(!rewrite_dep_file_paths(ctx, ""));
+ CHECK(!rewrite_dep_file_paths(ctx, content));
+ }
+
+ SUBCASE("Base directory in dep file content but not matching")
+ {
+ ctx.config.set_base_dir(fmt::format("{}/other", Util::dir_name(cwd)));
+ CHECK(!rewrite_dep_file_paths(ctx, ""));
+ CHECK(!rewrite_dep_file_paths(ctx, content));
+ }
+
+ SUBCASE("Absolute paths under base directory rewritten")
+ {
+ ctx.config.set_base_dir(cwd);
+ const auto actual = rewrite_dep_file_paths(ctx, content);
+ const auto expected = fmt::format(
+ "foo.o: bar.c ./bar.h \\\n {}/fie.h ./fum.h\n", Util::dir_name(cwd));
+ REQUIRE(actual);
+ CHECK(*actual == expected);
+ }
+}
+
+TEST_SUITE_END();
+++ /dev/null
-// 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
-
-// This file contains tests for the compopt_* functions.
-
-#include "../src/ccache.h"
-#include "../src/compopt.h"
-#include "framework.h"
-
-TEST_SUITE(compopt)
-
-TEST(option_table_should_be_sorted)
-{
- bool compopt_verify_sortedness_and_flags(void);
- CHECK(compopt_verify_sortedness_and_flags());
-}
-
-TEST(dash_I_affects_cpp)
-{
- CHECK(compopt_affects_cpp("-I"));
- CHECK(!compopt_affects_cpp("-Ifoo"));
-}
-
-TEST(compopt_short)
-{
- CHECK(compopt_short(compopt_affects_cpp, "-Ifoo"));
- CHECK(!compopt_short(compopt_affects_cpp, "-include"));
-}
-
-TEST(dash_V_doesnt_affect_cpp)
-{
- CHECK(!compopt_affects_cpp("-V"));
-}
-
-TEST(dash_doesntexist_doesnt_affect_cpp)
-{
- CHECK(!compopt_affects_cpp("-doesntexist"));
-}
-
-TEST(dash_MM_too_hard)
-{
- CHECK(compopt_too_hard("-MM"));
-}
-
-TEST(dash_save_temps_too_hard)
-{
- CHECK(compopt_too_hard("-save-temps"));
-}
-
-TEST(dash_save_temps_cwd_too_hard)
-{
- CHECK(compopt_too_hard("-save-temps=cwd"));
-}
-
-TEST(dash_save_temps_obj_too_hard)
-{
- CHECK(compopt_too_hard("-save-temps=obj"));
-}
-
-TEST(dash_MD_not_too_hard)
-{
- CHECK(!compopt_too_hard("-MD"));
-}
-
-TEST(dash_fprofile_arcs_not_too_hard)
-{
- CHECK(!compopt_too_hard("-fprofile-arcs"));
-}
-
-TEST(dash_ftest_coverage_not_too_hard)
-{
- CHECK(!compopt_too_hard("-ftest-coverage"));
-}
-
-TEST(dash_fstack_usage_not_too_hard)
-{
- CHECK(!compopt_too_hard("-fstack-usage"));
-}
-
-TEST(dash_doesntexist_not_too_hard)
-{
- CHECK(!compopt_too_hard("-doesntexist"));
-}
-
-TEST(dash_Xpreprocessor_too_hard_for_direct_mode)
-{
- CHECK(compopt_too_hard_for_direct_mode("-Xpreprocessor"));
-}
-
-TEST(dash_nostdinc_not_too_hard_for_direct_mode)
-{
- CHECK(!compopt_too_hard_for_direct_mode("-nostdinc"));
-}
-
-TEST(dash_I_takes_path)
-{
- CHECK(compopt_takes_path("-I"));
-}
-
-TEST(dash_Xlinker_takes_arg)
-{
- CHECK(compopt_takes_arg("-Xlinker"));
-}
-
-TEST(dash_xxx_doesnt_take_arg)
-{
- CHECK(!compopt_takes_arg("-xxx"));
-}
-
-TEST(dash_iframework_prefix_affects_cpp)
-{
- CHECK(compopt_prefix_affects_cpp("-iframework"));
-}
-
-TEST(dash_analyze_too_hard)
-{
- CHECK(compopt_too_hard("-analyze"));
-}
-
-TEST(dash_dash_analyze_too_hard)
-{
- CHECK(compopt_too_hard("--analyze"));
-}
-
-TEST_SUITE_END
--- /dev/null
+// 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 "../src/compopt.hpp"
+
+#include "third_party/doctest.h"
+
+bool compopt_verify_sortedness_and_flags();
+
+TEST_SUITE_BEGIN("compopt");
+
+TEST_CASE("option_table_should_be_sorted")
+{
+ CHECK(compopt_verify_sortedness_and_flags());
+}
+
+TEST_CASE("affects_cpp_output")
+{
+ CHECK(compopt_affects_cpp_output("-I"));
+ CHECK(!compopt_affects_cpp_output("-Ifoo"));
+ CHECK(!compopt_affects_cpp_output("-V"));
+ CHECK(!compopt_affects_cpp_output("-doesntexist"));
+}
+
+TEST_CASE("affects_compiler_output")
+{
+ CHECK(compopt_affects_compiler_output("-Xlinker"));
+ CHECK(compopt_affects_compiler_output("-all_load"));
+ CHECK(!compopt_affects_compiler_output("-U"));
+}
+
+TEST_CASE("too_hard")
+{
+ CHECK(compopt_too_hard("-MM"));
+ CHECK(compopt_too_hard("-save-temps"));
+ CHECK(compopt_too_hard("-save-temps=cwd"));
+ CHECK(compopt_too_hard("-save-temps=obj"));
+ CHECK(compopt_too_hard("-analyze"));
+ CHECK(compopt_too_hard("--analyze"));
+ CHECK(!compopt_too_hard("-MD"));
+ CHECK(!compopt_too_hard("-fprofile-arcs"));
+ CHECK(!compopt_too_hard("-ftest-coverage"));
+ CHECK(!compopt_too_hard("-fstack-usage"));
+ CHECK(!compopt_too_hard("-doesntexist"));
+}
+
+TEST_CASE("too_hard_for_direct_mode")
+{
+ CHECK(compopt_too_hard_for_direct_mode("-Xpreprocessor"));
+ CHECK(!compopt_too_hard_for_direct_mode("-nostdinc"));
+}
+
+TEST_CASE("compopt_takes_path")
+{
+ CHECK(compopt_takes_path("-I"));
+ CHECK(!compopt_takes_path("-L"));
+}
+
+TEST_CASE("compopt_takes_arg")
+{
+ CHECK(compopt_takes_arg("-Xlinker"));
+ CHECK(!compopt_takes_arg("-xxx"));
+}
+
+TEST_CASE("prefix_affects_cpp_output")
+{
+ CHECK(compopt_prefix_affects_cpp_output("-iframework"));
+ CHECK(compopt_prefix_affects_cpp_output("-iframework42"));
+ CHECK(!compopt_prefix_affects_cpp_output("-iframewor"));
+}
+
+TEST_CASE("prefix_affects_compiler_output")
+{
+ CHECK(compopt_prefix_affects_compiler_output("-Wa,"));
+ CHECK(compopt_prefix_affects_compiler_output("-Wa,something"));
+ CHECK(!compopt_prefix_affects_compiler_output("-Wa"));
+}
+
+TEST_SUITE_END();
+++ /dev/null
-// 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 "../src/conf.h"
-#include "framework.h"
-#include "util.h"
-
-#define N_CONFIG_ITEMS 33
-static struct {
- char *descr;
- char *origin;
-} received_conf_items[N_CONFIG_ITEMS];
-static size_t n_received_conf_items = 0;
-
-static void
-conf_item_receiver(const char *descr, const char *origin, void *context)
-{
- (void)context;
- received_conf_items[n_received_conf_items].descr = x_strdup(descr);
- received_conf_items[n_received_conf_items].origin = x_strdup(origin);
- ++n_received_conf_items;
-}
-
-static void
-free_received_conf_items(void)
-{
- while (n_received_conf_items > 0) {
- --n_received_conf_items;
- free(received_conf_items[n_received_conf_items].descr);
- free(received_conf_items[n_received_conf_items].origin);
- }
-}
-
-TEST_SUITE(conf)
-
-TEST(conf_create)
-{
- struct conf *conf = conf_create();
- CHECK_STR_EQ("", conf->base_dir);
- CHECK_STR_EQ_FREE1(format("%s/.ccache", get_home_directory()),
- conf->cache_dir);
- CHECK_INT_EQ(2, conf->cache_dir_levels);
- CHECK_STR_EQ("", conf->compiler);
- CHECK_STR_EQ("mtime", conf->compiler_check);
- CHECK(!conf->compression);
- CHECK_INT_EQ(6, conf->compression_level);
- CHECK_STR_EQ("", conf->cpp_extension);
- CHECK(!conf->debug);
- CHECK(!conf->depend_mode);
- CHECK(conf->direct_mode);
- CHECK(!conf->disable);
- CHECK_STR_EQ("", conf->extra_files_to_hash);
- CHECK(!conf->hard_link);
- CHECK(conf->hash_dir);
- CHECK_STR_EQ("", conf->ignore_headers_in_manifest);
- CHECK(!conf->keep_comments_cpp);
- CHECK_DOUBLE_EQ(0.8, conf->limit_multiple);
- CHECK_STR_EQ("", conf->log_file);
- CHECK_INT_EQ(0, conf->max_files);
- CHECK_INT_EQ((uint64_t)5 * 1000 * 1000 * 1000, conf->max_size);
- CHECK_STR_EQ("", conf->path);
- CHECK(!conf->pch_external_checksum);
- CHECK_STR_EQ("", conf->prefix_command);
- CHECK_STR_EQ("", conf->prefix_command_cpp);
- CHECK(!conf->read_only);
- CHECK(!conf->read_only_direct);
- CHECK(!conf->recache);
- CHECK(conf->run_second_cpp);
- CHECK_INT_EQ(0, conf->sloppiness);
- CHECK(conf->stats);
- CHECK_STR_EQ("", conf->temporary_dir);
- CHECK_INT_EQ(UINT_MAX, conf->umask);
- conf_free(conf);
-}
-
-TEST(conf_read_valid_config)
-{
- struct conf *conf = conf_create();
- char *errmsg, *user;
- putenv("USER=rabbit");
- user = getenv("USER");
- CHECK_STR_EQ("rabbit", user);
- create_file(
- "ccache.conf",
-#ifndef _WIN32
- "base_dir = /$USER/foo/${USER} \n"
-#else
- "base_dir = C:/$USER/foo/${USER}\n"
-#endif
- "cache_dir=\n"
- "cache_dir = $USER$/${USER}/.ccache\n"
- "\n"
- "\n"
- " #A comment\n"
- " cache_dir_levels = 4\n"
- "\t compiler = foo\n"
- "compiler_check = none\n"
- "compression=true\n"
- "compression_level= 2\n"
- "cpp_extension = .foo\n"
- "depend_mode = true\n"
- "direct_mode = false\n"
- "disable = true\n"
- "extra_files_to_hash = a:b c:$USER\n"
- "hard_link = true\n"
- "hash_dir = false\n"
- "ignore_headers_in_manifest = a:b/c\n"
- "keep_comments_cpp = true\n"
- "limit_multiple = 1.0\n"
- "log_file = $USER${USER} \n"
- "max_files = 17\n"
- "max_size = 123M\n"
- "path = $USER.x\n"
- "pch_external_checksum = true\n"
- "prefix_command = x$USER\n"
- "prefix_command_cpp = y\n"
- "read_only = true\n"
- "read_only_direct = true\n"
- "recache = true\n"
- "run_second_cpp = false\n"
- "sloppiness = time_macros ,include_file_mtime include_file_ctime,file_stat_matches,file_stat_matches_ctime,pch_defines , no_system_headers,system_headers,clang_index_store\n"
- "stats = false\n"
- "temporary_dir = ${USER}_foo\n"
- "umask = 777"); // Note: no newline.
- CHECK(conf_read(conf, "ccache.conf", &errmsg));
- CHECK(!errmsg);
-
-#ifndef _WIN32
- CHECK_STR_EQ_FREE1(format("/%s/foo/%s", user, user), conf->base_dir);
-#else
- CHECK_STR_EQ_FREE1(format("C:/%s/foo/%s", user, user), conf->base_dir);
-#endif
- CHECK_STR_EQ_FREE1(format("%s$/%s/.ccache", user, user), conf->cache_dir);
- CHECK_INT_EQ(4, conf->cache_dir_levels);
- CHECK_STR_EQ("foo", conf->compiler);
- CHECK_STR_EQ("none", conf->compiler_check);
- CHECK(conf->compression);
- CHECK_INT_EQ(2, conf->compression_level);
- CHECK_STR_EQ(".foo", conf->cpp_extension);
- CHECK(conf->depend_mode);
- CHECK(!conf->direct_mode);
- CHECK(conf->disable);
- CHECK_STR_EQ_FREE1(format("a:b c:%s", user), conf->extra_files_to_hash);
- CHECK(conf->hard_link);
- CHECK(!conf->hash_dir);
- CHECK_STR_EQ("a:b/c", conf->ignore_headers_in_manifest);
- CHECK(conf->keep_comments_cpp);
- CHECK_DOUBLE_EQ(1.0, conf->limit_multiple);
- CHECK_STR_EQ_FREE1(format("%s%s", user, user), conf->log_file);
- CHECK_INT_EQ(17, conf->max_files);
- CHECK_INT_EQ(123 * 1000 * 1000, conf->max_size);
- CHECK_STR_EQ_FREE1(format("%s.x", user), conf->path);
- CHECK(conf->pch_external_checksum);
- CHECK_STR_EQ_FREE1(format("x%s", user), conf->prefix_command);
- CHECK_STR_EQ("y", conf->prefix_command_cpp);
- CHECK(conf->read_only);
- CHECK(conf->read_only_direct);
- CHECK(conf->recache);
- CHECK(!conf->run_second_cpp);
- CHECK_INT_EQ(
- SLOPPY_INCLUDE_FILE_MTIME
- |SLOPPY_INCLUDE_FILE_CTIME
- |SLOPPY_TIME_MACROS
- |SLOPPY_FILE_STAT_MATCHES
- |SLOPPY_FILE_STAT_MATCHES_CTIME
- |SLOPPY_SYSTEM_HEADERS
- |SLOPPY_PCH_DEFINES
- |SLOPPY_CLANG_INDEX_STORE,
- conf->sloppiness);
- CHECK(!conf->stats);
- CHECK_STR_EQ_FREE1(format("%s_foo", user), conf->temporary_dir);
- CHECK_INT_EQ(0777, conf->umask);
-
- conf_free(conf);
-}
-
-TEST(conf_read_with_missing_equal_sign)
-{
- struct conf *conf = conf_create();
- char *errmsg;
- create_file("ccache.conf", "no equal sign");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_INT_EQ(errno, 0);
- CHECK_STR_EQ_FREE2("ccache.conf:1: missing equal sign",
- errmsg);
- conf_free(conf);
-}
-
-TEST(conf_read_with_unknown_config_key)
-{
- struct conf *conf = conf_create();
- char *errmsg;
- create_file("ccache.conf", "# Comment\nfoo = bar");
- CHECK(conf_read(conf, "ccache.conf", &errmsg));
- conf_free(conf);
-}
-
-TEST(conf_read_invalid_bool)
-{
- struct conf *conf = conf_create();
- char *errmsg;
-
- create_file("ccache.conf", "disable=");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_INT_EQ(errno, 0);
- CHECK_STR_EQ_FREE2("ccache.conf:1: not a boolean value: \"\"",
- errmsg);
-
- create_file("ccache.conf", "disable=foo");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_INT_EQ(errno, 0);
- CHECK_STR_EQ_FREE2("ccache.conf:1: not a boolean value: \"foo\"",
- errmsg);
- conf_free(conf);
-}
-
-TEST(conf_read_invalid_env_string)
-{
- struct conf *conf = conf_create();
- char *errmsg;
- create_file("ccache.conf", "base_dir = ${foo");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_INT_EQ(errno, 0);
- CHECK_STR_EQ_FREE2("ccache.conf:1: syntax error: missing '}' after \"foo\"",
- errmsg);
- // Other cases tested in test_util.c.
- conf_free(conf);
-}
-
-TEST(conf_read_empty_umask)
-{
- struct conf *conf = conf_create();
- char *errmsg;
- create_file("ccache.conf", "umask = ");
- CHECK(conf_read(conf, "ccache.conf", &errmsg));
- CHECK_INT_EQ(conf->umask, UINT_MAX);
- conf_free(conf);
-}
-
-TEST(conf_read_invalid_size)
-{
- struct conf *conf = conf_create();
- char *errmsg;
- create_file("ccache.conf", "max_size = foo");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_INT_EQ(errno, 0);
- CHECK_STR_EQ_FREE2("ccache.conf:1: invalid size: \"foo\"",
- errmsg);
- // Other cases tested in test_util.c.
- conf_free(conf);
-}
-
-TEST(conf_read_unknown_sloppiness)
-{
- struct conf *conf = conf_create();
- char *errmsg;
- create_file("ccache.conf", "sloppiness = time_macros, foo");
- CHECK(conf_read(conf, "ccache.conf", &errmsg));
- CHECK_INT_EQ(conf->sloppiness, SLOPPY_TIME_MACROS);
- conf_free(conf);
-}
-
-TEST(conf_read_invalid_unsigned)
-{
- struct conf *conf = conf_create();
- char *errmsg;
-
- create_file("ccache.conf", "max_files =");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_INT_EQ(errno, 0);
- CHECK_STR_EQ_FREE2("ccache.conf:1: invalid unsigned integer: \"\"",
- errmsg);
-
- create_file("ccache.conf", "max_files = -42");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_STR_EQ_FREE2("ccache.conf:1: invalid unsigned integer: \"-42\"",
- errmsg);
-
- create_file("ccache.conf", "max_files = foo");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_STR_EQ_FREE2("ccache.conf:1: invalid unsigned integer: \"foo\"",
- errmsg);
-
- conf_free(conf);
-}
-
-TEST(conf_read_missing_config_file)
-{
- struct conf *conf = conf_create();
- char *errmsg;
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_INT_EQ(errno, ENOENT);
-}
-
-TEST(verify_absolute_base_dir)
-{
- struct conf *conf = conf_create();
- char *errmsg;
-
- create_file("ccache.conf", "base_dir = relative/path");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_STR_EQ_FREE2("ccache.conf:1: not an absolute path: \"relative/path\"",
- errmsg);
-
- create_file("ccache.conf", "base_dir =");
- CHECK(conf_read(conf, "ccache.conf", &errmsg));
-
- conf_free(conf);
-}
-
-TEST(verify_dir_levels)
-{
- struct conf *conf = conf_create();
- char *errmsg;
-
- create_file("ccache.conf", "cache_dir_levels = 0");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_STR_EQ_FREE2(
- "ccache.conf:1: cache directory levels must be between 1 and 8",
- errmsg);
- create_file("ccache.conf", "cache_dir_levels = 9");
- CHECK(!conf_read(conf, "ccache.conf", &errmsg));
- CHECK_STR_EQ_FREE2(
- "ccache.conf:1: cache directory levels must be between 1 and 8",
- errmsg);
-
- conf_free(conf);
-}
-
-TEST(conf_update_from_environment)
-{
- struct conf *conf = conf_create();
- char *errmsg;
-
- putenv("CCACHE_COMPRESS=1");
- CHECK(conf_update_from_environment(conf, &errmsg));
- CHECK(conf->compression);
-
- x_unsetenv("CCACHE_COMPRESS");
- putenv("CCACHE_NOCOMPRESS=1");
- CHECK(conf_update_from_environment(conf, &errmsg));
- CHECK(!conf->compression);
-
- conf_free(conf);
-}
-
-TEST(conf_set_new_value)
-{
- char *errmsg;
- char *data;
-
- create_file("ccache.conf", "path = vanilla\n");
- CHECKM(conf_set_value_in_file("ccache.conf", "compiler", "chocolate",
- &errmsg),
- errmsg);
- data = read_text_file("ccache.conf", 0);
- CHECK(data);
- CHECK_STR_EQ_FREE2("path = vanilla\ncompiler = chocolate\n", data);
-}
-
-TEST(conf_set_existing_value)
-{
- char *errmsg;
- char *data;
-
- create_file("ccache.conf", "path = chocolate\nstats = chocolate\n");
- CHECKM(conf_set_value_in_file("ccache.conf", "path", "vanilla", &errmsg),
- errmsg);
- data = read_text_file("ccache.conf", 0);
- CHECK(data);
- CHECK_STR_EQ_FREE2("path = vanilla\nstats = chocolate\n", data);
-}
-
-TEST(conf_set_unknown_option)
-{
- char *errmsg;
- char *data;
-
- create_file("ccache.conf", "path = chocolate\nstats = chocolate\n");
- CHECKM(!conf_set_value_in_file("ccache.conf", "foo", "bar", &errmsg),
- errmsg);
- CHECK_STR_EQ_FREE2("unknown configuration option \"foo\"", errmsg);
-
- data = read_text_file("ccache.conf", 0);
- CHECK(data);
- CHECK_STR_EQ_FREE2("path = chocolate\nstats = chocolate\n", data);
-}
-
-TEST(conf_set_unknown_sloppiness)
-{
- char *errmsg;
- char *data;
-
- create_file("ccache.conf", "path = vanilla\n");
- CHECK(conf_set_value_in_file("ccache.conf", "sloppiness", "foo", &errmsg));
-
- data = read_text_file("ccache.conf", 0);
- CHECK(data);
- CHECK_STR_EQ_FREE2("path = vanilla\nsloppiness = foo\n", data);
-}
-
-TEST(conf_print_existing_value)
-{
- struct conf *conf = conf_create();
- conf->max_files = 42;
- char *errmsg;
- {
- FILE *log = fopen("log", "w");
- CHECK(log);
- CHECK(conf_print_value(conf, "max_files", log, &errmsg));
- fclose(log);
- }
- {
- FILE *log = fopen("log", "r");
- CHECK(log);
- char buf[100];
- CHECK(fgets(buf, sizeof(buf), log));
- CHECK_STR_EQ("42\n", buf);
- fclose(log);
- }
- conf_free(conf);
-}
-
-TEST(conf_print_unknown_value)
-{
- struct conf *conf = conf_create();
- char *errmsg;
- {
- FILE *log = fopen("log", "w");
- CHECK(log);
- CHECK(!conf_print_value(conf, "foo", log, &errmsg));
- CHECK_STR_EQ_FREE2("unknown configuration option \"foo\"",
- errmsg);
- fclose(log);
- }
- {
- FILE *log = fopen("log", "r");
- CHECK(log);
- char buf[100];
- CHECK(!fgets(buf, sizeof(buf), log));
- fclose(log);
- }
- conf_free(conf);
-}
-
-TEST(conf_print_items)
-{
- size_t i;
- struct conf conf = {
- "bd",
- "cd",
- 7,
- "c",
- "cc",
- true,
- 8,
- "ce",
- false,
- true,
- false,
- true,
- "efth",
- true,
- .hash_dir = false,
- "ihim",
- true,
- 0.0,
- "lf",
- 4711,
- 98.7 * 1000 * 1000,
- "p",
- true,
- "pc",
- "pcc",
- true,
- true,
- true,
- .run_second_cpp = false,
- SLOPPY_INCLUDE_FILE_MTIME|
- SLOPPY_INCLUDE_FILE_CTIME|SLOPPY_TIME_MACROS|
- SLOPPY_FILE_STAT_MATCHES|SLOPPY_FILE_STAT_MATCHES_CTIME|
- SLOPPY_PCH_DEFINES|SLOPPY_SYSTEM_HEADERS|SLOPPY_CLANG_INDEX_STORE,
- false,
- "td",
- 022,
- NULL
- };
- size_t n = 0;
-
- conf.item_origins = x_malloc(N_CONFIG_ITEMS * sizeof(char *));
- for (i = 0; i < N_CONFIG_ITEMS; ++i) {
-#ifndef __MINGW32__
- conf.item_origins[i] = format("origin%zu", i);
-#else
- conf.item_origins[i] = format("origin%u", (unsigned) i);
-#endif
- }
-
- conf_print_items(&conf, conf_item_receiver, NULL);
- CHECK_INT_EQ(N_CONFIG_ITEMS, n_received_conf_items);
- CHECK_STR_EQ("base_dir = bd", received_conf_items[n++].descr);
- CHECK_STR_EQ("cache_dir = cd", received_conf_items[n++].descr);
- CHECK_STR_EQ("cache_dir_levels = 7", received_conf_items[n++].descr);
- CHECK_STR_EQ("compiler = c", received_conf_items[n++].descr);
- CHECK_STR_EQ("compiler_check = cc", received_conf_items[n++].descr);
- CHECK_STR_EQ("compression = true", received_conf_items[n++].descr);
- CHECK_STR_EQ("compression_level = 8", received_conf_items[n++].descr);
- CHECK_STR_EQ("cpp_extension = ce", received_conf_items[n++].descr);
- CHECK_STR_EQ("debug = false", received_conf_items[n++].descr);
- CHECK_STR_EQ("depend_mode = true", received_conf_items[n++].descr);
- CHECK_STR_EQ("direct_mode = false", received_conf_items[n++].descr);
- CHECK_STR_EQ("disable = true", received_conf_items[n++].descr);
- CHECK_STR_EQ("extra_files_to_hash = efth", received_conf_items[n++].descr);
- CHECK_STR_EQ("hard_link = true", received_conf_items[n++].descr);
- CHECK_STR_EQ("hash_dir = false", received_conf_items[n++].descr);
- CHECK_STR_EQ("ignore_headers_in_manifest = ihim",
- received_conf_items[n++].descr);
- CHECK_STR_EQ("keep_comments_cpp = true", received_conf_items[n++].descr);
- CHECK_STR_EQ("limit_multiple = 0.0", received_conf_items[n++].descr);
- CHECK_STR_EQ("log_file = lf", received_conf_items[n++].descr);
- CHECK_STR_EQ("max_files = 4711", received_conf_items[n++].descr);
- CHECK_STR_EQ("max_size = 98.7M", received_conf_items[n++].descr);
- CHECK_STR_EQ("path = p", received_conf_items[n++].descr);
- CHECK_STR_EQ("pch_external_checksum = true", received_conf_items[n++].descr);
- CHECK_STR_EQ("prefix_command = pc", received_conf_items[n++].descr);
- CHECK_STR_EQ("prefix_command_cpp = pcc", received_conf_items[n++].descr);
- CHECK_STR_EQ("read_only = true", received_conf_items[n++].descr);
- CHECK_STR_EQ("read_only_direct = true", received_conf_items[n++].descr);
- CHECK_STR_EQ("recache = true", received_conf_items[n++].descr);
- CHECK_STR_EQ("run_second_cpp = false", received_conf_items[n++].descr);
- CHECK_STR_EQ("sloppiness = include_file_mtime,"
- " include_file_ctime, time_macros, pch_defines,"
- " file_stat_matches, file_stat_matches_ctime, system_headers,"
- " clang_index_store",
- received_conf_items[n++].descr);
- CHECK_STR_EQ("stats = false", received_conf_items[n++].descr);
- CHECK_STR_EQ("temporary_dir = td", received_conf_items[n++].descr);
- CHECK_STR_EQ("umask = 022", received_conf_items[n++].descr);
-
- for (i = 0; i < N_CONFIG_ITEMS; ++i) {
-#ifndef __MINGW32__
- char *expected = format("origin%zu", i);
-#else
- char *expected = format("origin%u", (unsigned) i);
-#endif
- CHECK_STR_EQ_FREE1(expected, received_conf_items[i].origin);
- }
-
- free_received_conf_items();
- free(conf.item_origins);
-}
-
-TEST_SUITE_END
+++ /dev/null
-// 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 "../src/ccache.h"
-#include "../src/counters.h"
-#include "framework.h"
-#include "util.h"
-
-TEST_SUITE(counters)
-
-TEST(counters_init_0_should_allocate_0)
-{
- struct counters *counters = counters_init(0);
-
- CHECK_INT_EQ(0, counters->allocated);
- CHECK_INT_EQ(0, counters->size);
-
- counters_free(counters);
-}
-
-TEST(counters_init_7_should_allocate_32)
-{
- int i;
- struct counters *counters = counters_init(7);
-
- CHECK_INT_EQ(32, counters->allocated);
- CHECK_INT_EQ(7, counters->size);
- for (i = 0; i < 7; i++) {
- CHECK_INT_EQ(0, counters->data[i]);
- }
-
- counters_free(counters);
-}
-
-TEST(counters_resize_50_should_allocate_96)
-{
- struct counters *counters = counters_init(0);
-
- CHECK_INT_EQ(0, counters->allocated);
- counters_resize(counters, 50);
- CHECK_INT_EQ(50, counters->size);
- CHECK_INT_EQ(96, counters->allocated);
-
- counters_free(counters);
-}
-
-TEST_SUITE_END
+++ /dev/null
-// 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
-
-// This file contains tests for functions in hash.c.
-
-#include "../src/ccache.h"
-#include "../src/hash.h"
-#include "framework.h"
-
-TEST_SUITE(mdfour)
-
-TEST(test_vectors_from_rfc_1320_should_be_correct)
-{
- {
- struct hash *h = hash_init();
- hash_string(h, "");
- CHECK_STR_EQ_FREE2("31d6cfe0d16ae931b73c59d7e0c089c0-0", hash_result(h));
- hash_free(h);
- }
-
- {
- struct hash *h = hash_init();
- hash_string(h, "a");
- CHECK_STR_EQ_FREE2("bde52cb31de33e46245e05fbdbd6fb24-1", hash_result(h));
- hash_free(h);
- }
-
- {
- struct hash *h = hash_init();
- hash_string(h, "message digest");
- CHECK_STR_EQ_FREE2("d9130a8164549fe818874806e1c7014b-14", hash_result(h));
- hash_free(h);
- }
-
- {
- struct hash *h = hash_init();
- hash_string(
- h,
- "12345678901234567890123456789012345678901234567890123456789012345678901"
- "234567890");
- CHECK_STR_EQ_FREE2("e33b4ddc9c38f2199c3e7b164fcc0536-80", hash_result(h));
- hash_free(h);
- }
-}
-
-TEST(hash_result_should_not_alter_state)
-{
- struct hash *h = hash_init();
- hash_string(h, "message");
- free(hash_result(h));
- hash_string(h, " digest");
- CHECK_STR_EQ_FREE2("d9130a8164549fe818874806e1c7014b-14", hash_result(h));
- hash_free(h);
-}
-
-TEST(hash_result_should_be_idempotent)
-{
- struct hash *h = hash_init();
-
- hash_string(h, "");
- CHECK_STR_EQ_FREE2("31d6cfe0d16ae931b73c59d7e0c089c0-0", hash_result(h));
- CHECK_STR_EQ_FREE2("31d6cfe0d16ae931b73c59d7e0c089c0-0", hash_result(h));
-
- hash_free(h);
-}
-
-TEST_SUITE_END
+++ /dev/null
-// 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
-
-// This file contains tests for functions in hashutil.c.
-
-#include "../src/ccache.h"
-#include "../src/hashutil.h"
-#include "framework.h"
-#include "util.h"
-
-TEST_SUITE(hashutil)
-
-TEST(hash_command_output_simple)
-{
- struct hash *h1 = hash_init();
- struct hash *h2 = hash_init();
-
- CHECK(hash_command_output(h1, "echo", "not used"));
- CHECK(hash_command_output(h2, "echo", "not used"));
- CHECK(hash_equal(h1, h2));
-
- hash_free(h2);
- hash_free(h1);
-}
-
-TEST(hash_command_output_space_removal)
-{
- struct hash *h1 = hash_init();
- struct hash *h2 = hash_init();
-
- CHECK(hash_command_output(h1, "echo", "not used"));
- CHECK(hash_command_output(h2, " echo ", "not used"));
- CHECK(hash_equal(h1, h2));
-
- hash_free(h2);
- hash_free(h1);
-}
-
-TEST(hash_command_output_hash_inequality)
-{
- struct hash *h1 = hash_init();
- struct hash *h2 = hash_init();
-
- CHECK(hash_command_output(h1, "echo foo", "not used"));
- CHECK(hash_command_output(h2, "echo bar", "not used"));
- CHECK(!hash_equal(h1, h2));
-
- hash_free(h2);
- hash_free(h1);
-}
-
-TEST(hash_command_output_compiler_substitution)
-{
- struct hash *h1 = hash_init();
- struct hash *h2 = hash_init();
-
- CHECK(hash_command_output(h1, "echo foo", "not used"));
- CHECK(hash_command_output(h2, "%compiler% foo", "echo"));
- CHECK(hash_equal(h1, h2));
-
- hash_free(h2);
- hash_free(h1);
-}
-
-TEST(hash_command_output_stdout_versus_stderr)
-{
- struct hash *h1 = hash_init();
- struct hash *h2 = hash_init();
-
-#ifndef _WIN32
- create_file("stderr.sh", "#!/bin/sh\necho foo >&2\n");
- chmod("stderr.sh", 0555);
- CHECK(hash_command_output(h1, "echo foo", "not used"));
- CHECK(hash_command_output(h2, "./stderr.sh", "not used"));
-#else
- create_file("stderr.bat", "@echo off\r\necho foo>&2\r\n");
- CHECK(hash_command_output(h1, "echo foo", "not used"));
- CHECK(hash_command_output(h2, "stderr.bat", "not used"));
-#endif
- CHECK(hash_equal(h1, h2));
-
- hash_free(h2);
- hash_free(h1);
-}
-
-TEST(hash_multicommand_output)
-{
- struct hash *h1 = hash_init();
- struct hash *h2 = hash_init();
-
-#ifndef _WIN32
- create_file("foo.sh", "#!/bin/sh\necho foo\necho bar\n");
- chmod("foo.sh", 0555);
- CHECK(hash_multicommand_output(h2, "echo foo; echo bar", "not used"));
- CHECK(hash_multicommand_output(h1, "./foo.sh", "not used"));
-#else
- create_file("foo.bat", "@echo off\r\necho foo\r\necho bar\r\n");
- CHECK(hash_multicommand_output(h2, "echo foo; echo bar", "not used"));
- CHECK(hash_multicommand_output(h1, "foo.bat", "not used"));
-#endif
- CHECK(hash_equal(h1, h2));
-
- hash_free(h2);
- hash_free(h1);
-}
-
-TEST(hash_multicommand_output_error_handling)
-{
- struct hash *h1 = hash_init();
- struct hash *h2 = hash_init();
-
- CHECK(!hash_multicommand_output(h2, "false; true", "not used"));
-
- hash_free(h2);
- hash_free(h1);
-}
-
-TEST(check_for_temporal_macros)
-{
- const char time_start[] =
- "__TIME__\n"
- "int a;\n";
- const char time_middle[] =
- "#define a __TIME__\n"
- "int a;\n";
- const char time_end[] =
- "#define a __TIME__";
-
- const char date_start[] =
- "__DATE__\n"
- "int ab;\n";
- const char date_middle[] =
- "#define ab __DATE__\n"
- "int ab;\n";
- const char date_end[] =
- "#define ab __DATE__";
-
- const char no_temporal[] =
- "#define ab a__DATE__\n"
- "#define ab __DATE__a\n"
- "#define ab A__DATE__\n"
- "#define ab __DATE__A\n"
- "#define ab 0__DATE__\n"
- "#define ab __DATE__0\n"
- "#define ab _ _DATE__\n"
- "#define ab _ _DATE__\n"
- "#define ab __ DATE__\n"
- "#define ab __D ATE__\n"
- "#define ab __DA TE__\n"
- "#define ab __DAT E__\n"
- "#define ab __DATE __\n"
- "#define ab __DATE_ _\n"
- "#define ab _ _TIME__\n"
- "#define ab __ TIME__\n"
- "#define ab __T IME__\n"
- "#define ab __TI ME__\n"
- "#define ab __TIM E__\n"
- "#define ab __TIME __\n"
- "#define ab __TIME_ _\n";
-
- CHECK(check_for_temporal_macros(time_start + 0, sizeof(time_start) - 0));
- CHECK(!check_for_temporal_macros(time_start + 1, sizeof(time_start) - 1));
-
- CHECK(check_for_temporal_macros(time_middle + 0, sizeof(time_middle) - 0));
- CHECK(check_for_temporal_macros(time_middle + 1, sizeof(time_middle) - 1));
- CHECK(check_for_temporal_macros(time_middle + 2, sizeof(time_middle) - 2));
- CHECK(check_for_temporal_macros(time_middle + 3, sizeof(time_middle) - 3));
- CHECK(check_for_temporal_macros(time_middle + 4, sizeof(time_middle) - 4));
- CHECK(check_for_temporal_macros(time_middle + 5, sizeof(time_middle) - 5));
- CHECK(check_for_temporal_macros(time_middle + 6, sizeof(time_middle) - 6));
- CHECK(check_for_temporal_macros(time_middle + 7, sizeof(time_middle) - 7));
-
- CHECK(check_for_temporal_macros(time_end + 0, sizeof(time_end) - 0));
- CHECK(check_for_temporal_macros(time_end + sizeof(time_end) - 9, 9));
- CHECK(!check_for_temporal_macros(time_end + sizeof(time_end) - 8, 8));
-
- CHECK(check_for_temporal_macros(date_start + 0, sizeof(date_start) - 0));
- CHECK(!check_for_temporal_macros(date_start + 1, sizeof(date_start) - 1));
-
- CHECK(check_for_temporal_macros(date_middle + 0, sizeof(date_middle) - 0));
- CHECK(check_for_temporal_macros(date_middle + 1, sizeof(date_middle) - 1));
- CHECK(check_for_temporal_macros(date_middle + 2, sizeof(date_middle) - 2));
- CHECK(check_for_temporal_macros(date_middle + 3, sizeof(date_middle) - 3));
- CHECK(check_for_temporal_macros(date_middle + 4, sizeof(date_middle) - 4));
- CHECK(check_for_temporal_macros(date_middle + 5, sizeof(date_middle) - 5));
- CHECK(check_for_temporal_macros(date_middle + 6, sizeof(date_middle) - 6));
- CHECK(check_for_temporal_macros(date_middle + 7, sizeof(date_middle) - 7));
-
- CHECK(check_for_temporal_macros(date_end + 0, sizeof(date_end) - 0));
- CHECK(check_for_temporal_macros(date_end + sizeof(date_end) - 9, 9));
- CHECK(!check_for_temporal_macros(date_end + sizeof(date_end) - 8, 8));
-
- CHECK(!check_for_temporal_macros(no_temporal + 0, sizeof(no_temporal) - 0));
- CHECK(!check_for_temporal_macros(no_temporal + 1, sizeof(no_temporal) - 1));
- CHECK(!check_for_temporal_macros(no_temporal + 2, sizeof(no_temporal) - 2));
- CHECK(!check_for_temporal_macros(no_temporal + 3, sizeof(no_temporal) - 3));
- CHECK(!check_for_temporal_macros(no_temporal + 4, sizeof(no_temporal) - 4));
- CHECK(!check_for_temporal_macros(no_temporal + 5, sizeof(no_temporal) - 5));
- CHECK(!check_for_temporal_macros(no_temporal + 6, sizeof(no_temporal) - 6));
- CHECK(!check_for_temporal_macros(no_temporal + 7, sizeof(no_temporal) - 7));
-}
-
-TEST_SUITE_END
--- /dev/null
+// 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 "../src/Context.hpp"
+#include "../src/Hash.hpp"
+#include "../src/hashutil.hpp"
+#include "TestUtil.hpp"
+
+#include "third_party/doctest.h"
+
+using nonstd::string_view;
+using TestUtil::TestContext;
+
+TEST_SUITE_BEGIN("hashutil");
+
+TEST_CASE("hash_command_output_simple")
+{
+ Hash h1;
+ Hash h2;
+
+ CHECK(hash_command_output(h1, "echo", "not used"));
+ CHECK(hash_command_output(h2, "echo", "not used"));
+ CHECK(h1.digest() == h2.digest());
+}
+
+TEST_CASE("hash_command_output_space_removal")
+{
+ Hash h1;
+ Hash h2;
+
+ CHECK(hash_command_output(h1, "echo", "not used"));
+ CHECK(hash_command_output(h2, " echo ", "not used"));
+ CHECK(h1.digest() == h2.digest());
+}
+
+TEST_CASE("hash_command_output_hash_inequality")
+{
+ Hash h1;
+ Hash h2;
+
+ CHECK(hash_command_output(h1, "echo foo", "not used"));
+ CHECK(hash_command_output(h2, "echo bar", "not used"));
+ CHECK(h1.digest() != h2.digest());
+}
+
+TEST_CASE("hash_command_output_compiler_substitution")
+{
+ Hash h1;
+ Hash h2;
+
+ CHECK(hash_command_output(h1, "echo foo", "not used"));
+ CHECK(hash_command_output(h2, "%compiler% foo", "echo"));
+ CHECK(h1.digest() == h2.digest());
+}
+
+TEST_CASE("hash_command_output_stdout_versus_stderr")
+{
+ TestContext test_context;
+
+ Hash h1;
+ Hash h2;
+
+#ifndef _WIN32
+ Util::write_file("stderr.sh", "#!/bin/sh\necho foo >&2\n");
+ chmod("stderr.sh", 0555);
+ CHECK(hash_command_output(h1, "echo foo", "not used"));
+ CHECK(hash_command_output(h2, "./stderr.sh", "not used"));
+#else
+ Util::write_file("stderr.bat", "@echo off\r\necho foo>&2\r\n");
+ CHECK(hash_command_output(h1, "echo foo", "not used"));
+ CHECK(hash_command_output(h2, "stderr.bat", "not used"));
+#endif
+ CHECK(h1.digest() == h2.digest());
+}
+
+TEST_CASE("hash_multicommand_output")
+{
+ Hash h1;
+ Hash h2;
+
+#ifndef _WIN32
+ Util::write_file("foo.sh", "#!/bin/sh\necho foo\necho bar\n");
+ chmod("foo.sh", 0555);
+ CHECK(hash_multicommand_output(h2, "echo foo; echo bar", "not used"));
+ CHECK(hash_multicommand_output(h1, "./foo.sh", "not used"));
+#else
+ Util::write_file("foo.bat", "@echo off\r\necho foo\r\necho bar\r\n");
+ CHECK(hash_multicommand_output(h2, "echo foo; echo bar", "not used"));
+ CHECK(hash_multicommand_output(h1, "foo.bat", "not used"));
+#endif
+ CHECK(h1.digest() == h2.digest());
+}
+
+TEST_CASE("hash_multicommand_output_error_handling")
+{
+ Context ctx;
+
+ Hash h1;
+ Hash h2;
+
+ CHECK(!hash_multicommand_output(h2, "false; true", "not used"));
+}
+
+TEST_CASE("check_for_temporal_macros")
+{
+ const string_view time_start =
+ "__TIME__\n"
+ "int a;\n";
+ const string_view time_middle =
+ "#define a __TIME__\n"
+ "int a;\n";
+ const string_view time_end = "#define a __TIME__";
+
+ const string_view date_start =
+ "__DATE__\n"
+ "int ab;\n";
+ const string_view date_middle =
+ "#define ab __DATE__\n"
+ "int ab;\n";
+ const string_view date_end = "#define ab __DATE__";
+
+ const string_view timestamp_start =
+ "__TIMESTAMP__\n"
+ "int c;\n";
+ const string_view timestamp_middle =
+ "#define c __TIMESTAMP__\n"
+ "int c;\n";
+ const string_view timestamp_end = "#define c __TIMESTAMP__";
+
+ const string_view no_temporal =
+ "#define ab a__DATE__\n"
+ "#define ab __DATE__a\n"
+ "#define ab A__DATE__\n"
+ "#define ab __DATE__A\n"
+ "#define ab 0__DATE__\n"
+ "#define ab __DATE__0\n"
+ "#define ab _ _DATE__\n"
+ "#define ab _ _DATE__\n"
+ "#define ab __ DATE__\n"
+ "#define ab __D ATE__\n"
+ "#define ab __DA TE__\n"
+ "#define ab __DAT E__\n"
+ "#define ab __DATE __\n"
+ "#define ab __DATE_ _\n"
+ "#define ab _ _TIME__\n"
+ "#define ab __ TIME__\n"
+ "#define ab __T IME__\n"
+ "#define ab __TI ME__\n"
+ "#define ab __TIM E__\n"
+ "#define ab __TIME __\n"
+ "#define ab __TIME_ _\n";
+
+ const string_view temporal_at_avx_boundary =
+ "#define alphabet abcdefghijklmnopqrstuvwxyz\n"
+ "__DATE__";
+
+ CHECK(check_for_temporal_macros(time_start));
+ CHECK(!check_for_temporal_macros(time_start.substr(1)));
+
+ CHECK(check_for_temporal_macros(time_middle.substr(0)));
+ CHECK(check_for_temporal_macros(time_middle.substr(1)));
+ CHECK(check_for_temporal_macros(time_middle.substr(2)));
+ CHECK(check_for_temporal_macros(time_middle.substr(3)));
+ CHECK(check_for_temporal_macros(time_middle.substr(4)));
+ CHECK(check_for_temporal_macros(time_middle.substr(5)));
+ CHECK(check_for_temporal_macros(time_middle.substr(6)));
+ CHECK(check_for_temporal_macros(time_middle.substr(7)));
+
+ CHECK(check_for_temporal_macros(time_end));
+ CHECK(check_for_temporal_macros(time_end.substr(time_end.length() - 8)));
+ CHECK(!check_for_temporal_macros(time_end.substr(time_end.length() - 7)));
+
+ CHECK(check_for_temporal_macros(date_start));
+ CHECK(!check_for_temporal_macros(date_start.substr(1)));
+
+ CHECK(check_for_temporal_macros(date_middle.substr(0)));
+ CHECK(check_for_temporal_macros(date_middle.substr(1)));
+ CHECK(check_for_temporal_macros(date_middle.substr(2)));
+ CHECK(check_for_temporal_macros(date_middle.substr(3)));
+ CHECK(check_for_temporal_macros(date_middle.substr(4)));
+ CHECK(check_for_temporal_macros(date_middle.substr(5)));
+ CHECK(check_for_temporal_macros(date_middle.substr(6)));
+ CHECK(check_for_temporal_macros(date_middle.substr(7)));
+
+ CHECK(check_for_temporal_macros(date_end));
+ CHECK(check_for_temporal_macros(date_end.substr(date_end.length() - 8)));
+ CHECK(!check_for_temporal_macros(date_end.substr(date_end.length() - 7)));
+
+ CHECK(check_for_temporal_macros(timestamp_start));
+ CHECK(!check_for_temporal_macros(timestamp_start.substr(1)));
+
+ CHECK(check_for_temporal_macros(timestamp_middle));
+ CHECK(check_for_temporal_macros(timestamp_middle.substr(1)));
+ CHECK(check_for_temporal_macros(timestamp_middle.substr(2)));
+ CHECK(check_for_temporal_macros(timestamp_middle.substr(3)));
+ CHECK(check_for_temporal_macros(timestamp_middle.substr(4)));
+ CHECK(check_for_temporal_macros(timestamp_middle.substr(5)));
+ CHECK(check_for_temporal_macros(timestamp_middle.substr(6)));
+ CHECK(check_for_temporal_macros(timestamp_middle.substr(7)));
+
+ CHECK(check_for_temporal_macros(timestamp_end));
+ CHECK(check_for_temporal_macros(
+ timestamp_end.substr(timestamp_end.length() - 13)));
+ CHECK(!check_for_temporal_macros(
+ timestamp_end.substr(timestamp_end.length() - 12)));
+
+ CHECK(!check_for_temporal_macros(no_temporal.substr(0)));
+ CHECK(!check_for_temporal_macros(no_temporal.substr(1)));
+ CHECK(!check_for_temporal_macros(no_temporal.substr(2)));
+ CHECK(!check_for_temporal_macros(no_temporal.substr(3)));
+ CHECK(!check_for_temporal_macros(no_temporal.substr(4)));
+ CHECK(!check_for_temporal_macros(no_temporal.substr(5)));
+ CHECK(!check_for_temporal_macros(no_temporal.substr(6)));
+ CHECK(!check_for_temporal_macros(no_temporal.substr(7)));
+
+ for (size_t i = 0; i < sizeof(temporal_at_avx_boundary) - 8; ++i) {
+ CHECK(check_for_temporal_macros(temporal_at_avx_boundary.substr(i)));
+ }
+}
+
+TEST_SUITE_END();
+++ /dev/null
-// 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
-
-// This file contains tests for functions in lockfile.c.
-
-#include "../src/ccache.h"
-#include "framework.h"
-#include "util.h"
-
-TEST_SUITE(lockfile)
-
-TEST(acquire_and_release)
-{
- CHECK(lockfile_acquire("test", 1000));
-
-#ifdef _WIN32
- CHECK(path_exists("test.lock"));
-#else
- CHECK(is_symlink("test.lock"));
-#endif
-
- lockfile_release("test");
- CHECK(!path_exists("test.lock"));
-}
-
-#ifndef _WIN32
-
-TEST(lock_breaking)
-{
- char *p;
-
- CHECK_INT_EQ(0, symlink("foo", "test.lock"));
- CHECK_INT_EQ(0, symlink("foo", "test.lock.lock"));
- CHECK(lockfile_acquire("test", 1000));
-
- p = x_readlink("test.lock");
- CHECK(p);
- CHECK(!str_eq(p, "foo"));
- CHECK(!path_exists("test.lock.lock"));
-
- free(p);
-}
-
-TEST(failed_lock_breaking)
-{
- create_file("test.lock", "");
- CHECK(!lockfile_acquire("test", 1000));
-}
-
-#endif // !_WIN32
-
-TEST_SUITE_END
+++ /dev/null
-// 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
-
-// This file contains tests for statistics handling.
-
-#include "../src/ccache.h"
-#include "../src/counters.h"
-#include "framework.h"
-#include "util.h"
-
-TEST_SUITE(stats)
-
-TEST(forward_compatibility)
-{
- unsigned i;
- FILE *f;
- struct counters *counters = counters_init(0);
-
- f = fopen("stats", "w");
- for (i = 0; i < 100; i++) {
- fprintf(f, "%u\n", i);
- }
- fclose(f);
-
- stats_read("stats", counters);
- CHECK_INT_EQ(100, counters->size);
- CHECK_INT_EQ(73, counters->data[73]);
-
- stats_write("stats", counters);
- CHECK_INT_EQ(100, counters->size);
- CHECK_INT_EQ(99, counters->data[99]);
-
- counters_free(counters);
-}
-
-TEST_SUITE_END
+++ /dev/null
-// 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
-
-// This file contains tests for functions in util.c.
-
-#include "../src/ccache.h"
-#include "framework.h"
-
-TEST_SUITE(util)
-
-TEST(basename)
-{
- CHECK_STR_EQ_FREE2("foo.c", basename("foo.c"));
- CHECK_STR_EQ_FREE2("foo.c", basename("dir1/dir2/foo.c"));
- CHECK_STR_EQ_FREE2("foo.c", basename("/dir/foo.c"));
- CHECK_STR_EQ_FREE2("", basename("dir1/dir2/"));
-}
-
-TEST(dirname)
-{
- CHECK_STR_EQ_FREE2(".", dirname("foo.c"));
- CHECK_STR_EQ_FREE2(".", dirname(""));
- CHECK_STR_EQ_FREE2("/", dirname("/"));
- CHECK_STR_EQ_FREE2("/", dirname("/foo.c"));
- CHECK_STR_EQ_FREE2("dir1/dir2", dirname("dir1/dir2/foo.c"));
- CHECK_STR_EQ_FREE2("/dir", dirname("/dir/foo.c"));
- CHECK_STR_EQ_FREE2("dir1/dir2", dirname("dir1/dir2/"));
-}
-
-TEST(common_dir_prefix_length)
-{
- CHECK_INT_EQ(0, common_dir_prefix_length("", ""));
- CHECK_INT_EQ(0, common_dir_prefix_length("/", "/"));
- CHECK_INT_EQ(0, common_dir_prefix_length("/", "/b"));
- CHECK_INT_EQ(0, common_dir_prefix_length("/a", "/b"));
- CHECK_INT_EQ(2, common_dir_prefix_length("/a", "/a"));
- CHECK_INT_EQ(2, common_dir_prefix_length("/a", "/a/b"));
- CHECK_INT_EQ(2, common_dir_prefix_length("/a/b", "/a/c"));
- CHECK_INT_EQ(4, common_dir_prefix_length("/a/b", "/a/b"));
- CHECK_INT_EQ(2, common_dir_prefix_length("/a/bc", "/a/b"));
- CHECK_INT_EQ(2, common_dir_prefix_length("/a/b", "/a/bc"));
-}
-
-TEST(get_relative_path)
-{
-#ifdef _WIN32
- CHECK_STR_EQ_FREE2("a", get_relative_path("C:/doesn't matter", "a"));
- CHECK_STR_EQ_FREE2("a/b", get_relative_path("C:/doesn't matter", "a/b"));
- CHECK_STR_EQ_FREE2(".", get_relative_path("C:/a", "C:/a"));
- CHECK_STR_EQ_FREE2("..", get_relative_path("C:/a/b", "C:/a"));
- CHECK_STR_EQ_FREE2("b", get_relative_path("C:/a", "C:/a/b"));
- CHECK_STR_EQ_FREE2("b/c", get_relative_path("C:/a", "C:/a/b/c"));
- CHECK_STR_EQ_FREE2("../c", get_relative_path("C:/a/b", "C:/a/c"));
- CHECK_STR_EQ_FREE2("../c/d", get_relative_path("C:/a/b", "C:/a/c/d"));
- CHECK_STR_EQ_FREE2("../../c/d", get_relative_path("C:/a/b/c", "C:/a/c/d"));
- CHECK_STR_EQ_FREE2("../..", get_relative_path("C:/a/b", "C:/"));
- CHECK_STR_EQ_FREE2("../../c", get_relative_path("C:/a/b", "C:/c"));
- CHECK_STR_EQ_FREE2("a/b", get_relative_path("C:/", "C:/a/b"));
-#else
- CHECK_STR_EQ_FREE2("a", get_relative_path("/doesn't matter", "a"));
- CHECK_STR_EQ_FREE2("a/b", get_relative_path("/doesn't matter", "a/b"));
- CHECK_STR_EQ_FREE2(".", get_relative_path("/a", "/a"));
- CHECK_STR_EQ_FREE2("..", get_relative_path("/a/b", "/a"));
- CHECK_STR_EQ_FREE2("b", get_relative_path("/a", "/a/b"));
- CHECK_STR_EQ_FREE2("b/c", get_relative_path("/a", "/a/b/c"));
- CHECK_STR_EQ_FREE2("../c", get_relative_path("/a/b", "/a/c"));
- CHECK_STR_EQ_FREE2("../c/d", get_relative_path("/a/b", "/a/c/d"));
- CHECK_STR_EQ_FREE2("../../c/d", get_relative_path("/a/b/c", "/a/c/d"));
- CHECK_STR_EQ_FREE2("../..", get_relative_path("/a/b", "/"));
- CHECK_STR_EQ_FREE2("../../c", get_relative_path("/a/b", "/c"));
- CHECK_STR_EQ_FREE2("a/b", get_relative_path("/", "/a/b"));
-#endif
-}
-
-TEST(format_hash_as_string)
-{
- unsigned char hash[16] = {
- "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
- };
-
- CHECK_STR_EQ_FREE2("00000000000000000000000000000000",
- format_hash_as_string(hash, -1));
- CHECK_STR_EQ_FREE2("00000000000000000000000000000000-0",
- format_hash_as_string(hash, 0));
- hash[0] = 17;
- hash[15] = 42;
- CHECK_STR_EQ_FREE2("1100000000000000000000000000002a-12345",
- format_hash_as_string(hash, 12345));
-}
-
-TEST(subst_env_in_string)
-{
- char *errmsg;
-
- putenv("FOO=bar");
-
- CHECK_STR_EQ_FREE2("bar",
- subst_env_in_string("$FOO", &errmsg));
- CHECK(!errmsg);
-
- errmsg = "";
- CHECK_STR_EQ_FREE2("$",
- subst_env_in_string("$", &errmsg));
- CHECK(!errmsg);
-
- errmsg = "";
- CHECK_STR_EQ_FREE2("bar bar:bar",
- subst_env_in_string("$FOO $FOO:$FOO", &errmsg));
- CHECK(!errmsg);
-
- errmsg = "";
- CHECK_STR_EQ_FREE2("xbar",
- subst_env_in_string("x$FOO", &errmsg));
- CHECK(!errmsg);
-
- errmsg = "";
- CHECK_STR_EQ_FREE2("barx",
- subst_env_in_string("${FOO}x", &errmsg));
- CHECK(!errmsg);
-
- CHECK(!subst_env_in_string("$surelydoesntexist", &errmsg));
- CHECK_STR_EQ_FREE2("environment variable \"surelydoesntexist\" not set",
- errmsg);
-
- CHECK(!subst_env_in_string("${FOO", &errmsg));
- CHECK_STR_EQ_FREE2("syntax error: missing '}' after \"FOO\"", errmsg);
-}
-
-TEST(format_human_readable_size)
-{
- CHECK_STR_EQ_FREE2("0.0 kB", format_human_readable_size(0));
- CHECK_STR_EQ_FREE2("0.0 kB", format_human_readable_size(49));
- CHECK_STR_EQ_FREE2("0.1 kB", format_human_readable_size(50));
- CHECK_STR_EQ_FREE2("42.0 kB", format_human_readable_size(42 * 1000));
- CHECK_STR_EQ_FREE2("1.0 MB", format_human_readable_size(1000 * 1000));
- CHECK_STR_EQ_FREE2("1.2 MB", format_human_readable_size(1234 * 1000));
- CHECK_STR_EQ_FREE2("438.5 MB",
- format_human_readable_size(438.5 * 1000 * 1000));
- CHECK_STR_EQ_FREE2("1.0 GB",
- format_human_readable_size(1000 * 1000 * 1000));
- CHECK_STR_EQ_FREE2("17.1 GB",
- format_human_readable_size(17.11 * 1000 * 1000 * 1000));
-}
-
-TEST(format_parsable_size_with_suffix)
-{
- CHECK_STR_EQ_FREE2("0", format_parsable_size_with_suffix(0));
- CHECK_STR_EQ_FREE2("42.0k", format_parsable_size_with_suffix(42 * 1000));
- CHECK_STR_EQ_FREE2("1.0M", format_parsable_size_with_suffix(1000 * 1000));
- CHECK_STR_EQ_FREE2("1.2M", format_parsable_size_with_suffix(1234 * 1000));
- CHECK_STR_EQ_FREE2("438.5M",
- format_parsable_size_with_suffix(438.5 * 1000 * 1000));
- CHECK_STR_EQ_FREE2("1.0G",
- format_parsable_size_with_suffix(1000 * 1000 * 1000));
- CHECK_STR_EQ_FREE2(
- "17.1G",
- format_parsable_size_with_suffix(17.11 * 1000 * 1000 * 1000));
-}
-
-TEST(parse_size_with_suffix)
-{
- uint64_t size;
- size_t i;
- struct { const char *size; int64_t expected; } sizes[] = {
- {"0", 0},
- {"42", (int64_t)42 * 1000 * 1000 * 1000}, // Default suffix: G
-
- {"78k", 78 * 1000},
- {"78K", 78 * 1000},
- {"1.1 M", 1.1 * 1000 * 1000},
- {"438.55M", 438.55 * 1000 * 1000},
- {"1 G", 1 * 1000 * 1000 * 1000},
- {"2T", (int64_t)2 * 1000 * 1000 * 1000 * 1000},
-
- {"78 Ki", 78 * 1024},
- {"1.1Mi", 1.1 * 1024 * 1024},
- {"438.55 Mi", 438.55 * 1024 * 1024},
- {"1Gi", 1 * 1024 * 1024 * 1024},
- {"2 Ti", (int64_t)2 * 1024 * 1024 * 1024 * 1024},
-
- };
-
- for (i = 0; i < ARRAY_SIZE(sizes); ++i) {
- CHECKM(parse_size_with_suffix(sizes[i].size, &size), sizes[i].size);
- CHECK_INT_EQ(sizes[i].expected, size);
- }
-}
-
-TEST(format_command)
-{
- char *argv[] = {"foo", "bar", NULL};
-
- CHECK_STR_EQ_FREE2("foo bar\n", format_command(argv));
-
-}
-
-TEST_SUITE_END
+++ /dev/null
-// 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 "../src/system.h"
-#include "util.h"
-
-#ifdef _WIN32
-# define lstat(a, b) stat(a, b)
-#endif
-
-bool
-path_exists(const char *path)
-{
- struct stat st;
- return lstat(path, &st) == 0;
-}
-
-void
-create_file(const char *path, const char *content)
-{
- 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);
- }
-}
+++ /dev/null
-#ifndef TEST_UTIL_H
-#define TEST_UTIL_H
-
-#include <stdbool.h>
-
-bool path_exists(const char *path);
-bool is_symlink(const char *path);
-void create_file(const char *path, const char *content);
-
-#endif